diff options
Diffstat (limited to 'protocols/Twitter')
-rw-r--r-- | protocols/Twitter/src/connection.cpp | 219 | ||||
-rw-r--r-- | protocols/Twitter/src/http.cpp | 124 | ||||
-rw-r--r-- | protocols/Twitter/src/http.h | 10 | ||||
-rw-r--r-- | protocols/Twitter/src/oauth.cpp | 61 | ||||
-rw-r--r-- | protocols/Twitter/src/proto.cpp | 81 | ||||
-rw-r--r-- | protocols/Twitter/src/proto.h | 82 | ||||
-rw-r--r-- | protocols/Twitter/src/stdafx.h | 3 | ||||
-rw-r--r-- | protocols/Twitter/src/twitter.cpp | 20 | ||||
-rw-r--r-- | protocols/Twitter/src/ui.cpp | 11 | ||||
-rw-r--r-- | protocols/Twitter/src/ui.h | 1 | ||||
-rw-r--r-- | protocols/Twitter/src/utility.cpp | 90 | ||||
-rw-r--r-- | protocols/Twitter/twitter.vcxproj | 1 | ||||
-rw-r--r-- | protocols/Twitter/twitter.vcxproj.filters | 9 |
13 files changed, 366 insertions, 346 deletions
diff --git a/protocols/Twitter/src/connection.cpp b/protocols/Twitter/src/connection.cpp index bb3d36c620..48182b4dea 100644 --- a/protocols/Twitter/src/connection.cpp +++ b/protocols/Twitter/src/connection.cpp @@ -18,70 +18,40 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "stdafx.h"
-AsyncHttpRequest::AsyncHttpRequest(int type, const char *szUrl)
+void CTwitterProto::OnLoggedIn()
{
- requestType = type;
- m_szUrl = szUrl;
-}
-
-void CALLBACK CTwitterProto::APC_callback(ULONG_PTR p)
-{
- reinterpret_cast<CTwitterProto*>(p)->debugLogA("***** Executing APC");
-}
+ ptrW wszGroup(getWStringA(TWITTER_KEY_GROUP));
+ if (wszGroup)
+ Clist_GroupCreate(0, wszGroup);
-template<typename T>
-inline static T db_pod_get(MCONTACT hContact, const char *module, const char *setting, T errorValue)
-{
- DBVARIANT dbv;
- if (db_get(hContact, module, setting, &dbv))
- return errorValue;
+ setAllContactStatuses(ID_STATUS_ONLINE);
+ SetChatStatus(ID_STATUS_ONLINE);
- T ret = *(T*)dbv.pbVal;
- db_free(&dbv);
- return ret;
+ int old_status = m_iStatus;
+ // m_szMyId = root["id_str"].as_mstring(); !!!!!!!!!!!!!!
+ m_iStatus = m_iDesiredStatus;
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
}
-template<typename T>
-inline static INT_PTR db_pod_set(MCONTACT hContact, const char *module, const char *setting, T val)
+void CTwitterProto::OnLoggedFail()
{
- return db_set_blob(hContact, module, setting, &val, sizeof(T));
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, ID_STATUS_OFFLINE);
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
+
+ setAllContactStatuses(ID_STATUS_OFFLINE, false);
}
-void CTwitterProto::SignOn(void*)
+void CTwitterProto::BeginConnection()
{
- debugLogA("***** Beginning SignOn process");
- mir_cslock lck(signon_lock_);
-
- // Kill the old thread if it's still around
- // this doesn't seem to work.. should we wait infinitely?
- if (hMsgLoop_) {
- debugLogA("***** Requesting MessageLoop to exit");
- QueueUserAPC(APC_callback, hMsgLoop_, (ULONG_PTR)this);
- debugLogA("***** Waiting for old MessageLoop to exit");
- //WaitForSingleObject(hMsgLoop_,INFINITE);
- WaitForSingleObject(hMsgLoop_, 180000);
- CloseHandle(hMsgLoop_);
- }
- if (NegotiateConnection()) // Could this be? The legendary Go Time??
- {
- if (!m_si && getByte(TWITTER_KEY_CHATFEED))
- OnJoinChat(0, true);
-
- setAllContactStatuses(ID_STATUS_ONLINE);
- SetChatStatus(ID_STATUS_ONLINE);
- hMsgLoop_ = ForkThreadEx(&CTwitterProto::MessageLoop, nullptr, nullptr);
- }
+ if (!m_si && getByte(TWITTER_KEY_CHATFEED))
+ OnJoinChat(0, true);
- debugLogA("***** SignOn complete");
-}
+ if (!m_hWorkerThreadId)
+ ForkThread(&CTwitterProto::ServerThread);
-bool CTwitterProto::NegotiateConnection()
-{
debugLogA("***** Negotiating connection with Twitter");
- disconnectionCount = 0;
// saving the current status to a temp var
- int old_status = m_iStatus;
CMStringA szOauthToken = getMStringA(TWITTER_KEY_OAUTH_TOK);
CMStringA szOauthTokenSecret = getMStringA(TWITTER_KEY_OAUTH_TOK_SEC);
@@ -91,140 +61,42 @@ bool CTwitterProto::NegotiateConnection() if (szOauthToken.IsEmpty() || szOauthTokenSecret.IsEmpty()) {
// first, reset all the keys so we can start fresh
- debugLogA("**NegotiateConnection - Reset OAuth Keys");
- resetOAuthKeys();
+ ResetOauthKeys();
m_szUserName.Empty();
debugLogA("**NegotiateConnection - Requesting oauthTokens");
- http::response resp = request_token();
-
- StringPairs response = ParseQueryString(resp.data);
- szOauthToken = response[L"oauth_token"];
- szOauthTokenSecret = response[L"oauth_token_secret"];
-
- 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
- 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
- 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();
+ RequestOauthAuth();
+ return;
}
- ptrW wszGroup(getWStringA(TWITTER_KEY_GROUP));
- if (wszGroup)
- Clist_GroupCreate(0, wszGroup);
-
- // remember, dbTOK is 0 (false) if the db setting has returned something
szOauthToken = getMStringA(TWITTER_KEY_OAUTH_ACCESS_TOK);
szOauthTokenSecret = getMStringA(TWITTER_KEY_OAUTH_ACCESS_SEC);
- 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..");
- 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();
-
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)old_status, m_iStatus);
-
- // Set to offline
- old_status = m_iStatus;
- m_iDesiredStatus = m_iStatus = ID_STATUS_OFFLINE;
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
- return false;
- }
-
- debugLogA("**NegotiateConnection - requesting 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."));
-
- resetOAuthKeys();
-
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)old_status, m_iStatus);
-
- // Set to offline
- old_status = m_iStatus;
- m_iDesiredStatus = m_iStatus = ID_STATUS_OFFLINE;
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
-
- return false;
- }
-
- debugLogA("**NegotiateConnection - Successfully retrieved Access Tokens");
-
- 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
- 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);
- }
- else {
- m_szAccessToken = szOauthToken;
- m_szAccessTokenSecret = szOauthTokenSecret;
- }
-
- debugLogA("**NegotiateConnection - Setting Consumer Keys and verifying creds...");
-
- 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");
- }
-
- auto *req = new AsyncHttpRequest(REQUEST_GET, "/account/verify_credentials.json");
- auto resp(Execute(req));
- if (resp.code != 200) {
- debugLogA("**NegotiateConnection - Verifying credentials failed! No internet maybe?");
-
-LBL_Error:
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)old_status, m_iStatus);
-
- // Set to offline
- old_status = m_iStatus;
- m_iDesiredStatus = m_iStatus = ID_STATUS_OFFLINE;
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
+ debugLogA("**NegotiateConnection - Successfully retrieved Access Tokens");
- return false;
- }
+ StringPairs accessTokenParameters = ParseQueryString("");
+ 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());
- JSONNode root = JSONNode::parse(resp.data.c_str());
- if (!root) {
- debugLogA("unable to parse response");
- goto LBL_Error;
- }
+ // 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);
- m_szMyId = root["id_str"].as_mstring();
- m_iStatus = m_iDesiredStatus;
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
- return true;
+ m_szAccessToken = szOauthToken;
+ m_szAccessTokenSecret = szOauthTokenSecret;
}
void CTwitterProto::MessageLoop(void*)
{
debugLogA("***** Entering Twitter::MessageLoop");
- since_id_ = db_pod_get<twitter_id>(0, m_szModuleName, TWITTER_KEY_SINCEID, 0);
- dm_since_id_ = db_pod_get<twitter_id>(0, m_szModuleName, TWITTER_KEY_DMSINCEID, 0);
+ since_id_ = getId(TWITTER_KEY_SINCEID);
+ dm_since_id_ = getId(TWITTER_KEY_DMSINCEID);
bool new_account = getByte(TWITTER_KEY_NEW, 1) != 0;
bool popups = getByte(TWITTER_KEY_POPUP_SIGNON, 1) != 0;
@@ -348,6 +220,7 @@ void CTwitterProto::UpdateAvatar(MCONTACT hContact, const CMStringA &url, bool f void CTwitterProto::UpdateFriends()
{
+ /*
auto *req = new AsyncHttpRequest(REQUEST_GET, CMStringA(FORMAT, "/2/users/%s/followers", m_szMyId.c_str()));
http::response resp = Execute(req);
if (resp.code != 200) {
@@ -376,7 +249,7 @@ void CTwitterProto::UpdateFriends() setUString(hContact, "Nick", real_name.c_str());
UpdateAvatar(hContact, profile_image_url.c_str());
}
- disconnectionCount = 0;
+ */
debugLogA("***** Friends list updated");
}
@@ -441,6 +314,7 @@ void CTwitterProto::ShowContactPopup(MCONTACT hContact, const CMStringA &text, c void CTwitterProto::UpdateStatuses(bool pre_read, bool popups, bool tweetToMsg)
{
+ /*
auto *req = new AsyncHttpRequest(REQUEST_GET, "/statuses/home_timeline.json");
req << INT_PARAM("count", 200);
if (since_id_ != 0)
@@ -535,14 +409,15 @@ void CTwitterProto::UpdateStatuses(bool pre_read, bool popups, bool tweetToMsg) ShowContactPopup(hContact, u->status.text.c_str(), new CMStringA(FORMAT, "https://twitter.com/%s/status/%lld", u->username.c_str(), u->status.id));
}
}
+ */
- db_pod_set(0, m_szModuleName, TWITTER_KEY_SINCEID, since_id_);
- disconnectionCount = 0;
+ setId(TWITTER_KEY_SINCEID, since_id_);
debugLogA("***** Status messages updated");
}
void CTwitterProto::UpdateMessages(bool pre_read)
{
+ /*
auto *req = new AsyncHttpRequest(REQUEST_GET, "/direct_messages/events/list.json");
req << INT_PARAM("count", 50);
if (dm_since_id_ != 0)
@@ -603,13 +478,13 @@ void CTwitterProto::UpdateMessages(bool pre_read) if (!msgid.empty())
m_arChatMarks.insert(new CChatMark(hDbEVent, msgid.c_str()));
}
+ */
- db_pod_set(0, m_szModuleName, TWITTER_KEY_DMSINCEID, dm_since_id_);
- disconnectionCount = 0;
+ setId(TWITTER_KEY_DMSINCEID, dm_since_id_);
debugLogA("***** Direct messages updated");
}
-void CTwitterProto::resetOAuthKeys()
+void CTwitterProto::ResetOauthKeys()
{
delSetting(TWITTER_KEY_OAUTH_ACCESS_TOK);
delSetting(TWITTER_KEY_OAUTH_ACCESS_SEC);
diff --git a/protocols/Twitter/src/http.cpp b/protocols/Twitter/src/http.cpp new file mode 100644 index 0000000000..888afdfa84 --- /dev/null +++ b/protocols/Twitter/src/http.cpp @@ -0,0 +1,124 @@ +/* +Copyright © 2012-23 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/>. +*/ + +#include "stdafx.h" + +AsyncHttpRequest::AsyncHttpRequest(int type, const char *szUrl, MTHttpRequestHandler pHandler) +{ + m_pFunc = pHandler; + requestType = type; + m_szUrl = szUrl; +} + +void CTwitterProto::Execute(AsyncHttpRequest *pReq) +{ + if (pReq->m_szUrl[0] == '/') + pReq->m_szUrl.Insert(0, "https://api.twitter.com"); + + bool bIsJson = false; + if (!pReq->m_szParam.IsEmpty()) { + if (pReq->requestType == REQUEST_POST) { + if (pReq->m_szParam[0] == '{') { + bIsJson = true; + pReq->AddHeader("Content-Type", "application/json"); + } + else pReq->AddHeader("Content-Type", "application/x-www-form-urlencoded"); + pReq->AddHeader("Cache-Control", "no-cache"); + + pReq->dataLength = (int)pReq->m_szParam.GetLength(); + pReq->pData = pReq->m_szParam.Detach(); + } + else { + if (pReq->requestType == REQUEST_PATCH) + pReq->requestType = REQUEST_POST; + + pReq->m_szUrl.AppendChar('?'); + pReq->m_szUrl += pReq->m_szParam; + } + } + + // CMStringA auth(FORMAT, "%s:%s", OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET); + // auth.Format("Basic: %s", ptrA(mir_base64_encode(auth, auth.GetLength())).get()); + // if (pReq->requestType == REQUEST_GET) + // auth = OAuthWebRequestSubmit(pReq->m_szUrl, "GET", ""); + //else + // auth = OAuthWebRequestSubmit(pReq->m_szUrl, "POST", (bIsJson) ? "" : pReq->pData); + // pReq->AddHeader("Authorization", auth); + + pReq->szUrl = pReq->m_szUrl.GetBuffer(); + pReq->flags = NLHRF_HTTP11 | NLHRF_PERSISTENT | NLHRF_REDIRECT; + pReq->nlc = m_hConnHttp; + + NLHR_PTR resp(Netlib_HttpTransaction(m_hNetlibUser, pReq)); + if (resp) { + m_hConnHttp = resp->nlc; + + if (pReq->m_pFunc != nullptr) + (this->*(pReq->m_pFunc))(resp, pReq); + } + else m_hConnHttp = nullptr; + + delete pReq; +} + +void CTwitterProto::Push(AsyncHttpRequest *pReq) +{ + { + mir_cslock lck(m_csHttpQueue); + m_arHttpQueue.insert(pReq); + } + + SetEvent(m_evRequestsQueue); +} + +void __cdecl CTwitterProto::ServerThread(void *) +{ + m_bTerminated = false; + m_hWorkerThreadId = GetCurrentThreadId(); + debugLogA("CTwitterProto::ServerThread: %s", "entering"); + + while (true) { + WaitForSingleObject(m_evRequestsQueue, 1000); + if (m_bTerminated) + break; + + while (true) { + bool bNeedSleep = false; + AsyncHttpRequest *pReq; + { + mir_cslock lck(m_csHttpQueue); + if (m_arHttpQueue.getCount() == 0) + break; + + pReq = m_arHttpQueue[0]; + m_arHttpQueue.remove(0); + bNeedSleep = (m_arHttpQueue.getCount() > 1); + } + + if (m_bTerminated) + break; + + Execute(pReq); + if (bNeedSleep) + Sleep(200); + } + } + + m_hWorkerThreadId = 0; + debugLogA("CTwitterProto::ServerThread: %s", "leaving"); +} diff --git a/protocols/Twitter/src/http.h b/protocols/Twitter/src/http.h index dc5c5debed..a16fb92cd1 100644 --- a/protocols/Twitter/src/http.h +++ b/protocols/Twitter/src/http.h @@ -18,13 +18,3 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#pragma once
-
-namespace http
-{
- struct response
- {
- response() : code(0) {}
- int code;
- CMStringA data;
- };
-}
diff --git a/protocols/Twitter/src/oauth.cpp b/protocols/Twitter/src/oauth.cpp index 16fc13e609..9ef2c9bf7c 100644 --- a/protocols/Twitter/src/oauth.cpp +++ b/protocols/Twitter/src/oauth.cpp @@ -160,3 +160,64 @@ CMStringA CTwitterProto::OAuthCreateSignature(const CMStringA &signatureBase, co HMAC(EVP_sha1(), key.c_str(), (int)key.GetLength(), (uint8_t*)signatureBase.c_str(), signatureBase.GetLength(), digest, &len);
return CMStringA(ptrA(mir_base64_encode(digest, sizeof(digest))));
}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTwitterProto::Oauth2RequestToken(NETLIBHTTPREQUEST *pResp, AsyncHttpRequest *)
+{
+ if (pResp->resultCode != 200) {
+ OnLoggedFail();
+ return;
+ }
+
+ // StringPairs response = ParseQueryString(resp.data);
+ // szOauthToken = response[L"oauth_token"];
+ // szOauthTokenSecret = response[L"oauth_token_secret"];
+
+ if (m_szAccessToken.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;
+ }
+
+ // write those bitches to the db foe latta
+ setString(TWITTER_KEY_OAUTH_TOK, m_szAccessToken);
+ setString(TWITTER_KEY_OAUTH_TOK_SEC, m_szAccessTokenSecret);
+}
+
+void CTwitterProto::RequestOauthToken(const char *szPin)
+{
+ auto *pReq = new AsyncHttpRequest(REQUEST_POST, "/oauth2/token", &CTwitterProto::Oauth2RequestToken);
+ pReq << CHAR_PARAM("grant_type", "authorization_code") << CHAR_PARAM("code_verifier", "zzzzzzz") << CHAR_PARAM("client_id", OAUTH_CONSUMER_KEY)
+ << CHAR_PARAM("callback", "https://miranda-ng.org/oauth") << CHAR_PARAM("code", szPin);
+
+ Push(pReq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTwitterProto::Oauth2RequestAuth(NETLIBHTTPREQUEST *pResp, AsyncHttpRequest *)
+{
+ if (pResp->resultCode != 200) {
+ OnLoggedFail();
+ return;
+ }
+
+}
+
+void CTwitterProto::RequestOauthAuth()
+{
+ Utils_GetRandom(code_verifier, sizeof(code_verifier));
+
+ uint8_t hash[32];
+ mir_sha256_hash(code_verifier, sizeof(code_verifier), hash);
+ code_challenge = ptrA(mir_base64_encode(hash, sizeof(hash)));
+
+ auto *pReq = new AsyncHttpRequest(REQUEST_PATCH, "https://twitter.com/i/oauth2/authorize", &CTwitterProto::Oauth2RequestAuth);
+ pReq->flags |= NLHRF_REDIRECT;
+ pReq << CHAR_PARAM("client_id", OAUTH_CONSUMER_KEY) << CHAR_PARAM("scope", "tweet.read tweet.write users.read offline.access")
+ << CHAR_PARAM("response_type", "code") << CHAR_PARAM("callback", "https://oauth.miranda-ng.org") << CHAR_PARAM("state", "state")
+ << CHAR_PARAM("code_challenge_method", "s256") << CHAR_PARAM("code_challenge", code_challenge);
+
+ Push(pReq);
+}
diff --git a/protocols/Twitter/src/proto.cpp b/protocols/Twitter/src/proto.cpp index 3cf8a955dd..4dd61c6c53 100644 --- a/protocols/Twitter/src/proto.cpp +++ b/protocols/Twitter/src/proto.cpp @@ -25,6 +25,8 @@ static volatile LONG g_msgid = 1; CTwitterProto::CTwitterProto(const char *proto_name, const wchar_t *username) :
PROTO<CTwitterProto>(proto_name, username),
+ m_arHttpQueue(10),
+ m_evRequestsQueue(CreateEvent(nullptr, FALSE, FALSE, nullptr)),
m_arChatMarks(10, NumericKeySortT)
{
CreateProtoService(PS_JOINCHAT, &CTwitterProto::OnJoinChat);
@@ -91,8 +93,6 @@ CTwitterProto::CTwitterProto(const char *proto_name, const wchar_t *username) : CTwitterProto::~CTwitterProto()
{
- Disconnect();
-
if (hAvatarNetlib_)
Netlib_CloseHandle(hAvatarNetlib_);
}
@@ -138,40 +138,29 @@ int CTwitterProto::SendMsg(MCONTACT hContact, int, const char *msg) /////////////////////////////////////////////////////////////////////////////////////////
-int CTwitterProto::SetStatus(int new_status)
+int CTwitterProto::SetStatus(int iNewStatus)
{
- int old_status = m_iStatus;
- if (new_status == m_iStatus)
+ if (iNewStatus == m_iStatus)
return 0;
- m_iDesiredStatus = 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 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)
- return 0;
-
- // if we're already connected, and we change to another connected status, don't try and reconnect!
- if (old_status >= 40072 && old_status <= 40078)
- return 0;
-
- // i think here we tell the proto interface struct that we're connecting, just so it knows
- m_iStatus = ID_STATUS_CONNECTING;
- // 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(&CTwitterProto::SignOn, this);
- }
- else if (new_status == ID_STATUS_OFFLINE) {
- Disconnect();
+ int iOldStatus = m_iStatus;
+ if (iNewStatus == ID_STATUS_OFFLINE) {
m_iStatus = m_iDesiredStatus;
setAllContactStatuses(ID_STATUS_OFFLINE);
SetChatStatus(ID_STATUS_OFFLINE);
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus);
+ }
+ else if (!IsStatusConnecting(m_iStatus)) {
+ m_iStatus = ID_STATUS_CONNECTING;
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus);
+
+ BeginConnection();
+ }
+ else {
+ // Twitter supports only online status
+ m_iStatus = ID_STATUS_ONLINE;
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus);
}
return 0;
@@ -281,40 +270,6 @@ int CTwitterProto::OnPrebuildContactMenu(WPARAM wParam, LPARAM) return 0;
}
-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 CTwitterProto::ShowPopup(const wchar_t *text, int Error)
-{
- POPUPDATAW popup = {};
- mir_snwprintf(popup.lpwzContactName, TranslateT("%s Protocol"), m_tszUserName);
- wcsncpy_s(popup.lpwzText, text, _TRUNCATE);
-
- if (Error) {
- popup.iSeconds = -1;
- popup.colorBack = 0x000000FF;
- popup.colorText = 0x00FFFFFF;
- }
- PUAddPopupW(&popup);
-}
-
-void CTwitterProto::ShowPopup(const char *text, int Error)
-{
- POPUPDATAW popup = {};
- mir_snwprintf(popup.lpwzContactName, TranslateT("%s Protocol"), m_tszUserName);
- wcsncpy_s(popup.lpwzText, Utf2T(text), _TRUNCATE);
- if (Error) {
- popup.iSeconds = -1;
- popup.colorBack = 0x000000FF;
- popup.colorText = 0x00FFFFFF;
- }
- PUAddPopupW(&popup);
-}
-
/////////////////////////////////////////////////////////////////////////////////////////
// TODO: the more I think about it, the more I think all twit.* methods should
// be in MessageLoop
diff --git a/protocols/Twitter/src/proto.h b/protocols/Twitter/src/proto.h index 544c08d9a7..51efed7e0c 100644 --- a/protocols/Twitter/src/proto.h +++ b/protocols/Twitter/src/proto.h @@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. typedef __int64 twitter_id;
+class CTwitterProto;
+
struct twitter_status
{
std::string text;
@@ -39,11 +41,6 @@ struct twitter_user time_t parse_time(const CMStringA &str);
-struct AsyncHttpRequest : public MHttpRequest
-{
- AsyncHttpRequest(int type, const char *szUrl);
-};
-
struct CChatMark
{
CChatMark(MEVENT _p1, const CMStringA &_p2) :
@@ -56,26 +53,34 @@ struct CChatMark CMStringA szId;
};
+struct AsyncHttpRequest : public MTHttpRequest<CTwitterProto>
+{
+ AsyncHttpRequest(int type, const char *szUrl, MTHttpRequestHandler pHandler = nullptr);
+};
+
class CTwitterProto : public PROTO<CTwitterProto>
{
SESSION_INFO *m_si;
- http::response request_token();
- http::response request_access_tokens();
-
- bool get_info(const CMStringA &name, twitter_user *);
- bool get_info_by_email(const CMStringA &email, twitter_user *);
+ // http server thread routines
+ bool m_bTerminated;
+ mir_cs m_csHttpQueue;
+ HANDLE m_evRequestsQueue;
+ LIST<AsyncHttpRequest> m_arHttpQueue;
- bool add_friend(const CMStringA &name, twitter_user &u);
- void remove_friend(const CMStringA &name);
+ void Push(AsyncHttpRequest *req);
+ void Execute(AsyncHttpRequest *req);
- void mark_read(MCONTACT hContact, const CMStringA &msgId);
+ void BeginConnection();
+ void OnLoggedIn();
+ void OnLoggedFail();
- void set_status(const CMStringA &text);
- void send_direct(const CMStringA &name, const CMStringA &text);
+ int m_hWorkerThreadId;
+ void __cdecl ServerThread(void *);
- http::response Execute(AsyncHttpRequest *req);
+ HNETLIBCONN m_hConnHttp;
+ // internal data
CMStringA m_szUserName;
CMStringA m_szMyId;
CMStringA m_szPassword;
@@ -87,7 +92,6 @@ class CTwitterProto : public PROTO<CTwitterProto> CMStringW GetAvatarFolder();
- mir_cs signon_lock_;
mir_cs avatar_lock_;
mir_cs twitter_lock_;
@@ -99,14 +103,25 @@ class CTwitterProto : public PROTO<CTwitterProto> twitter_id since_id_;
twitter_id dm_since_id_;
- int disconnectionCount;
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // utils
+
+ twitter_id getId(const char *szSetting);
+ void setId(const char *szSetting, twitter_id id);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // oauth
+
+ uint8_t code_verifier[32];
+ CMStringA code_challenge;
+
+ void RequestOauthAuth();
+ void RequestOauthToken(const char *szPin = nullptr);
+ void ResetOauthKeys();
+
+ void Oauth2RequestAuth(NETLIBHTTPREQUEST *, AsyncHttpRequest *);
+ void Oauth2RequestToken(NETLIBHTTPREQUEST *, AsyncHttpRequest *);
- // 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);
@@ -116,11 +131,6 @@ class CTwitterProto : public PROTO<CTwitterProto> 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; }
-
- bool NegotiateConnection();
-
void UpdateStatuses(bool pre_read, bool popups, bool tweetToMsg);
void UpdateMessages(bool pre_read);
void UpdateFriends();
@@ -136,14 +146,21 @@ class CTwitterProto : public PROTO<CTwitterProto> MCONTACT AddToClientList(const char *, const char *);
MCONTACT FindContactById(const char *);
- static void CALLBACK APC_callback(ULONG_PTR p);
-
void UpdateChat(const twitter_user &update);
void AddChatContact(const char *name, const char *nick = nullptr);
void DeleteChatContact(const char *name);
void SetChatStatus(int);
- void resetOAuthKeys();
+ 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 mark_read(MCONTACT hContact, const CMStringA &msgId);
+
+ void set_status(const CMStringA &text);
+ void send_direct(const CMStringA &name, const CMStringA &text);
public:
CTwitterProto(const char*,const wchar_t*);
@@ -201,7 +218,6 @@ public: void __cdecl AddToListWorker(void *p);
void __cdecl DoSearch(void *);
- void __cdecl SignOn(void *);
void __cdecl MessageLoop(void *);
void __cdecl GetAwayMsgWorker(void *);
void __cdecl UpdateAvatarWorker(void *);
diff --git a/protocols/Twitter/src/stdafx.h b/protocols/Twitter/src/stdafx.h index 63b4eecc35..c5a799fcd5 100644 --- a/protocols/Twitter/src/stdafx.h +++ b/protocols/Twitter/src/stdafx.h @@ -8,7 +8,7 @@ #undef _HAS_EXCEPTIONS
#define _HAS_EXCEPTIONS 1
-#include <Windows.h>
+#include <winsock2.h>
#include <Shlwapi.h>
#include <Wincrypt.h>
#include <stdio.h>
@@ -57,7 +57,6 @@ using std::map; #pragma comment(lib, "libcrypto.lib")
#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
diff --git a/protocols/Twitter/src/twitter.cpp b/protocols/Twitter/src/twitter.cpp index fa6d441363..695c93b8f0 100644 --- a/protocols/Twitter/src/twitter.cpp +++ b/protocols/Twitter/src/twitter.cpp @@ -20,23 +20,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. // utility functions
-http::response CTwitterProto::request_token()
-{
- auto *req = new AsyncHttpRequest(REQUEST_GET, "https://api.twitter.com/oauth/request_token");
- return Execute(req);
-}
-
-http::response CTwitterProto::request_access_tokens()
-{
- auto *req = new AsyncHttpRequest(REQUEST_GET, "https://api.twitter.com/oauth/access_token");
- return Execute(req);
-}
-
bool CTwitterProto::get_info(const CMStringA &name, twitter_user *info)
{
if (!info)
return false;
-
+ /*
auto *req = new AsyncHttpRequest(REQUEST_GET, "/users/show/" + mir_urlEncode(name) + ".json");
http::response resp = Execute(req);
if (resp.code != 200)
@@ -52,6 +40,7 @@ bool CTwitterProto::get_info(const CMStringA &name, twitter_user *info) info->username = root["screen_name"].as_string();
info->real_name = root["name"].as_string();
info->profile_image_url = root["profile_image_url"].as_string();
+ */
return true;
}
@@ -60,6 +49,7 @@ bool CTwitterProto::get_info_by_email(const CMStringA &email, twitter_user *info if (!info)
return false;
+ /*
auto *req = new AsyncHttpRequest(REQUEST_GET, "/users/show.json?email=" + mir_urlEncode(email));
http::response resp = Execute(req);
if (resp.code != 200)
@@ -75,11 +65,13 @@ bool CTwitterProto::get_info_by_email(const CMStringA &email, twitter_user *info info->username = root["screen_name"].as_string();
info->real_name = root["name"].as_string();
info->profile_image_url = root["profile_image_url"].as_string();
+ */
return true;
}
bool CTwitterProto::add_friend(const CMStringA &name, twitter_user &ret)
{
+ /*
auto *req = new AsyncHttpRequest(REQUEST_POST, "/friendships/create/" + mir_urlEncode(name) + ".json");
http::response resp = Execute(req);
if (resp.code != 200)
@@ -98,7 +90,7 @@ bool CTwitterProto::add_friend(const CMStringA &name, twitter_user &ret) ret.status.text = pStatus["text"].as_string();
ret.status.id = _atoi64(pStatus["id"].as_string().c_str());
}
-
+ */
return true;
}
diff --git a/protocols/Twitter/src/ui.cpp b/protocols/Twitter/src/ui.cpp index 978f791f83..d2a4065ccd 100644 --- a/protocols/Twitter/src/ui.cpp +++ b/protocols/Twitter/src/ui.cpp @@ -422,7 +422,9 @@ INT_PTR CALLBACK popup_options_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA return false;
}
-INT_PTR CALLBACK pin_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR CALLBACK pin_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
CTwitterProto *proto;
@@ -452,3 +454,10 @@ INT_PTR CALLBACK pin_proc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) return false;
}
+
+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;
+}
diff --git a/protocols/Twitter/src/ui.h b/protocols/Twitter/src/ui.h index 8762886cc8..a2d72b2d25 100644 --- a/protocols/Twitter/src/ui.h +++ b/protocols/Twitter/src/ui.h @@ -22,6 +22,5 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. INT_PTR CALLBACK first_run_dialog(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam);
INT_PTR CALLBACK tweet_proc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam);
-INT_PTR CALLBACK pin_proc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam);
INT_PTR CALLBACK options_proc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam);
INT_PTR CALLBACK popup_options_proc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam);
\ No newline at end of file diff --git a/protocols/Twitter/src/utility.cpp b/protocols/Twitter/src/utility.cpp index 3d380b85a8..d9031b51ce 100644 --- a/protocols/Twitter/src/utility.cpp +++ b/protocols/Twitter/src/utility.cpp @@ -20,59 +20,55 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <io.h>
-http::response CTwitterProto::Execute(AsyncHttpRequest *pReq)
+twitter_id CTwitterProto::getId(const char *szSetting)
{
- if (pReq->m_szUrl[0] == '/')
- pReq->m_szUrl.Insert(0, "https://api.twitter.com/2");
-
- bool bIsJson = false;
- if (!pReq->m_szParam.IsEmpty()) {
- if (pReq->requestType == REQUEST_POST) {
- if (pReq->m_szParam[0] == '{') {
- bIsJson = true;
- pReq->AddHeader("Content-Type", "application/json");
- }
- else pReq->AddHeader("Content-Type", "application/x-www-form-urlencoded");
- pReq->AddHeader("Cache-Control", "no-cache");
-
- pReq->dataLength = (int)pReq->m_szParam.GetLength();
- pReq->pData = pReq->m_szParam.Detach();
- }
- else {
- pReq->m_szUrl.AppendChar('?');
- pReq->m_szUrl += pReq->m_szParam;
- }
- }
+ DBVARIANT dbv;
+ dbv.type = DBVT_BLOB;
+ if (db_get(0, m_szModuleName, szSetting, &dbv))
+ return 0;
- CMStringA auth;
- if (pReq->requestType == REQUEST_GET)
- auth = OAuthWebRequestSubmit(pReq->m_szUrl, "GET", "");
- else
- auth = OAuthWebRequestSubmit(pReq->m_szUrl, "POST", (bIsJson) ? "" : pReq->pData);
- pReq->AddHeader("Authorization", auth);
-
- pReq->szUrl = pReq->m_szUrl.GetBuffer();
- pReq->flags = NLHRF_HTTP11 | NLHRF_PERSISTENT | NLHRF_REDIRECT;
- pReq->nlc = m_hConnHttp;
- http::response resp_data;
- NLHR_PTR resp(Netlib_HttpTransaction(m_hNetlibUser, pReq));
- if (resp) {
- debugLogA("**SLURP - the server has responded!");
- m_hConnHttp = resp->nlc;
- resp_data.code = resp->resultCode;
- if (resp->pData)
- resp_data.data = resp->pData;
- }
- else {
- m_hConnHttp = nullptr;
- resp_data.code = 500;
- debugLogA("SLURP - there was no response!");
+ twitter_id ret = *(twitter_id *)dbv.pbVal;
+ db_free(&dbv);
+ return ret;
+}
+
+void CTwitterProto::setId(const char *szSetting, twitter_id id)
+{
+ db_set_blob(0, m_szModuleName, szSetting, &id, sizeof(id));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// popups
+
+void CTwitterProto::ShowPopup(const wchar_t *text, int Error)
+{
+ POPUPDATAW popup = {};
+ mir_snwprintf(popup.lpwzContactName, TranslateT("%s Protocol"), m_tszUserName);
+ wcsncpy_s(popup.lpwzText, text, _TRUNCATE);
+
+ if (Error) {
+ popup.iSeconds = -1;
+ popup.colorBack = 0x000000FF;
+ popup.colorText = 0x00FFFFFF;
}
+ PUAddPopupW(&popup);
+}
- delete pReq;
- return resp_data;
+void CTwitterProto::ShowPopup(const char *text, int Error)
+{
+ POPUPDATAW popup = {};
+ mir_snwprintf(popup.lpwzContactName, TranslateT("%s Protocol"), m_tszUserName);
+ wcsncpy_s(popup.lpwzText, Utf2T(text), _TRUNCATE);
+ if (Error) {
+ popup.iSeconds = -1;
+ popup.colorBack = 0x000000FF;
+ popup.colorText = 0x00FFFFFF;
+ }
+ PUAddPopupW(&popup);
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
bool save_url(HNETLIBUSER hNetlib, const CMStringA &url, const CMStringW &filename)
{
NETLIBHTTPREQUEST req = { sizeof(req) };
diff --git a/protocols/Twitter/twitter.vcxproj b/protocols/Twitter/twitter.vcxproj index 61b82f865b..1ad66becae 100644 --- a/protocols/Twitter/twitter.vcxproj +++ b/protocols/Twitter/twitter.vcxproj @@ -29,6 +29,7 @@ <ClCompile Include="src\chat.cpp" />
<ClCompile Include="src\connection.cpp" />
<ClCompile Include="src\contacts.cpp" />
+ <ClCompile Include="src\http.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\oauth.cpp" />
<ClCompile Include="src\proto.cpp" />
diff --git a/protocols/Twitter/twitter.vcxproj.filters b/protocols/Twitter/twitter.vcxproj.filters index 38aa9d68e3..4930423ac6 100644 --- a/protocols/Twitter/twitter.vcxproj.filters +++ b/protocols/Twitter/twitter.vcxproj.filters @@ -38,6 +38,9 @@ <ClCompile Include="src\chat.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\http.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\stdafx.cxx">
@@ -50,9 +53,6 @@ </Image>
</ItemGroup>
<ItemGroup>
- <ClInclude Include="src\http.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\proto.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -74,6 +74,9 @@ <ClInclude Include="src\version.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\http.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="res\twitter.rc">
|