// 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)); }
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); }
/*++ * @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; }
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; }
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 ); std::string separator; bool do_urlencode; if (string_type == AuthorizationHeaderString) { separator = ","; do_urlencode = false; } else { /*if (string_type == 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); } // 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<std::string> 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::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::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 ) { StringConvertFunction encoder = (urlEncodeValues ? URLEncode : PassThrough); /* Generate nonce and timestamp if required */ if( generateTimestamp ) { generateNonceTimeStamp(); } /* Consumer key and its value */ keyValueMap[Defaults::CONSUMERKEY_KEY] = encoder(mConsumer->key()); /* Nonce key and its value */ keyValueMap[Defaults::NONCE_KEY] = encoder(m_nonce); /* Signature if supplied */ if( oauthSignature.length() ) { keyValueMap[Defaults::SIGNATURE_KEY] = encoder(oauthSignature); } /* Signature method, only HMAC-SHA1 as of now */ keyValueMap[Defaults::SIGNATUREMETHOD_KEY] = std::string( "HMAC-SHA1" ); /* Timestamp */ keyValueMap[Defaults::TIMESTAMP_KEY] = encoder(m_timeStamp); /* Token */ if( mToken && mToken->key().length() ) { keyValueMap[Defaults::TOKEN_KEY] = encoder(mToken->key()); } /* Verifier */ if( includeOAuthVerifierPin && mToken && mToken->pin().length() ) { keyValueMap[Defaults::VERIFIER_KEY] = encoder(mToken->pin()); } /* Version */ 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 ); keyValueMap[dummyStrKey] = dummyStrValue; } } return ( keyValueMap.size() ) ? true : false; }