diff --git a/3party/3party.pro b/3party/3party.pro index 49f5a11ead..9d4f0ae9ff 100644 --- a/3party/3party.pro +++ b/3party/3party.pro @@ -7,7 +7,7 @@ TEMPLATE = subdirs CONFIG *= desktop } -SUBDIRS = freetype fribidi minizip jansson tomcrypt protobuf osrm expat succinct pugixml +SUBDIRS = freetype fribidi minizip jansson tomcrypt protobuf osrm expat succinct pugixml liboauthcpp # TODO(mgsrergio): Move opening hours out of 3party to the main project tree. # See https://trello.com/c/tWYSnXSS/22-opening-hours-3party-boost-test-framework. diff --git a/3party/liboauthcpp/.gitignore b/3party/liboauthcpp/.gitignore new file mode 100644 index 0000000000..a7baf33224 --- /dev/null +++ b/3party/liboauthcpp/.gitignore @@ -0,0 +1,9 @@ +# Build (by)products +build/CMakeCache.txt +build/CMakeFiles +build/Makefile +build/cmake_install.cmake +build/liboauthcpp.a +build/simple_auth +build/simple_request +build/tests diff --git a/3party/liboauthcpp/LICENSE b/3party/liboauthcpp/LICENSE new file mode 100644 index 0000000000..8d08adbe74 --- /dev/null +++ b/3party/liboauthcpp/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2011 Stanford University (liboauthcpp) +Copyright (C) 2011 by swatkat (swatkat.thinkdigitATgmailDOTcom) (libtwitcurl) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/3party/liboauthcpp/README.md b/3party/liboauthcpp/README.md new file mode 100644 index 0000000000..cd787d6139 --- /dev/null +++ b/3party/liboauthcpp/README.md @@ -0,0 +1,172 @@ +liboauthcpp +----------- + +liboauthcpp is a pure C++ library for performing OAuth requests. It +doesn't contain any networking code -- you provide for performing HTTP +requests yourself, however you like -- instead focusing on performing +OAuth-specific functionality and providing a nice interface for it. +If you already have infrastructure for making HTTP requests and are +looking to add OAuth support, liboauthcpp is for you. + +liboauthcpp currently implements OAuth 1.0a (see +http://tools.ietf.org/html/rfc5849). + +Buildbot +-------- +[![Build Status](https://secure.travis-ci.org/sirikata/liboauthcpp.png)](http://travis-ci.org/sirikata/liboauthcpp) + +Requirements +------------ + +You should only need: + + * CMake + * A C++ compiler for your platform (e.g. g++, Microsoft Visual C++) + +Compiling +--------- + +The build process is simple: + + cd liboauthcpp + cd build + cmake . + make # or open Visual Studio and build the solution + +If your own project uses CMake you can also include +build/CMakeLists.txt directly into your project and reference the +target "oauthcpp", a static library, in your project. + +Percent (URL) Encoding +---------------------- + +To get correct results, you need to pass your URL properly encoded to +liboauthcpp. If you are not at all familiar, you should probably start +by reading the [URI Spec](http://tools.ietf.org/html/rfc3986), especially +Section 2. Alternatively, +[this article](http://blog.lunatech.com/2009/02/03/what-every-web-developer-must-know-about-url-encoding) +gives a more readable overview. + +The basic idea is that there are 3 classes of characters: reserved, +unreserved, and other. Reserved characters are special characters that +are used in the URI syntax itself, e.g. ':' (after the scheme), '/' +(the hierarchical path separator), and '?' (prefixing the query +string). Unreserved characters are characters that are always safe to +include unencoded, e.g. the alphanumerics. Other characters must +always be encoded, mainly covering special characters like ' ', '<' or +'>', and '{' or '}'. + +The basic rule is that reserved characters must be encoded if they +appear in any part of the URI when not being used as a +separator. Unreserved characters are always safe. And the other +characters they didn't know if they would be safe or not so they must +always be encoded. + +Unfortunately, the reserved set is a bit more complicated. They are +broken down into 'general delimiters' and 'sub delimiters'. The ones +already mentioned, like ':', can appear in many forms of URIs (say, +http, ftp, about, gopher, mailto, etc. Those are called general +delimiters. Others (e.g. '(', ')', '!', '$', '+', ',', '=', and more) +are called subdelimiters because their use depends on the URI +scheme. Worse, their use depends on the *part of the URI*. Depending +on the particular URI scheme, these may or may not have to be encoded, +and it might also depend on where they appear. (As an example, an '&' +in an http URI isn't an issue if it appears in the path -- before the +query string -- i.e. before a '?' appears. Worse, '=' can appear unencoded in +the path, or in a query parameter value, but not in a query parameter key since +it would be interpreted as the end of the key.) + +*Additionally*, in many cases it is permitted to encode a character +unnecessarily and the result is supposed to be the same. This means +that it's possible to percent encode some URLs in multiple ways +(e.g. encoding the unreserved set unnecessarily). It is possible, but not +guaranteed, that if you pass *exactly* the same URI to liboauthcpp and the +OAuth server, it will handle it regardless of the variant of encoding, so long +as it is a valid encoding. + +The short version: percent encoding a URL properly is non-trivial and +you can even encode the same URL multiple ways, but has to be done +correctly so that the OAuth signature can be computed. Sadly, +"correctly" in this case really means "in whatever way the server your +interacting with wants it encoded". + +Internally, liboauthcpp needs to do another step of percent encoding, +but the OAuth spec is very precise about how that works (none of these +scheme-dependent issues). liboauth applies this percent encoding, but +assumes that you have encoded your URLs properly. This assumption +makes sense since the actual request is made separately, and the URI +has to be specified in it, so you should already have a form which the +server will accept. + +However, in order to aid you, a very simple percent encoding API is exposed. It +should help you encode URLs minimally and in a way that many services accept. In +most cases you should use `HttpPercentEncodePath()`, +`HttpPercentEncodeQueryKey()`, and `HttpPercentEncodeQueryValue()` to encode +those parts of your http URL, then combine them and pass them to liboauthcpp for +signing. + + +Thread Safety +------------- + +liboauthcpp doesn't provide any thread safety guarantees. That said, there is +very little shared state, and some classes (e.g. Consumer) are naturally +immutable and therefore thread safe. Similarly, nearly the entire library uses +no static/shared state, so as long as you create separate objects for separate +threads, you should be safe. + +The one exception is nonces: the Client class needs to generate a nonce for +authorization. To do so, the random number generator needs to be seeded. We do +this with the current time, but fast, repeated use of the Client class from +different threads could result in the same nonce. To avoid requiring an entire +thread library just for this one case, you can call Client::initialize() +explicitly before using the Client from multiple threads. For single-threaded +use, you are not required to call it. + +Demos +----- +There are two demos included in the demos/ directory, and they are built by +default with the instructions above. In both, you enter key/secret information +and it generates URLs for you to visit (in a browser) and copy data back into +the program. + +simple_auth should be executed first. It starts with only a consumer key and +secret and performs 3-legged auth: you enter in consumer keys, it generates URLs +to authenticate the user and generate access tokens. It requires 3 steps: +request_token, authorize, and access_token (which correspond the URLs +accessed). At the end of this process, you'll be provided an access key/secret +pair which you can use to access actual resources. + +simple_request actually does something useful now that your application is +authorized. Enter your consumer key/secret and the access key/secret from +simple_auth (or which you've generated elsewhere) and it will generate a URL you +can use to access your home timeline in JSON format. It adds a parameter to ask +for only 5 entries (demonstrating that signing works properly over additional +query parameters). This is a one-step process -- it just gives you the URL and +you get the results in your browser. + +In both, the URLs accessed are specified at the top of the demo +files. simple_auth requires URLs for request_token, authorize_url, and +access_token. Some providers require additional parameters (notably an +oauth_callback for Twitter, even if its out of band, or oob), which you can also +specify in that location. simple_request only needs the URL of the resource +being accessed (i.e. the URL for the home_timeline JSON data used by default in +the demo), with optional parameters stored as a query string. + +Both demos only use GET requests with query strings, but all HTTP methods +(e.g. POST, PUT, DELETE) and approaches to sending parameters (e.g. HTTP +headers, url-encoded body) should be supported in the API. + +License +------- + +liboauthcpp is MIT licensed. See the LICENSE file for more details. + +liboauthcpp is mostly taken from libtwitcurl +(http://code.google.com/p/twitcurl/), which is similarly licensed. It +mostly serves to isolate the OAuth code from libtwitcurl's Twitter and +cURL specific code. + +libtwitcurl also borrowed code from other projects: +twitcurl uses HMAC_SHA1 from http://www.codeproject.com/KB/recipes/HMACSHA1class.aspx +twitcurl uses base64 from http://www.adp-gmbh.ch/cpp/common/base64.html diff --git a/3party/liboauthcpp/include/liboauthcpp/liboauthcpp.h b/3party/liboauthcpp/include/liboauthcpp/liboauthcpp.h new file mode 100644 index 0000000000..b510d43997 --- /dev/null +++ b/3party/liboauthcpp/include/liboauthcpp/liboauthcpp.h @@ -0,0 +1,286 @@ +#ifndef __LIBOAUTHCPP_LIBOAUTHCPP_H__ +#define __LIBOAUTHCPP_LIBOAUTHCPP_H__ + +#include +#include +#include +#include +#include + +namespace OAuth { + +namespace Http { +typedef enum _RequestType +{ + Invalid = 0, + Head, + Get, + Post, + Delete, + Put +} RequestType; +} // namespace Http + +typedef std::list KeyValueList; +typedef std::multimap KeyValuePairs; + +typedef enum _LogLevel +{ + LogLevelNone = 0, + LogLevelDebug = 1 +} LogLevel; + +/** Set the log level. Log messages are sent to stderr. Currently, and for the + * foreseeable future, logging only consists of debug messages to help track + * down protocol implementation issues. + */ +void SetLogLevel(LogLevel lvl); + +/** Deprecated. Complete percent encoding of URLs. Equivalent to + * PercentEncode. + */ +std::string URLEncode(const std::string& decoded); + +/** Percent encode a string value. This version is *thorough* about + * encoding: it encodes all reserved characters (even those safe in + * http URLs) and "other" characters not specified by the URI + * spec. If you're looking to encode http:// URLs, see the + * HttpEncode* functions. + */ +std::string PercentEncode(const std::string& decoded); + +/** Percent encodes the path portion of an http URL (i.e. the /foo/bar + * in http://foo/bar?a=1&b=2). This encodes minimally, so reserved + * subdelimiters that have no meaning in the path are *not* encoded. + */ +std::string HttpEncodePath(const std::string& decoded); + +/** Percent encodes a query string key in an http URL (i.e. 'a', 'b' in + * http://foo/bar?a=1&b=2). This encodes minimally, so reserved subdelimiters + * that have no meaning in the query string are *not* encoded. + */ +std::string HttpEncodeQueryKey(const std::string& decoded); + +/** Percent encodes a query string value in an http URL (i.e. '1', '2' in + * http://foo/bar?a=1&b=2). This encodes minimally, so reserved subdelimiters + * that have no meaning in the query string are *not* encoded. + */ +std::string HttpEncodeQueryValue(const std::string& decoded); + +/** Parses key value pairs into a map. + * \param encoded the encoded key value pairs, i.e. the url encoded parameters + * \returns a map of string keys to string values + * \throws ParseError if the encoded data cannot be decoded + */ +KeyValuePairs ParseKeyValuePairs(const std::string& encoded); + +class ParseError : public std::runtime_error { +public: + ParseError(const std::string msg) + : std::runtime_error(msg) + {} +}; + +class MissingKeyError : public std::runtime_error { +public: + MissingKeyError(const std::string msg) + : std::runtime_error(msg) + {} +}; + +/** A consumer of OAuth-protected services. It is the client to an + * OAuth service provider and is usually registered with the service + * provider, resulting in a consumer *key* and *secret* used to + * identify the consumer. The key is included in all requests and the + * secret is used to *sign* all requests. Signed requests allow the + * consumer to securely perform operations, including kicking off + * three-legged authentication to enable performing operations on + * behalf of a user of the service provider. + */ +class Consumer { +public: + Consumer(const std::string& key, const std::string& secret); + + const std::string& key() const { return mKey; } + const std::string& secret() const { return mSecret; } + +private: + const std::string mKey; + const std::string mSecret; +}; + +/** An OAuth credential used to request authorization or a protected + * resource. + * + * Tokens in OAuth comprise a *key* and a *secret*. The key is + * included in requests to identify the token being used, but the + * secret is used only in the signature, to prove that the requester + * is who the server gave the token to. + * + * When first negotiating the authorization, the consumer asks for a + * *request token* that the live user authorizes with the service + * provider. The consumer then exchanges the request token for an + * *access token* that can be used to access protected resources. + */ +class Token { +public: + Token(const std::string& key, const std::string& secret); + Token(const std::string& key, const std::string& secret, const std::string& pin); + + /** Construct a token, extracting the key and secret from a set of + * key-value pairs (e.g. those parsed from an request or access + * token request). + */ + static Token extract(const KeyValuePairs& response); + /** Construct a token, extracting the key and secret from a raw, + * encoded response. + */ + static Token extract(const std::string& requestTokenResponse); + + const std::string& key() const { return mKey; } + const std::string& secret() const { return mSecret; } + + const std::string& pin() const { return mPin; } + void setPin(const std::string& pin_) { mPin = pin_; } + +private: + + const std::string mKey; + const std::string mSecret; + std::string mPin; +}; + +class Client { +public: + /** Perform static initialization. This will be called automatically, but + * you can call it explicitly to ensure thread safety. If you do not call + * this explicitly before using the Client class, the same nonce may be + * generated twice. + */ + static void initialize(); + /** Alternative initialize method which lets you specify the seed and + * control the timestamp used in generating signatures. This only exists + * for testing purposes and should not be used in practice. + */ + static void initialize(int nonce, time_t timestamp); + + /** Exposed for testing only. + */ + static void __resetInitialize(); + + /** Construct an OAuth Client using only a consumer key and + * secret. You can use this to start a three-legged + * authentication (to acquire an access token for a user) or for + * simple two-legged authentication (signing with empty access + * token info). + * + * \param consumer Consumer information. The caller must ensure + * it remains valid during the lifetime of this object + */ + Client(const Consumer* consumer); + /** Construct an OAuth Client with consumer key and secret (yours) + * and access token key and secret (acquired and stored during + * three-legged authentication). + * + * \param consumer Consumer information. The caller must ensure + * it remains valid during the lifetime of this object + * \param token Access token information. The caller must ensure + * it remains valid during the lifetime of this object + */ + Client(const Consumer* consumer, const Token* token); + + ~Client(); + + /** Build an OAuth HTTP header for the given request. This version provides + * only the field value. + * + * \param eType the HTTP request type, e.g. GET or POST + * \param rawUrl the raw request URL (should include query parameters) + * \param rawData the raw HTTP request data (can be empty) + * \param includeOAuthVerifierPin if true, adds oauth_verifier parameter + * \returns a string containing the HTTP header + */ + std::string getHttpHeader(const Http::RequestType eType, + const std::string& rawUrl, + const std::string& rawData = "", + const bool includeOAuthVerifierPin = false); + /** Build an OAuth HTTP header for the given request. This version gives a + * fully formatted header, i.e. including the header field name. + * + * \param eType the HTTP request type, e.g. GET or POST + * \param rawUrl the raw request URL (should include query parameters) + * \param rawData the raw HTTP request data (can be empty) + * \param includeOAuthVerifierPin if true, adds oauth_verifier parameter + * \returns a string containing the HTTP header + */ + std::string getFormattedHttpHeader(const Http::RequestType eType, + const std::string& rawUrl, + const std::string& rawData = "", + const bool includeOAuthVerifierPin = false); + /** Build an OAuth HTTP header for the given request. + * + * \param eType the HTTP request type, e.g. GET or POST + * \param rawUrl the raw request URL (should include query parameters) + * \param rawData the raw HTTP request data (can be empty) + * \param includeOAuthVerifierPin if true, adds oauth_verifier parameter + * \returns a string containing the query string, including the query + * parameters in the rawUrl + */ + std::string getURLQueryString(const Http::RequestType eType, + const std::string& rawUrl, + const std::string& rawData = "", + const bool includeOAuthVerifierPin = false); +private: + /** Disable default constructur -- must provide consumer + * information. + */ + Client(); + + static bool initialized; + static int testingNonce; + static time_t testingTimestamp; + + /* OAuth data */ + const Consumer* mConsumer; + const Token* mToken; + std::string m_nonce; + std::string m_timeStamp; + + /* OAuth related utility methods */ + bool buildOAuthTokenKeyValuePairs( const bool includeOAuthVerifierPin, /* in */ + const std::string& rawData, /* in */ + const std::string& oauthSignature, /* in */ + KeyValuePairs& keyValueMap /* out */, + const bool urlEncodeValues /* in */, + const bool generateTimestamp /* in */); + + bool getStringFromOAuthKeyValuePairs( const KeyValuePairs& rawParamMap, /* in */ + std::string& rawParams, /* out */ + const std::string& paramsSeperator /* in */ ); + + typedef enum _ParameterStringType { + QueryStringString, + AuthorizationHeaderString + } ParameterStringType; + // Utility for building OAuth HTTP header or query string. The string type + // controls the separator and also filters parameters: for query strings, + // all parameters are included. For HTTP headers, only auth parameters are + // included. + std::string buildOAuthParameterString( + ParameterStringType string_type, + const Http::RequestType eType, + const std::string& rawUrl, + const std::string& rawData, + const bool includeOAuthVerifierPin); + + bool getSignature( const Http::RequestType eType, /* in */ + const std::string& rawUrl, /* in */ + const KeyValuePairs& rawKeyValuePairs, /* in */ + std::string& oAuthSignature /* out */ ); + + void generateNonceTimeStamp(); +}; + +} // namespace OAuth + +#endif // __LIBOAUTHCPP_LIBOAUTHCPP_H__ diff --git a/3party/liboauthcpp/liboauthcpp.pro b/3party/liboauthcpp/liboauthcpp.pro new file mode 100644 index 0000000000..0600961cfe --- /dev/null +++ b/3party/liboauthcpp/liboauthcpp.pro @@ -0,0 +1,18 @@ +TARGET = oauthcpp +ROOT_DIR = ../.. +include($$ROOT_DIR/common.pri) + +INCLUDEPATH += src include + +TEMPLATE = lib +CONFIG += staticlib + +SOURCES += \ + src/base64.cpp \ + src/HMAC_SHA1.cpp \ + src/SHA1.cpp \ + src/urlencode.cpp \ + src/liboauthcpp.cpp \ + +HEADERS += \ + include/liboauthcpp/liboauthcpp.h \ diff --git a/3party/liboauthcpp/src/HMAC_SHA1.cpp b/3party/liboauthcpp/src/HMAC_SHA1.cpp new file mode 100644 index 0000000000..59b2766a54 --- /dev/null +++ b/3party/liboauthcpp/src/HMAC_SHA1.cpp @@ -0,0 +1,59 @@ +//****************************************************************************** +//* HMAC_SHA1.cpp : Implementation of HMAC SHA1 algorithm +//* Comfort to RFC 2104 +//* +//****************************************************************************** +#include "HMAC_SHA1.h" +#include +#include + + +void CHMAC_SHA1::HMAC_SHA1(BYTE *text, int text_len, BYTE *key, int key_len, BYTE *digest) +{ + memset(SHA1_Key, 0, SHA1_BLOCK_SIZE); + + /* repeated 64 times for values in ipad and opad */ + memset(m_ipad, 0x36, sizeof(m_ipad)); + memset(m_opad, 0x5c, sizeof(m_opad)); + + /* STEP 1 */ + if (key_len > SHA1_BLOCK_SIZE) + { + CSHA1::Reset(); + CSHA1::Update((UINT_8 *)key, key_len); + CSHA1::Final(); + + CSHA1::GetHash((UINT_8 *)SHA1_Key); + } + else + memcpy(SHA1_Key, key, key_len); + + /* STEP 2 */ + for (int i=0; i<(int)sizeof(m_ipad); i++) + { + m_ipad[i] ^= SHA1_Key[i]; + } + + /* STEP 4 */ + CSHA1::Reset(); + CSHA1::Update((UINT_8 *)m_ipad, sizeof(m_ipad)); + CSHA1::Update((UINT_8 *)text, text_len); + CSHA1::Final(); + + char szReport[SHA1_DIGEST_LENGTH]; + CSHA1::GetHash((UINT_8 *)szReport); + + /* STEP 5 */ + for (int j=0; j<(int)sizeof(m_opad); j++) + { + m_opad[j] ^= SHA1_Key[j]; + } + + /*STEP 7 */ + CSHA1::Reset(); + CSHA1::Update((UINT_8 *)m_opad, sizeof(m_opad)); + CSHA1::Update((UINT_8 *)szReport, SHA1_DIGEST_LENGTH); + CSHA1::Final(); + + CSHA1::GetHash((UINT_8 *)digest); +} diff --git a/3party/liboauthcpp/src/HMAC_SHA1.h b/3party/liboauthcpp/src/HMAC_SHA1.h new file mode 100644 index 0000000000..19c6b5e237 --- /dev/null +++ b/3party/liboauthcpp/src/HMAC_SHA1.h @@ -0,0 +1,37 @@ +/* + 100% free public domain implementation of the HMAC-SHA1 algorithm + by Chien-Chung, Chung (Jim Chung) +*/ + + +#ifndef __HMAC_SHA1_H__ +#define __HMAC_SHA1_H__ + +#include "SHA1.h" + +typedef unsigned char BYTE ; + +class CHMAC_SHA1 : public CSHA1 +{ +public: + + enum { + SHA1_DIGEST_LENGTH = 20, + SHA1_BLOCK_SIZE = 64 + } ; + +private: + BYTE m_ipad[SHA1_BLOCK_SIZE]; + BYTE m_opad[SHA1_BLOCK_SIZE]; + + // This holds one SHA1 block's worth of data, zero padded if necessary. + char SHA1_Key[SHA1_BLOCK_SIZE]; + +public: + CHMAC_SHA1() {} + + void HMAC_SHA1(BYTE *text, int text_len, BYTE *key, int key_len, BYTE *digest); +}; + + +#endif /* __HMAC_SHA1_H__ */ diff --git a/3party/liboauthcpp/src/SHA1.cpp b/3party/liboauthcpp/src/SHA1.cpp new file mode 100644 index 0000000000..ce1ba683e6 --- /dev/null +++ b/3party/liboauthcpp/src/SHA1.cpp @@ -0,0 +1,277 @@ +/* + 100% free public domain implementation of the SHA-1 algorithm + by Dominik Reichl + Web: http://www.dominik-reichl.de/ + + Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches) + - You can set the endianness in your files, no need to modify the + header file of the CSHA1 class any more + - Aligned data support + - Made support/compilation of the utility functions (ReportHash + and HashFile) optional (useful, if bytes count, for example in + embedded environments) + + Version 1.5 - 2005-01-01 + - 64-bit compiler compatibility added + - Made variable wiping optional (define SHA1_WIPE_VARIABLES) + - Removed unnecessary variable initializations + - ROL32 improvement for the Microsoft compiler (using _rotl) + + ======== Test Vectors (from FIPS PUB 180-1) ======== + + SHA1("abc") = + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + + SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + + SHA1(A million repetitions of "a") = + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +#include "SHA1.h" +#include + +#ifdef SHA1_UTILITY_FUNCTIONS +#define SHA1_MAX_FILE_BUFFER 8000 +#endif + +// Rotate x bits to the left +#ifndef ROL32 +#ifdef _MSC_VER +#define ROL32(_val32, _nBits) _rotl(_val32, _nBits) +#else +#define ROL32(_val32, _nBits) (((_val32)<<(_nBits))|((_val32)>>(32-(_nBits)))) +#endif +#endif + +#ifdef SHA1_LITTLE_ENDIAN +#define SHABLK0(i) (m_block->l[i] = \ + (ROL32(m_block->l[i],24) & 0xFF00FF00) | (ROL32(m_block->l[i],8) & 0x00FF00FF)) +#else +#define SHABLK0(i) (m_block->l[i]) +#endif + +#define SHABLK(i) (m_block->l[i&15] = ROL32(m_block->l[(i+13)&15] ^ m_block->l[(i+8)&15] \ + ^ m_block->l[(i+2)&15] ^ m_block->l[i&15],1)) + +// SHA-1 rounds +#define _R0(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } +#define _R1(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } +#define _R2(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5); w=ROL32(w,30); } +#define _R3(v,w,x,y,z,i) { z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5); w=ROL32(w,30); } +#define _R4(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5); w=ROL32(w,30); } + +CSHA1::CSHA1() +{ + m_block = (SHA1_WORKSPACE_BLOCK *)m_workspace; + + Reset(); +} + +CSHA1::~CSHA1() +{ + Reset(); +} + +void CSHA1::Reset() +{ + // SHA1 initialization constants + m_state[0] = 0x67452301; + m_state[1] = 0xEFCDAB89; + m_state[2] = 0x98BADCFE; + m_state[3] = 0x10325476; + m_state[4] = 0xC3D2E1F0; + + m_count[0] = 0; + m_count[1] = 0; +} + +void CSHA1::Transform(UINT_32 *state, UINT_8 *buffer) +{ + // Copy state[] to working vars + UINT_32 a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; + + memcpy(m_block, buffer, 64); + + // 4 rounds of 20 operations each. Loop unrolled. + _R0(a,b,c,d,e, 0); _R0(e,a,b,c,d, 1); _R0(d,e,a,b,c, 2); _R0(c,d,e,a,b, 3); + _R0(b,c,d,e,a, 4); _R0(a,b,c,d,e, 5); _R0(e,a,b,c,d, 6); _R0(d,e,a,b,c, 7); + _R0(c,d,e,a,b, 8); _R0(b,c,d,e,a, 9); _R0(a,b,c,d,e,10); _R0(e,a,b,c,d,11); + _R0(d,e,a,b,c,12); _R0(c,d,e,a,b,13); _R0(b,c,d,e,a,14); _R0(a,b,c,d,e,15); + _R1(e,a,b,c,d,16); _R1(d,e,a,b,c,17); _R1(c,d,e,a,b,18); _R1(b,c,d,e,a,19); + _R2(a,b,c,d,e,20); _R2(e,a,b,c,d,21); _R2(d,e,a,b,c,22); _R2(c,d,e,a,b,23); + _R2(b,c,d,e,a,24); _R2(a,b,c,d,e,25); _R2(e,a,b,c,d,26); _R2(d,e,a,b,c,27); + _R2(c,d,e,a,b,28); _R2(b,c,d,e,a,29); _R2(a,b,c,d,e,30); _R2(e,a,b,c,d,31); + _R2(d,e,a,b,c,32); _R2(c,d,e,a,b,33); _R2(b,c,d,e,a,34); _R2(a,b,c,d,e,35); + _R2(e,a,b,c,d,36); _R2(d,e,a,b,c,37); _R2(c,d,e,a,b,38); _R2(b,c,d,e,a,39); + _R3(a,b,c,d,e,40); _R3(e,a,b,c,d,41); _R3(d,e,a,b,c,42); _R3(c,d,e,a,b,43); + _R3(b,c,d,e,a,44); _R3(a,b,c,d,e,45); _R3(e,a,b,c,d,46); _R3(d,e,a,b,c,47); + _R3(c,d,e,a,b,48); _R3(b,c,d,e,a,49); _R3(a,b,c,d,e,50); _R3(e,a,b,c,d,51); + _R3(d,e,a,b,c,52); _R3(c,d,e,a,b,53); _R3(b,c,d,e,a,54); _R3(a,b,c,d,e,55); + _R3(e,a,b,c,d,56); _R3(d,e,a,b,c,57); _R3(c,d,e,a,b,58); _R3(b,c,d,e,a,59); + _R4(a,b,c,d,e,60); _R4(e,a,b,c,d,61); _R4(d,e,a,b,c,62); _R4(c,d,e,a,b,63); + _R4(b,c,d,e,a,64); _R4(a,b,c,d,e,65); _R4(e,a,b,c,d,66); _R4(d,e,a,b,c,67); + _R4(c,d,e,a,b,68); _R4(b,c,d,e,a,69); _R4(a,b,c,d,e,70); _R4(e,a,b,c,d,71); + _R4(d,e,a,b,c,72); _R4(c,d,e,a,b,73); _R4(b,c,d,e,a,74); _R4(a,b,c,d,e,75); + _R4(e,a,b,c,d,76); _R4(d,e,a,b,c,77); _R4(c,d,e,a,b,78); _R4(b,c,d,e,a,79); + + // Add the working vars back into state + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + // Wipe variables +#ifdef SHA1_WIPE_VARIABLES + a = b = c = d = e = 0; +#endif +} + +// Use this function to hash in binary data and strings +void CSHA1::Update(UINT_8 *data, UINT_32 len) +{ + UINT_32 i, j; + + j = (m_count[0] >> 3) & 63; + + if((m_count[0] += len << 3) < (len << 3)) m_count[1]++; + + m_count[1] += (len >> 29); + + if((j + len) > 63) + { + i = 64 - j; + memcpy(&m_buffer[j], data, i); + Transform(m_state, m_buffer); + + for(; i + 63 < len; i += 64) Transform(m_state, &data[i]); + + j = 0; + } + else i = 0; + + memcpy(&m_buffer[j], &data[i], len - i); +} + +#ifdef SHA1_UTILITY_FUNCTIONS +// Hash in file contents +bool CSHA1::HashFile(char *szFileName) +{ + unsigned long ulFileSize, ulRest, ulBlocks; + unsigned long i; + UINT_8 uData[SHA1_MAX_FILE_BUFFER]; + FILE *fIn; + + if(szFileName == NULL) return false; + + fIn = fopen(szFileName, "rb"); + if(fIn == NULL) return false; + + fseek(fIn, 0, SEEK_END); + ulFileSize = (unsigned long)ftell(fIn); + fseek(fIn, 0, SEEK_SET); + + if(ulFileSize != 0) + { + ulBlocks = ulFileSize / SHA1_MAX_FILE_BUFFER; + ulRest = ulFileSize % SHA1_MAX_FILE_BUFFER; + } + else + { + ulBlocks = 0; + ulRest = 0; + } + + for(i = 0; i < ulBlocks; i++) + { + size_t nread = fread(uData, 1, SHA1_MAX_FILE_BUFFER, fIn); + assert(nread == SHA1_MAX_FILE_BUFFER); + Update((UINT_8 *)uData, SHA1_MAX_FILE_BUFFER); + } + + if(ulRest != 0) + { + size_t nread = fread(uData, 1, ulRest, fIn); + assert(nread == ulRest); + Update((UINT_8 *)uData, ulRest); + } + + fclose(fIn); fIn = NULL; + return true; +} +#endif + +void CSHA1::Final() +{ + UINT_32 i; + UINT_8 finalcount[8]; + + for(i = 0; i < 8; i++) + finalcount[i] = (UINT_8)((m_count[((i >= 4) ? 0 : 1)] + >> ((3 - (i & 3)) * 8) ) & 255); // Endian independent + + Update((UINT_8 *)"\200", 1); + + while ((m_count[0] & 504) != 448) + Update((UINT_8 *)"\0", 1); + + Update(finalcount, 8); // Cause a SHA1Transform() + + for(i = 0; i < 20; i++) + { + m_digest[i] = (UINT_8)((m_state[i >> 2] >> ((3 - (i & 3)) * 8) ) & 255); + } + + // Wipe variables for security reasons +#ifdef SHA1_WIPE_VARIABLES + i = 0; + memset(m_buffer, 0, 64); + memset(m_state, 0, 20); + memset(m_count, 0, 8); + memset(finalcount, 0, 8); + Transform(m_state, m_buffer); +#endif +} + +#ifdef SHA1_UTILITY_FUNCTIONS +// Get the final hash as a pre-formatted string +void CSHA1::ReportHash(char *szReport, unsigned char uReportType) +{ + unsigned char i; + char szTemp[16]; + + if(szReport == NULL) return; + + if(uReportType == REPORT_HEX) + { + sprintf(szTemp, "%02X", m_digest[0]); + strcat(szReport, szTemp); + + for(i = 1; i < 20; i++) + { + sprintf(szTemp, " %02X", m_digest[i]); + strcat(szReport, szTemp); + } + } + else if(uReportType == REPORT_DIGIT) + { + sprintf(szTemp, "%u", m_digest[0]); + strcat(szReport, szTemp); + + for(i = 1; i < 20; i++) + { + sprintf(szTemp, " %u", m_digest[i]); + strcat(szReport, szTemp); + } + } + else strcpy(szReport, "Error: Unknown report type!"); +} +#endif + +// Get the raw message digest +void CSHA1::GetHash(UINT_8 *puDest) +{ + memcpy(puDest, m_digest, 20); +} diff --git a/3party/liboauthcpp/src/SHA1.h b/3party/liboauthcpp/src/SHA1.h new file mode 100644 index 0000000000..a326911343 --- /dev/null +++ b/3party/liboauthcpp/src/SHA1.h @@ -0,0 +1,148 @@ +/* + 100% free public domain implementation of the SHA-1 algorithm + by Dominik Reichl + Web: http://www.dominik-reichl.de/ + + Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches) + - You can set the endianness in your files, no need to modify the + header file of the CSHA1 class any more + - Aligned data support + - Made support/compilation of the utility functions (ReportHash + and HashFile) optional (useful, if bytes count, for example in + embedded environments) + + Version 1.5 - 2005-01-01 + - 64-bit compiler compatibility added + - Made variable wiping optional (define SHA1_WIPE_VARIABLES) + - Removed unnecessary variable initializations + - ROL32 improvement for the Microsoft compiler (using _rotl) + + ======== Test Vectors (from FIPS PUB 180-1) ======== + + SHA1("abc") = + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + + SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + + SHA1(A million repetitions of "a") = + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +#ifndef ___SHA1_HDR___ +#define ___SHA1_HDR___ + +#if !defined(SHA1_UTILITY_FUNCTIONS) && !defined(SHA1_NO_UTILITY_FUNCTIONS) +#define SHA1_UTILITY_FUNCTIONS +#endif + +#include // Needed for memset and memcpy + +#ifdef SHA1_UTILITY_FUNCTIONS +#include // Needed for file access and sprintf +#include // Needed for strcat and strcpy +#endif + +#ifdef _MSC_VER +#include +#endif + +// You can define the endian mode in your files, without modifying the SHA1 +// source files. Just #define SHA1_LITTLE_ENDIAN or #define SHA1_BIG_ENDIAN +// in your files, before including the SHA1.h header file. If you don't +// define anything, the class defaults to little endian. + +#if !defined(SHA1_LITTLE_ENDIAN) && !defined(SHA1_BIG_ENDIAN) +#define SHA1_LITTLE_ENDIAN +#endif + +// Same here. If you want variable wiping, #define SHA1_WIPE_VARIABLES, if +// not, #define SHA1_NO_WIPE_VARIABLES. If you don't define anything, it +// defaults to wiping. + +#if !defined(SHA1_WIPE_VARIABLES) && !defined(SHA1_NO_WIPE_VARIABLES) +#define SHA1_WIPE_VARIABLES +#endif + +///////////////////////////////////////////////////////////////////////////// +// Define 8- and 32-bit variables + +#ifndef UINT_32 + +#ifdef _MSC_VER + +#define UINT_8 unsigned __int8 +#define UINT_32 unsigned __int32 + +#else + +#define UINT_8 unsigned char + +#if (ULONG_MAX == 0xFFFFFFFF) +#define UINT_32 unsigned long +#else +#define UINT_32 unsigned int +#endif + +#endif +#endif + +///////////////////////////////////////////////////////////////////////////// +// Declare SHA1 workspace + +typedef union +{ + UINT_8 c[64]; + UINT_32 l[16]; +} SHA1_WORKSPACE_BLOCK; + +class CSHA1 +{ +public: +#ifdef SHA1_UTILITY_FUNCTIONS + // Two different formats for ReportHash(...) + enum + { + REPORT_HEX = 0, + REPORT_DIGIT = 1 + }; +#endif + + // Constructor and Destructor + CSHA1(); + ~CSHA1(); + + UINT_32 m_state[5]; + UINT_32 m_count[2]; + UINT_32 __reserved1[1]; + UINT_8 m_buffer[64]; + UINT_8 m_digest[20]; + UINT_32 __reserved2[3]; + + void Reset(); + + // Update the hash value + void Update(UINT_8 *data, UINT_32 len); +#ifdef SHA1_UTILITY_FUNCTIONS + bool HashFile(char *szFileName); +#endif + + // Finalize hash and report + void Final(); + + // Report functions: as pre-formatted and raw data +#ifdef SHA1_UTILITY_FUNCTIONS + void ReportHash(char *szReport, unsigned char uReportType = REPORT_HEX); +#endif + void GetHash(UINT_8 *puDest); + +private: + // Private SHA-1 transformation + void Transform(UINT_32 *state, UINT_8 *buffer); + + // Member variables + UINT_8 m_workspace[64]; + SHA1_WORKSPACE_BLOCK *m_block; // SHA1 pointer to the byte array above +}; + +#endif diff --git a/3party/liboauthcpp/src/base64.cpp b/3party/liboauthcpp/src/base64.cpp new file mode 100644 index 0000000000..8cd3346c1e --- /dev/null +++ b/3party/liboauthcpp/src/base64.cpp @@ -0,0 +1,123 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" +#include + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} + +std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} \ No newline at end of file diff --git a/3party/liboauthcpp/src/base64.h b/3party/liboauthcpp/src/base64.h new file mode 100644 index 0000000000..ceb13579ce --- /dev/null +++ b/3party/liboauthcpp/src/base64.h @@ -0,0 +1,4 @@ +#include + +std::string base64_encode(unsigned char const* , unsigned int len); +std::string base64_decode(std::string const& s); \ No newline at end of file diff --git a/3party/liboauthcpp/src/liboauthcpp.cpp b/3party/liboauthcpp/src/liboauthcpp.cpp new file mode 100644 index 0000000000..c269100b6e --- /dev/null +++ b/3party/liboauthcpp/src/liboauthcpp.cpp @@ -0,0 +1,621 @@ +#include +#include "HMAC_SHA1.h" +#include "base64.h" +#include "urlencode.h" +#include +#include +#include + +namespace OAuth { + +namespace Defaults +{ + /* Constants */ + const int BUFFSIZE = 1024; + const int BUFFSIZE_LARGE = 1024; + const std::string CONSUMERKEY_KEY = "oauth_consumer_key"; + const std::string CALLBACK_KEY = "oauth_callback"; + const std::string VERSION_KEY = "oauth_version"; + const std::string SIGNATUREMETHOD_KEY = "oauth_signature_method"; + const std::string SIGNATURE_KEY = "oauth_signature"; + const std::string TIMESTAMP_KEY = "oauth_timestamp"; + const std::string NONCE_KEY = "oauth_nonce"; + const std::string TOKEN_KEY = "oauth_token"; + const std::string TOKENSECRET_KEY = "oauth_token_secret"; + const std::string VERIFIER_KEY = "oauth_verifier"; + + const std::string AUTHHEADER_FIELD = "Authorization: "; + const std::string AUTHHEADER_PREFIX = "OAuth "; +}; + +/** std::string -> std::string conversion function */ +typedef std::string(*StringConvertFunction)(const std::string&); + +LogLevel gLogLevel = LogLevelNone; + +void SetLogLevel(LogLevel lvl) { + gLogLevel = lvl; +} +#define LOG(lvl, msg) \ + do { \ + if (lvl <= gLogLevel) std::cerr << "OAUTH: " << msg << std::endl; \ + } while(0) + +std::string PercentEncode(const std::string& decoded) { + return urlencode(decoded, URLEncode_Everything); +} + +std::string URLEncode(const std::string& decoded) { + return PercentEncode(decoded); +} + +std::string HttpEncodePath(const std::string& decoded) { + return urlencode(decoded, URLEncode_Path); +} + +std::string HttpEncodeQueryKey(const std::string& decoded) { + return urlencode(decoded, URLEncode_QueryKey); +} + +std::string HttpEncodeQueryValue(const std::string& decoded) { + return urlencode(decoded, URLEncode_QueryValue); +} + +namespace { +std::string PassThrough(const std::string& decoded) { + return decoded; +} + +std::string RequestTypeString(const Http::RequestType rt) { + switch(rt) { + case Http::Invalid: return "Invalid Request Type"; break; + case Http::Head: return "HEAD"; break; + case Http::Get: return "GET"; break; + case Http::Post: return "POST"; break; + case Http::Delete: return "DELETE"; break; + case Http::Put: return "PUT"; break; + default: return "Unknown Request Type"; break; + } + return ""; +} +} + +// Parse a single key-value pair +static std::pair ParseKeyValuePair(const std::string& encoded) { + std::size_t eq_pos = encoded.find("="); + if (eq_pos == std::string::npos) + throw ParseError("Failed to find '=' in key-value pair."); + return std::pair( + encoded.substr(0, eq_pos), + encoded.substr(eq_pos+1) + ); +} + +KeyValuePairs ParseKeyValuePairs(const std::string& encoded) { + KeyValuePairs result; + + if (encoded.length() == 0) return result; + + // Split by & + std::size_t last_amp = 0; + // We can bail when the last one "found" was the end of the string + while(true) { + std::size_t next_amp = encoded.find('&', last_amp+1); + std::string keyval = + (next_amp == std::string::npos) ? + encoded.substr(last_amp) : + encoded.substr(last_amp, next_amp-last_amp); + result.insert(ParseKeyValuePair(keyval)); + // Track spot after the & so the first iteration works without dealing + // with -1 index + last_amp = next_amp+1; + + // Exit condition + if (next_amp == std::string::npos) break; + } + return result; +} + +// Helper for parameters in key-value pair lists that should only appear +// once. Either replaces an existing entry or adds a new entry. +static void ReplaceOrInsertKeyValuePair(KeyValuePairs& kvp, const std::string& key, const std::string& value) { + assert(kvp.count(key) <= 1); + KeyValuePairs::iterator it = kvp.find(key); + if (it != kvp.end()) + it->second = value; + else + kvp.insert(KeyValuePairs::value_type(key, value)); +} + +Consumer::Consumer(const std::string& key, const std::string& secret) + : mKey(key), mSecret(secret) +{ +} + + + +Token::Token(const std::string& key, const std::string& secret) + : mKey(key), mSecret(secret) +{ +} + +Token::Token(const std::string& key, const std::string& secret, const std::string& pin) + : mKey(key), mSecret(secret), mPin(pin) +{ +} + +Token Token::extract(const std::string& response) { + return Token::extract(ParseKeyValuePairs(response)); +} + +Token Token::extract(const KeyValuePairs& response) { + std::string token_key, token_secret; + + KeyValuePairs::const_iterator it = response.find(Defaults::TOKEN_KEY); + if (it == response.end()) + throw MissingKeyError("Couldn't find oauth_token in response"); + token_key = it->second; + + it = response.find(Defaults::TOKENSECRET_KEY); + if (it == response.end()) + throw MissingKeyError("Couldn't find oauth_token_secret in response"); + token_secret = it->second; + + return Token(token_key, token_secret); +} + + +bool Client::initialized = false; +int Client::testingNonce = 0; +time_t Client::testingTimestamp = 0; + +void Client::initialize() { + if(!initialized) { + srand( time( NULL ) ); + initialized = true; + } +} + +void Client::initialize(int nonce, time_t timestamp) { + if(!initialized) { + testingNonce = nonce; + testingTimestamp = timestamp; + initialized = true; + } +} + +void Client::__resetInitialize() { + testingNonce = 0; + testingTimestamp = 0; + initialized = false; +} + +Client::Client(const Consumer* consumer) + : mConsumer(consumer), + mToken(NULL) +{ +} + +Client::Client(const Consumer* consumer, const Token* token) + : mConsumer(consumer), + mToken(token) +{ +} + + +Client::~Client() +{ +} + + + +/*++ +* @method: Client::generateNonceTimeStamp +* +* @description: this method generates nonce and timestamp for OAuth header +* +* @input: none +* +* @output: none +* +* @remarks: internal method +* +*--*/ +void Client::generateNonceTimeStamp() +{ + // Make sure the random seed has been initialized + Client::initialize(); + + char szTime[Defaults::BUFFSIZE]; + char szRand[Defaults::BUFFSIZE]; + memset( szTime, 0, Defaults::BUFFSIZE ); + memset( szRand, 0, Defaults::BUFFSIZE ); + + // Any non-zero timestamp triggers testing mode with fixed values. Fixing + // both values makes life easier because generating a signature is + // idempotent -- otherwise using macros can cause double evaluation and + // incorrect results because of repeated calls to rand(). + sprintf( szRand, "%x", ((testingTimestamp != 0) ? testingNonce : rand()) ); + sprintf( szTime, "%ld", ((testingTimestamp != 0) ? testingTimestamp : time( NULL )) ); + + m_nonce.assign( szTime ); + m_nonce.append( szRand ); + + m_timeStamp.assign( szTime ); +} + +/*++ +* @method: Client::buildOAuthTokenKeyValuePairs +* +* @description: this method prepares key-value pairs required for OAuth header +* and signature generation. +* +* @input: includeOAuthVerifierPin - flag to indicate whether oauth_verifer key-value +* pair needs to be included. oauth_verifer is only +* used during exchanging request token with access token. +* rawData - url encoded data. this is used during signature generation. +* oauthSignature - base64 and url encoded OAuth signature. +* generateTimestamp - If true, then generate new timestamp for nonce. +* +* @input: urlEncodeValues - if true, URLEncode the values inserted into the +* output keyValueMap +* @output: keyValueMap - map in which key-value pairs are populated +* +* @remarks: internal method +* +*--*/ +bool Client::buildOAuthTokenKeyValuePairs( const bool includeOAuthVerifierPin, + const std::string& rawData, + const std::string& oauthSignature, + KeyValuePairs& keyValueMap, + const bool urlEncodeValues, + const bool generateTimestamp ) +{ + // Encodes value part of key-value pairs depending on type of output (query + // string vs. HTTP headers. + StringConvertFunction value_encoder = (urlEncodeValues ? HttpEncodeQueryValue : PassThrough); + + /* Generate nonce and timestamp if required */ + if( generateTimestamp ) + { + generateNonceTimeStamp(); + } + + /* Consumer key and its value */ + ReplaceOrInsertKeyValuePair(keyValueMap, Defaults::CONSUMERKEY_KEY, value_encoder(mConsumer->key())); + + /* Nonce key and its value */ + ReplaceOrInsertKeyValuePair(keyValueMap, Defaults::NONCE_KEY, value_encoder(m_nonce)); + + /* Signature if supplied */ + if( oauthSignature.length() ) + { + // Signature is exempt from encoding. The procedure for + // computing it already percent-encodes it as required by the + // spec for both query string and Auth header + // methods. Therefore, it's pass-through in both cases. + ReplaceOrInsertKeyValuePair(keyValueMap, Defaults::SIGNATURE_KEY, oauthSignature); + } + + /* Signature method, only HMAC-SHA1 as of now */ + ReplaceOrInsertKeyValuePair(keyValueMap, Defaults::SIGNATUREMETHOD_KEY, std::string( "HMAC-SHA1" )); + + /* Timestamp */ + ReplaceOrInsertKeyValuePair(keyValueMap, Defaults::TIMESTAMP_KEY, value_encoder(m_timeStamp)); + + /* Token */ + if( mToken && mToken->key().length() ) + { + ReplaceOrInsertKeyValuePair(keyValueMap, Defaults::TOKEN_KEY, value_encoder(mToken->key())); + } + + /* Verifier */ + if( includeOAuthVerifierPin && mToken && mToken->pin().length() ) + { + ReplaceOrInsertKeyValuePair(keyValueMap, Defaults::VERIFIER_KEY, value_encoder(mToken->pin())); + } + + /* Version */ + ReplaceOrInsertKeyValuePair(keyValueMap, Defaults::VERSION_KEY, std::string( "1.0" )); + + /* Data if it's present */ + if( rawData.length() ) + { + /* Data should already be urlencoded once */ + std::string dummyStrKey; + std::string dummyStrValue; + size_t nPos = rawData.find_first_of( "=" ); + if( std::string::npos != nPos ) + { + dummyStrKey = rawData.substr( 0, nPos ); + dummyStrValue = rawData.substr( nPos + 1 ); + ReplaceOrInsertKeyValuePair(keyValueMap, dummyStrKey, dummyStrValue); + } + } + + return ( keyValueMap.size() ) ? true : false; +} + +/*++ +* @method: Client::getSignature +* +* @description: this method calculates HMAC-SHA1 signature of OAuth header +* +* @input: eType - HTTP request type +* rawUrl - raw url of the HTTP request +* rawKeyValuePairs - key-value pairs containing OAuth headers and HTTP data +* +* @output: oAuthSignature - base64 and url encoded signature +* +* @remarks: internal method +* +*--*/ +bool Client::getSignature( const Http::RequestType eType, + const std::string& rawUrl, + const KeyValuePairs& rawKeyValuePairs, + std::string& oAuthSignature ) +{ + std::string rawParams; + std::string paramsSeperator; + std::string sigBase; + + /* Initially empty signature */ + oAuthSignature.assign( "" ); + + /* Build a string using key-value pairs */ + paramsSeperator = "&"; + getStringFromOAuthKeyValuePairs( rawKeyValuePairs, rawParams, paramsSeperator ); + LOG(LogLevelDebug, "Normalized parameters: " << rawParams); + + /* Start constructing base signature string. Refer http://dev.twitter.com/auth#intro */ + switch( eType ) + { + case Http::Head: + { + sigBase.assign( "HEAD&" ); + } + break; + + case Http::Get: + { + sigBase.assign( "GET&" ); + } + break; + + case Http::Post: + { + sigBase.assign( "POST&" ); + } + break; + + case Http::Delete: + { + sigBase.assign( "DELETE&" ); + } + break; + + case Http::Put: + { + sigBase.assign( "PUT&" ); + } + break; + + default: + { + return false; + } + break; + } + sigBase.append( PercentEncode( rawUrl ) ); + sigBase.append( "&" ); + sigBase.append( PercentEncode( rawParams ) ); + LOG(LogLevelDebug, "Signature base string: " << sigBase); + + /* Now, hash the signature base string using HMAC_SHA1 class */ + CHMAC_SHA1 objHMACSHA1; + std::string secretSigningKey; + unsigned char strDigest[Defaults::BUFFSIZE_LARGE]; + + memset( strDigest, 0, Defaults::BUFFSIZE_LARGE ); + + /* Signing key is composed of consumer_secret&token_secret */ + secretSigningKey.assign( PercentEncode(mConsumer->secret()) ); + secretSigningKey.append( "&" ); + if( mToken && mToken->secret().length() ) + { + secretSigningKey.append( PercentEncode(mToken->secret()) ); + } + + objHMACSHA1.HMAC_SHA1( (unsigned char*)sigBase.c_str(), + sigBase.length(), + (unsigned char*)secretSigningKey.c_str(), + secretSigningKey.length(), + strDigest ); + + /* Do a base64 encode of signature */ + std::string base64Str = base64_encode( strDigest, 20 /* SHA 1 digest is 160 bits */ ); + LOG(LogLevelDebug, "Signature: " << base64Str); + + /* Do an url encode */ + oAuthSignature = PercentEncode( base64Str ); + LOG(LogLevelDebug, "Percent-encoded Signature: " << oAuthSignature); + + return ( oAuthSignature.length() ) ? true : false; +} + +std::string Client::getHttpHeader(const Http::RequestType eType, + const std::string& rawUrl, + const std::string& rawData, + const bool includeOAuthVerifierPin) +{ + return Defaults::AUTHHEADER_PREFIX + buildOAuthParameterString(AuthorizationHeaderString, eType, rawUrl, rawData, includeOAuthVerifierPin); +} + +std::string Client::getFormattedHttpHeader(const Http::RequestType eType, + const std::string& rawUrl, + const std::string& rawData, + const bool includeOAuthVerifierPin) +{ + return Defaults::AUTHHEADER_FIELD + Defaults::AUTHHEADER_PREFIX + buildOAuthParameterString(AuthorizationHeaderString, eType, rawUrl, rawData, includeOAuthVerifierPin); +} + +std::string Client::getURLQueryString(const Http::RequestType eType, + const std::string& rawUrl, + const std::string& rawData, + const bool includeOAuthVerifierPin) +{ + return buildOAuthParameterString(QueryStringString, eType, rawUrl, rawData, includeOAuthVerifierPin); +} + +std::string Client::buildOAuthParameterString( + ParameterStringType string_type, + const Http::RequestType eType, + const std::string& rawUrl, + const std::string& rawData, + const bool includeOAuthVerifierPin) +{ + KeyValuePairs rawKeyValuePairs; + std::string rawParams; + std::string oauthSignature; + std::string paramsSeperator; + std::string pureUrl( rawUrl ); + + LOG(LogLevelDebug, "Signing request " << RequestTypeString(eType) << " " << rawUrl << " " << rawData); + + std::string separator; + bool do_urlencode; + if (string_type == AuthorizationHeaderString) { + separator = ","; + do_urlencode = false; + } + else { // QueryStringString + separator = "&"; + do_urlencode = true; + } + + /* Clear header string initially */ + rawKeyValuePairs.clear(); + + /* If URL itself contains ?key=value, then extract and put them in map */ + size_t nPos = rawUrl.find_first_of( "?" ); + if( std::string::npos != nPos ) + { + /* Get only URL */ + pureUrl = rawUrl.substr( 0, nPos ); + + /* Get only key=value data part */ + std::string dataPart = rawUrl.substr( nPos + 1 ); + rawKeyValuePairs = ParseKeyValuePairs(dataPart); + } + + // NOTE: We always request URL encoding on the first pass so that the + // signature generation works properly. This *relies* on + // buildOAuthTokenKeyValuePairs overwriting values when we do the second + // pass to get the values in the form we actually want. The signature and + // rawdata are the only things that change, but the signature is only used + // in the second pass and the rawdata is already encoded, regardless of + // request type. + + /* Build key-value pairs needed for OAuth request token, without signature */ + buildOAuthTokenKeyValuePairs( includeOAuthVerifierPin, rawData, std::string( "" ), rawKeyValuePairs, true, true ); + + /* Get url encoded base64 signature using request type, url and parameters */ + getSignature( eType, pureUrl, rawKeyValuePairs, oauthSignature ); + + /* Now, again build key-value pairs with signature this time */ + buildOAuthTokenKeyValuePairs( includeOAuthVerifierPin, std::string( "" ), oauthSignature, rawKeyValuePairs, do_urlencode, false ); + + /* Get OAuth header in string format. If we're getting the Authorization + * header, we need to filter out other parameters. + */ + if (string_type == AuthorizationHeaderString) { + KeyValuePairs oauthKeyValuePairs; + std::vector oauth_keys; + oauth_keys.push_back(Defaults::CONSUMERKEY_KEY); + oauth_keys.push_back(Defaults::NONCE_KEY); + oauth_keys.push_back(Defaults::SIGNATURE_KEY); + oauth_keys.push_back(Defaults::SIGNATUREMETHOD_KEY); + oauth_keys.push_back(Defaults::TIMESTAMP_KEY); + oauth_keys.push_back(Defaults::TOKEN_KEY); + oauth_keys.push_back(Defaults::VERIFIER_KEY); + oauth_keys.push_back(Defaults::VERSION_KEY); + + for(size_t i = 0; i < oauth_keys.size(); i++) { + assert(rawKeyValuePairs.count(oauth_keys[i]) <= 1); + KeyValuePairs::iterator oauth_key_it = rawKeyValuePairs.find(oauth_keys[i]); + if (oauth_key_it != rawKeyValuePairs.end()) + ReplaceOrInsertKeyValuePair(oauthKeyValuePairs, oauth_keys[i], oauth_key_it->second); + } + getStringFromOAuthKeyValuePairs( oauthKeyValuePairs, rawParams, separator ); + } + else if (string_type == QueryStringString) { + getStringFromOAuthKeyValuePairs( rawKeyValuePairs, rawParams, separator ); + } + + /* Build authorization header */ + return rawParams; +} + +/*++ +* @method: Client::getStringFromOAuthKeyValuePairs +* +* @description: this method builds a sorted string from key-value pairs +* +* @input: rawParamMap - key-value pairs map +* paramsSeperator - sepearator, either & or , +* +* @output: rawParams - sorted string of OAuth parameters +* +* @remarks: internal method +* +*--*/ +bool Client::getStringFromOAuthKeyValuePairs( const KeyValuePairs& rawParamMap, + std::string& rawParams, + const std::string& paramsSeperator ) +{ + rawParams.assign( "" ); + if( rawParamMap.size() ) + { + KeyValueList keyValueList; + std::string dummyStr; + + /* Push key-value pairs to a list of strings */ + keyValueList.clear(); + KeyValuePairs::const_iterator itMap = rawParamMap.begin(); + for( ; itMap != rawParamMap.end(); itMap++ ) + { + dummyStr.assign( itMap->first ); + dummyStr.append( "=" ); + if( paramsSeperator == "," ) + { + dummyStr.append( "\"" ); + } + dummyStr.append( itMap->second ); + if( paramsSeperator == "," ) + { + dummyStr.append( "\"" ); + } + keyValueList.push_back( dummyStr ); + } + + /* Sort key-value pairs based on key name */ + keyValueList.sort(); + + /* Now, form a string */ + dummyStr.assign( "" ); + KeyValueList::iterator itKeyValue = keyValueList.begin(); + for( ; itKeyValue != keyValueList.end(); itKeyValue++ ) + { + if( dummyStr.length() ) + { + dummyStr.append( paramsSeperator ); + } + dummyStr.append( itKeyValue->c_str() ); + } + rawParams.assign( dummyStr ); + } + return ( rawParams.length() ) ? true : false; +} + + +} // namespace OAuth diff --git a/3party/liboauthcpp/src/urlencode.cpp b/3party/liboauthcpp/src/urlencode.cpp new file mode 100644 index 0000000000..9ad5f3b172 --- /dev/null +++ b/3party/liboauthcpp/src/urlencode.cpp @@ -0,0 +1,102 @@ +#include "urlencode.h" +#include + +std::string char2hex( char dec ) +{ + char dig1 = (dec&0xF0)>>4; + char dig2 = (dec&0x0F); + if ( 0<= dig1 && dig1<= 9) dig1+=48; //0,48 in ascii + if (10<= dig1 && dig1<=15) dig1+=65-10; //A,65 in ascii + if ( 0<= dig2 && dig2<= 9) dig2+=48; + if (10<= dig2 && dig2<=15) dig2+=65-10; + + std::string r; + r.append( &dig1, 1); + r.append( &dig2, 1); + return r; +} + +std::string urlencode( const std::string &c, URLEncodeType enctype) +{ + + std::string escaped; + int max = c.length(); + for(int i=0; i +#include + +std::string char2hex( char dec ); +enum URLEncodeType { + URLEncode_Everything, + URLEncode_Path, + URLEncode_QueryKey, + URLEncode_QueryValue, +}; +std::string urlencode( const std::string &c, URLEncodeType enctype ); + +#endif // __URLENCODE_H__