/*
Copyright © 2012-20 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "stdafx.h"
static CMStringA OAuthNormalizeUrl(const CMStringA &url)
{
CMStringA normalUrl = url;
int idx = normalUrl.Find('#');
if (idx != -1)
normalUrl.Truncate(idx);
idx = normalUrl.Find('?');
if (idx != -1)
normalUrl.Truncate(idx);
return normalUrl;
}
static int CompareCMString(const CMStringA *p1, const CMStringA *p2)
{
return mir_strcmp(p1->c_str(), p2->c_str());
}
static CMStringA OAuthNormalizeRequestParameters(const CMStringA &requestParameters, bool bShort)
{
OBJLIST params(10);
Split(requestParameters, params, '&');
LIST sorted(params.getCount(), CompareCMString);
for (auto &it : params)
sorted.insert(it);
CMStringA res;
for (auto &it : sorted) {
if (!res.IsEmpty())
res.AppendChar(bShort ? '&' : ',');
if (!bShort) {
it->Replace("=", "=\"");
it->AppendChar('\"');
}
res += *it;
}
return res;
}
CMStringA CTwitterProto::BuildSignedOAuthParameters(const CMStringA &request, const CMStringA &url, const char *httpMethod, const char *postData)
{
CMStringA timestamp(FORMAT, "%lld", _time64(0));
CMStringA nonce = OAuthCreateNonce();
// create oauth requestParameters
auto *req = new AsyncHttpRequest(!mir_strcmp(httpMethod, "GET") ? REQUEST_GET : REQUEST_POST, url);
req << CHAR_PARAM("oauth_timestamp", timestamp) << CHAR_PARAM("oauth_nonce", nonce) << CHAR_PARAM("oauth_version", "1.0")
<< CHAR_PARAM("oauth_signature_method", "HMAC-SHA1") << CHAR_PARAM("oauth_consumer_key", OAUTH_CONSUMER_KEY) << CHAR_PARAM("oauth_callback", "oob");
// add the request token if found
if (!m_szAccessToken.IsEmpty())
req << CHAR_PARAM("oauth_token", m_szAccessToken);
// add the authorization pin if found
if (!m_szPin.IsEmpty())
req << CHAR_PARAM("oauth_verifier", m_szPin);
// create a parameter list containing both oauth and original parameters
// this will be used to create the parameter signature
if (!request.IsEmpty()) {
req->m_szParam.AppendChar('&');
req->m_szParam.Append(request);
}
if (!mir_strlen(postData)) {
req->m_szParam.AppendChar('&');
req->m_szParam.Append(postData);
}
// prepare a signature base, a carefully formatted string containing
// all of the necessary information needed to generate a valid signature
CMStringA normalUrl = OAuthNormalizeUrl(url);
CMStringA normalizedParameters = OAuthNormalizeRequestParameters(req->m_szParam, true);
CMStringA signatureBase = CMStringA(httpMethod) + "&" + mir_urlEncode(normalUrl) + "&" + mir_urlEncode(normalizedParameters);
// obtain a signature and add it to header requestParameters
CMStringA signature = OAuthCreateSignature(signatureBase, OAUTH_CONSUMER_SECRET, m_szAccessTokenSecret);
req << CHAR_PARAM("oauth_signature", signature);
CMStringA ret = OAuthNormalizeRequestParameters(req->m_szParam, false);
delete req;
return ret;
}
CMStringA CTwitterProto::UrlGetQuery(const CMStringA &url)
{
CMStringA query = url;
int idx = query.Find('?');
if (idx == -1)
return "";
query.Delete(0, idx+1);
idx = query.Find('#');
if (idx != -1)
query.Truncate(idx);
return query;
}
// OAuthWebRequest used for all OAuth related queries
//
// consumerKey and consumerSecret - must be provided for every call, they identify the application
// oauthToken and oauthTokenSecret - need to be provided for every call, except for the first token request before authorizing
// pin - only used during authorization, when the user enters the PIN they received from the CTwitterProto website
CMStringA CTwitterProto::OAuthWebRequestSubmit(const CMStringA &url, const char *httpMethod, const char *postData)
{
CMStringA query = UrlGetQuery(url);
CMStringA oauthSignedParameters = BuildSignedOAuthParameters(query, url, httpMethod, postData);
oauthSignedParameters.Insert(0, "OAuth ");
return oauthSignedParameters;
}
static char ALPHANUMERIC[62+1] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
CMStringA CTwitterProto::OAuthCreateNonce()
{
CMStringA nonce;
for (int i = 0; i <= 16; ++i)
nonce.AppendChar(ALPHANUMERIC[rand() % 62]);
return nonce;
}
CMStringA CTwitterProto::OAuthCreateSignature(const CMStringA &signatureBase, const CMStringA &consumerSecret, const CMStringA &requestTokenSecret)
{
// URL encode key elements
CMStringA key = mir_urlEncode(consumerSecret) + "&" + mir_urlEncode(requestTokenSecret);
BYTE digest[MIR_SHA1_HASH_SIZE];
unsigned int len;
HMAC(EVP_sha1(), key.c_str(), (int)key.GetLength(), (PBYTE)signatureBase.c_str(), signatureBase.GetLength(), digest, &len);
return CMStringA(ptrA(mir_base64_encode(digest, sizeof(digest))));
}