LatitudeUpdater 0.1, fix the previous commit
[googlelatitude] / libkqoauth / kqoauthmanager.cpp
1 /**
2  * KQOAuth - An OAuth authentication library for Qt.
3  *
4  * Author: Johan Paul (johan.paul@d-pointer.com)
5  *         http://www.d-pointer.com
6  *
7  *  KQOAuth is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  KQOAuth is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with KQOAuth.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 #include <QtCore>
21 // #include <QDesktopServices>
22
23 #include "kqoauthmanager.h"
24 #include "kqoauthmanager_p.h"
25
26
27 ////////////// Private d_ptr implementation ////////////////
28
29 KQOAuthManagerPrivate::KQOAuthManagerPrivate(KQOAuthManager *parent) :
30     error(KQOAuthManager::NoError) ,
31     r(0) ,
32     opaqueRequest(new KQOAuthRequest) ,
33     q_ptr(parent) ,
34     callbackServer(new KQOAuthAuthReplyServer(parent)) ,
35     isVerified(false) ,
36     isAuthorized(false) ,
37     autoAuth(false),
38     networkManager(new QNetworkAccessManager),
39     managerUserSet(false)
40 {
41
42 }
43
44 KQOAuthManagerPrivate::~KQOAuthManagerPrivate() {
45     delete opaqueRequest;
46     opaqueRequest = 0;
47
48     if (!managerUserSet) {
49         delete networkManager;
50         networkManager = 0;
51     }
52 }
53
54 QList< QPair<QString, QString> > KQOAuthManagerPrivate::createQueryParams(const KQOAuthParameters &requestParams) {
55     QList<QString> requestKeys = requestParams.keys();
56     QList<QString> requestValues = requestParams.values();
57
58     QList< QPair<QString, QString> > result;
59     for(int i=0; i<requestKeys.size(); i++) {
60         result.append( qMakePair(requestKeys.at(i),
61                                  requestValues.at(i))
62                       );
63     }
64
65     return result;
66 }
67
68 QMultiMap<QString, QString> KQOAuthManagerPrivate::createTokensFromResponse(QByteArray reply) {
69     QMultiMap<QString, QString> result;
70     QString replyString(reply);
71
72     QStringList parameterPairs = replyString.split('&', QString::SkipEmptyParts);
73     foreach (const QString &parameterPair, parameterPairs) {
74         QStringList parameter = parameterPair.split('=');
75         result.insert(parameter.value(0), parameter.value(1));
76     }
77
78     return result;
79 }
80
81 bool KQOAuthManagerPrivate::setSuccessfulRequestToken(const QMultiMap<QString, QString> &request) {
82     if (currentRequestType == KQOAuthRequest::TemporaryCredentials) {
83         hasTemporaryToken = (!QString(request.value("oauth_token")).isEmpty() && !QString(request.value("oauth_token_secret")).isEmpty());
84     } else {
85         return false;
86     }
87
88     if (hasTemporaryToken) {
89         requestToken = QUrl::fromPercentEncoding( QString(request.value("oauth_token")).toLocal8Bit() );
90         requestTokenSecret =  QUrl::fromPercentEncoding( QString(request.value("oauth_token_secret")).toLocal8Bit() );
91     }
92
93     return hasTemporaryToken;
94 }
95
96 bool KQOAuthManagerPrivate::setSuccessfulAuthorized(const QMultiMap<QString, QString> &request ) {
97     if (currentRequestType == KQOAuthRequest::AccessToken) {
98         isAuthorized = (!QString(request.value("oauth_token")).isEmpty() && !QString(request.value("oauth_token_secret")).isEmpty());
99     } else {
100         return false;
101     }
102
103     if (isAuthorized) {
104         requestToken = QUrl::fromPercentEncoding( QString(request.value("oauth_token")).toLocal8Bit() );
105         requestTokenSecret =  QUrl::fromPercentEncoding( QString(request.value("oauth_token_secret")).toLocal8Bit() );
106     }
107
108     return isAuthorized;
109 }
110
111 void KQOAuthManagerPrivate::emitTokens() {
112     Q_Q(KQOAuthManager);
113
114     if (this->requestToken.isEmpty() || this->requestTokenSecret.isEmpty()) {
115         error = KQOAuthManager::RequestUnauthorized;
116     }
117
118     if (currentRequestType == KQOAuthRequest::TemporaryCredentials) {
119         // Signal that we are ready to use the protected resources.
120         emit q->temporaryTokenReceived(this->requestToken, this->requestTokenSecret);
121     }
122
123     if (currentRequestType == KQOAuthRequest::AccessToken) {
124         // Signal that we are ready to use the protected resources.
125         emit q->accessTokenReceived(this->requestToken, this->requestTokenSecret);
126     }
127
128     emit q->receivedToken(this->requestToken, this->requestTokenSecret);
129 }
130
131 bool KQOAuthManagerPrivate::setupCallbackServer() {
132     return callbackServer->listen();
133 }
134
135
136 /////////////// Public implementation ////////////////
137
138 KQOAuthManager::KQOAuthManager(QObject *parent) :
139     QObject(parent) ,
140     d_ptr(new KQOAuthManagerPrivate(this))
141 {
142
143 }
144
145 KQOAuthManager::~KQOAuthManager()
146 {
147     delete d_ptr;
148 }
149
150 void KQOAuthManager::executeRequest(KQOAuthRequest *request) {
151     Q_D(KQOAuthManager);
152
153     d->r = request;
154
155     if (request == 0) {
156         qWarning() << "Request is NULL. Cannot proceed.";
157         d->error = KQOAuthManager::RequestError;
158         return;
159     }
160
161     if (!request->requestEndpoint().isValid()) {
162         qWarning() << "Request endpoint URL is not valid. Cannot proceed.";
163         d->error = KQOAuthManager::RequestEndpointError;
164         return;
165     }
166
167     if (!request->isValid()) {
168         qWarning() << "Request is not valid. Cannot proceed.";
169         d->error = KQOAuthManager::RequestValidationError;
170         return;
171     }
172
173     d->currentRequestType = request->requestType();
174
175     QNetworkRequest networkRequest;
176     networkRequest.setUrl( request->requestEndpoint() );
177
178     if (d->autoAuth && d->currentRequestType == KQOAuthRequest::TemporaryCredentials) {
179         d->setupCallbackServer();
180         connect(d->callbackServer, SIGNAL(verificationReceived(QMultiMap<QString, QString>)),
181                 this, SLOT( onVerificationReceived(QMultiMap<QString, QString>)));
182
183         QString serverString = "http://localhost:";
184         serverString.append(QString::number(d->callbackServer->serverPort()));
185         request->setCallbackUrl(QUrl(serverString));
186     }
187
188     // And now fill the request with "Authorization" header data.
189     QList<QByteArray> requestHeaders = request->requestParameters();
190     QByteArray authHeader;
191
192     bool first = true;
193     foreach (const QByteArray header, requestHeaders) {
194         if (!first) {
195             authHeader.append(", ");
196         } else {
197             authHeader.append("OAuth ");
198             first = false;
199         }
200
201         authHeader.append(header);
202     }
203     networkRequest.setRawHeader("Authorization", authHeader);
204
205     connect(d->networkManager, SIGNAL(finished(QNetworkReply *)),
206             this, SLOT(onRequestReplyReceived(QNetworkReply *)));
207
208     if (request->httpMethod() == KQOAuthRequest::GET) {
209         // Get the requested additional params as a list of pairs we can give QUrl
210         QList< QPair<QString, QString> > urlParams = d->createQueryParams(request->additionalParameters());
211
212         // Take the original URL and append the query params to it.
213         QUrl urlWithParams = networkRequest.url();
214         urlWithParams.setQueryItems(urlParams);
215         networkRequest.setUrl(urlWithParams);
216
217         // Submit the request including the params.
218         QNetworkReply *reply = d->networkManager->get(networkRequest);
219         connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
220                  this, SLOT(slotError(QNetworkReply::NetworkError)));
221
222     } else if (request->httpMethod() == KQOAuthRequest::POST) {
223
224         networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, request->contentType());
225
226         qDebug() << networkRequest.rawHeaderList();
227         qDebug() << networkRequest.rawHeader("Authorization");
228         qDebug() << networkRequest.rawHeader("Content-Type");
229
230         QNetworkReply *reply;
231         if (request->contentType() == "application/x-www-form-urlencoded") {
232           reply = d->networkManager->post(networkRequest, request->requestBody());
233         } else {
234           reply = d->networkManager->post(networkRequest, request->rawData());
235         }
236
237         connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
238                  this, SLOT(slotError(QNetworkReply::NetworkError)));
239     }
240
241     d->r->requestTimerStart();
242 }
243
244
245 void KQOAuthManager::setHandleUserAuthorization(bool set) {
246     Q_D(KQOAuthManager);
247
248     d->autoAuth = set;
249 }
250
251 bool KQOAuthManager::hasTemporaryToken() {
252     Q_D(KQOAuthManager);
253
254     return d->hasTemporaryToken;
255 }
256
257 bool KQOAuthManager::isVerified() {
258     Q_D(KQOAuthManager);
259
260     return d->isVerified;
261 }
262
263 bool KQOAuthManager::isAuthorized() {
264     Q_D(KQOAuthManager);
265
266     return d->isAuthorized;
267 }
268
269 KQOAuthManager::KQOAuthError KQOAuthManager::lastError() {
270     Q_D(KQOAuthManager);
271
272     return d->error;
273 }
274
275 void KQOAuthManager::setNetworkManager(QNetworkAccessManager *manager) {
276     Q_D(KQOAuthManager);
277
278     if (manager == 0) {
279         d->error = KQOAuthManager::ManagerError;
280         return;
281     }
282
283     if (!d->managerUserSet) {
284         delete d->networkManager;
285     }
286
287     d->managerUserSet = true;
288     d->networkManager = manager;
289 }
290
291 QNetworkAccessManager * KQOAuthManager::networkManager() const {
292     Q_D(const KQOAuthManager);
293
294     if (d->managerUserSet) {
295         return d->networkManager;
296     } else {
297         return NULL;
298     }
299
300 }
301
302
303 //////////// Public convenience API /////////////
304
305 QUrl KQOAuthManager::getUserAuthorization(QUrl authorizationEndpoint) {
306     Q_D(KQOAuthManager);
307
308     if (!d->hasTemporaryToken) {
309         qWarning() << "No temporary tokens retreieved. Cannot get user authorization.";
310         d->error = KQOAuthManager::RequestUnauthorized;
311         return QString();
312     }
313
314     if (!authorizationEndpoint.isValid()) {
315         qWarning() << "Authorization endpoint not valid. Cannot proceed.";
316         d->error = KQOAuthManager::RequestEndpointError;
317         return QString();
318     }
319
320     d->error = KQOAuthManager::NoError;
321
322     QPair<QString, QString> tokenParam = qMakePair(QString("oauth_token"), QString(d->requestToken));
323     QUrl openWebPageUrl(authorizationEndpoint.toString(), QUrl::StrictMode);
324     openWebPageUrl.addQueryItem(tokenParam.first, tokenParam.second);
325
326     // Open the user's default browser to the resource authorization page provided
327     // by the service.
328     // QDesktopServices::openUrl(openWebPageUrl);
329     qDebug() << "* KQOAuthManager::getUserAuthorization -> QDesktopServices::openUrl" << openWebPageUrl;
330     return openWebPageUrl;
331 }
332
333 void KQOAuthManager::getUserAccessTokens(QUrl accessTokenEndpoint) {
334     Q_D(KQOAuthManager);
335
336     if (!d->isVerified) {
337         qWarning() << "Not verified. Cannot get access tokens.";
338         d->error = KQOAuthManager::RequestUnauthorized;
339         return;
340     }
341
342     if (!accessTokenEndpoint.isValid()) {
343         qWarning() << "Endpoint for access token exchange is not valid. Cannot proceed.";
344         d->error = KQOAuthManager::RequestEndpointError;
345         return;
346     }
347
348     d->error = KQOAuthManager::NoError;
349
350     d->opaqueRequest->clearRequest();
351     d->opaqueRequest->initRequest(KQOAuthRequest::AccessToken, accessTokenEndpoint);
352     d->opaqueRequest->setToken(d->requestToken);
353     d->opaqueRequest->setTokenSecret(d->requestTokenSecret);
354     d->opaqueRequest->setVerifier(d->requestVerifier);
355     d->opaqueRequest->setConsumerKey(d->consumerKey);
356     d->opaqueRequest->setConsumerSecretKey(d->consumerKeySecret);
357
358     executeRequest(d->opaqueRequest);
359 }
360
361 void KQOAuthManager::sendAuthorizedRequest(QUrl requestEndpoint, const KQOAuthParameters &requestParameters) {
362     Q_D(KQOAuthManager);
363
364     if (!d->isAuthorized) {
365         qWarning() << "No access tokens retrieved. Cannot send authorized requests.";
366         d->error = KQOAuthManager::RequestUnauthorized;
367         return;
368     }
369
370     if (!requestEndpoint.isValid()) {
371         qWarning() << "Endpoint for authorized request is not valid. Cannot proceed.";
372         d->error = KQOAuthManager::RequestEndpointError;
373         return;
374     }
375
376     d->error = KQOAuthManager::NoError;
377
378     d->opaqueRequest->clearRequest();
379     d->opaqueRequest->initRequest(KQOAuthRequest::AuthorizedRequest, requestEndpoint);
380     d->opaqueRequest->setAdditionalParameters(requestParameters);
381     d->opaqueRequest->setToken(d->requestToken);
382     d->opaqueRequest->setTokenSecret(d->requestTokenSecret);
383     d->opaqueRequest->setConsumerKey(d->consumerKey);
384     d->opaqueRequest->setConsumerSecretKey(d->consumerKeySecret);
385
386     executeRequest(d->opaqueRequest);
387 }
388
389
390 /////////////// Private slots //////////////////
391
392 void KQOAuthManager::onRequestReplyReceived( QNetworkReply *reply ) {
393     Q_D(KQOAuthManager);
394
395     QNetworkReply::NetworkError networkError = reply->error();
396     switch (networkError) {
397     case QNetworkReply::NoError:
398         d->error = KQOAuthManager::NoError;
399         break;
400
401     case QNetworkReply::ContentAccessDenied:
402     case QNetworkReply::AuthenticationRequiredError:
403         d->error = KQOAuthManager::RequestUnauthorized;
404         break;
405
406     default:
407         d->error = KQOAuthManager::NetworkError;
408         break;
409     }
410
411     // Read the content of the reply from the network.
412     QByteArray networkReply = reply->readAll();
413
414     // Stop any timer we have set on the request.
415     d->r->requestTimerStop();
416
417     // Just don't do anything if we didn't get anything useful.
418     if(networkReply.isEmpty()) {
419         reply->deleteLater();
420         return;
421     }
422     QMultiMap<QString, QString> responseTokens;
423
424     // We need to emit the signal even if we got an error.
425     if (d->error != KQOAuthManager::NoError) {
426         reply->deleteLater();
427         emit requestReady(networkReply);
428         d->emitTokens();
429         return;
430     }
431
432     responseTokens = d->createTokensFromResponse(networkReply);
433     d->opaqueRequest->clearRequest();
434     d->opaqueRequest->setHttpMethod(KQOAuthRequest::POST);   // XXX FIXME: Convenient API does not support GET
435     if (!d->isAuthorized || !d->isVerified) {
436         if (d->setSuccessfulRequestToken(responseTokens)) {
437             qDebug() << "Successfully got request tokens.";
438             d->consumerKey = d->r->consumerKeyForManager();
439             d->consumerKeySecret = d->r->consumerKeySecretForManager();
440             d->opaqueRequest->setSignatureMethod(KQOAuthRequest::HMAC_SHA1);
441             d->opaqueRequest->setCallbackUrl(d->r->callbackUrlForManager());
442
443             d->emitTokens();
444
445         } else if (d->setSuccessfulAuthorized(responseTokens)) {
446               qDebug() << "Successfully got access tokens.";
447               d->opaqueRequest->setSignatureMethod(KQOAuthRequest::HMAC_SHA1);
448
449               d->emitTokens();
450           } else if (d->currentRequestType == KQOAuthRequest::AuthorizedRequest) {
451                 emit authorizedRequestDone();
452             }
453     }
454
455     emit requestReady(networkReply);
456
457     reply->deleteLater();           // We need to clean this up, after the event processing is done.
458 }
459
460 void KQOAuthManager::onVerificationReceived(QMultiMap<QString, QString> response) {
461     Q_D(KQOAuthManager);
462
463     QString token = response.value("oauth_token");
464     QString verifier = response.value("oauth_verifier");
465     if (verifier.isEmpty()) {
466         d->error = KQOAuthManager::RequestUnauthorized;
467     }
468
469     verifier = QUrl::fromPercentEncoding(verifier.toUtf8());     // We get the raw URL response here so we need to convert it back
470                                                                  // to plain string so we can percent encode it again later in requests.
471
472     if (d->error == KQOAuthManager::NoError) {
473         d->requestVerifier = verifier;
474         d->isVerified = true;
475     }
476
477     emit authorizationReceived(token, verifier);
478 }
479
480 void KQOAuthManager::slotError(QNetworkReply::NetworkError error) {
481     Q_UNUSED(error)
482     Q_D(KQOAuthManager);
483
484     d->error = KQOAuthManager::NetworkError;
485     QByteArray emptyResponse;
486     emit requestReady(emptyResponse);
487     emit authorizedRequestDone();
488
489     QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
490     reply->deleteLater();
491 }
492