goko4 / twitcurl

Automatically exported from code.google.com/p/twitcurl
0 stars 0 forks source link

oauth lib doesn't support multiple key value pairs for signature #11

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
The getOAuthHeader method doesn't take into account that there may be more than 
one query string value or more than one post value and as such, if you put more 
than one in, the signature will not get generated properly.

I found this by extending twitcurl to allow reply functionality where you must 
pass in a second post variable to signify what id you are replying to.  I also 
had the problem with the query string trying to set the optional parameters to 
return entities.

I got it to work and here are the code modifications I used.  Basically all I 
did was add a method to parse the key value pair to a map and then use that 
data for the signature.  It looks at both url and post data and combines them 
into one map.  I know this can be simplified and needs a bit of refactoring, 
but it works.  I also passed a blank string to buildOAuthTokenKeyValuePairs for 
the rawData since I am parsing it and adding it to the rawKeyValuePairs.  It 
made more sense to me to do all the parsing at once instead of haivng the 
buildOAuthTokenKeyValuePairs method parse the rawData.

bool oAuth::getOAuthHeader( const eOAuthHttpRequestType eType,
                            const std::string& rawUrl,
                            const std::string& rawData,
                            std::string& oAuthHttpHeader,
                            const bool includeOAuthVerifierPin )
{
    oAuthKeyValuePairs rawKeyValuePairs;
    std::string rawParams( "" );
    std::string oauthSignature( "" );
    std::string paramsSeperator( "" );
    std::string pureUrl( rawUrl );

    /* Clear header string initially */
    oAuthHttpHeader.assign( "" );
    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 );

        std::map<std::string,std::string> querystring;
        char* data = const_cast<char *>(dataPart.c_str());
        querystring = ParseKeyValuePair(data);

        std::map<std::string, std::string>::iterator qspos;
        for(qspos = querystring.begin(); qspos != querystring.end(); ++qspos)
        {
            rawKeyValuePairs[qspos->first] = urlencode(qspos->second);
        }
    }

    if(rawData != "")
    {
        std::map<std::string,std::string> querystring;
        char* data = const_cast<char *>(rawData.c_str());
        querystring = ParseKeyValuePair(data);

        std::map<std::string, std::string>::iterator qspos;
        for(qspos = querystring.begin(); qspos != querystring.end(); ++qspos)
        {
            rawKeyValuePairs[qspos->first] = urlencode(qspos->second);
        }
    }

    /* Build key-value pairs needed for OAuth request token, without signature */
    buildOAuthTokenKeyValuePairs( includeOAuthVerifierPin, "", std::string( "" ), rawKeyValuePairs );

    /* 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 );

    /* Get OAuth header in string format */
    paramsSeperator = ",";
    getStringFromOAuthKeyValuePairs( rawKeyValuePairs, rawParams, paramsSeperator );

    /* Build authorization header */
    oAuthHttpHeader.assign( oAuthLibDefaults::OAUTHLIB_AUTHHEADER_STRING.c_str() );
    oAuthHttpHeader.append( rawParams.c_str() );

    return ( oAuthHttpHeader.length() > 0 ) ? true : false;
}

std::map<std::string,std::string> oAuth::ParseKeyValuePair(char * data)
{
    std::map<std::string,std::string> valuemap;

    std::string tmpkey, tmpvalue;
    std::string *tmpstr = &tmpkey;
    register char* raw_get = data;
    if (raw_get==NULL) {
        valuemap.clear();
        return valuemap;
    }
    while (*raw_get != '\0') {
        if (*raw_get=='&') {
            if (tmpkey!="") {
                valuemap[UrlDecode(tmpkey)] = UrlDecode(tmpvalue);
            }
            tmpkey.clear();
            tmpvalue.clear();
            tmpstr = &tmpkey;
        } else if (*raw_get=='=') {
            tmpstr = &tmpvalue;
        } else {
            (*tmpstr) += (*raw_get);
        }
        raw_get++;
    }
    //enter the last pair to the map
    if (tmpkey!="") {
        valuemap[UrlDecode(tmpkey)] = UrlDecode(tmpvalue);
        tmpkey.clear();
        tmpvalue.clear();
    }

    return valuemap;
}

Original issue reported on code.google.com by dtmorgri...@gmail.com on 20 Dec 2010 at 2:57

GoogleCodeExporter commented 8 years ago
Thanks for the heads-up and code. I have integrated it to the trunk and branch 
:)

Original comment by swatkat....@gmail.com on 6 Mar 2011 at 4:00