File indexing completed on 2024-05-19 04:50:10
0001 /**************************************************************************************** 0002 * Copyright (c) 2007 Nikolaj Hald Nielsen <nhn@kde.org> * 0003 * (c) 2010 Ian Monroe <ian@monroe.nu> * 0004 * (c) 2013 Ralf Engels <ralf-engels@gmx.de> * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify it under * 0007 * the terms of the GNU General Public License as published by the Free Software * 0008 * Foundation; either version 2 of the License, or (at your option) version 3 or * 0009 * any later version accepted by the membership of KDE e.V. (or its successor approved * 0010 * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * 0011 * version 3 of the license. * 0012 * * 0013 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0014 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0015 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0016 * * 0017 * You should have received a copy of the GNU General Public License along with * 0018 * this program. If not, see <http://www.gnu.org/licenses/>. * 0019 ****************************************************************************************/ 0020 0021 0022 #include "AmpacheAccountLogin.h" 0023 0024 #include "core/support/Amarok.h" 0025 #include "core/support/Components.h" 0026 #include "core/support/Debug.h" 0027 0028 #include <QCryptographicHash> 0029 #include <QDomDocument> 0030 #include <QNetworkRequest> 0031 #include <QUrlQuery> 0032 0033 #include <KLocalizedString> 0034 #include <KPasswordDialog> 0035 #include <KMessageBox> 0036 0037 0038 AmpacheAccountLogin::AmpacheAccountLogin( const QUrl& url, const QString& username, const QString& password, QWidget* parent ) 0039 : QObject(parent) 0040 , m_authenticated( false ) 0041 , m_server( url ) 0042 , m_username( username ) 0043 , m_password( password ) 0044 , m_authRequest( nullptr ) 0045 , m_pingRequest( nullptr ) 0046 { 0047 reauthenticate(); 0048 } 0049 0050 0051 AmpacheAccountLogin::~AmpacheAccountLogin() 0052 { 0053 } 0054 0055 void 0056 AmpacheAccountLogin::reauthenticate() 0057 { 0058 DEBUG_BLOCK 0059 0060 // We need to check the version of Ampache we are attempting to authenticate against, as this changes how we deal with it 0061 QUrl url = getRequestUrl( "ping" ); 0062 0063 debug() << "Verifying Ampache Version Using: " << url.url(); 0064 0065 m_pingRequest = The::networkAccessManager()->getData( url, this, &AmpacheAccountLogin::authenticate ); 0066 0067 if( !m_pingRequest ) 0068 Q_EMIT finished(); 0069 } 0070 0071 void 0072 AmpacheAccountLogin::authenticate( const QUrl &requestUrl, const QByteArray &data, const NetworkAccessManagerProxy::Error &e ) 0073 { 0074 if( !m_pingRequest ) 0075 return; 0076 0077 DEBUG_BLOCK 0078 Q_UNUSED( requestUrl ); 0079 0080 QDomDocument doc; 0081 doc.setContent( data ); 0082 0083 if( !generalVerify( m_pingRequest, doc, e ) ) 0084 return; 0085 0086 // so lets figure out what we got here: 0087 debug() << "Version reply: " << data; 0088 int version = getVersion( doc ); 0089 0090 QUrl url = getRequestUrl( "handshake" ); 0091 QUrlQuery query( url ); 0092 QString timestamp = QString::number( QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000 ); 0093 QString passPhrase; 0094 0095 // We need to use different authentication strings depending on the version of ampache 0096 if( version > 350000 ) 0097 { 0098 debug() << "New Password Scheme " << version; 0099 query.addQueryItem( "version", "350001" ); 0100 0101 QCryptographicHash sha256Hash( QCryptographicHash::Sha256 ); 0102 sha256Hash.addData( m_password.toUtf8() ); 0103 QString hashedPassword = sha256Hash.result().toHex(); 0104 0105 QString rawHandshake = timestamp + hashedPassword; 0106 sha256Hash.reset(); 0107 sha256Hash.addData( rawHandshake.toUtf8() ); 0108 0109 passPhrase = sha256Hash.result().toHex(); 0110 0111 } 0112 else 0113 { 0114 debug() << "Version Older than 35001 Generated MD5 Auth " << version; 0115 0116 QString rawHandshake = timestamp + m_password; 0117 QCryptographicHash md5Hash( QCryptographicHash::Md5 ); 0118 0119 md5Hash.addData( rawHandshake.toUtf8() ); 0120 passPhrase = md5Hash.result().toHex(); 0121 } 0122 0123 query.addQueryItem( "timestamp", timestamp ); 0124 query.addQueryItem( "auth", passPhrase ); 0125 url.setQuery( query ); 0126 0127 debug() << "Authenticating with string: " << url.url() << passPhrase; 0128 0129 // TODO: Amarok::Logger::newProgressOperation( m_xmlDownloadJob, i18n( "Authenticating with Ampache" ) ); 0130 m_authRequest = The::networkAccessManager()->getData( url, this, &AmpacheAccountLogin::authenticationComplete ); 0131 0132 if( !m_authRequest ) 0133 Q_EMIT finished(); 0134 } 0135 0136 void AmpacheAccountLogin::authenticationComplete( const QUrl &requestUrl, const QByteArray &data, const NetworkAccessManagerProxy::Error &e ) 0137 { 0138 Q_UNUSED( requestUrl ); 0139 0140 if( !m_authRequest ) 0141 return; 0142 0143 DEBUG_BLOCK 0144 0145 QDomDocument doc; 0146 doc.setContent( data ); 0147 0148 if( !generalVerify( m_authRequest, doc, e ) ) 0149 return; 0150 0151 // so lets figure out what we got here: 0152 debug() << "Authentication reply: " << data; 0153 QDomElement root = doc.firstChildElement("root"); 0154 0155 //find status code: 0156 QDomElement element = root.firstChildElement("auth"); 0157 if( element.isNull() ) 0158 { 0159 // Default the Version down if it didn't work 0160 debug() << "authenticationComplete failed"; 0161 KMessageBox::error( qobject_cast<QWidget*>(parent()), 0162 i18n( "Authentication failed." ), 0163 i18n( "Authentication Error" ) ); 0164 Q_EMIT finished(); 0165 return; 0166 } 0167 0168 m_sessionId = element.text(); 0169 m_authenticated = true; 0170 0171 Q_EMIT loginSuccessful(); 0172 Q_EMIT finished(); 0173 } 0174 0175 int 0176 AmpacheAccountLogin::getVersion( const QDomDocument& doc ) const 0177 { 0178 DEBUG_BLOCK 0179 0180 QDomElement root = doc.firstChildElement("root"); 0181 //is this an error? 0182 QDomElement error = root.firstChildElement("error"); 0183 //find status code: 0184 QDomElement version = root.firstChildElement("version"); 0185 0186 // It's OK if we get a null response from the version, that just means we're dealing with an older version 0187 if( !error.isNull() ) 0188 { 0189 // Default the Version down if it didn't work 0190 debug() << "getVersion error: " << error.text(); 0191 return 100000; 0192 } 0193 else if( !version.isNull() ) 0194 { 0195 debug() << "getVersion returned: " << version.text(); 0196 return version.text().toInt(); 0197 } 0198 else 0199 { 0200 debug() << "getVersion no version"; 0201 return 0; 0202 } 0203 } 0204 0205 bool 0206 AmpacheAccountLogin::generalVerify( QNetworkReply *reply, const QDomDocument& doc, const NetworkAccessManagerProxy::Error &e ) 0207 { 0208 Q_ASSERT( reply ); 0209 0210 if( reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() != 200 ) 0211 { 0212 debug() << "server response code:" << 0213 reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() << 0214 reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString(); 0215 0216 Q_EMIT finished(); 0217 return false; 0218 } 0219 0220 if( e.code != QNetworkReply::NoError ) 0221 { 0222 debug() << "authenticate Error:" << e.description; 0223 Q_EMIT finished(); 0224 return false; 0225 } 0226 0227 QDomElement root = doc.firstChildElement("root"); 0228 QDomElement error = root.firstChildElement("error"); 0229 0230 if( !error.isNull() ) 0231 { 0232 // Default the Version down if it didn't work 0233 debug() << "generalVerify error: " << error.text(); 0234 KMessageBox::error( qobject_cast<QWidget*>(parent()), error.text(), i18n( "Authentication Error" ) ); 0235 Q_EMIT finished(); 0236 return false; 0237 } 0238 0239 return true; 0240 } 0241 0242 QUrl 0243 AmpacheAccountLogin::getRequestUrl( const QString &action ) const 0244 { 0245 //lets keep this around for now if we want to allow people to add a service that prompts for stuff 0246 /* But comment it out since the AmpacheQueryMaker does not do this 0247 if ( m_server.isEmpty() || m_password.isEmpty() ) 0248 { 0249 KPasswordDialog dlg( 0 , KPasswordDialog::ShowUsernameLine ); 0250 dlg.setPrompt( i18n( "Enter the server name and a password" ) ); 0251 if( !dlg.exec() ) 0252 return QUrl(); //the user canceled 0253 0254 m_server = QUrl( dlg.username() ).url(); 0255 m_password = dlg.password(); 0256 } 0257 */ 0258 0259 QUrl url = m_server; 0260 url.setPath( m_server.path() + "/server/xml.server.php" ); 0261 QString scheme = m_server.scheme(); 0262 0263 if( scheme != "http" && scheme != "https" ) 0264 url.setScheme( "http" ); 0265 0266 QUrlQuery query( m_server ); 0267 0268 if( !action.isEmpty() ) 0269 query.addQueryItem( "action", action ); 0270 0271 if( !m_username.isEmpty() && action != "ping" ) 0272 query.addQueryItem( "user", m_username ); 0273 0274 if( !m_sessionId.isEmpty() && action == "ping" ) 0275 query.addQueryItem( "auth", m_sessionId ); 0276 0277 url.setQuery( query ); 0278 0279 return url; 0280 } 0281