changes for formeego, do not use
[googlelatitude] / libkqoauth / kqoauthrequest.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 <QByteArray>
21 #include <QDateTime>
22 #include <QCryptographicHash>
23 #include <QPair>
24 #include <QStringList>
25
26 #include <QtDebug>
27 #include <QtAlgorithms>
28
29 #include "kqoauthrequest.h"
30 #include "kqoauthrequest_p.h"
31 #include "kqoauthutils.h"
32 #include "kqoauthglobals.h"
33
34
35 //////////// Private d_ptr implementation /////////
36
37 KQOAuthRequestPrivate::KQOAuthRequestPrivate() :
38     timeout(0)
39 {
40
41 }
42
43 KQOAuthRequestPrivate::~KQOAuthRequestPrivate()
44 {
45
46 }
47
48 // This method will not include the "oauthSignature" paramater, since it is calculated from these parameters.
49 void KQOAuthRequestPrivate::prepareRequest() {
50
51     // If parameter list is not empty, we don't want to insert these values by
52     // accident a second time. So giving up.
53     if( !requestParameters.isEmpty() ) {
54         return;
55     }
56
57     switch ( requestType ) {
58     case KQOAuthRequest::TemporaryCredentials:
59         requestParameters.append( qMakePair( OAUTH_KEY_CALLBACK, oauthCallbackUrl.toString()) );  // This is so ugly that it is almost beautiful.
60         requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod) );
61         requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
62         requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
63         requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
64         requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
65         break;
66
67     case KQOAuthRequest::AccessToken:
68         requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
69         requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
70         requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
71         requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
72         requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
73         requestParameters.append( qMakePair( OAUTH_KEY_VERIFIER, oauthVerifier ));
74         requestParameters.append( qMakePair( OAUTH_KEY_TOKEN, oauthToken ));
75         break;
76
77     case KQOAuthRequest::AuthorizedRequest:
78         requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
79         requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
80         requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
81         requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
82         requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
83         requestParameters.append( qMakePair( OAUTH_KEY_TOKEN, oauthToken ));
84         break;
85
86     default:
87         break;
88     }
89 }
90
91 void KQOAuthRequestPrivate::signRequest() {
92     QString signature = this->oauthSignature();
93     requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE, signature) );
94 }
95
96 QString KQOAuthRequestPrivate::oauthSignature()  {
97     /**
98      * http://oauth.net/core/1.0/#anchor16
99      * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] where the
100      * Signature Base String is the text and the key is the concatenated values (each first encoded per Parameter
101      * Encoding) of the Consumer Secret and Token Secret, separated by an ‘&’ character (ASCII code 38) even if empty.
102      **/
103     QByteArray baseString = this->requestBaseString();
104
105     QString secret = QString(QUrl::toPercentEncoding(oauthConsumerSecretKey)) + "&" + QString(QUrl::toPercentEncoding(oauthTokenSecret));
106     QString signature = KQOAuthUtils::hmac_sha1(baseString, secret);
107
108     if (debugOutput) {
109         qDebug() << "========== KQOAuthRequest has the following signature:";
110         qDebug() << " * Signature : " << QUrl::toPercentEncoding(signature) << "\n";
111     }
112     return QString( QUrl::toPercentEncoding(signature) );
113 }
114
115 bool normalizedParameterSort(const QPair<QString, QString> &left, const QPair<QString, QString> &right) {
116     QString keyLeft = left.first;
117     QString valueLeft = left.second;
118     QString keyRight = right.first;
119     QString valueRight = right.second;
120
121     if(keyLeft == keyRight) {
122         return (valueLeft < valueRight);
123     } else {
124         return (keyLeft < keyRight);
125     }
126 }
127 QByteArray KQOAuthRequestPrivate::requestBaseString() {
128     QByteArray baseString;
129
130     // Every request has these as the commont parameters.
131     baseString.append( oauthHttpMethodString.toUtf8() + "&");                                                     // HTTP method
132     baseString.append( QUrl::toPercentEncoding( oauthRequestEndpoint.toString(QUrl::RemoveQuery) ) + "&" ); // The path and query components
133
134     QList< QPair<QString, QString> > baseStringParameters;
135     baseStringParameters.append(requestParameters);
136     baseStringParameters.append(additionalParameters);
137
138     // Sort the request parameters. These parameters have been
139     // initialized earlier.
140     qSort(baseStringParameters.begin(),
141           baseStringParameters.end(),
142           normalizedParameterSort
143           );
144
145     // Last append the request parameters correctly encoded.
146     baseString.append( encodedParamaterList(baseStringParameters) );
147
148     if (debugOutput) {
149         qDebug() << "========== KQOAuthRequest has the following base string:";
150         qDebug() << baseString << "\n";
151     }
152
153     return baseString;
154 }
155
156 QByteArray KQOAuthRequestPrivate::encodedParamaterList(const QList< QPair<QString, QString> > &parameters) {
157     QByteArray resultList;
158
159     bool first = true;
160     QPair<QString, QString> parameter;
161
162     // Do the debug output.
163     if (debugOutput) {
164         qDebug() << "========== KQOAuthRequest has the following parameters:";
165     }
166     foreach (parameter, parameters) {
167         if(!first) {
168             resultList.append( "&" );
169         } else {
170             first = false;
171         }
172
173         // Here we don't need to explicitely encode the strings to UTF-8 since
174         // QUrl::toPercentEncoding() takes care of that for us.
175         resultList.append( QUrl::toPercentEncoding(parameter.first)     // Parameter key
176                            + "="
177                            + QUrl::toPercentEncoding(parameter.second)  // Parameter value
178                           );
179         if (debugOutput) {
180             qDebug() << " * "
181                      << parameter.first
182                      << " : "
183                      << parameter.second;
184         }
185     }
186     if (debugOutput) {
187         qDebug() << "\n";
188     }
189
190     return QUrl::toPercentEncoding(resultList);
191 }
192
193 QString KQOAuthRequestPrivate::oauthTimestamp() const {
194     // This is basically for unit tests only. In most cases we don't set the nonce beforehand.
195     if (!oauthTimestamp_.isEmpty()) {
196         return oauthTimestamp_;
197     }
198
199 #if QT_VERSION >= 0x040700
200     return QString::number(QDateTime::currentDateTimeUtc().toTime_t());
201 #else
202    return QString::number(QDateTime::currentDateTime().toUTC().toTime_t());
203 #endif
204
205 }
206
207 QString KQOAuthRequestPrivate::oauthNonce() const {
208     // This is basically for unit tests only. In most cases we don't set the nonce beforehand.
209     if (!oauthNonce_.isEmpty()) {
210         return oauthNonce_;
211     }
212
213     return QString::number(qrand());
214 }
215
216 bool KQOAuthRequestPrivate::validateRequest() const {
217     switch ( requestType ) {
218     case KQOAuthRequest::TemporaryCredentials:
219         if (oauthRequestEndpoint.isEmpty()
220             || oauthConsumerKey.isEmpty()
221             || oauthNonce_.isEmpty()
222             || oauthSignatureMethod.isEmpty()
223             || oauthTimestamp_.isEmpty()
224             || oauthVersion.isEmpty())
225         {
226             return false;
227         }
228         return true;
229
230     case KQOAuthRequest::AccessToken:
231         if (oauthRequestEndpoint.isEmpty()
232             || oauthVerifier.isEmpty()
233             || oauthConsumerKey.isEmpty()
234             || oauthNonce_.isEmpty()
235             || oauthSignatureMethod.isEmpty()
236             || oauthTimestamp_.isEmpty()
237             || oauthToken.isEmpty()
238             || oauthTokenSecret.isEmpty()
239             || oauthVersion.isEmpty())
240         {
241             return false;
242         }
243         return true;
244
245     case KQOAuthRequest::AuthorizedRequest:
246         if (oauthRequestEndpoint.isEmpty()
247             || oauthConsumerKey.isEmpty()
248             || oauthNonce_.isEmpty()
249             || oauthSignatureMethod.isEmpty()
250             || oauthTimestamp_.isEmpty()
251             || oauthToken.isEmpty()
252             || oauthTokenSecret.isEmpty()
253             || oauthVersion.isEmpty())
254         {
255             return false;
256         }
257         return true;
258
259     default:
260         return false;
261     }
262
263     // We should not come here.
264     return false;
265 }
266
267 //////////// Public implementation ////////////////
268
269 KQOAuthRequest::KQOAuthRequest(QObject *parent) :
270     QObject(parent),
271     d_ptr(new KQOAuthRequestPrivate)
272 {
273     d_ptr->debugOutput = false;  // No debug output by default.
274     qsrand(QTime::currentTime().msec());  // We need to seed the nonce random number with something.
275                                           // However, we cannot do this while generating the nonce since
276                                           // we might get the same seed. So initializing here should be fine.
277 }
278
279 KQOAuthRequest::~KQOAuthRequest()
280 {
281     delete d_ptr;
282 }
283
284 void KQOAuthRequest::initRequest(KQOAuthRequest::RequestType type, const QUrl &requestEndpoint) {
285     Q_D(KQOAuthRequest);
286
287     if (!requestEndpoint.isValid()) {
288         qWarning() << "Endpoint URL is not valid. Ignoring. This request might not work.";
289         return;
290     }
291
292     if (type < 0 || type > KQOAuthRequest::AuthorizedRequest) {
293         qWarning() << "Invalid request type. Ignoring. This request might not work.";
294         return;
295     }
296
297     // Clear the request
298     clearRequest();
299
300     // Set smart defaults.
301     d->requestType = type;
302     d->oauthRequestEndpoint = requestEndpoint;
303     d->oauthTimestamp_ = d->oauthTimestamp();
304     d->oauthNonce_ = d->oauthNonce();
305     this->setSignatureMethod(KQOAuthRequest::HMAC_SHA1);
306     this->setHttpMethod(KQOAuthRequest::POST);
307     d->oauthVersion = "1.0"; // Currently supports only version 1.0
308
309     d->contentType = "application/x-www-form-urlencoded";
310 }
311
312 void KQOAuthRequest::setConsumerKey(const QString &consumerKey) {
313     Q_D(KQOAuthRequest);
314     d->oauthConsumerKey = consumerKey;
315 }
316
317 void KQOAuthRequest::setConsumerSecretKey(const QString &consumerSecretKey) {
318     Q_D(KQOAuthRequest);
319     d->oauthConsumerSecretKey = consumerSecretKey;
320 }
321
322 void KQOAuthRequest::setCallbackUrl(const QUrl &callbackUrl) {
323     Q_D(KQOAuthRequest);
324
325     d->oauthCallbackUrl = callbackUrl;
326 }
327
328 void KQOAuthRequest::setSignatureMethod(KQOAuthRequest::RequestSignatureMethod requestMethod) {
329     Q_D(KQOAuthRequest);
330     QString requestMethodString;
331
332     switch (requestMethod) {
333     case KQOAuthRequest::PLAINTEXT:
334         requestMethodString = "PLAINTEXT";
335         break;
336     case KQOAuthRequest::HMAC_SHA1:
337         requestMethodString = "HMAC-SHA1";
338         break;
339     case KQOAuthRequest::RSA_SHA1:
340         requestMethodString = "RSA-SHA1";
341         break;
342     default:
343         // We should not come here
344         qWarning() << "Invalid signature method set.";
345         break;
346     }
347
348     d->oauthSignatureMethod = requestMethodString;
349 }
350
351 void KQOAuthRequest::setTokenSecret(const QString &tokenSecret) {
352     Q_D(KQOAuthRequest);
353
354     d->oauthTokenSecret = tokenSecret;
355 }
356
357 void KQOAuthRequest::setToken(const QString &token) {
358     Q_D(KQOAuthRequest);
359
360     d->oauthToken = token;
361 }
362
363 void KQOAuthRequest::setVerifier(const QString &verifier) {
364     Q_D(KQOAuthRequest);
365
366     d->oauthVerifier = verifier;
367 }
368
369
370 void KQOAuthRequest::setHttpMethod(KQOAuthRequest::RequestHttpMethod httpMethod) {
371     Q_D(KQOAuthRequest);
372
373     QString requestHttpMethodString;
374
375     switch (httpMethod) {
376     case KQOAuthRequest::GET:
377         requestHttpMethodString = "GET";
378         break;
379     case KQOAuthRequest::POST:
380         requestHttpMethodString = "POST";
381         break;
382     default:
383         qWarning() << "Invalid HTTP method set.";
384         break;
385     }
386
387     d->oauthHttpMethod = httpMethod;
388     d->oauthHttpMethodString = requestHttpMethodString;
389 }
390
391 KQOAuthRequest::RequestHttpMethod KQOAuthRequest::httpMethod() const {
392     Q_D(const KQOAuthRequest);
393
394     return d->oauthHttpMethod;
395 }
396
397 void KQOAuthRequest::setAdditionalParameters(const KQOAuthParameters &additionalParams) {
398     Q_D(KQOAuthRequest);
399
400     QList<QString> additionalKeys = additionalParams.keys();
401     QList<QString> additionalValues = additionalParams.values();
402
403     int i=0;
404     foreach(QString key, additionalKeys) {
405         QString value = additionalValues.at(i);
406         d->additionalParameters.append( qMakePair(key, value) );
407         i++;
408     }
409 }
410
411 KQOAuthParameters KQOAuthRequest::additionalParameters() const {
412     Q_D(const KQOAuthRequest);
413
414     QMultiMap<QString, QString> additionalParams;
415     for(int i=0; i<d->additionalParameters.size(); i++) {
416         additionalParams.insert(d->additionalParameters.at(i).first,
417                                 d->additionalParameters.at(i).second);
418     }
419
420     return additionalParams;
421 }
422
423 KQOAuthRequest::RequestType KQOAuthRequest::requestType() const {
424     Q_D(const KQOAuthRequest);
425     return d->requestType;
426 }
427
428 QUrl KQOAuthRequest::requestEndpoint() const {
429     Q_D(const KQOAuthRequest);
430     return d->oauthRequestEndpoint;
431 }
432
433 QList<QByteArray> KQOAuthRequest::requestParameters() {
434     Q_D(KQOAuthRequest);
435
436     QList<QByteArray> requestParamList;
437
438     d->prepareRequest();
439     if (!isValid() ) {
440         qWarning() << "Request is not valid! I will still sign it, but it will probably not work.";
441     }
442     d->signRequest();
443
444     QPair<QString, QString> requestParam;
445     QString param;
446     QString value;
447     foreach (requestParam, d->requestParameters) {
448         param = requestParam.first;
449         value = requestParam.second;
450         requestParamList.append(QString(param + "=\"" + value +"\"").toUtf8());
451     }
452
453     return requestParamList;
454 }
455
456 QString KQOAuthRequest::contentType()
457 {
458     Q_D(const KQOAuthRequest);
459     return d->contentType;
460 }
461
462 void KQOAuthRequest::setContentType(const QString &contentType)
463 {
464     Q_D(KQOAuthRequest);
465     d->contentType = contentType;
466 }
467
468 QByteArray KQOAuthRequest::rawData()
469 {
470     Q_D(const KQOAuthRequest);
471     return d->postRawData;
472 }
473
474 void KQOAuthRequest::setRawData(const QByteArray &rawData)
475 {
476     Q_D(KQOAuthRequest);
477     d->postRawData = rawData;
478 }
479
480 QByteArray KQOAuthRequest::requestBody() const {
481     Q_D(const KQOAuthRequest);
482
483     QByteArray postBodyContent;
484     bool first = true;
485     for(int i=0; i < d->additionalParameters.size(); i++) {
486         if(!first) {
487             postBodyContent.append("&");
488         } else {
489             first = false;
490         }
491
492         QString key = d->additionalParameters.at(i).first;
493         QString value = d->additionalParameters.at(i).second;
494
495         postBodyContent.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() +
496                                QUrl::toPercentEncoding(value));
497     }
498     return postBodyContent;
499 }
500
501 bool KQOAuthRequest::isValid() const {
502     Q_D(const KQOAuthRequest);
503
504     return d->validateRequest();
505 }
506
507 void KQOAuthRequest::setTimeout(int timeoutMilliseconds) {
508     Q_D(KQOAuthRequest);
509     d->timeout = timeoutMilliseconds;
510 }
511
512 void KQOAuthRequest::clearRequest() {
513     Q_D(KQOAuthRequest);
514
515     d->oauthRequestEndpoint = "";
516     d->oauthHttpMethodString = "";
517     d->oauthConsumerKey = "";
518     d->oauthConsumerSecretKey = "";
519     d->oauthToken = "";
520     d->oauthTokenSecret = "";
521     d->oauthSignatureMethod = "";
522     d->oauthCallbackUrl = "";
523     d->oauthVerifier = "";
524     d->oauthTimestamp_ = "";
525     d->oauthNonce_ = "";
526     d->requestParameters.clear();
527     d->additionalParameters.clear();
528     d->timeout = 0;
529 }
530
531 void KQOAuthRequest::setEnableDebugOutput(bool enabled) {
532     Q_D(KQOAuthRequest);
533     d->debugOutput = enabled;
534 }
535
536 /**
537  * Protected implementations for inherited classes
538  */
539 bool KQOAuthRequest::validateXAuthRequest() const {
540     Q_D(const KQOAuthRequest);
541
542     if (d->oauthRequestEndpoint.isEmpty()
543         || d->oauthConsumerKey.isEmpty()
544         || d->oauthNonce_.isEmpty()
545         || d->oauthSignatureMethod.isEmpty()
546         || d->oauthTimestamp_.isEmpty()
547         || d->oauthVersion.isEmpty())
548     {
549         return false;
550     }
551     return true;
552 }
553
554
555 /**
556  * Private implementations for friend classes
557  */
558 QString KQOAuthRequest::consumerKeyForManager() const {
559     Q_D(const KQOAuthRequest);
560     return d->oauthConsumerKey;
561 }
562
563 QString KQOAuthRequest::consumerKeySecretForManager() const {
564     Q_D(const KQOAuthRequest);
565     return d->oauthConsumerSecretKey;
566 }
567
568 QUrl KQOAuthRequest::callbackUrlForManager() const {
569     Q_D(const KQOAuthRequest);
570     return d->oauthCallbackUrl;
571 }
572
573 void KQOAuthRequest::requestTimerStart()
574 {
575     Q_D(KQOAuthRequest);
576     if (d->timeout > 0) {
577         connect(&(d->timer), SIGNAL(timeout()), this, SIGNAL(requestTimedout()));
578         d->timer.start(d->timeout);
579     }
580 }
581
582 void KQOAuthRequest::requestTimerStop()
583 {
584     Q_D(KQOAuthRequest);
585     if (d->timeout > 0) {
586         disconnect(&(d->timer), SIGNAL(timeout()), this, SIGNAL(requestTimedout()));
587         d->timer.stop();
588     }
589 }