summaryrefslogtreecommitdiff
path: root/protocols
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2020-02-18 21:36:18 +0300
committerGeorge Hazan <ghazan@miranda.im>2020-02-18 21:36:18 +0300
commit5f1b32a8d66e21430457a5e33b58b27fa10110af (patch)
tree48f17e495ad4d049f3fe28a4c00f06c66662edbb /protocols
parent410ea51a6e4821beb207537c0e4f49d7bf343b93 (diff)
Twitter:
- major code rework due to changed API; - whole bunch of std++ trash wiped out; - modern http requests implementation using MHttpRequest; - first digit in version changed
Diffstat (limited to 'protocols')
-rw-r--r--protocols/Twitter/src/StringUtil.cpp101
-rw-r--r--protocols/Twitter/src/StringUtil.h50
-rw-r--r--protocols/Twitter/src/chat.cpp34
-rw-r--r--protocols/Twitter/src/connection.cpp505
-rw-r--r--protocols/Twitter/src/contacts.cpp83
-rw-r--r--protocols/Twitter/src/http.h10
-rw-r--r--protocols/Twitter/src/main.cpp3
-rw-r--r--protocols/Twitter/src/oauth.cpp361
-rw-r--r--protocols/Twitter/src/proto.cpp96
-rw-r--r--protocols/Twitter/src/proto.h99
-rw-r--r--protocols/Twitter/src/resource.h2
-rw-r--r--protocols/Twitter/src/stdafx.h69
-rw-r--r--protocols/Twitter/src/theme.cpp13
-rw-r--r--protocols/Twitter/src/twitter.cpp296
-rw-r--r--protocols/Twitter/src/twitter.h110
-rw-r--r--protocols/Twitter/src/ui.cpp33
-rw-r--r--protocols/Twitter/src/utility.cpp125
-rw-r--r--protocols/Twitter/src/utility.h80
-rw-r--r--protocols/Twitter/twitter.vcxproj5
19 files changed, 670 insertions, 1405 deletions
diff --git a/protocols/Twitter/src/StringUtil.cpp b/protocols/Twitter/src/StringUtil.cpp
index a902495c20..243082fcec 100644
--- a/protocols/Twitter/src/StringUtil.cpp
+++ b/protocols/Twitter/src/StringUtil.cpp
@@ -1,40 +1,34 @@
/*
+Copyright © 2012-20 Miranda NG team
+Copyright © 2009 Jim Porter
-Copyright (c) 2010 Brook Miles
+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.
-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.
+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 <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
-void Split(const wstring& str, std::vector<wstring>& out, wchar_t sep, bool includeEmpty)
+void Split(const CMStringA &str, OBJLIST<CMStringA> &out, char sep, bool includeEmpty)
{
- unsigned start = 0;
- unsigned end = 0;
+ int start = 0;
+ int end = 0;
while (true) {
- if (end == str.size() || str[end] == sep) {
+ if (end == str.GetLength() || str[end] == sep) {
if (end > start || includeEmpty)
- out.push_back(str.substr(start, end - start));
+ out.insert(new CMStringA(str.Mid(start, end - start)));
- if (end == str.size())
+ if (end == str.GetLength())
break;
++end;
@@ -44,55 +38,26 @@ void Split(const wstring& str, std::vector<wstring>& out, wchar_t sep, bool incl
}
}
-wstring GetWord(const wstring& str, unsigned index, bool getRest)
+StringPairs ParseQueryString(const CMStringA &query)
{
- unsigned start = 0;
- unsigned end = 0;
-
- unsigned count = 0;
-
- while (true) {
- if (end == str.size() || str[end] == ' ') {
- if (end > start) {
- if (count == index) {
- if (getRest)
- return str.substr(start);
-
- return str.substr(start, end - start);
- }
- ++count;
- }
+ StringPairs ret;
- if (end == str.size())
- break;
+ OBJLIST<CMStringA> queryParams(10);
+ Split(query, queryParams, '&', false);
- ++end;
- start = end;
- }
- else ++end;
+ for (auto &it : queryParams) {
+ OBJLIST<CMStringA> paramElements(2);
+ Split(*it, paramElements, '=', true);
+ if (paramElements.getCount() == 2)
+ ret[paramElements[0]] = paramElements[1];
}
- return L"";
+ return ret;
}
-// takes a pointer to a string, and does an inplace replace of all the characters "from" found
-// within the string with "to". returns the pointer to the string which is kinda silly IMO
-std::string& replaceAll(std::string& context, const std::string& from, const std::string& to)
+void htmlEntitiesDecode(CMStringA &context)
{
- size_t lookHere = 0;
- size_t foundHere;
- while ((foundHere = context.find(from, lookHere)) != std::string::npos) {
- context.replace(foundHere, from.size(), to);
- lookHere = foundHere + to.size();
- }
- return context;
+ context.Replace("&amp;", "&");
+ context.Replace("&quot;", "\"");
+ context.Replace("&lt;", "<");
+ context.Replace("&gt;", ">");
}
-
-std::string& htmlEntitiesDecode(std::string& context)
-{
- replaceAll(context, "&amp;", "&");
- replaceAll(context, "&quot;", "\"");
- replaceAll(context, "&lt;", "<");
- replaceAll(context, "&gt;", ">");
-
- return context;
-} \ No newline at end of file
diff --git a/protocols/Twitter/src/StringUtil.h b/protocols/Twitter/src/StringUtil.h
deleted file mode 100644
index 51a13123e8..0000000000
--- a/protocols/Twitter/src/StringUtil.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-
-Copyright (c) 2010 Brook Miles
-
-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.
-
-*/
-
-#ifndef _STRINGUTIL_H_INCLUDED_
-#define _STRINGUTIL_H_INCLUDED_
-
-void Split(const wstring& str, std::vector<wstring>& out, wchar_t sep, bool includeEmpty);
-wstring GetWord(const wstring& str, unsigned index, bool getRest = false);
-
-std::string& replaceAll(std::string& context, const std::string& from, const std::string& to);
-
-std::string& htmlEntitiesDecode(std::string& context);
-
-inline std::string WideToUTF8(const std::wstring& str)
-{
- return (char*)ptrA(mir_utf8encodeW(str.c_str()));
-}
-
-inline std::wstring UTF8ToWide(const std::string& str)
-{
- return (wchar_t*)ptrW(mir_utf8decodeW(str.c_str()));
-}
-
-inline bool Compare(const wstring& one, const wstring& two, bool caseSensitive)
-{
- return caseSensitive ? (one == two) : (mir_wstrcmpi(one.c_str(), two.c_str()) == 0);
-}
-
-#endif//_STRINGUTIL_H_INCLUDED_
diff --git a/protocols/Twitter/src/chat.cpp b/protocols/Twitter/src/chat.cpp
index a9e75c3392..72705dd926 100644
--- a/protocols/Twitter/src/chat.cpp
+++ b/protocols/Twitter/src/chat.cpp
@@ -17,22 +17,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
-#include "proto.h"
#include <set>
#include <ctime>
-void TwitterProto::UpdateChat(const twitter_user &update)
+void CTwitterProto::UpdateChat(const twitter_user &update)
{
GCEVENT gce = { m_szModuleName, m_szChatId, GC_EVENT_MESSAGE };
gce.dwFlags = GCEF_UTF8 + GCEF_ADDTOLOG;
- gce.bIsMe = (update.username == twit_.get_username());
+ gce.bIsMe = (update.username.c_str() == m_szUserName);
gce.pszUID.a = update.username.c_str();
- //TODO: write code here to replace % with %% in update.status.text (which is a std::string)
+ //TODO: write code here to replace % with %% in update.status.text (which is a CMStringA)
- std::string chatText = update.status.text;
-
- replaceAll(chatText, "%", "%%");
+ CMStringA chatText = update.status.text.c_str();
+ chatText.Replace("%", "%%");
gce.pszText.a = chatText.c_str();
gce.time = static_cast<DWORD>(update.status.time);
@@ -51,7 +49,7 @@ void TwitterProto::UpdateChat(const twitter_user &update)
mir_free(const_cast<wchar_t*>(gce.pszText.w));
}
-int TwitterProto::OnChatOutgoing(WPARAM, LPARAM lParam)
+int CTwitterProto::OnChatOutgoing(WPARAM, LPARAM lParam)
{
GCHOOK *hook = reinterpret_cast<GCHOOK*>(lParam);
if (mir_strcmp(hook->si->pszModule, m_szModuleName))
@@ -61,13 +59,11 @@ int TwitterProto::OnChatOutgoing(WPARAM, LPARAM lParam)
case GC_USER_MESSAGE:
debugLogW(L"**Chat - Outgoing message: %s", hook->ptszText);
{
- T2Utf text(hook->ptszText);
-
- std::string tweet(text);
- replaceAll(tweet, "%%", "%"); // the chat plugin will turn "%" into "%%", so we have to change it back :/
+ CMStringA tweet(T2Utf(hook->ptszText).get());
+ tweet.Replace("%%", "%"); // the chat plugin will turn "%" into "%%", so we have to change it back :/
char *varTweet = mir_strdup(tweet.c_str());
- ForkThread(&TwitterProto::SendTweetWorker, varTweet);
+ ForkThread(&CTwitterProto::SendTweetWorker, varTweet);
}
break;
@@ -83,7 +79,7 @@ int TwitterProto::OnChatOutgoing(WPARAM, LPARAM lParam)
}
// TODO: remove nick?
-void TwitterProto::AddChatContact(const char *name, const char *nick)
+void CTwitterProto::AddChatContact(const char *name, const char *nick)
{
GCEVENT gce = { m_szModuleName, m_szChatId, GC_EVENT_JOIN };
gce.dwFlags = GCEF_UTF8;
@@ -94,7 +90,7 @@ void TwitterProto::AddChatContact(const char *name, const char *nick)
Chat_Event(&gce);
}
-void TwitterProto::DeleteChatContact(const char *name)
+void CTwitterProto::DeleteChatContact(const char *name)
{
GCEVENT gce = { m_szModuleName, m_szChatId, GC_EVENT_PART };
gce.dwFlags = GCEF_UTF8;
@@ -103,7 +99,7 @@ void TwitterProto::DeleteChatContact(const char *name)
Chat_Event(&gce);
}
-INT_PTR TwitterProto::OnJoinChat(WPARAM, LPARAM suppress)
+INT_PTR CTwitterProto::OnJoinChat(WPARAM, LPARAM suppress)
{
// ***** Create the group chat session
SESSION_INFO *si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, m_tszUserName, m_tszUserName);
@@ -114,7 +110,7 @@ INT_PTR TwitterProto::OnJoinChat(WPARAM, LPARAM suppress)
Chat_AddGroup(si, TranslateT("Normal"));
// ***** Hook events
- HookProtoEvent(ME_GC_EVENT, &TwitterProto::OnChatOutgoing);
+ HookProtoEvent(ME_GC_EVENT, &CTwitterProto::OnChatOutgoing);
// Note: Initialization will finish up in SetChatStatus, called separately
if (!suppress)
@@ -124,7 +120,7 @@ INT_PTR TwitterProto::OnJoinChat(WPARAM, LPARAM suppress)
return 0;
}
-INT_PTR TwitterProto::OnLeaveChat(WPARAM, LPARAM)
+INT_PTR CTwitterProto::OnLeaveChat(WPARAM, LPARAM)
{
in_chat_ = false;
@@ -133,7 +129,7 @@ INT_PTR TwitterProto::OnLeaveChat(WPARAM, LPARAM)
return 0;
}
-void TwitterProto::SetChatStatus(int status)
+void CTwitterProto::SetChatStatus(int status)
{
if (status == ID_STATUS_ONLINE) {
// Add all friends to contact list
diff --git a/protocols/Twitter/src/connection.cpp b/protocols/Twitter/src/connection.cpp
index b3db9bf89e..278afcb4ae 100644
--- a/protocols/Twitter/src/connection.cpp
+++ b/protocols/Twitter/src/connection.cpp
@@ -17,12 +17,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
-#include "proto.h"
-#include "twitter.h"
-void CALLBACK TwitterProto::APC_callback(ULONG_PTR p)
+AsyncHttpRequest::AsyncHttpRequest(int type, const char *szUrl)
{
- reinterpret_cast<TwitterProto*>(p)->debugLogA("***** Executing APC");
+ requestType = type;
+ m_szUrl = szUrl;
+}
+
+void CALLBACK CTwitterProto::APC_callback(ULONG_PTR p)
+{
+ reinterpret_cast<CTwitterProto*>(p)->debugLogA("***** Executing APC");
}
template<typename T>
@@ -47,7 +51,7 @@ inline static INT_PTR db_pod_set(MCONTACT hContact, const char *module, const ch
return db_set_blob(hContact, module, setting, &val, sizeof(T));
}
-void TwitterProto::SignOn(void*)
+void CTwitterProto::SignOn(void*)
{
debugLogA("***** Beginning SignOn process");
mir_cslock lck(signon_lock_);
@@ -69,54 +73,27 @@ void TwitterProto::SignOn(void*)
setAllContactStatuses(ID_STATUS_ONLINE);
SetChatStatus(ID_STATUS_ONLINE);
- hMsgLoop_ = ForkThreadEx(&TwitterProto::MessageLoop, nullptr, nullptr);
+ hMsgLoop_ = ForkThreadEx(&CTwitterProto::MessageLoop, nullptr, nullptr);
}
debugLogA("***** SignOn complete");
}
-bool TwitterProto::NegotiateConnection()
+bool CTwitterProto::NegotiateConnection()
{
debugLogA("***** Negotiating connection with Twitter");
disconnectionCount = 0;
// saving the current status to a temp var
int old_status = m_iStatus;
- DBVARIANT dbv;
-
- wstring oauthToken;
- wstring oauthTokenSecret;
- wstring oauthAccessToken;
- wstring oauthAccessTokenSecret;
- string screenName;
-
- INT_PTR dbTOK = getWString(TWITTER_KEY_OAUTH_TOK, &dbv);
- if (!dbTOK) {
- oauthToken = dbv.pwszVal;
- db_free(&dbv);
- }
-
- INT_PTR dbTOKSec = getWString(TWITTER_KEY_OAUTH_TOK_SECRET, &dbv);
- if (!dbTOKSec) {
- oauthTokenSecret = dbv.pwszVal;
- db_free(&dbv);
- }
- INT_PTR dbName = getString(TWITTER_KEY_NICK, &dbv);
- if (!dbName) {
- screenName = dbv.pszVal;
- db_free(&dbv);
- }
- else {
- dbName = getString(TWITTER_KEY_UN, &dbv);
- if (!dbName) {
- screenName = dbv.pszVal;
- setString(TWITTER_KEY_NICK, dbv.pszVal);
- db_free(&dbv);
- }
- }
+ CMStringA szOauthToken = getMStringA(TWITTER_KEY_OAUTH_TOK);
+ CMStringA szOauthTokenSecret = getMStringA(TWITTER_KEY_OAUTH_TOK_SEC);
+ m_szUserName = getMStringA(TWITTER_KEY_NICK);
+ if (m_szUserName.IsEmpty())
+ m_szUserName = getMStringA(TWITTER_KEY_UN);
- // twitter changed the base URL in v1.1 of the API, I don't think users will need to modify it, so
+ // CTwitterProto changed the base URL in v1.1 of the API, I don't think users will need to modify it, so
// i'll be forcing it to the new API URL here. After a while I can get rid of this as users will
// have either had this run at least once, or have reset their miranda profile. 14/10/2012
if (getByte("UpgradeBaseURL", 1)) {
@@ -124,80 +101,50 @@ bool TwitterProto::NegotiateConnection()
setByte("UpgradeBaseURL", 0);
}
- if ((oauthToken.size() <= 1) || (oauthTokenSecret.size() <= 1)) {
+ if (szOauthToken.IsEmpty() || szOauthTokenSecret.IsEmpty()) {
// first, reset all the keys so we can start fresh
- resetOAuthKeys();
debugLogA("**NegotiateConnection - Reset OAuth Keys");
+ resetOAuthKeys();
- debugLogA("**NegotiateConnection - Setting Consumer Keys...");
- twit_.set_credentials("", OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, oauthToken, oauthTokenSecret, L"", false);
+ m_szUserName.Empty();
debugLogA("**NegotiateConnection - Requesting oauthTokens");
- http::response resp = twit_.request_token();
+ http::response resp = request_token();
- wstring rdata_WSTR = UTF8ToWide(resp.data);
+ StringPairs response = ParseQueryString(resp.data);
+ szOauthToken = response[L"oauth_token"];
+ szOauthTokenSecret = response[L"oauth_token_secret"];
- OAuthParameters response = twit_.ParseQueryString(rdata_WSTR);
- oauthToken = response[L"oauth_token"];
- oauthTokenSecret = response[L"oauth_token_secret"];
-
- if (oauthToken.length() < 1) {
- ShowPopup("OAuth Tokens not received, check your internet connection?", 1);
+ if (szOauthToken.IsEmpty()) {
+ ShowPopup("OAuth token not received, check your internet connection?", 1);
debugLogA("**NegotiateConnection - OAuth tokens not received, stopping before we open the web browser..");
return false;
}
// write those bitches to the db foe latta
- setWString(TWITTER_KEY_OAUTH_TOK, oauthToken.c_str());
- setWString(TWITTER_KEY_OAUTH_TOK_SECRET, oauthTokenSecret.c_str());
+ setString(TWITTER_KEY_OAUTH_TOK, m_szAccessToken = szOauthToken);
+ setString(TWITTER_KEY_OAUTH_TOK_SEC, m_szAccessTokenSecret = szOauthTokenSecret);
// this looks like bad code.. can someone clean this up please? or confirm that it's ok
- wchar_t buf[1024] = {};
- mir_snwprintf(buf, L"https://api.twitter.com/oauth/authorize?oauth_token=%s", oauthToken.c_str());
-
- debugLogW(L"**NegotiateConnection - Launching %s", buf);
- ShellExecute(nullptr, L"open", buf, nullptr, nullptr, SW_SHOWNORMAL);
+ char buf[1024];
+ mir_snprintf(buf, "https://api.twitter.com/oauth/authorize?oauth_token=%s", szOauthToken.c_str());
+ debugLogA("**NegotiateConnection - Launching %s", buf);
+ Utils_OpenUrl(buf);
ShowPinDialog();
}
- if (!getWString(TWITTER_KEY_GROUP, &dbv)) {
- Clist_GroupCreate(0, dbv.pwszVal);
- db_free(&dbv);
- }
-
- bool realAccessTok = false;
- bool realAccessTokSecret = false;
+ ptrW wszGroup(getWStringA(TWITTER_KEY_GROUP));
+ if (wszGroup)
+ Clist_GroupCreate(0, wszGroup);
// remember, dbTOK is 0 (false) if the db setting has returned something
- dbTOK = getWString(TWITTER_KEY_OAUTH_ACCESS_TOK, &dbv);
- if (!dbTOK) {
- oauthAccessToken = dbv.pwszVal;
- db_free(&dbv);
- // this bit is saying "if we have found the db key, but it contains no data, then set dbTOK to 1"
- if (oauthAccessToken.size() > 1)
- realAccessTok = true;
- else
- debugLogA("**NegotiateConnection - oauthAccesToken too small? this is.. weird.");
- }
-
- dbTOKSec = getWString(TWITTER_KEY_OAUTH_ACCESS_TOK_SECRET, &dbv);
- if (!dbTOKSec) {
- oauthAccessTokenSecret = dbv.pwszVal;
- db_free(&dbv);
- if (oauthAccessTokenSecret.size() > 1)
- realAccessTokSecret = true;
- else
- debugLogA("**NegotiateConnection - oauthAccessTokenSecret too small? weird");
- }
+ szOauthToken = getMStringA(TWITTER_KEY_OAUTH_ACCESS_TOK);
+ szOauthTokenSecret = getMStringA(TWITTER_KEY_OAUTH_ACCESS_SEC);
- if (!realAccessTok || !realAccessTokSecret) { // if we don't have one of these beasties then lets go get 'em!
- wstring pin;
+ if (szOauthToken.IsEmpty() || szOauthTokenSecret.IsEmpty()) { // if we don't have one of these beasties then lets go get 'em!
debugLogA("**NegotiateConnection - either the accessToken or accessTokenSecret was not there..");
- if (!getWString(TWITTER_KEY_OAUTH_PIN, &dbv)) {
- pin = dbv.pwszVal;
- db_free(&dbv);
- }
- else {
+ m_szPin = getMStringA(TWITTER_KEY_OAUTH_PIN);
+ if (m_szPin.IsEmpty()) {
ShowPopup(TranslateT("OAuth variables are out of sequence, they have been reset. Please reconnect and reauthorize Miranda to Twitter.com (do the PIN stuff again)"));
debugLogA("**NegotiateConnection - We don't have a PIN? this doesn't make sense. Resetting OAuth keys and setting offline.");
resetOAuthKeys();
@@ -211,12 +158,9 @@ bool TwitterProto::NegotiateConnection()
return false;
}
- debugLogA("**NegotiateConnection - Setting Consumer Keys and PIN...");
-
- twit_.set_credentials("", OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, oauthToken, oauthTokenSecret, pin, false);
-
debugLogA("**NegotiateConnection - requesting access tokens...");
- http::response accessResp = twit_.request_access_tokens();
+ http::response accessResp = request_access_tokens();
+ m_szPin.Empty();
if (accessResp.code != 200) {
debugLogA("**NegotiateConnection - Failed to get Access Tokens, HTTP response code is: %d", accessResp.code);
ShowPopup(TranslateT("Failed to get Twitter Access Tokens, please go offline and try again. If this keeps happening, check your internet connection."));
@@ -232,45 +176,37 @@ bool TwitterProto::NegotiateConnection()
return false;
}
- else {
- debugLogA("**NegotiateConnection - Successfully retrieved Access Tokens");
- wstring rdata_WSTR2 = UTF8ToWide(accessResp.data);
+ debugLogA("**NegotiateConnection - Successfully retrieved Access Tokens");
- OAuthParameters accessTokenParameters = twit_.ParseQueryString(rdata_WSTR2);
- oauthAccessToken = accessTokenParameters[L"oauth_token"];
- oauthAccessTokenSecret = accessTokenParameters[L"oauth_token_secret"];
- screenName = WideToUTF8(accessTokenParameters[L"screen_name"]);
- debugLogA("**NegotiateConnection - screen name is %s", screenName.c_str());
+ StringPairs accessTokenParameters = ParseQueryString(accessResp.data);
+ m_szAccessToken = accessTokenParameters[L"oauth_token"];
+ m_szAccessTokenSecret = accessTokenParameters[L"oauth_token_secret"];
+ m_szUserName = accessTokenParameters[L"screen_name"];
+ debugLogA("**NegotiateConnection - screen name is %s", m_szUserName.c_str());
- // save em
- setWString(TWITTER_KEY_OAUTH_ACCESS_TOK, oauthAccessToken.c_str());
- setWString(TWITTER_KEY_OAUTH_ACCESS_TOK_SECRET, oauthAccessTokenSecret.c_str());
- setString(TWITTER_KEY_NICK, screenName.c_str());
- setString(TWITTER_KEY_UN, screenName.c_str());
- }
+ // save em
+ setUString(TWITTER_KEY_OAUTH_ACCESS_TOK, m_szAccessToken);
+ setUString(TWITTER_KEY_OAUTH_ACCESS_SEC, m_szAccessTokenSecret);
+ setUString(TWITTER_KEY_NICK, m_szUserName);
+ setUString(TWITTER_KEY_UN, m_szUserName);
}
-
- if (!getString(TWITTER_KEY_BASEURL, &dbv)) {
- mir_cslock s(twitter_lock_);
- twit_.set_base_url(dbv.pszVal);
- db_free(&dbv);
+ else {
+ m_szAccessToken = szOauthToken;
+ m_szAccessTokenSecret = szOauthTokenSecret;
}
+ m_szBaseUrl = getMStringA(TWITTER_KEY_BASEURL);
+
debugLogA("**NegotiateConnection - Setting Consumer Keys and verifying creds...");
- if (screenName.empty()) {
+ if (m_szUserName.IsEmpty()) {
ShowPopup(TranslateT("You're missing the Nick key in the database. This isn't really a big deal, but you'll notice some minor quirks (self contact in list, no group chat outgoing message highlighting, etc). To fix it either add it manually or recreate your Miranda Twitter account"));
debugLogA("**NegotiateConnection - Missing the Nick key in the database. Everything will still work, but it's nice to have");
}
- bool success;
- {
- mir_cslock s(twitter_lock_);
- success = twit_.set_credentials(screenName, OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, oauthAccessToken, oauthAccessTokenSecret, L"", true);
- }
-
- if (!success) {
+ auto *req = new AsyncHttpRequest(REQUEST_GET, m_szBaseUrl + "1.1/account/verify_credentials.json");
+ if (Execute(req).code != 200) {
debugLogA("**NegotiateConnection - Verifying credentials failed! No internet maybe?");
ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)old_status, m_iStatus);
@@ -288,7 +224,7 @@ bool TwitterProto::NegotiateConnection()
return true;
}
-void TwitterProto::MessageLoop(void*)
+void CTwitterProto::MessageLoop(void*)
{
debugLogA("***** Entering Twitter::MessageLoop");
@@ -328,45 +264,41 @@ void TwitterProto::MessageLoop(void*)
if (m_iStatus != ID_STATUS_ONLINE)
break;
- debugLogA("***** TwitterProto::MessageLoop going to sleep...");
+ debugLogA("***** CTwitterProto::MessageLoop going to sleep...");
if (SleepEx(poll_rate * 1000, true) == WAIT_IO_COMPLETION)
break;
- debugLogA("***** TwitterProto::MessageLoop waking up...");
+ debugLogA("***** CTwitterProto::MessageLoop waking up...");
popups = true;
}
- {
- mir_cslock s(twitter_lock_);
- twit_.set_credentials("", L"", L"", L"", L"", L"", false);
- }
- debugLogA("***** Exiting TwitterProto::MessageLoop");
+ debugLogA("***** Exiting CTwitterProto::MessageLoop");
}
struct update_avatar
{
- update_avatar(MCONTACT hContact, const std::string &url) : hContact(hContact), url(url) {}
+ update_avatar(MCONTACT hContact, const CMStringA &url) : hContact(hContact), url(url) {}
MCONTACT hContact;
- std::string url;
+ CMStringA url;
};
/* void *p should always be a struct of type update_avatar */
-void TwitterProto::UpdateAvatarWorker(void *p)
+void CTwitterProto::UpdateAvatarWorker(void *p)
{
if (p == nullptr)
return;
- std::unique_ptr<update_avatar> data(static_cast<update_avatar*>(p));
- DBVARIANT dbv = { 0 };
+
+ std::unique_ptr<update_avatar> data((update_avatar*)p);
// db_get_s returns 0 when it suceeds, so if this suceeds it will return 0, or false.
// therefore if it returns 1, or true, we want to return as there is no such user.
// as a side effect, dbv now has the username in it i think
- if (getWString(data->hContact, TWITTER_KEY_UN, &dbv))
+ CMStringA username(getMStringA(data->hContact, TWITTER_KEY_UN));
+ if (username.IsEmpty())
return;
- std::string ext = data->url.substr(data->url.rfind('.')); // finds the filetype of the avatar
- std::wstring filename = GetAvatarFolder() + L'\\' + dbv.pwszVal + (wchar_t*)_A2T(ext.c_str()); // local filename and path
- db_free(&dbv);
+ CMStringA ext = data->url.Right(data->url.ReverseFind('.')); // finds the filetype of the avatar
+ CMStringW filename(FORMAT, L"%s\\%S%S", GetAvatarFolder().c_str(), username.c_str(), ext.c_str()); // local filename and path
PROTO_AVATAR_INFORMATION ai = { 0 };
ai.hContact = data->hContact;
@@ -397,7 +329,7 @@ void TwitterProto::UpdateAvatarWorker(void *p)
debugLogA("***** Done avatar: %s", data->url.c_str());
}
-void TwitterProto::UpdateAvatar(MCONTACT hContact, const std::string &url, bool force)
+void CTwitterProto::UpdateAvatar(MCONTACT hContact, const CMStringA &url, bool force)
{
DBVARIANT dbv = { 0 };
@@ -414,55 +346,53 @@ void TwitterProto::UpdateAvatar(MCONTACT hContact, const std::string &url, bool
ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &ai, 0);
}
else {
- ForkThread(&TwitterProto::UpdateAvatarWorker, new update_avatar(hContact, url));
+ ForkThread(&CTwitterProto::UpdateAvatarWorker, new update_avatar(hContact, url));
}
}
db_free(&dbv);
}
-void TwitterProto::UpdateFriends()
+void CTwitterProto::UpdateFriends()
{
- try {
- std::vector<twitter_user> friends;
- {
- mir_cslock s(twitter_lock_);
- friends = twit_.get_friends();
- }
-
- for (auto &i : friends) {
- if (i.username == twit_.get_username())
- continue;
-
- MCONTACT hContact = AddToClientList(i.username.c_str(), i.status.text.c_str());
- setUString(hContact, "Nick", i.real_name.c_str());
- UpdateAvatar(hContact, i.profile_image_url);
- }
- disconnectionCount = 0;
- debugLogA("***** Friends list updated");
+ auto *req = new AsyncHttpRequest(REQUEST_GET, m_szBaseUrl + "1.1/friends/list.json");
+ http::response resp = Execute(req);
+ if (resp.code != 200) {
+ debugLogA("Friend list reading failed");
+ return;
}
- catch (const bad_response &) {
- ++disconnectionCount;
- debugLogA("***** UpdateFriends - Bad response from server, this has happened %d time(s)", disconnectionCount);
- if (disconnectionCount > 2) {
- debugLogA("***** UpdateFriends - Too many bad responses from the server, signing off");
- SetStatus(ID_STATUS_OFFLINE);
- }
+
+ JSONNode root = JSONNode::parse(resp.data.c_str());
+ if (!root) {
+ debugLogA("unable to parse response");
+ return;
}
- catch (const std::exception &e) {
- ShowPopup((std::string("While updating friends list, an error occurred: ") + e.what()).c_str());
- debugLogA("***** Error updating friends list: %s", e.what());
+
+ for (auto &one : root["users"]) {
+ std::string username = one["screen_name"].as_string();
+ if (m_szUserName == username.c_str())
+ continue;
+
+ std::string real_name = one["name"].as_string();
+ std::string profile_image_url = one["profile_image_url"].as_string();
+ std::string status_text = one["status"]["text"].as_string();
+
+ MCONTACT hContact = AddToClientList(username.c_str(), status_text.c_str());
+ setUString(hContact, "Nick", real_name.c_str());
+ UpdateAvatar(hContact, profile_image_url.c_str());
}
+ disconnectionCount = 0;
+ debugLogA("***** Friends list updated");
}
LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
- std::string *url;
+ CMStringA *url;
switch (message) {
case WM_COMMAND:
// Get the plugin data (we need the Popup service to do it)
- url = (std::string *)PUGetPluginData(hwnd);
+ url = (CMStringA *)PUGetPluginData(hwnd);
if (url != nullptr)
Utils_OpenUrl(url->c_str());
@@ -475,7 +405,7 @@ LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM
case UM_FREEPLUGINDATA:
// After close, free
- url = (std::string *)PUGetPluginData(hwnd);
+ url = (CMStringA *)PUGetPluginData(hwnd);
delete url;
return FALSE;
}
@@ -483,7 +413,7 @@ LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM
return DefWindowProc(hwnd, message, wParam, lParam);
};
-void TwitterProto::ShowContactPopup(MCONTACT hContact, const std::string &text, const std::string *url)
+void CTwitterProto::ShowContactPopup(MCONTACT hContact, const CMStringA &text, const CMStringA *url)
{
if (!getByte(TWITTER_KEY_POPUP_SHOW))
return;
@@ -510,119 +440,162 @@ void TwitterProto::ShowContactPopup(MCONTACT hContact, const std::string &text,
popup.PluginData = (void *)url;
}
- mbcs_to_tcs(CP_UTF8, text.c_str(), popup.lpwzText, MAX_SECONDLINE);
+ wcsncpy_s(popup.lpwzText, Utf2T(text), MAX_SECONDLINE);
PUAddPopupW(&popup);
}
-void TwitterProto::UpdateStatuses(bool pre_read, bool popups, bool tweetToMsg)
+void CTwitterProto::UpdateStatuses(bool pre_read, bool popups, bool tweetToMsg)
{
- try {
- twitter::status_list updates;
- {
- mir_cslock s(twitter_lock_);
- updates = twit_.get_statuses(200, since_id_);
+ auto *req = new AsyncHttpRequest(REQUEST_GET, m_szBaseUrl + "1.1/statuses/home_timeline.json");
+ req << INT_PARAM("count", 200);
+ if (since_id_ != 0)
+ req << INT64_PARAM("since_id", since_id_);
+
+ http::response resp = Execute(req);
+ if (resp.code != 200) {
+ debugLogA("Status update failed with error %d", resp.code);
+ return;
+ }
+
+ JSONNode root = JSONNode::parse(resp.data.c_str());
+ if (!root) {
+ debugLogA("Status update failed: unable to parse response");
+ return;
+ }
+
+ for (auto &one : root) {
+ const JSONNode &pUser = one["user"];
+
+ twitter_user u;
+ u.username = pUser["screen_name"].as_string();
+ u.real_name = pUser["name"].as_string();
+ u.profile_image_url = pUser["profile_image_url"].as_string();
+
+ CMStringA retweeteesName, retweetText;
+
+ // the tweet will be truncated unless we take action. i hate you CTwitterProto API
+ const JSONNode &pStatus = one["retweeted_status"];
+ if (pStatus) {
+ // here we grab the "retweeted_status" um.. section? it's in here that all the info we need is
+ // at this point the user will get no tweets and an error popup if the tweet happens to be exactly 140 chars, start with
+ // "RT @", end in " ...", and notactually be a real retweet. it's possible but unlikely, wish i knew how to get
+ // the retweet_count variable to work :(
+ const JSONNode &pRetweet = one["retweeted_status"],
+ &pUser2 = pRetweet["user"];
+
+ retweeteesName = pUser2["screen_name"].as_string().c_str(); // the user that is being retweeted
+ retweetText = pRetweet["text"].as_string().c_str(); // their tweet in all it's untruncated glory
+
+ // fix html entities in the text
+ htmlEntitiesDecode(retweetText);
+
+ u.status.text = "RT @" + retweeteesName + " " + retweetText; // mash it together in some format people will understand
}
+ else {
+ // if it's not truncated, then the CTwitterProto API returns the native RT correctly anyway,
+ CMStringA rawText = one["text"].as_string().c_str();
+
+ // fix html entities in the text
+ htmlEntitiesDecode(rawText);
- if (!updates.empty())
- since_id_ = max(since_id_, updates[0].status.id);
-
- for (twitter::status_list::reverse_iterator i = updates.rbegin(); i != updates.rend(); ++i) {
-
- if (!pre_read && in_chat_)
- UpdateChat(*i);
-
- if (i->username == twit_.get_username())
- continue;
-
- MCONTACT hContact = AddToClientList(i->username.c_str(), "");
- UpdateAvatar(hContact, i->profile_image_url);
-
- // if we send twits as messages, add an unread event
- if (tweetToMsg) {
- DBEVENTINFO dbei = {};
- dbei.pBlob = (BYTE*)(i->status.text.c_str());
- dbei.cbBlob = (int)i->status.text.size() + 1;
- dbei.eventType = TWITTER_DB_EVENT_TYPE_TWEET;
- dbei.flags = DBEF_UTF;
- dbei.timestamp = static_cast<DWORD>(i->status.time);
- dbei.szModule = m_szModuleName;
- db_event_add(hContact, &dbei);
- }
- else db_set_utf(hContact, "CList", "StatusMsg", i->status.text.c_str());
-
- if (!pre_read && popups) {
- std::stringstream url;
- url << std::string("https://twitter.com/") << i->username << std::string("/status/") << i->status.id;
- Skin_PlaySound("TwitterNew");
- ShowContactPopup(hContact, i->status.text, new std::string(url.str()));
- }
+ u.status.text = rawText;
}
- db_pod_set(0, m_szModuleName, TWITTER_KEY_SINCEID, since_id_);
- disconnectionCount = 0;
- debugLogA("***** Status messages updated");
- }
- catch (const bad_response &) {
- ++disconnectionCount;
- debugLogA("***** UpdateStatuses - Bad response from server, this has happened %d time(s)", disconnectionCount);
- if (disconnectionCount > 2) {
- debugLogA("***** UpdateStatuses - Too many bad responses from the server, signing off");
- SetStatus(ID_STATUS_OFFLINE);
+ twitter_id id = _atoi64(one["id"].as_string().c_str());
+ if (id > since_id_)
+ since_id_ = id;
+
+ std::string timestr = one["created_at"].as_string();
+ u.status.time = parse_time(timestr.c_str());
+
+ if (!pre_read && in_chat_)
+ UpdateChat(u);
+
+ if (u.username.c_str() == m_szUserName)
+ continue;
+
+ MCONTACT hContact = AddToClientList(u.username.c_str(), "");
+ UpdateAvatar(hContact, u.profile_image_url.c_str());
+
+ // if we send twits as messages, add an unread event
+ if (tweetToMsg) {
+ DBEVENTINFO dbei = {};
+ dbei.pBlob = (BYTE*)(u.status.text.c_str());
+ dbei.cbBlob = (int)u.status.text.length() + 1;
+ dbei.eventType = TWITTER_DB_EVENT_TYPE_TWEET;
+ dbei.flags = DBEF_UTF;
+ dbei.timestamp = static_cast<DWORD>(u.status.time);
+ dbei.szModule = m_szModuleName;
+ db_event_add(hContact, &dbei);
+ }
+ else db_set_utf(hContact, "CList", "StatusMsg", u.status.text.c_str());
+
+ if (!pre_read && popups) {
+ Skin_PlaySound("TwitterNew");
+ ShowContactPopup(hContact, u.status.text.c_str(), new CMStringA(FORMAT, "https://twitter.com/%s/status/%lld", u.username.c_str(), u.status.id));
}
}
- catch (const std::exception &e) {
- ShowPopup((std::string("While updating status messages, an error occurred: ")
- + e.what()).c_str());
- debugLogA("***** Error updating status messages: %s", e.what());
- }
+
+ db_pod_set(0, m_szModuleName, TWITTER_KEY_SINCEID, since_id_);
+ disconnectionCount = 0;
+ debugLogA("***** Status messages updated");
+
}
-void TwitterProto::UpdateMessages(bool pre_read)
+void CTwitterProto::UpdateMessages(bool pre_read)
{
- try {
- twitter::status_list messages;
- {
- mir_cslock s(twitter_lock_);
- messages = twit_.get_direct(dm_since_id_);
- }
+ OBJLIST<twitter_user> messages(50);
- if (messages.size())
- dm_since_id_ = max(dm_since_id_, messages[0].status.id);
+ auto *req = new AsyncHttpRequest(REQUEST_GET, m_szBaseUrl + "1.1/direct_messages/events/list.json");
+ req << INT_PARAM("count", 50);
+ if (dm_since_id_ != 0)
+ req << INT64_PARAM("since_id", dm_since_id_);
- for (twitter::status_list::reverse_iterator i = messages.rbegin(); i != messages.rend(); ++i) {
- MCONTACT hContact = AddToClientList(i->username.c_str(), "");
-
- PROTORECVEVENT recv = { 0 };
- if (pre_read)
- recv.flags |= PREF_CREATEREAD;
- recv.szMessage = const_cast<char*>(i->status.text.c_str());
- recv.timestamp = static_cast<DWORD>(i->status.time);
- ProtoChainRecvMsg(hContact, &recv);
- }
+ http::response resp = Execute(req);
+ if (resp.code != 200) {
+ debugLogA("Message request failed, error %d", resp.code);
+ return;
+ }
- db_pod_set(0, m_szModuleName, TWITTER_KEY_DMSINCEID, dm_since_id_);
- disconnectionCount = 0;
- debugLogA("***** Direct messages updated");
+ JSONNode root = JSONNode::parse(resp.data.c_str());
+ if (!root) {
+ debugLogA("unable to parse response");
+ return;
}
- catch (const bad_response &) {
- ++disconnectionCount;
- debugLogA("***** UpdateMessages - Bad response from server, this has happened %d time(s)", disconnectionCount);
- if (disconnectionCount > 2) {
- debugLogA("***** UpdateMessages - Too many bad responses from the server, signing off");
- SetStatus(ID_STATUS_OFFLINE);
- }
+
+ for (auto &one : root["events"]) {
+ twitter_user *u = new twitter_user();
+ u->username = one["sender_screen_name"].as_string();
+ u->status.text = one["text"].as_string();
+ u->status.time = parse_time(one["created_at"].as_string().c_str());
+ messages.insert(u);
+
+ twitter_id id = _atoi64(one["id"].as_string().c_str());
+ if (id > dm_since_id_)
+ dm_since_id_ = id;
}
- catch (const std::exception &e) {
- ShowPopup((std::string("While updating direct messages, an error occurred: ") + e.what()).c_str());
- debugLogA("***** Error updating direct messages: %s", e.what());
+
+ for (auto &it : messages.rev_iter()) {
+ MCONTACT hContact = AddToClientList(it->username.c_str(), "");
+
+ PROTORECVEVENT recv = { 0 };
+ if (pre_read)
+ recv.flags |= PREF_CREATEREAD;
+ recv.szMessage = const_cast<char*>(it->status.text.c_str());
+ recv.timestamp = static_cast<DWORD>(it->status.time);
+ ProtoChainRecvMsg(hContact, &recv);
}
+
+ db_pod_set(0, m_szModuleName, TWITTER_KEY_DMSINCEID, dm_since_id_);
+ disconnectionCount = 0;
+ debugLogA("***** Direct messages updated");
}
-void TwitterProto::resetOAuthKeys()
+void CTwitterProto::resetOAuthKeys()
{
delSetting(TWITTER_KEY_OAUTH_ACCESS_TOK);
- delSetting(TWITTER_KEY_OAUTH_ACCESS_TOK_SECRET);
+ delSetting(TWITTER_KEY_OAUTH_ACCESS_SEC);
delSetting(TWITTER_KEY_OAUTH_TOK);
- delSetting(TWITTER_KEY_OAUTH_TOK_SECRET);
+ delSetting(TWITTER_KEY_OAUTH_TOK_SEC);
delSetting(TWITTER_KEY_OAUTH_PIN);
}
diff --git a/protocols/Twitter/src/contacts.cpp b/protocols/Twitter/src/contacts.cpp
index 02ca6b313d..7cdf32b6e3 100644
--- a/protocols/Twitter/src/contacts.cpp
+++ b/protocols/Twitter/src/contacts.cpp
@@ -17,9 +17,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
-#include "proto.h"
-void TwitterProto::AddToListWorker(void *pArg)
+void CTwitterProto::AddToListWorker(void *pArg)
{
// TODO: what happens if there is an error?
if (pArg == nullptr)
@@ -27,35 +26,26 @@ void TwitterProto::AddToListWorker(void *pArg)
char *name = static_cast<char*>(pArg);
- try {
- twitter_user user;
- {
- mir_cslock s(twitter_lock_);
- user = twit_.add_friend(name);
- }
-
+ twitter_user user;
+ if (add_friend(name, user)) {
MCONTACT hContact = UsernameToHContact(name);
- UpdateAvatar(hContact, user.profile_image_url);
- }
- catch (const std::exception &e) {
- ShowPopup((std::string("While adding a friend, an error occurred: ") + e.what()).c_str());
- debugLogA("***** Error adding friend: %s", e.what());
+ UpdateAvatar(hContact, user.profile_image_url.c_str());
}
mir_free(name);
}
-MCONTACT TwitterProto::AddToList(int, PROTOSEARCHRESULT *psr)
+MCONTACT CTwitterProto::AddToList(int, PROTOSEARCHRESULT *psr)
{
if (m_iStatus != ID_STATUS_ONLINE)
return 0;
- ForkThread(&TwitterProto::AddToListWorker, mir_utf8encodeW(psr->nick.w));
+ ForkThread(&CTwitterProto::AddToListWorker, mir_utf8encodeW(psr->nick.w));
return AddToClientList(_T2A(psr->nick.w), "");
}
// *************************
-void TwitterProto::UpdateInfoWorker(void *arg)
+void CTwitterProto::UpdateInfoWorker(void *arg)
{
MCONTACT hContact = (MCONTACT)(DWORD_PTR)arg;
twitter_user user;
@@ -66,14 +56,14 @@ void TwitterProto::UpdateInfoWorker(void *arg)
{
mir_cslock s(twitter_lock_);
- twit_.get_info(std::string(username), &user);
+ get_info(CMStringA(username), &user);
}
- UpdateAvatar(hContact, user.profile_image_url, true);
+ UpdateAvatar(hContact, user.profile_image_url.c_str(), true);
ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, nullptr);
}
-int TwitterProto::GetInfo(MCONTACT hContact, int info_type)
+int CTwitterProto::GetInfo(MCONTACT hContact, int info_type)
{
if (m_iStatus != ID_STATUS_ONLINE)
return 1;
@@ -82,7 +72,7 @@ int TwitterProto::GetInfo(MCONTACT hContact, int info_type)
return 1;
if (info_type == 0) { // From clicking "Update" in the Userinfo dialog
- ForkThread(&TwitterProto::UpdateInfoWorker, (void*)hContact);
+ ForkThread(&CTwitterProto::UpdateInfoWorker, (void*)hContact);
return 0;
}
@@ -93,14 +83,14 @@ int TwitterProto::GetInfo(MCONTACT hContact, int info_type)
struct search_query
{
- search_query(const std::wstring &_query, bool _by_email) : query(_query), by_email(_by_email)
+ search_query(const CMStringA &_query, bool _by_email) : query(_query), by_email(_by_email)
{
}
- std::wstring query;
+ CMStringW query;
bool by_email;
};
-void TwitterProto::DoSearch(void *pArg)
+void CTwitterProto::DoSearch(void *pArg)
{
if (pArg == nullptr)
return;
@@ -108,21 +98,14 @@ void TwitterProto::DoSearch(void *pArg)
search_query *query = static_cast<search_query*>(pArg);
twitter_user info;
- bool found = false;
- try {
- T2Utf p(query->query.c_str());
+ bool found ;
+ T2Utf p(query->query.c_str());
- mir_cslock s(twitter_lock_);
- if (query->by_email)
- found = twit_.get_info_by_email(p.str(), &info);
- else
- found = twit_.get_info(p.str(), &info);
- }
- catch (const std::exception &e) {
- ShowPopup((std::string("While searching for contacts, an error occurred: ") + e.what()).c_str());
- debugLogA("***** Error searching for contacts: %s", e.what());
- found = false;
- }
+ mir_cslock s(twitter_lock_);
+ if (query->by_email)
+ found = get_info_by_email(p.get(), &info);
+ else
+ found = get_info(p.get(), &info);
if (found) {
PROTOSEARCHRESULT psr = { sizeof(psr) };
@@ -141,21 +124,21 @@ void TwitterProto::DoSearch(void *pArg)
delete query;
}
-HANDLE TwitterProto::SearchBasic(const wchar_t *username)
+HANDLE CTwitterProto::SearchBasic(const wchar_t *username)
{
- ForkThread(&TwitterProto::DoSearch, new search_query(username, false));
+ ForkThread(&CTwitterProto::DoSearch, new search_query(username, false));
return (HANDLE)1;
}
-HANDLE TwitterProto::SearchByEmail(const wchar_t *email)
+HANDLE CTwitterProto::SearchByEmail(const wchar_t *email)
{
- ForkThread(&TwitterProto::DoSearch, new search_query(email, true));
+ ForkThread(&CTwitterProto::DoSearch, new search_query(email, true));
return (HANDLE)1;
}
// *************************
-void TwitterProto::GetAwayMsgWorker(void *arg)
+void CTwitterProto::GetAwayMsgWorker(void *arg)
{
MCONTACT hContact = (MCONTACT)(DWORD_PTR)arg;
if (hContact == 0)
@@ -165,13 +148,13 @@ void TwitterProto::GetAwayMsgWorker(void *arg)
ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, wszMsg);
}
-HANDLE TwitterProto::GetAwayMsg(MCONTACT hContact)
+HANDLE CTwitterProto::GetAwayMsg(MCONTACT hContact)
{
- ForkThread(&TwitterProto::GetAwayMsgWorker, (void*)hContact);
+ ForkThread(&CTwitterProto::GetAwayMsgWorker, (void*)hContact);
return (HANDLE)1;
}
-int TwitterProto::OnContactDeleted(WPARAM wParam, LPARAM)
+int CTwitterProto::OnContactDeleted(WPARAM wParam, LPARAM)
{
MCONTACT hContact = (MCONTACT)wParam;
if (m_iStatus != ID_STATUS_ONLINE)
@@ -186,7 +169,7 @@ int TwitterProto::OnContactDeleted(WPARAM wParam, LPARAM)
DeleteChatContact(dbv.pszVal);
mir_cslock s(twitter_lock_);
- twit_.remove_friend(dbv.pszVal); // Be careful about this until Miranda is fixed
+ remove_friend(dbv.pszVal); // Be careful about this until Miranda is fixed
db_free(&dbv);
}
return 0;
@@ -194,7 +177,7 @@ int TwitterProto::OnContactDeleted(WPARAM wParam, LPARAM)
// *************************
-bool TwitterProto::IsMyContact(MCONTACT hContact, bool include_chat)
+bool CTwitterProto::IsMyContact(MCONTACT hContact, bool include_chat)
{
char *proto = Proto_GetBaseAccountName(hContact);
if (proto && mir_strcmp(m_szModuleName, proto) == 0) {
@@ -205,7 +188,7 @@ bool TwitterProto::IsMyContact(MCONTACT hContact, bool include_chat)
else return false;
}
-MCONTACT TwitterProto::UsernameToHContact(const char *name)
+MCONTACT CTwitterProto::UsernameToHContact(const char *name)
{
for (auto &hContact : AccContacts()) {
if (getByte(hContact, "ChatRoom"))
@@ -218,7 +201,7 @@ MCONTACT TwitterProto::UsernameToHContact(const char *name)
return 0;
}
-MCONTACT TwitterProto::AddToClientList(const char *name, const char *status)
+MCONTACT CTwitterProto::AddToClientList(const char *name, const char *status)
{
// First, check if this contact exists
MCONTACT hContact = UsernameToHContact(name);
diff --git a/protocols/Twitter/src/http.h b/protocols/Twitter/src/http.h
index e3ed522383..0522fa53ab 100644
--- a/protocols/Twitter/src/http.h
+++ b/protocols/Twitter/src/http.h
@@ -19,20 +19,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
-#include <string>
-
namespace http
{
- enum method
- {
- get,
- post
- };
-
struct response
{
response() : code(0) {}
int code;
- std::string data;
+ CMStringA data;
};
}
diff --git a/protocols/Twitter/src/main.cpp b/protocols/Twitter/src/main.cpp
index f3d26d3199..dc320b1c45 100644
--- a/protocols/Twitter/src/main.cpp
+++ b/protocols/Twitter/src/main.cpp
@@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
#include "version.h"
-#include "proto.h"
#include "theme.h"
CMPlugin g_plugin;
@@ -40,7 +39,7 @@ PLUGININFOEX pluginInfoEx = {
};
CMPlugin::CMPlugin() :
- ACCPROTOPLUGIN<TwitterProto>("Twitter", pluginInfoEx)
+ ACCPROTOPLUGIN<CTwitterProto>("Twitter", pluginInfoEx)
{
SetUniqueId(TWITTER_KEY_UN);
}
diff --git a/protocols/Twitter/src/oauth.cpp b/protocols/Twitter/src/oauth.cpp
index fd41d8ccaa..ee42730b47 100644
--- a/protocols/Twitter/src/oauth.cpp
+++ b/protocols/Twitter/src/oauth.cpp
@@ -17,329 +17,150 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
-#include "twitter.h"
-#include "utility.h"
-
-OAuthParameters mir_twitter::BuildSignedOAuthParameters(
- const OAuthParameters& requestParameters,
- const std::wstring& url,
- const std::wstring& httpMethod,
- const OAuthParameters *postData,
- const std::wstring& consumerKey,
- const std::wstring& consumerSecret,
- const std::wstring& requestToken = L"",
- const std::wstring& requestTokenSecret = L"",
- const std::wstring& pin = L"")
-{
- wstring timestamp = OAuthCreateTimestamp();
- wstring nonce = OAuthCreateNonce();
-
- // create oauth requestParameters
- OAuthParameters oauthParameters;
-
- oauthParameters[L"oauth_timestamp"] = timestamp;
- oauthParameters[L"oauth_nonce"] = nonce;
- oauthParameters[L"oauth_version"] = L"1.0";
- oauthParameters[L"oauth_signature_method"] = L"HMAC-SHA1";
- oauthParameters[L"oauth_consumer_key"] = consumerKey;
- oauthParameters[L"oauth_callback"] = L"oob";
-
- // add the request token if found
- if (!requestToken.empty())
- oauthParameters[L"oauth_token"] = requestToken;
-
- // add the authorization pin if found
- if (!pin.empty()) {
- oauthParameters[L"oauth_verifier"] = pin;
- }
-
- // create a parameter list containing both oauth and original parameters
- // this will be used to create the parameter signature
- OAuthParameters allParameters = requestParameters;
-
- if (Compare(httpMethod, L"POST", false) && postData)
- allParameters.insert(postData->begin(), postData->end());
-
- allParameters.insert(oauthParameters.begin(), oauthParameters.end());
-
- // prepare a signature base, a carefully formatted string containing
- // all of the necessary information needed to generate a valid signature
- wstring normalUrl = OAuthNormalizeUrl(url);
- wstring normalizedParameters = OAuthNormalizeRequestParameters(allParameters);
- wstring signatureBase = OAuthConcatenateRequestElements(httpMethod, normalUrl, normalizedParameters);
-
- // obtain a signature and add it to header requestParameters
- wstring signature = OAuthCreateSignature(signatureBase, consumerSecret, requestTokenSecret);
- oauthParameters[L"oauth_signature"] = signature;
-
- return oauthParameters;
-}
-wstring mir_twitter::UrlGetQuery(const wstring& url)
+static CMStringA OAuthNormalizeUrl(const CMStringA &url)
{
- map<wstring, wstring> brokenURL = CrackURL(url);
+ CMStringA normalUrl = url;
- wstring query = brokenURL[L"extraInfo"];
- wstring::size_type q = query.find_first_of(L'?');
- if (q != wstring::npos)
- query = query.substr(q + 1);
+ int idx = normalUrl.Find('#');
+ if (idx != -1)
+ normalUrl.Truncate(idx);
- wstring::size_type h = query.find_first_of(L'#');
- if (h != wstring::npos)
- query = query.substr(0, h);
+ idx = normalUrl.Find('?');
+ if (idx != -1)
+ normalUrl.Truncate(idx);
- return query;
+ return normalUrl;
}
-// 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 twitter website
-wstring mir_twitter::OAuthWebRequestSubmit(
- const wstring& url,
- const wstring& httpMethod,
- const OAuthParameters *postData,
- const wstring& consumerKey,
- const wstring& consumerSecret,
- const wstring& oauthToken,
- const wstring& oauthTokenSecret,
- const wstring& pin)
+static int CompareCMString(const CMStringA *p1, const CMStringA *p2)
{
- wstring query = UrlGetQuery(url);
-
- OAuthParameters originalParameters = ParseQueryString(query);
-
- OAuthParameters oauthSignedParameters = BuildSignedOAuthParameters(
- originalParameters,
- url,
- httpMethod, postData,
- consumerKey, consumerSecret,
- oauthToken, oauthTokenSecret,
- pin);
- return OAuthWebRequestSubmit(oauthSignedParameters, url);
+ return mir_strcmp(p1->c_str(), p2->c_str());
}
-wstring mir_twitter::OAuthWebRequestSubmit(const OAuthParameters &parameters, const wstring&)
+static CMStringA OAuthNormalizeRequestParameters(const CMStringA &requestParameters, bool bShort)
{
- wstring oauthHeader;
-
- for (auto &it : parameters) {
- if (oauthHeader.empty())
- oauthHeader += L"OAuth ";
- else
- oauthHeader += L",";
+ OBJLIST<CMStringA> params(10);
+ Split(requestParameters, params, '&');
- wstring pair;
- pair += it.first + L"=\"" + it.second + L"\"";
- oauthHeader += pair;
- }
-
- return oauthHeader;
-}
-
-// parameters must already be URL encoded before calling BuildQueryString
-std::wstring mir_twitter::BuildQueryString(const OAuthParameters &parameters)
-{
- wstring query;
+ LIST<CMStringA> sorted(params.getCount(), CompareCMString);
+ for (auto &it : params)
+ sorted.insert(it);
- for (auto &it : parameters) {
- if (it != *parameters.begin())
- query += L"&";
+ CMStringA res;
+ for (auto &it : sorted) {
+ if (!res.IsEmpty())
+ res.AppendChar(bShort ? '&' : ',');
- wstring pair;
- pair += it.first + L"=" + it.second + L"";
- query += pair;
+ if (!bShort) {
+ it->Replace("=", "=\"");
+ it->AppendChar('\"');
+ }
+ res += *it;
}
- return query;
-}
-wstring mir_twitter::OAuthConcatenateRequestElements(const wstring& httpMethod, wstring url, const wstring& parameters)
-{
- wstring escapedUrl = UrlEncode(url);
- wstring escapedParameters = UrlEncode(parameters);
- wstring ret = httpMethod + L"&" + escapedUrl + L"&" + escapedParameters;
- return ret;
+ return res;
}
-// CrackURL.. just basically pulls apart a url into a map of wstrings:
-// scheme, domain, port, path, extraInfo, explicitPort
-// explicitPort will be 0 or 1, 0 if there was no actual port in the url,
-// and 1 if it was explicitely specified.
-// eg "http://twitter.com/blah.htm" will give:
-// http, twitter.com, 80, blah.htm, "", 0
-// "https://twitter.com:989/blah.htm?boom" will give:
-// https, twitter.com, 989, blah.htm?boom, ?boom, 1
-
-map<wstring, wstring> mir_twitter::CrackURL(wstring url)
+CMStringA CTwitterProto::BuildSignedOAuthParameters(
+ const CMStringA &requestParameters,
+ const CMStringA &url,
+ const CMStringA &httpMethod,
+ const CMStringA &postData)
{
- wstring scheme1, domain1, port1, path1, extraInfo, explicitPort;
- vector<wstring> urlToks, urlToks2;
-
- Split(url, urlToks, L':', false);
+ CMStringA timestamp(FORMAT, "%lld", _time64(0));
+ CMStringA nonce = OAuthCreateNonce();
- scheme1 = urlToks[0];
-
- if (urlToks.size() == 2) { // if there is only 1 ":" in the url
- if (Compare(scheme1, L"http", false)) {
- port1 = L"80";
- }
- else {
- port1 = L"443";
- }
+ // create oauth requestParameters
+ auto *req = new AsyncHttpRequest(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");
- Split(urlToks[1], urlToks2, L'/', false);
- domain1 = urlToks2[0];
- explicitPort = L"0";
- }
- else if (urlToks.size() == 3) { // if there are 2 ":"s in the URL, ie a port is explicitly set
- domain1 = urlToks[1].substr(2, urlToks[1].size());
+ // add the request token if found
+ if (!m_szAccessToken.IsEmpty())
+ req << CHAR_PARAM("oauth_token", m_szAccessToken);
- Split(urlToks[2], urlToks2, L'/', false);
- port1 = urlToks2[0];
+ // add the authorization pin if found
+ if (!m_szPin.IsEmpty())
+ req << CHAR_PARAM("oauth_verifier", m_szPin);
- explicitPort = L"1";
+ // create a parameter list containing both oauth and original parameters
+ // this will be used to create the parameter signature
+ if (!requestParameters.IsEmpty()) {
+ req->m_szParam.AppendChar('&');
+ req->m_szParam.Append(requestParameters);
}
- else ppro_->debugLogW(L"**CRACK - not a proper URL? doesn't have a colon. URL is %s", url.c_str());
- for (size_t i = 1; i < urlToks2.size(); ++i) {
- if (i > 1) {
- path1 += L"/";
- }
- path1 += urlToks2[i];
+ if (!postData.IsEmpty()) {
+ req->m_szParam.AppendChar('&');
+ req->m_szParam.Append(postData);
}
- wstring::size_type foundHash = path1.find(L"#");
- wstring::size_type foundQ = path1.find(L"?");
-
- if ((foundHash != wstring::npos) || (foundQ != wstring::npos)) { // if we've found a # or a ?...
- if (foundHash == wstring::npos) { // if we didn't find a #, we must have found a ?
- extraInfo = path1.substr(foundQ);
- }
- else if (foundQ == wstring::npos) { // if we didn't find a ?, we must have found a #
- extraInfo = path1.substr(foundHash);
- }
- else { // we found both a # and a ?, whichever came first we grab the sub string from there
- if (foundQ < foundHash) {
- extraInfo = path1.substr(foundQ);
- }
- else {
- extraInfo = path1.substr(foundHash);
- }
- }
- }
- else { // we have no # or ? in the path...
- extraInfo = L"";
- }
+ // 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 = httpMethod + "&" + mir_urlEncode(normalUrl) + "&" + mir_urlEncode(normalizedParameters);
- map<wstring, wstring> result;
- result[L"scheme"] = scheme1;
- result[L"domain"] = domain1;
- result[L"port"] = port1;
- result[L"path"] = path1;
- result[L"extraInfo"] = extraInfo;
- result[L"explicitPort"] = explicitPort;
+ // obtain a signature and add it to header requestParameters
+ CMStringA signature = OAuthCreateSignature(signatureBase, OAUTH_CONSUMER_SECRET, m_szAccessTokenSecret);
+ req << CHAR_PARAM("oauth_signature", signature);
- return result;
+ CMStringA ret = OAuthNormalizeRequestParameters(req->m_szParam, false);
+ delete req;
+ return ret;
}
-wstring mir_twitter::OAuthNormalizeUrl(const wstring& url)
+CMStringA CTwitterProto::UrlGetQuery(const CMStringA &url)
{
- wstring normalUrl = url;
- map<wstring, wstring> brokenURL = CrackURL(url);
+ CMStringA query = url;
- wchar_t port[10] = {};
+ int idx = query.Find('?');
+ if (idx == -1)
+ return "";
- // The port number must only be included if it is non-standard
- if (Compare(brokenURL[L"scheme"], L"http", false) && !(Compare(brokenURL[L"port"], L"80", false)) ||
- (Compare(brokenURL[L"scheme"], L"https", false) && !(Compare(brokenURL[L"port"], L"443", false))))
- {
- mir_snwprintf(port, L":%s", brokenURL[L"port"].c_str());
- }
+ query.Delete(0, idx+1);
- // InternetCrackUrl includes ? and # elements in the path,
- // which we need to strip off
- wstring pathOnly = brokenURL[L"path"];
- wstring::size_type q = pathOnly.find_first_of(L"#?");
- if (q != wstring::npos)
- pathOnly = pathOnly.substr(0, q);
+ idx = query.Find('#');
+ if (idx != -1)
+ query.Truncate(idx);
- return brokenURL[L"scheme"] + L"://" + brokenURL[L"domain"] + port + L"/" + pathOnly;
+ return query;
}
-wstring mir_twitter::OAuthNormalizeRequestParameters(const OAuthParameters& requestParameters)
+// 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)
{
- list<wstring> sorted;
- for (auto &it : requestParameters) {
- wstring param = it.first + L"=" + it.second;
- sorted.push_back(param);
- }
- sorted.sort();
-
- wstring params;
- for (auto &it : sorted) {
- if (params.size() > 0)
- params += L"&";
-
- params += it;
- }
+ CMStringA query = UrlGetQuery(url);
- return params;
+ CMStringA oauthSignedParameters = BuildSignedOAuthParameters(query, url, httpMethod, postData);
+ oauthSignedParameters.Insert(0, "OAuth ");
+ return oauthSignedParameters;
}
-OAuthParameters mir_twitter::ParseQueryString(const wstring& url)
-{
- OAuthParameters ret;
-
- vector<wstring> queryParams;
- Split(url, queryParams, L'&', false);
+static char ALPHANUMERIC[62+1] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- for (size_t i = 0; i < queryParams.size(); ++i) {
- vector<wstring> paramElements;
- Split(queryParams[i], paramElements, L'=', true);
- _ASSERTE(paramElements.size() == 2);
- if (paramElements.size() == 2)
- ret[paramElements[0]] = paramElements[1];
- }
- return ret;
-}
-
-wstring mir_twitter::OAuthCreateNonce()
+CMStringA CTwitterProto::OAuthCreateNonce()
{
- wchar_t ALPHANUMERIC[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- wstring nonce;
+ CMStringA nonce;
for (int i = 0; i <= 16; ++i)
- nonce += ALPHANUMERIC[rand() % (_countof(ALPHANUMERIC) - 1)]; // don't count null terminator in array
+ nonce.AppendChar(ALPHANUMERIC[rand() % 62]);
return nonce;
}
-wstring mir_twitter::OAuthCreateTimestamp()
-{
- __time64_t utcNow;
- _time64(&utcNow);
- _ASSERTE(utcNow != -1);
-
- wchar_t buf[100] = {};
- mir_snwprintf(buf, L"%I64u", utcNow);
-
- return buf;
-}
-
-wstring mir_twitter::OAuthCreateSignature(const wstring& signatureBase, const wstring& consumerSecret, const wstring& requestTokenSecret)
+CMStringA CTwitterProto::OAuthCreateSignature(const CMStringA &signatureBase, const CMStringA &consumerSecret, const CMStringA &requestTokenSecret)
{
// URL encode key elements
- wstring escapedConsumerSecret = UrlEncode(consumerSecret);
- wstring escapedTokenSecret = UrlEncode(requestTokenSecret);
-
- wstring key = escapedConsumerSecret + L"&" + escapedTokenSecret;
- string keyBytes = WideToUTF8(key);
+ CMStringA key = mir_urlEncode(consumerSecret) + "&" + mir_urlEncode(requestTokenSecret);
BYTE digest[MIR_SHA1_HASH_SIZE];
unsigned int len;
- string data = WideToUTF8(signatureBase);
- HMAC(EVP_sha1(), keyBytes.c_str(), (int)keyBytes.size(), (PBYTE)data.c_str(), data.size(), digest, &len);
- ptrA encoded(mir_base64_encode(digest, sizeof(digest)));
- return UrlEncode((wchar_t*)_A2T(encoded));
+ 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))));
}
diff --git a/protocols/Twitter/src/proto.cpp b/protocols/Twitter/src/proto.cpp
index c35be99ad1..1a6996192c 100644
--- a/protocols/Twitter/src/proto.cpp
+++ b/protocols/Twitter/src/proto.cpp
@@ -17,29 +17,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
-#include "proto.h"
-#include "utility.h"
#include "theme.h"
#include "ui.h"
static volatile LONG g_msgid = 1;
-TwitterProto::TwitterProto(const char *proto_name, const wchar_t *username) :
- PROTO<TwitterProto>(proto_name, username),
- m_szChatId(mir_utf8encodeW(username))
+CTwitterProto::CTwitterProto(const char *proto_name, const wchar_t *username) :
+ PROTO<CTwitterProto>(proto_name, username),
+ m_szChatId(mir_utf8encodeW(username)),
+ m_szBaseUrl("https://api.twitter.com/")
{
- CreateProtoService(PS_CREATEACCMGRUI, &TwitterProto::SvcCreateAccMgrUI);
+ CreateProtoService(PS_CREATEACCMGRUI, &CTwitterProto::SvcCreateAccMgrUI);
- CreateProtoService(PS_JOINCHAT, &TwitterProto::OnJoinChat);
- CreateProtoService(PS_LEAVECHAT, &TwitterProto::OnLeaveChat);
+ CreateProtoService(PS_JOINCHAT, &CTwitterProto::OnJoinChat);
+ CreateProtoService(PS_LEAVECHAT, &CTwitterProto::OnLeaveChat);
- CreateProtoService(PS_GETMYAVATAR, &TwitterProto::GetAvatar);
- CreateProtoService(PS_SETMYAVATAR, &TwitterProto::SetAvatar);
+ CreateProtoService(PS_GETMYAVATAR, &CTwitterProto::GetAvatar);
+ CreateProtoService(PS_SETMYAVATAR, &CTwitterProto::SetAvatar);
- HookProtoEvent(ME_OPT_INITIALISE, &TwitterProto::OnOptionsInit);
- HookProtoEvent(ME_DB_CONTACT_DELETED, &TwitterProto::OnContactDeleted);
- HookProtoEvent(ME_CLIST_PREBUILDSTATUSMENU, &TwitterProto::OnBuildStatusMenu);
+ HookProtoEvent(ME_OPT_INITIALISE, &CTwitterProto::OnOptionsInit);
+ HookProtoEvent(ME_DB_CONTACT_DELETED, &CTwitterProto::OnContactDeleted);
+ HookProtoEvent(ME_CLIST_PREBUILDSTATUSMENU, &CTwitterProto::OnBuildStatusMenu);
// Initialize hotkeys
char text[512];
@@ -82,16 +81,14 @@ TwitterProto::TwitterProto(const char *proto_name, const wchar_t *username) :
MessageBox(nullptr, error, L"Miranda NG", MB_OK | MB_ICONERROR);
}
- twit_.set_handle(this, m_hNetlibUser);
-
db_set_resident(m_szModuleName, "Homepage");
for (auto &it : AccContacts())
setString(it, "Homepage", "https://twitter.com/" + getMStringA(it, TWITTER_KEY_UN));
}
-TwitterProto::~TwitterProto()
+CTwitterProto::~CTwitterProto()
{
- twit_.Disconnect();
+ Disconnect();
if (hAvatarNetlib_)
Netlib_CloseHandle(hAvatarNetlib_);
@@ -99,7 +96,7 @@ TwitterProto::~TwitterProto()
/////////////////////////////////////////////////////////////////////////////////////////
-INT_PTR TwitterProto::GetCaps(int type, MCONTACT)
+INT_PTR CTwitterProto::GetCaps(int type, MCONTACT)
{
switch (type) {
case PFLAGNUM_1:
@@ -121,27 +118,28 @@ INT_PTR TwitterProto::GetCaps(int type, MCONTACT)
/////////////////////////////////////////////////////////////////////////////////////////
-struct send_direct
+struct TSendDirect
{
- __inline send_direct(MCONTACT _hContact, const std::string &_msg, int _msgid) :
+ __inline TSendDirect(MCONTACT _hContact, const CMStringA &_msg, int _msgid) :
hContact(_hContact), msg(_msg), msgid(_msgid)
{}
MCONTACT hContact;
- std::string msg;
+ CMStringA msg;
int msgid;
};
-void TwitterProto::SendSuccess(void *p)
+void CTwitterProto::SendSuccess(void *p)
{
if (p == nullptr)
return;
- send_direct *data = static_cast<send_direct*>(p);
+
+ auto *data = (TSendDirect*)p;
DBVARIANT dbv;
if (!db_get_s(data->hContact, m_szModuleName, TWITTER_KEY_UN, &dbv)) {
mir_cslock s(twitter_lock_);
- twit_.send_direct(dbv.pszVal, data->msg);
+ send_direct(dbv.pszVal, data->msg);
ProtoBroadcastAck(data->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)data->msgid, 0);
db_free(&dbv);
@@ -150,19 +148,19 @@ void TwitterProto::SendSuccess(void *p)
delete data;
}
-int TwitterProto::SendMsg(MCONTACT hContact, int, const char *msg)
+int CTwitterProto::SendMsg(MCONTACT hContact, int, const char *msg)
{
if (m_iStatus != ID_STATUS_ONLINE)
return 0;
int seq = InterlockedIncrement(&g_msgid);
- ForkThread(&TwitterProto::SendSuccess, new send_direct(hContact, msg, seq));
+ ForkThread(&CTwitterProto::SendSuccess, new TSendDirect(hContact, msg, seq));
return seq;
}
/////////////////////////////////////////////////////////////////////////////////////////
-int TwitterProto::SetStatus(int new_status)
+int CTwitterProto::SetStatus(int new_status)
{
int old_status = m_iStatus;
if (new_status == m_iStatus)
@@ -172,7 +170,7 @@ int TwitterProto::SetStatus(int new_status)
// 40072 - 40078 are the "online" statuses, basically every status except offline. see statusmodes.h
if (new_status >= 40072 && new_status <= 40078) {
- m_iDesiredStatus = ID_STATUS_ONLINE; //i think i have to set this so it forces the twitter proto to be online (and not away, DND, etc)
+ m_iDesiredStatus = ID_STATUS_ONLINE; //i think i have to set this so it forces the CTwitterProto proto to be online (and not away, DND, etc)
// if we're already connecting and they want to go online, BAIL! we're already trying to connect you dumbass
if (old_status == ID_STATUS_CONNECTING)
@@ -187,10 +185,10 @@ int TwitterProto::SetStatus(int new_status)
// ok.. here i think we're telling the core that this protocol something.. but why?
ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
- ForkThread(&TwitterProto::SignOn, this);
+ ForkThread(&CTwitterProto::SignOn, this);
}
else if (new_status == ID_STATUS_OFFLINE) {
- twit_.Disconnect();
+ Disconnect();
m_iStatus = m_iDesiredStatus;
setAllContactStatuses(ID_STATUS_OFFLINE);
SetChatStatus(ID_STATUS_OFFLINE);
@@ -203,12 +201,12 @@ int TwitterProto::SetStatus(int new_status)
/////////////////////////////////////////////////////////////////////////////////////////
-INT_PTR TwitterProto::SvcCreateAccMgrUI(WPARAM, LPARAM lParam)
+INT_PTR CTwitterProto::SvcCreateAccMgrUI(WPARAM, LPARAM lParam)
{
return (INT_PTR)CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_TWITTERACCOUNT), (HWND)lParam, first_run_dialog, (LPARAM)this);
}
-INT_PTR TwitterProto::ReplyToTweet(WPARAM wParam, LPARAM)
+INT_PTR CTwitterProto::ReplyToTweet(WPARAM wParam, LPARAM)
{
MCONTACT hContact = (MCONTACT) wParam;
// TODO: support replying to tweets instead of just users
@@ -225,7 +223,7 @@ INT_PTR TwitterProto::ReplyToTweet(WPARAM wParam, LPARAM)
return 0;
}
-INT_PTR TwitterProto::VisitHomepage(WPARAM hContact, LPARAM)
+INT_PTR CTwitterProto::VisitHomepage(WPARAM hContact, LPARAM)
{
Utils_OpenUrl(getMStringA(MCONTACT(hContact), "Homepage"));
return 0;
@@ -233,7 +231,7 @@ INT_PTR TwitterProto::VisitHomepage(WPARAM hContact, LPARAM)
/////////////////////////////////////////////////////////////////////////////////////////
-int TwitterProto::OnBuildStatusMenu(WPARAM, LPARAM)
+int CTwitterProto::OnBuildStatusMenu(WPARAM, LPARAM)
{
CMenuItem mi(&g_plugin);
mi.root = Menu_GetProtocolRoot(this);
@@ -244,7 +242,7 @@ int TwitterProto::OnBuildStatusMenu(WPARAM, LPARAM)
// TODO: Disable this menu item when offline
// "Send Tweet..."
mi.pszService = "/Tweet";
- CreateProtoService(mi.pszService, &TwitterProto::OnTweet);
+ CreateProtoService(mi.pszService, &CTwitterProto::OnTweet);
mi.name.w = LPGENW("Send Tweet...");
mi.position = 200001;
mi.hIcolibItem = GetIconHandle("tweet");
@@ -252,7 +250,7 @@ int TwitterProto::OnBuildStatusMenu(WPARAM, LPARAM)
return 0;
}
-int TwitterProto::OnOptionsInit(WPARAM wParam, LPARAM)
+int CTwitterProto::OnOptionsInit(WPARAM wParam, LPARAM)
{
OPTIONSDIALOGPAGE odp = {};
odp.position = 271828;
@@ -273,7 +271,7 @@ int TwitterProto::OnOptionsInit(WPARAM wParam, LPARAM)
return 0;
}
-INT_PTR TwitterProto::OnTweet(WPARAM, LPARAM)
+INT_PTR CTwitterProto::OnTweet(WPARAM, LPARAM)
{
if (m_iStatus != ID_STATUS_ONLINE)
return 1;
@@ -283,7 +281,7 @@ INT_PTR TwitterProto::OnTweet(WPARAM, LPARAM)
return 0;
}
-void TwitterProto::OnModulesLoaded()
+void CTwitterProto::OnModulesLoaded()
{
GCREGISTER gcr = {};
gcr.pszModule = m_szModuleName;
@@ -302,7 +300,7 @@ void TwitterProto::OnModulesLoaded()
SetChatStatus(ID_STATUS_OFFLINE);
}
-int TwitterProto::OnPrebuildContactMenu(WPARAM wParam, LPARAM)
+int CTwitterProto::OnPrebuildContactMenu(WPARAM wParam, LPARAM)
{
MCONTACT hContact = (MCONTACT) wParam;
if (IsMyContact(hContact))
@@ -311,14 +309,14 @@ int TwitterProto::OnPrebuildContactMenu(WPARAM wParam, LPARAM)
return 0;
}
-int TwitterProto::ShowPinDialog()
+int CTwitterProto::ShowPinDialog()
{
HWND hDlg = (HWND)DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_TWITTERPIN), nullptr, pin_proc, reinterpret_cast<LPARAM>(this));
ShowWindow(hDlg, SW_SHOW);
return 0;
}
-void TwitterProto::ShowPopup(const wchar_t *text, int Error)
+void CTwitterProto::ShowPopup(const wchar_t *text, int Error)
{
POPUPDATAW popup = {};
mir_snwprintf(popup.lpwzContactName, TranslateT("%s Protocol"), m_tszUserName);
@@ -332,11 +330,11 @@ void TwitterProto::ShowPopup(const wchar_t *text, int Error)
PUAddPopupW(&popup);
}
-void TwitterProto::ShowPopup(const char *text, int Error)
+void CTwitterProto::ShowPopup(const char *text, int Error)
{
POPUPDATAW popup = {};
mir_snwprintf(popup.lpwzContactName, TranslateT("%s Protocol"), m_tszUserName);
- mbcs_to_tcs(CP_UTF8, text, popup.lpwzText, _countof(popup.lpwzText));
+ wcsncpy_s(popup.lpwzText, Utf2T(text), _TRUNCATE);
if (Error) {
popup.iSeconds = -1;
popup.colorBack = 0x000000FF;
@@ -349,7 +347,7 @@ void TwitterProto::ShowPopup(const char *text, int Error)
// TODO: the more I think about it, the more I think all twit.* methods should
// be in MessageLoop
-void TwitterProto::SendTweetWorker(void *p)
+void CTwitterProto::SendTweetWorker(void *p)
{
if (p == nullptr)
return;
@@ -363,12 +361,12 @@ void TwitterProto::SendTweetWorker(void *p)
}
mir_cslock s(twitter_lock_);
- twit_.set_status(text);
+ set_status(text);
mir_free(text);
}
-void TwitterProto::UpdateSettings()
+void CTwitterProto::UpdateSettings()
{
if (getByte(TWITTER_KEY_CHATFEED)) {
if (!in_chat_)
@@ -387,19 +385,19 @@ void TwitterProto::UpdateSettings()
}
}
-std::wstring TwitterProto::GetAvatarFolder()
+CMStringW CTwitterProto::GetAvatarFolder()
{
wchar_t path[MAX_PATH];
mir_snwprintf(path, L"%s\\%s", VARSW(L"%miranda_avatarcache%").get(), m_tszUserName);
return path;
}
-INT_PTR TwitterProto::GetAvatar(WPARAM, LPARAM)
+INT_PTR CTwitterProto::GetAvatar(WPARAM, LPARAM)
{
return 0;
}
-INT_PTR TwitterProto::SetAvatar(WPARAM, LPARAM)
+INT_PTR CTwitterProto::SetAvatar(WPARAM, LPARAM)
{
return 0;
}
diff --git a/protocols/Twitter/src/proto.h b/protocols/Twitter/src/proto.h
index 718eadba52..147fee84c8 100644
--- a/protocols/Twitter/src/proto.h
+++ b/protocols/Twitter/src/proto.h
@@ -18,18 +18,85 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
-#include "utility.h"
#include "..\..\..\..\miranda-private-keys\Twitter\oauth.dev.h"
-class TwitterProto : public PROTO<TwitterProto>
+typedef __int64 twitter_id;
+
+struct twitter_status
+{
+ std::string text;
+ twitter_id id;
+ time_t time;
+};
+
+struct twitter_user
+{
+ std::string username;
+ std::string real_name;
+ std::string profile_image_url;
+ twitter_status status;
+};
+
+time_t parse_time(const CMStringA &str);
+
+struct AsyncHttpRequest : public MHttpRequest
+{
+ AsyncHttpRequest(int type, const char *szUrl);
+};
+
+class CTwitterProto : public PROTO<CTwitterProto>
{
- ptrA m_szChatId;
+ ptrA m_szChatId;
+
+ http::response request_token();
+ http::response request_access_tokens();
+
+ void set_base_url(const CMStringA &base_url);
+
+ bool get_info(const CMStringA &name, twitter_user *);
+ bool get_info_by_email(const CMStringA &email, twitter_user *);
+
+ bool add_friend(const CMStringA &name, twitter_user &u);
+ void remove_friend(const CMStringA &name);
+
+ void set_status(const CMStringA &text);
+ void send_direct(const CMStringA &name, const CMStringA &text);
+
+ http::response Execute(AsyncHttpRequest *req);
+
+ CMStringA m_szUserName;
+ CMStringA m_szPassword;
+ CMStringA m_szBaseUrl;
+ CMStringA m_szConsumerKey;
+ CMStringA m_szConsumerSecret;
+ CMStringA m_szAccessToken;
+ CMStringA m_szAccessTokenSecret;
+ CMStringA m_szPin;
+
+ // 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 OAuthWebRequestSubmit(const CMStringA &url, const char *httpMethod, const char *postData);
+
+ CMStringA UrlGetQuery(const CMStringA &url);
+
+ CMStringA BuildSignedOAuthParameters(const CMStringA &requestParameters, const CMStringA &url, const CMStringA &httpMethod, const CMStringA &postData);
+
+ CMStringA OAuthCreateNonce();
+ CMStringA OAuthCreateSignature(const CMStringA &signatureBase, const CMStringA &consumerSecret, const CMStringA &requestTokenSecret);
+
+ HNETLIBCONN m_hConnHttp;
+ void Disconnect(void) { if (m_hConnHttp) Netlib_CloseHandle(m_hConnHttp); m_hConnHttp = nullptr; }
public:
- TwitterProto(const char*,const wchar_t*);
- ~TwitterProto();
+ CTwitterProto(const char*,const wchar_t*);
+ ~CTwitterProto();
- //PROTO_INTERFACE
+ //////////////////////////////////////////////////////////////////////////////////////
+ // PROTO_INTERFACE
MCONTACT AddToList(int,PROTOSEARCHRESULT *) override;
@@ -49,7 +116,9 @@ public:
void UpdateSettings();
+ //////////////////////////////////////////////////////////////////////////////////////
// Services
+
INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM,LPARAM);
INT_PTR __cdecl ReplyToTweet(WPARAM,LPARAM);
INT_PTR __cdecl VisitHomepage(WPARAM,LPARAM);
@@ -61,7 +130,9 @@ public:
INT_PTR __cdecl OnTweet(WPARAM,LPARAM);
+ //////////////////////////////////////////////////////////////////////////////////////
// Events
+
int __cdecl OnContactDeleted(WPARAM,LPARAM);
int __cdecl OnBuildStatusMenu(WPARAM,LPARAM);
int __cdecl OnOptionsInit(WPARAM,LPARAM);
@@ -69,8 +140,11 @@ public:
int __cdecl OnChatOutgoing(WPARAM,LPARAM);
void __cdecl SendTweetWorker(void *);
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // Threads
+
private:
- // Worker threads
void __cdecl AddToListWorker(void *p);
void __cdecl SendSuccess(void *);
void __cdecl DoSearch(void *);
@@ -85,12 +159,12 @@ private:
void UpdateStatuses(bool pre_read,bool popups, bool tweetToMsg);
void UpdateMessages(bool pre_read);
void UpdateFriends();
- void UpdateAvatar(MCONTACT, const std::string &, bool force = false);
+ void UpdateAvatar(MCONTACT, const CMStringA &, bool force = false);
int ShowPinDialog();
void ShowPopup(const wchar_t *, int Error = 0);
void ShowPopup(const char *, int Error = 0);
- void ShowContactPopup(MCONTACT, const std::string &, const std::string *);
+ void ShowContactPopup(MCONTACT, const CMStringA &, const CMStringA *);
bool IsMyContact(MCONTACT, bool include_chat = false);
MCONTACT UsernameToHContact(const char *);
@@ -103,9 +177,9 @@ private:
void DeleteChatContact(const char *name);
void SetChatStatus(int);
- void TwitterProto::resetOAuthKeys();
+ void CTwitterProto::resetOAuthKeys();
- std::wstring GetAvatarFolder();
+ CMStringW GetAvatarFolder();
mir_cs signon_lock_;
mir_cs avatar_lock_;
@@ -113,7 +187,6 @@ private:
HNETLIBUSER hAvatarNetlib_;
HANDLE hMsgLoop_;
- mir_twitter twit_;
twitter_id since_id_;
twitter_id dm_since_id_;
@@ -123,7 +196,7 @@ private:
int disconnectionCount;
};
-struct CMPlugin : public ACCPROTOPLUGIN<TwitterProto>
+struct CMPlugin : public ACCPROTOPLUGIN<CTwitterProto>
{
CMPlugin();
diff --git a/protocols/Twitter/src/resource.h b/protocols/Twitter/src/resource.h
index 909c859e88..ffbfaaa57c 100644
--- a/protocols/Twitter/src/resource.h
+++ b/protocols/Twitter/src/resource.h
@@ -1,6 +1,6 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
-// Used by twitter.rc
+// Used by CTwitterProto.rc
//
#define IDD_TWITTERACCOUNT 101
#define IDI_TWITTER 102
diff --git a/protocols/Twitter/src/stdafx.h b/protocols/Twitter/src/stdafx.h
index 8317f41793..5e2d67dd7b 100644
--- a/protocols/Twitter/src/stdafx.h
+++ b/protocols/Twitter/src/stdafx.h
@@ -15,24 +15,11 @@
#include <direct.h>
#include <time.h>
-#include <cstdio>
-#include <ctime>
-#include <fstream>
#include <string>
#include <memory>
-using std::string;
-using std::wstring;
+
#include <map>
using std::map;
-#include <vector>
-using std::vector;
-#include <list>
-using std::list;
-#include <algorithm>
-using std::min;
-#include <sstream>
-
-typedef std::basic_string<wchar_t> wstring;
#include "resource.h"
@@ -69,34 +56,36 @@ typedef std::basic_string<wchar_t> wstring;
#include <openssl/sha.h>
#pragma comment(lib, "libeay32.lib")
-#include "StringUtil.h"
+#include "utility.h"
+#include "http.h"
+#include "proto.h"
-#define TWITTER_KEY_NICK "Nick" // we need one called Nick for the chat thingo to work
-#define TWITTER_KEY_UN "Username"
-#define TWITTER_KEY_PASS "Password"
-#define TWITTER_KEY_OAUTH_PIN "OAuthPIN"
-#define TWITTER_KEY_OAUTH_TOK "OAuthToken"
-#define TWITTER_KEY_OAUTH_TOK_SECRET "OAuthTokenSecret"
+#define TWITTER_KEY_NICK "Nick" // we need one called Nick for the chat thingo to work
+#define TWITTER_KEY_UN "Username"
+#define TWITTER_KEY_PASS "Password"
+#define TWITTER_KEY_OAUTH_PIN "OAuthPIN"
+#define TWITTER_KEY_OAUTH_TOK "OAuthToken"
+#define TWITTER_KEY_OAUTH_TOK_SEC "OAuthTokenSecret"
#define TWITTER_KEY_OAUTH_ACCESS_TOK "OAuthAccessToken"
-#define TWITTER_KEY_OAUTH_ACCESS_TOK_SECRET "OAuthAccessTokenSecret"
-#define TWITTER_KEY_BASEURL "BaseURL"
-#define TWITTER_KEY_CHATFEED "ChatFeed"
-#define TWITTER_KEY_POLLRATE "PollRate"
-#define TWITTER_KEY_GROUP "DefaultGroup"
-
-#define TWITTER_KEY_POPUP_SHOW "Popup/Show"
-#define TWITTER_KEY_POPUP_SIGNON "Popup/Signon"
-#define TWITTER_KEY_POPUP_COLBACK "Popup/ColorBack"
-#define TWITTER_KEY_POPUP_COLTEXT "Popup/ColorText"
-#define TWITTER_KEY_POPUP_TIMEOUT "Popup/Timeout"
-
-#define TWITTER_KEY_TWEET_TO_MSG "TweetToMsg"
-
-#define TWITTER_KEY_SINCEID "SinceID"
-#define TWITTER_KEY_DMSINCEID "DMSinceID"
-#define TWITTER_KEY_NEW "NewAcc"
-
-#define TWITTER_KEY_AV_URL "AvatarURL"
+#define TWITTER_KEY_OAUTH_ACCESS_SEC "OAuthAccessTokenSecret"
+#define TWITTER_KEY_BASEURL "BaseURL"
+#define TWITTER_KEY_CHATFEED "ChatFeed"
+#define TWITTER_KEY_POLLRATE "PollRate"
+#define TWITTER_KEY_GROUP "DefaultGroup"
+
+#define TWITTER_KEY_POPUP_SHOW "Popup/Show"
+#define TWITTER_KEY_POPUP_SIGNON "Popup/Signon"
+#define TWITTER_KEY_POPUP_COLBACK "Popup/ColorBack"
+#define TWITTER_KEY_POPUP_COLTEXT "Popup/ColorText"
+#define TWITTER_KEY_POPUP_TIMEOUT "Popup/Timeout"
+
+#define TWITTER_KEY_TWEET_TO_MSG "TweetToMsg"
+
+#define TWITTER_KEY_SINCEID "SinceID"
+#define TWITTER_KEY_DMSINCEID "DMSinceID"
+#define TWITTER_KEY_NEW "NewAcc"
+
+#define TWITTER_KEY_AV_URL "AvatarURL"
#define TWITTER_DB_EVENT_TYPE_TWEET 2718
diff --git a/protocols/Twitter/src/theme.cpp b/protocols/Twitter/src/theme.cpp
index 71d28f5332..4ca97fe05e 100644
--- a/protocols/Twitter/src/theme.cpp
+++ b/protocols/Twitter/src/theme.cpp
@@ -18,11 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
#include "theme.h"
-#include "proto.h"
static IconItem iconList[] =
{
- { LPGEN("Twitter Icon"), "twitter", IDI_TWITTER },
+ { LPGEN("Twitter Icon"), "CTwitterProto", IDI_TWITTER },
{ LPGEN("Tweet"), "tweet", IDI_TWITTER },
{ LPGEN("Reply to Tweet"), "reply", IDI_TWITTER },
@@ -56,10 +55,10 @@ static HGENMENU g_hMenuItems[2];
// Helper functions
-template<INT_PTR(__cdecl TwitterProto::*Fcn)(WPARAM, LPARAM)>
+template<INT_PTR(__cdecl CTwitterProto::*Fcn)(WPARAM, LPARAM)>
INT_PTR GlobalService(WPARAM hContact, LPARAM lParam)
{
- TwitterProto *proto = CMPlugin::getInstance(MCONTACT(hContact));
+ CTwitterProto *proto = CMPlugin::getInstance(MCONTACT(hContact));
return proto ? (proto->*Fcn)(hContact, lParam) : 0;
}
@@ -67,7 +66,7 @@ static int PrebuildContactMenu(WPARAM hContact, LPARAM lParam)
{
ShowContactMenus(false);
- TwitterProto *proto = CMPlugin::getInstance(MCONTACT(hContact));
+ CTwitterProto *proto = CMPlugin::getInstance(MCONTACT(hContact));
return proto ? proto->OnPrebuildContactMenu(hContact, lParam) : 0;
}
@@ -84,7 +83,7 @@ void InitContactMenus()
mi.name.w = LPGENW("Reply...");
mi.pszService = "Twitter/ReplyToTweet";
g_hMenuItems[0] = Menu_AddContactMenuItem(&mi);
- CreateServiceFunction(mi.pszService, GlobalService<&TwitterProto::ReplyToTweet>);
+ CreateServiceFunction(mi.pszService, GlobalService<&CTwitterProto::ReplyToTweet>);
SET_UID(mi, 0x7f7e4c24, 0x821c, 0x450f, 0x93, 0x76, 0xbe, 0x65, 0xe9, 0x2f, 0xb6, 0xc2);
mi.position = -2000006000;
@@ -92,7 +91,7 @@ void InitContactMenus()
mi.name.w = LPGENW("Visit Homepage");
mi.pszService = "Twitter/VisitHomepage";
g_hMenuItems[1] = Menu_AddContactMenuItem(&mi);
- CreateServiceFunction(mi.pszService, GlobalService<&TwitterProto::VisitHomepage>);
+ CreateServiceFunction(mi.pszService, GlobalService<&CTwitterProto::VisitHomepage>);
}
void ShowContactMenus(bool show)
diff --git a/protocols/Twitter/src/twitter.cpp b/protocols/Twitter/src/twitter.cpp
index ba58874ce0..2a8625e10b 100644
--- a/protocols/Twitter/src/twitter.cpp
+++ b/protocols/Twitter/src/twitter.cpp
@@ -17,98 +17,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
-#include "twitter.h"
-#include "utility.h"
// utility functions
-twitter::twitter() : base_url_("https://api.twitter.com/")
-{}
-
-bool twitter::set_credentials(const std::string &username, const std::wstring &consumerKey, const std::wstring &consumerSecret,
- const std::wstring &oauthAccessToken, const std::wstring &oauthAccessTokenSecret, const std::wstring &pin, bool test)
-{
- username_ = username;
- consumerKey_ = consumerKey;
- consumerSecret_ = consumerSecret;
- oauthAccessToken_ = oauthAccessToken;
- oauthAccessTokenSecret_ = oauthAccessTokenSecret;
- pin_ = pin;
-
- if (test)
- return slurp(base_url_ + "1.1/account/verify_credentials.json", http::get).code == 200;
- else
- return true;
-}
-
-http::response twitter::request_token()
-{
- return slurp("https://api.twitter.com/oauth/request_token", http::get);
-}
-
-http::response twitter::request_access_tokens()
-{
- return slurp("https://api.twitter.com/oauth/access_token", http::get);
-}
-
-void twitter::set_base_url(const std::string &base_url)
+http::response CTwitterProto::request_token()
{
- base_url_ = base_url;
+ auto *req = new AsyncHttpRequest(REQUEST_GET, "https://api.twitter.com/oauth/request_token");
+ return Execute(req);
}
-const std::string & twitter::get_username() const
+http::response CTwitterProto::request_access_tokens()
{
- return username_;
-}
-
-const std::string & twitter::get_base_url() const
-{
- return base_url_;
-}
-
-std::vector<twitter_user> twitter::get_friends()
-{
- std::vector<twitter_user> friends;
- http::response resp = slurp(base_url_ + "1.1/friends/list.json", http::get);
-
- if (resp.code != 200)
- throw bad_response();
-
- JSONNode root = JSONNode::parse(resp.data.c_str());
- if (!root)
- throw std::exception("unable to parse response");
-
- for (auto &one : root["users"]) {
- twitter_user user;
- user.username = one["screen_name"].as_string();
- user.real_name = one["name"].as_string();
- user.profile_image_url = one["profile_image_url"].as_string();
-
- const JSONNode &pStatus = one["status"];
- if (pStatus) {
- user.status.text = pStatus["text"].as_string();
- user.status.id = str2int(pStatus["id"].as_string());
-
- std::string timestr = pStatus["created_at"].as_string();
- user.status.time = parse_time(timestr);
- }
-
- friends.push_back(user);
- }
-
- return friends;
+ auto *req = new AsyncHttpRequest(REQUEST_GET, "https://api.twitter.com/oauth/access_token");
+ return Execute(req);
}
-bool twitter::get_info(const std::string &name, twitter_user *info)
+bool CTwitterProto::get_info(const CMStringA &name, twitter_user *info)
{
if (!info)
return false;
- std::string url = base_url_ + "1.1/users/show/" + mir_urlEncode(name.c_str()).c_str() + ".json";
-
- http::response resp = slurp(url, http::get);
+ auto *req = new AsyncHttpRequest(REQUEST_GET, m_szBaseUrl + "1.1/users/show/" + mir_urlEncode(name.c_str()).c_str() + ".json");
+ http::response resp = Execute(req);
if (resp.code != 200)
- throw bad_response();
+ return false;
JSONNode root = JSONNode::parse(resp.data.c_str());
if (!root)
@@ -123,16 +55,15 @@ bool twitter::get_info(const std::string &name, twitter_user *info)
return true;
}
-bool twitter::get_info_by_email(const std::string &email, twitter_user *info)
+bool CTwitterProto::get_info_by_email(const CMStringA &email, twitter_user *info)
{
if (!info)
return false;
- std::string url = base_url_ + "1.1/users/show.json?email=" + mir_urlEncode(email.c_str()).c_str();
-
- http::response resp = slurp(url, http::get);
+ auto *req = new AsyncHttpRequest(REQUEST_GET, m_szBaseUrl + "1.1/users/show.json?email=" + mir_urlEncode(email));
+ http::response resp = Execute(req);
if (resp.code != 200)
- throw bad_response();
+ return false;
JSONNode root = JSONNode::parse(resp.data.c_str());
if (!root)
@@ -147,19 +78,17 @@ bool twitter::get_info_by_email(const std::string &email, twitter_user *info)
return true;
}
-twitter_user twitter::add_friend(const std::string &name)
+bool CTwitterProto::add_friend(const CMStringA &name, twitter_user &ret)
{
- std::string url = base_url_ + "1.1/friendships/create/" + mir_urlEncode(name.c_str()).c_str() + ".json";
-
- http::response resp = slurp(url, http::post);
+ auto *req = new AsyncHttpRequest(REQUEST_POST, m_szBaseUrl + "1.1/friendships/create/" + mir_urlEncode(name) + ".json");
+ http::response resp = Execute(req);
if (resp.code != 200)
- throw bad_response();
+ return false;
JSONNode root = JSONNode::parse(resp.data.c_str());
if (!root)
- throw std::exception("unable to parse response");
+ return false;
- twitter_user ret;
ret.username = root["screen_name"].as_string();
ret.real_name = root["name"].as_string();
ret.profile_image_url = root["profile_image_url"].as_string();
@@ -167,186 +96,33 @@ twitter_user twitter::add_friend(const std::string &name)
JSONNode &pStatus = root["status"];
if (pStatus) {
ret.status.text = pStatus["text"].as_string();
- ret.status.id = str2int(pStatus["id"].as_string());
+ ret.status.id = _atoi64(pStatus["id"].as_string().c_str());
}
- return ret;
-}
-
-void twitter::remove_friend(const std::string &name)
-{
- std::string url = base_url_ + "1.1/friendships/destroy/" + mir_urlEncode(name.c_str()).c_str() + ".json";
-
- slurp(url, http::post);
-}
-
-void twitter::set_status(const std::string &text)
-{
- if (text.size()) {
- std::wstring wTweet = UTF8ToWide(text);
- OAuthParameters postParams;
- postParams[L"status"] = UrlEncode(wTweet);
-
- slurp(base_url_ + "1.1/statuses/update.json", http::post, postParams);
- }
-}
-
-void twitter::send_direct(const std::string &name, const std::string &text)
-{
- std::wstring temp = UTF8ToWide(text);
- OAuthParameters postParams;
- postParams[L"text"] = UrlEncode(temp);
- postParams[L"screen_name"] = UTF8ToWide(name);
- slurp(base_url_ + "1.1/direct_messages/new.json", http::post, postParams);
-}
-
-std::vector<twitter_user> twitter::get_statuses(int count, twitter_id id)
-{
- std::vector<twitter_user> statuses;
-
- std::string url = base_url_ + "1.1/statuses/home_timeline.json?count=" +
- int2str(count);
- if (id != 0)
- url += "&since_id=" + int2str(id);
-
- http::response resp = slurp(url, http::get);
- if (resp.code != 200)
- throw bad_response();
-
- JSONNode root = JSONNode::parse(resp.data.c_str());
- if (!root)
- throw std::exception("unable to parse response");
-
- for (auto &one : root) {
- const JSONNode &pUser = one["user"];
-
- twitter_user u;
- u.username = pUser["screen_name"].as_string();
- u.real_name = pUser["name"].as_string();
- u.profile_image_url = pUser["profile_image_url"].as_string();
-
- // the tweet will be truncated unless we take action. i hate you twitter API
- const JSONNode &pStatus = one["retweeted_status"];
- if (pStatus) {
- // here we grab the "retweeted_status" um.. section? it's in here that all the info we need is
- // at this point the user will get no tweets and an error popup if the tweet happens to be exactly 140 chars, start with
- // "RT @", end in " ...", and notactually be a real retweet. it's possible but unlikely, wish i knew how to get
- // the retweet_count variable to work :(
- const JSONNode &pRetweet = one["retweeted_status"],
- &pUser2 = pRetweet["user"];
-
- std::string retweeteesName = pUser2["screen_name"].as_string(); // the user that is being retweeted
- std::string retweetText = pRetweet["text"].as_string(); // their tweet in all it's untruncated glory
-
- // fix html entities in the text
- htmlEntitiesDecode(retweetText);
-
- u.status.text = "RT @" + retweeteesName + " " + retweetText; // mash it together in some format people will understand
- }
- else {
- // if it's not truncated, then the twitter API returns the native RT correctly anyway,
- std::string rawText = one["text"].as_string();
-
- // fix html entities in the text
- htmlEntitiesDecode(rawText);
-
- u.status.text = rawText;
- }
-
- u.status.id = str2int(one["id"].as_string());
- std::string timestr = one["created_at"].as_string();
- u.status.time = parse_time(timestr);
-
- statuses.push_back(u);
- }
-
-
- return statuses;
+ return true;
}
-std::vector<twitter_user> twitter::get_direct(twitter_id id)
+void CTwitterProto::remove_friend(const CMStringA &name)
{
- std::vector<twitter_user> messages;
-
- std::string url = base_url_ + "1.1/direct_messages.json";
- if (id != 0)
- url += "?since_id=" + int2str(id);
-
- http::response resp = slurp(url, http::get);
- if (resp.code != 200)
- throw bad_response();
-
- JSONNode root = JSONNode::parse(resp.data.c_str());
- if (!root)
- throw std::exception("unable to parse response");
-
- for (auto &one : root) {
- twitter_user u;
- u.username = one["sender_screen_name"].as_string();
- u.status.text = one["text"].as_string();
- u.status.id = str2int(one["id"].as_string());
- std::string timestr = one["created_at"].as_string();
- u.status.time = parse_time(timestr);
- messages.push_back(u);
- }
-
- return messages;
+ auto *req = new AsyncHttpRequest(REQUEST_POST, m_szBaseUrl + "1.1/friendships/destroy/" + mir_urlEncode(name) + ".json");
+ Execute(req);
}
-string twitter::urlencode(const string &c)
+void CTwitterProto::set_status(const CMStringA &text)
{
- string escaped;
- size_t max = c.length();
- for (size_t i = 0; i < max; i++) {
- if ((48 <= c[i] && c[i] <= 57) ||//0-9
- (65 <= c[i] && c[i] <= 90) ||//ABC...XYZ
- (97 <= c[i] && c[i] <= 122) || //abc...xyz
- (c[i] == '~' || c[i] == '-' || c[i] == '_' || c[i] == '.'))
- {
- escaped.append(&c[i], 1);
- }
- else {
- escaped.append("%");
- escaped.append(char2hex(c[i]));//converts char 255 to string "FF"
- }
- }
- return escaped;
-}
+ if (text.IsEmpty())
+ return;
-
-wstring twitter::UrlEncode(const wstring& url)
-{
- // multiple encodings r sux
- return UTF8ToWide(urlencode(WideToUTF8(url)));
+ auto *req = new AsyncHttpRequest(REQUEST_POST, m_szBaseUrl + "1.1/statuses/update.json");
+ req << CHAR_PARAM("status", text);
+ Execute(req);
}
-// char2hex and urlencode from http://www.zedwood.com/article/111/cpp-urlencode-function
-// modified according to http://oauth.net/core/1.0a/#encoding_parameters
-//
-//5.1. Parameter Encoding
-//
-//All parameter names and values are escaped using the [RFC3986]
-//percent-encoding (%xx) mechanism. Characters not in the unreserved character set
-//MUST be encoded. Characters in the unreserved character set MUST NOT be encoded.
-//Hexadecimal characters in encodings MUST be upper case.
-//Text names and values MUST be encoded as UTF-8
-// octets before percent-encoding them per [RFC3629].
-//
-// unreserved = ALPHA, DIGIT, '-', '.', '_', '~'
-
-string twitter::char2hex(char dec)
+void CTwitterProto::send_direct(const CMStringA &name, const CMStringA &text)
{
- 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;
-
- string r;
- r.append(&dig1, 1);
- r.append(&dig2, 1);
- return r;
+ auto *req = new AsyncHttpRequest(REQUEST_POST, m_szBaseUrl + "1.1/direct_messages/events/new.json");
+ req << CHAR_PARAM("text", text) << CHAR_PARAM("screen_name", name);
+ Execute(req);
}
// Some Unices get this, now we do too!
@@ -370,13 +146,13 @@ int parse_month(const char *m)
return -1;
}
-time_t parse_time(const std::string &s)
+time_t parse_time(const CMStringA &s)
{
struct tm t;
char day[4], month[4];
char plus;
int zone;
- if (sscanf(s.c_str(), "%3s %3s %d %d:%d:%d %c%d %d", day, month, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &plus, &zone, &t.tm_year) == 9) {
+ if (sscanf(s, "%3s %3s %d %d:%d:%d %c%d %d", day, month, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &plus, &zone, &t.tm_year) == 9) {
t.tm_year -= 1900;
t.tm_mon = parse_month(month);
if (t.tm_mon == -1)
diff --git a/protocols/Twitter/src/twitter.h b/protocols/Twitter/src/twitter.h
deleted file mode 100644
index f757b05981..0000000000
--- a/protocols/Twitter/src/twitter.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-using std::string;
-using std::wstring;
-using std::map;
-using std::vector;
-
-#include "http.h"
-
-#define wstring wstring
-
-typedef unsigned long long twitter_id;
-typedef std::map<std::wstring, std::wstring> OAuthParameters;
-
-struct twitter_status
-{
- std::string text;
- twitter_id id;
- time_t time;
-};
-
-struct twitter_user
-{
- std::string username;
- std::string real_name;
- std::string profile_image_url;
- twitter_status status;
-};
-
-time_t parse_time(const std::string &);
-
-class bad_response : public std::exception
-{
-public:
- virtual const char * what() const
- {
- return "bad http response";
- }
-};
-
-class twitter
-{
-public:
- typedef std::vector<twitter_user> status_list;
- typedef std::map<std::string,status_list> status_map;
-
- twitter();
-
- bool twitter::set_credentials(const std::string&, const std::wstring&, const std::wstring&,
- const std::wstring&, const std::wstring&, const std::wstring &, bool);
-
- http::response twitter::request_token();
- http::response twitter::request_access_tokens();
-
-
- void set_base_url(const std::string &base_url);
-
- const std::string & get_username() const;
- const std::string & get_base_url() const;
-
- bool get_info(const std::string &name,twitter_user *);
- bool get_info_by_email(const std::string &email,twitter_user *);
- std::vector<twitter_user> get_friends();
-
- //js::array buildFriendList();
-
- twitter_user add_friend(const std::string &name);
- void remove_friend(const std::string &name);
-
- void set_status(const std::string &text);
- std::vector<twitter_user> get_statuses(int count=20,twitter_id id=0);
-
- void send_direct(const std::string &name,const std::string &text);
- std::vector<twitter_user> get_direct(twitter_id id=0);
-
- std::string urlencode(const std::string &c);
- std::wstring UrlEncode( const std::wstring& url );
- std::string char2hex( char dec );
-
-protected:
- virtual http::response slurp(const std::string &,http::method,
- OAuthParameters postParams = OAuthParameters()) = 0;
-
- std::string username_;
- std::string password_;
- std::string base_url_;
- std::wstring consumerKey_;
- std::wstring consumerSecret_;
- std::wstring oauthAccessToken_;
- std::wstring oauthAccessTokenSecret_;
- std::wstring pin_;
-};
diff --git a/protocols/Twitter/src/ui.cpp b/protocols/Twitter/src/ui.cpp
index 8ad626201f..405faaa5c5 100644
--- a/protocols/Twitter/src/ui.cpp
+++ b/protocols/Twitter/src/ui.cpp
@@ -19,9 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
#include "ui.h"
-#include "proto.h"
-#include "twitter.h"
-
static const wchar_t *sites[] = {
L"https://api.twitter.com/",
L"https://identi.ca/api/"
@@ -29,13 +26,13 @@ static const wchar_t *sites[] = {
INT_PTR CALLBACK first_run_dialog(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
- TwitterProto *proto;
+ CTwitterProto *proto;
switch (msg) {
case WM_INITDIALOG:
TranslateDialogDefault(hwndDlg);
- proto = reinterpret_cast<TwitterProto*>(lParam);
+ proto = reinterpret_cast<CTwitterProto*>(lParam);
SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
DBVARIANT dbv;
@@ -78,7 +75,7 @@ INT_PTR CALLBACK first_run_dialog(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM
case WM_NOTIFY: // might be able to get rid of this bit?
if (reinterpret_cast<NMHDR*>(lParam)->code == PSN_APPLY) {
- proto = reinterpret_cast<TwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
+ proto = reinterpret_cast<CTwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
char str[128];
wchar_t tstr[128];
@@ -100,13 +97,13 @@ INT_PTR CALLBACK first_run_dialog(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM
INT_PTR CALLBACK tweet_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
- TwitterProto *proto;
+ CTwitterProto *proto;
switch (uMsg) {
case WM_INITDIALOG:
TranslateDialogDefault(hwndDlg);
- proto = reinterpret_cast<TwitterProto*>(lParam);
+ proto = reinterpret_cast<CTwitterProto*>(lParam);
SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
SendDlgItemMessage(hwndDlg, IDC_TWEETMSG, EM_LIMITTEXT, 140, 0);
SetDlgItemText(hwndDlg, IDC_CHARACTERS, L"140");
@@ -120,13 +117,13 @@ INT_PTR CALLBACK tweet_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
case WM_COMMAND:
if (LOWORD(wParam) == IDOK) {
wchar_t msg[141];
- proto = reinterpret_cast<TwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
+ proto = reinterpret_cast<CTwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
GetDlgItemText(hwndDlg, IDC_TWEETMSG, msg, _countof(msg));
ShowWindow(hwndDlg, SW_HIDE);
char *narrow = mir_u2a_cp(msg, CP_UTF8);
- proto->ForkThread(&TwitterProto::SendTweetWorker, narrow);
+ proto->ForkThread(&CTwitterProto::SendTweetWorker, narrow);
EndDialog(hwndDlg, wParam);
return true;
@@ -164,13 +161,13 @@ INT_PTR CALLBACK tweet_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
INT_PTR CALLBACK options_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
- TwitterProto *proto;
+ CTwitterProto *proto;
switch (msg) {
case WM_INITDIALOG:
TranslateDialogDefault(hwndDlg);
- proto = reinterpret_cast<TwitterProto*>(lParam);
+ proto = reinterpret_cast<CTwitterProto*>(lParam);
DBVARIANT dbv;
if (!proto->getString(TWITTER_KEY_UN, &dbv)) {
@@ -221,7 +218,7 @@ INT_PTR CALLBACK options_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lPar
break;
case WM_NOTIFY:
if (reinterpret_cast<NMHDR*>(lParam)->code == PSN_APPLY) {
- proto = reinterpret_cast<TwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
+ proto = reinterpret_cast<CTwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
char str[128];
GetDlgItemTextA(hwndDlg, IDC_UN, str, _countof(str));
@@ -350,7 +347,7 @@ void CheckAndUpdateDlgButton(HWND hWnd, int button, BOOL check)
INT_PTR CALLBACK popup_options_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
using namespace popup_options;
- TwitterProto *proto;
+ CTwitterProto *proto;
int text_color, back_color, timeout;
@@ -358,7 +355,7 @@ INT_PTR CALLBACK popup_options_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA
case WM_INITDIALOG:
TranslateDialogDefault(hwndDlg);
- proto = reinterpret_cast<TwitterProto*>(lParam);
+ proto = reinterpret_cast<CTwitterProto*>(lParam);
CheckAndUpdateDlgButton(hwndDlg, IDC_SHOWPOPUPS, proto->getByte(TWITTER_KEY_POPUP_SHOW, 0));
CheckDlgButton(hwndDlg, IDC_NOSIGNONPOPUPS, !proto->getByte(TWITTER_KEY_POPUP_SIGNON, 0) ? BST_CHECKED : BST_UNCHECKED);
@@ -440,7 +437,7 @@ INT_PTR CALLBACK popup_options_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA
case WM_NOTIFY:
if (reinterpret_cast<NMHDR*>(lParam)->code == PSN_APPLY) {
- proto = reinterpret_cast<TwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
+ proto = reinterpret_cast<CTwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
proto->setByte(TWITTER_KEY_POPUP_SHOW, IsDlgButtonChecked(hwndDlg, IDC_SHOWPOPUPS) != 0);
proto->setByte(TWITTER_KEY_POPUP_SIGNON, BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_NOSIGNONPOPUPS));
@@ -462,7 +459,7 @@ INT_PTR CALLBACK popup_options_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA
INT_PTR CALLBACK pin_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
- TwitterProto *proto;
+ CTwitterProto *proto;
switch (msg) {
case WM_INITDIALOG:
@@ -472,7 +469,7 @@ INT_PTR CALLBACK pin_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_COMMAND:
if (LOWORD(wParam) == IDOK) {
- proto = reinterpret_cast<TwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
+ proto = reinterpret_cast<CTwitterProto*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
char str[128];
GetDlgItemTextA(hwndDlg, IDC_PIN, str, _countof(str));
diff --git a/protocols/Twitter/src/utility.cpp b/protocols/Twitter/src/utility.cpp
index be54107c5e..f839bb4838 100644
--- a/protocols/Twitter/src/utility.cpp
+++ b/protocols/Twitter/src/utility.cpp
@@ -17,114 +17,54 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
-#include "utility.h"
#include <io.h>
-std::string b64encode(const std::string &s)
+http::response CTwitterProto::Execute(AsyncHttpRequest *req)
{
- return std::string(ptrA(mir_base64_encode(s.c_str(), s.length())));
-}
-
-std::string int2str(int32_t iVal)
-{
- char buf[100];
- _itoa_s(iVal, buf, 10);
- return std::string(buf);
-}
-
-std::string int2str(uint64_t iVal)
-{
- char buf[100];
- _i64toa_s(iVal, buf, _countof(buf), 10);
- return std::string(buf);
-}
-
-uint64_t str2int(const std::string &str)
-{
- return _atoi64(str.c_str());
-}
-
-http::response mir_twitter::slurp(const std::string &url, http::method meth, OAuthParameters postParams)
-{
- NETLIBHTTPREQUEST req = { sizeof(req) };
- req.requestType = (meth == http::get) ? REQUEST_GET : REQUEST_POST;
- req.szUrl = const_cast<char*>(url.c_str());
-
- std::wstring url_WSTR = UTF8ToWide(url);
- std::string pdata_STR;
- std::wstring pdata_WSTR;
- std::wstring auth;
+ if (!req->m_szParam.IsEmpty()) {
+ if (req->requestType == REQUEST_POST) {
+ req->AddHeader("Content-Type", "application/x-www-form-urlencoded");
+ req->AddHeader("Cache-Control", "no-cache");
- if (meth == http::get) {
- if (url_WSTR.size() > 0) { ppro_->debugLogW(L"**SLURP::GET - we have a URL: %s", url_WSTR.c_str()); }
- if (consumerKey_.size() > 0) { ppro_->debugLogA("**SLURP::GET - we have a consumerKey"); }
- if (consumerSecret_.size() > 0) { ppro_->debugLogA("**SLURP::GET - we have a consumerSecret"); }
- if (oauthAccessToken_.size() > 0) { ppro_->debugLogA("**SLURP::GET - we have a oauthAccessToken"); }
- if (oauthAccessTokenSecret_.size() > 0) { ppro_->debugLogA("**SLURP::GET - we have a oauthAccessTokenSecret"); }
- if (pin_.size() > 0) { ppro_->debugLogA("**SLURP::GET - we have a pin"); }
-
- auth = OAuthWebRequestSubmit(url_WSTR, L"GET", nullptr, consumerKey_, consumerSecret_,
- oauthAccessToken_, oauthAccessTokenSecret_, pin_);
- }
- else {
- // OAuthParameters postParams;
- if (url_WSTR.size() > 0) { ppro_->debugLogW(L"**SLURP::POST - we have a URL: %s", url_WSTR.c_str()); }
- if (consumerKey_.size() > 0) { ppro_->debugLogA("**SLURP::POST - we have a consumerKey"); }
- if (consumerSecret_.size() > 0) { ppro_->debugLogA("**SLURP::POST - we have a consumerSecret"); }
- if (oauthAccessToken_.size() > 0) { ppro_->debugLogA("**SLURP::POST - we have a oauthAccessToken"); }
- if (oauthAccessTokenSecret_.size() > 0) { ppro_->debugLogA("**SLURP::POST - we have a oauthAccessTokenSecret"); }
- if (pin_.size() > 0) { ppro_->debugLogA("**SLURP::POST - we have a pin"); }
-
- pdata_WSTR = BuildQueryString(postParams);
-
- ppro_->debugLogW(L"**SLURP::POST - post data is: %s", pdata_WSTR.c_str());
-
- auth = OAuthWebRequestSubmit(url_WSTR, L"POST", &postParams, consumerKey_, consumerSecret_, oauthAccessToken_, oauthAccessTokenSecret_);
+ req->dataLength = (int)req->m_szParam.GetLength();
+ req->pData = req->m_szParam.GetBuffer();
+ }
+ else {
+ req->m_szUrl.AppendChar('?');
+ req->m_szUrl += req->m_szParam;
+ }
}
- std::string auth_STR = WideToUTF8(auth);
-
- NETLIBHTTPHEADER hdr[3];
- hdr[0].szName = "Authorization";
- hdr[0].szValue = const_cast<char*>(auth_STR.c_str());
-
- req.headers = hdr;
- req.headersCount = 1;
-
- if (meth == http::post) {
- hdr[1].szName = "Content-Type";
- hdr[1].szValue = "application/x-www-form-urlencoded";
- hdr[2].szName = "Cache-Control";
- hdr[2].szValue = "no-cache";
-
- pdata_STR = WideToUTF8(pdata_WSTR);
-
- req.headersCount = 3;
- req.dataLength = (int)pdata_STR.size();
- req.pData = const_cast<char*>(pdata_STR.c_str());
- ppro_->debugLogA("**SLURP::POST - req.pdata is %s", req.pData);
- }
+ CMStringA auth;
+ if (req->requestType == REQUEST_GET)
+ auth = OAuthWebRequestSubmit(req->m_szUrl, "GET", "");
+ else
+ auth = OAuthWebRequestSubmit(req->m_szUrl, "POST", req->m_szParam);
+ req->AddHeader("Authorization", auth);
- req.flags = NLHRF_HTTP11 | NLHRF_PERSISTENT | NLHRF_REDIRECT;
- req.nlc = httpPOST_;
+ req->szUrl = req->m_szUrl.GetBuffer();
+ req->flags = NLHRF_HTTP11 | NLHRF_PERSISTENT | NLHRF_REDIRECT;
+ req->nlc = m_hConnHttp;
http::response resp_data;
- NLHR_PTR resp(Netlib_HttpTransaction(handle_, &req));
+ NLHR_PTR resp(Netlib_HttpTransaction(m_hNetlibUser, req));
if (resp) {
- ppro_->debugLogA("**SLURP - the server has responded!");
- httpPOST_ = resp->nlc;
+ debugLogA("**SLURP - the server has responded!");
+ m_hConnHttp = resp->nlc;
resp_data.code = resp->resultCode;
- resp_data.data = resp->pData ? resp->pData : "";
+ if (resp->pData)
+ resp_data.data = resp->pData;
}
else {
- httpPOST_ = nullptr;
- ppro_->debugLogA("SLURP - there was no response!");
+ m_hConnHttp = nullptr;
+ resp_data.code = 500;
+ debugLogA("SLURP - there was no response!");
}
return resp_data;
}
-bool save_url(HNETLIBUSER hNetlib, const std::string &url, const std::wstring &filename)
+bool save_url(HNETLIBUSER hNetlib, const CMStringA &url, const CMStringW &filename)
{
NETLIBHTTPREQUEST req = { sizeof(req) };
req.requestType = REQUEST_GET;
@@ -136,11 +76,10 @@ bool save_url(HNETLIBUSER hNetlib, const std::string &url, const std::wstring &f
bool success = (resp->resultCode == 200);
if (success) {
// Create folder if necessary
- std::wstring dir = filename.substr(0, filename.rfind('\\'));
- CreateDirectoryTreeW(dir.c_str());
+ CreatePathToFileW(filename);
// Write to file
- FILE *f = _wfopen(filename.c_str(), L"wb");
+ FILE *f = _wfopen(filename, L"wb");
fwrite(resp->pData, 1, resp->dataLength, f);
fclose(f);
}
diff --git a/protocols/Twitter/src/utility.h b/protocols/Twitter/src/utility.h
index b8a5a233b9..b452b55abf 100644
--- a/protocols/Twitter/src/utility.h
+++ b/protocols/Twitter/src/utility.h
@@ -18,81 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
-#include "http.h"
-#include "twitter.h"
+typedef std::map<CMStringA, CMStringA> StringPairs;
+StringPairs ParseQueryString(const CMStringA &queryString);
-std::string b64encode(const std::string &s);
+void Split(const CMStringA &str, OBJLIST<CMStringA> &out, char sep, bool includeEmpty = false);
-std::string int2str(int32_t);
-std::string int2str(uint64_t);
-uint64_t str2int(const std::string &str);
+void htmlEntitiesDecode(CMStringA &context);
-class mir_twitter : public twitter
-{
-public:
- mir_twitter() : twitter(), handle_(nullptr), httpPOST_(nullptr) {}
- void set_handle(PROTO_INTERFACE *ppro, HNETLIBUSER h)
- {
- ppro_ = ppro;
- handle_ = h;
- }
-
- // 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 twitter website
- std::wstring OAuthWebRequestSubmit(
- const std::wstring& url,
- const std::wstring& httpMethod,
- const OAuthParameters *postData,
- const std::wstring& consumerKey,
- const std::wstring& consumerSecret,
- const std::wstring& oauthToken = L"",
- const std::wstring& oauthTokenSecret = L"",
- const std::wstring& pin = L""
- );
-
- std::wstring OAuthWebRequestSubmit(
- const OAuthParameters& parameters,
- const std::wstring& url);
-
- std::wstring UrlGetQuery( const std::wstring& url );
-
- OAuthParameters BuildSignedOAuthParameters( const OAuthParameters& requestParameters,
- const std::wstring& url,
- const std::wstring& httpMethod,
- const OAuthParameters *postData,
- const std::wstring& consumerKey,
- const std::wstring& consumerSecret,
- const std::wstring& requestToken,
- const std::wstring& requestTokenSecret,
- const std::wstring& pin );
-
- std::wstring BuildQueryString( const OAuthParameters &parameters ) ;
- std::wstring OAuthConcatenateRequestElements( const std::wstring& httpMethod, std::wstring url, const std::wstring& parameters );
- std::map<std::wstring, std::wstring> CrackURL(std::wstring );
- std::wstring brook_httpsend(std::wstring, std::wstring, std::wstring, std::wstring);
- void Disconnect(void) { if (httpPOST_) Netlib_CloseHandle(httpPOST_); httpPOST_ = nullptr; }
- std::wstring OAuthNormalizeUrl( const std::wstring& url );
- std::wstring OAuthNormalizeRequestParameters( const OAuthParameters& requestParameters );
- OAuthParameters ParseQueryString( const std::wstring& url );
-
- std::wstring OAuthCreateNonce();
- std::wstring OAuthCreateTimestamp();
- std::wstring OAuthCreateSignature( const std::wstring& signatureBase, const std::wstring& consumerSecret, const std::wstring& requestTokenSecret );
-
-protected:
- http::response slurp(const std::string &,http::method, OAuthParameters );
-
- HNETLIBCONN httpPOST_;
- HNETLIBUSER handle_;
- PROTO_INTERFACE *ppro_;
-};
-
-inline void mbcs_to_tcs(UINT code_page, const char *mbstr, wchar_t *tstr, int tlen)
-{
- MultiByteToWideChar(code_page, 0, mbstr, -1, tstr, tlen);
-}
-
-bool save_url(HNETLIBUSER hNetlib,const std::string &url,const std::wstring &filename);
+bool save_url(HNETLIBUSER hNetlib,const CMStringA &url,const CMStringW &filename);
diff --git a/protocols/Twitter/twitter.vcxproj b/protocols/Twitter/twitter.vcxproj
index c0636e19cc..ce6809dcde 100644
--- a/protocols/Twitter/twitter.vcxproj
+++ b/protocols/Twitter/twitter.vcxproj
@@ -25,11 +25,6 @@
<ImportGroup Label="PropertySheets">
<Import Project="$(ProjectDir)..\..\build\vc.common\plugin.props" />
</ImportGroup>
- <ItemDefinitionGroup>
- <ClCompile>
- <ExceptionHandling>Sync</ExceptionHandling>
- </ClCompile>
- </ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\..\libs\libjson\libjson.vcxproj">
<Project>{f6a9340e-b8d9-4c75-be30-47dc66d0abc7}</Project>