From 74bbe14fb8a018a65f89d28386eb3e88edfd0516 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sun, 6 Oct 2013 13:17:00 +0000 Subject: VK: async http requests git-svn-id: http://svn.miranda-ng.org/main/trunk@6373 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/VKontakte/src/misc.cpp | 11 +++ protocols/VKontakte/src/stdafx.h | 2 + protocols/VKontakte/src/vk.h | 5 ++ protocols/VKontakte/src/vk_proto.cpp | 32 ++++++- protocols/VKontakte/src/vk_proto.h | 47 ++++++++-- protocols/VKontakte/src/vk_queue.cpp | 137 ++++++++++++++++++++++++++++++ protocols/VKontakte/src/vk_thread.cpp | 16 +++- protocols/VKontakte/vk_10.vcxproj | 1 + protocols/VKontakte/vk_10.vcxproj.filters | 3 + protocols/VKontakte/vk_11.vcxproj | 1 + protocols/VKontakte/vk_11.vcxproj.filters | 3 + 11 files changed, 243 insertions(+), 15 deletions(-) create mode 100644 protocols/VKontakte/src/vk_queue.cpp diff --git a/protocols/VKontakte/src/misc.cpp b/protocols/VKontakte/src/misc.cpp index 0802d73405..b45ce5da7f 100644 --- a/protocols/VKontakte/src/misc.cpp +++ b/protocols/VKontakte/src/misc.cpp @@ -27,6 +27,17 @@ TCHAR* CVkProto::GetUserStoredPassword() return NULL; } +void CVkProto::SetAllContactStatuses(int iStatus) +{ + for (HANDLE hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) { + if (isChatRoom(hContact)) + continue; + + if (getWord(hContact, "Status", 0) != iStatus) + setWord(hContact, "Status", iStatus); + } +} + int CVkProto::SetServerStatus(int iStatus) { return 0; diff --git a/protocols/VKontakte/src/stdafx.h b/protocols/VKontakte/src/stdafx.h index e3137a17a5..adf0de846a 100644 --- a/protocols/VKontakte/src/stdafx.h +++ b/protocols/VKontakte/src/stdafx.h @@ -56,6 +56,8 @@ along with this program. If not, see . #include #include +#include + #include "win2k.h" #include "resource.h" diff --git a/protocols/VKontakte/src/vk.h b/protocols/VKontakte/src/vk.h index 23d92fa906..51723a527d 100644 --- a/protocols/VKontakte/src/vk.h +++ b/protocols/VKontakte/src/vk.h @@ -15,4 +15,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#define VK_APP_ID 3917910 + +#define VK_API_URL "api.vk.com" +#define VK_REDIRECT_URL "http://" VK_API_URL "/blank.html" + extern HINSTANCE hInst; \ No newline at end of file diff --git a/protocols/VKontakte/src/vk_proto.cpp b/protocols/VKontakte/src/vk_proto.cpp index fe308aff71..d2e213e998 100644 --- a/protocols/VKontakte/src/vk_proto.cpp +++ b/protocols/VKontakte/src/vk_proto.cpp @@ -18,13 +18,33 @@ along with this program. If not, see . #include "stdafx.h" CVkProto::CVkProto(const char *szModuleName, const TCHAR *ptszUserName) : - PROTO(szModuleName, ptszUserName) + PROTO(szModuleName, ptszUserName), + m_arRequestsQueue(10) { + InitQueue(); + CreateProtoService(PS_CREATEACCMGRUI, &CVkProto::SvcCreateAccMgrUI); + + TCHAR descr[512]; + mir_sntprintf(descr, SIZEOF(descr), TranslateT("%s server connection"), m_tszUserName); + + NETLIBUSER nlu = {sizeof(nlu)}; + nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_TCHAR; + nlu.szSettingsModule = m_szModuleName; + nlu.szSettingsModule = m_szModuleName; + nlu.ptszDescriptiveName = descr; + m_hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu); + + mir_sntprintf(descr, SIZEOF(descr), _T("%%miranda_avatarcache%%\\%s"), m_tszUserName); + hAvatarFolder = FoldersRegisterCustomPathT(LPGEN("Avatars"), m_szModuleName, descr, m_tszUserName); + + // Set all contacts offline -- in case we crashed + SetAllContactStatuses(ID_STATUS_OFFLINE); } CVkProto::~CVkProto() { + UninitQueue(); } int CVkProto::OnModulesLoaded(WPARAM wParam, LPARAM lParam) @@ -34,6 +54,8 @@ int CVkProto::OnModulesLoaded(WPARAM wParam, LPARAM lParam) int CVkProto::OnPreShutdown(WPARAM wParam, LPARAM lParam) { + m_bTerminated = true; + SetEvent(m_evRequestsQueue); return 0; } @@ -77,17 +99,19 @@ int CVkProto::SetStatus(int iNewStatus) return 0; int oldStatus = m_iStatus; - if (iNewStatus == ID_STATUS_OFFLINE) { + m_iDesiredStatus = iNewStatus; + + if (iNewStatus == ID_STATUS_OFFLINE) { if ( IsOnline()) ShutdownSession(); m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus); } - else if (!m_hWorkerThread && !(m_iStatus >= ID_STATUS_CONNECTING && m_iStatus < ID_STATUS_CONNECTING + MAX_CONNECT_RETRIES)) { + else if ( !(m_iStatus >= ID_STATUS_CONNECTING && m_iStatus < ID_STATUS_CONNECTING + MAX_CONNECT_RETRIES)) { m_iStatus = ID_STATUS_CONNECTING; ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus); - ForkThread(&CVkProto::WorkerThread, 0); + m_hWorkerThread = ForkThreadEx(&CVkProto::WorkerThread, 0, NULL); } else if ( IsOnline()) SetServerStatus(iNewStatus); diff --git a/protocols/VKontakte/src/vk_proto.h b/protocols/VKontakte/src/vk_proto.h index cc1d82884c..2965ab8e74 100644 --- a/protocols/VKontakte/src/vk_proto.h +++ b/protocols/VKontakte/src/vk_proto.h @@ -15,6 +15,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +struct CVkProto; +typedef void (CVkProto::*VK_REQUEST_HANDLER)(NETLIBHTTPREQUEST*); + struct CVkProto : public PROTO { CVkProto(const char*, const TCHAR*); @@ -75,6 +78,8 @@ struct CVkProto : public PROTO int __cdecl OnOptionsInit(WPARAM, LPARAM); int __cdecl OnPreShutdown(WPARAM, LPARAM); + void OnOAuthAuthorize(NETLIBHTTPREQUEST*); + //==== Services ====================================================================== INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM); @@ -85,13 +90,41 @@ struct CVkProto : public PROTO __forceinline bool IsOnline() const { return m_bOnline; } - void ShutdownSession(); - void OnLoggedOut(); - void __cdecl WorkerThread(void*); + void RequestMyInfo(); private: - int SetServerStatus(int); - - bool m_bOnline; - UINT m_hWorkerThread; + struct AsyncHttpRequest : public NETLIBHTTPREQUEST, public MZeroedObject + { + ~AsyncHttpRequest() + { + mir_free(szUrl); + } + + VK_REQUEST_HANDLER m_pFunc; + time_t m_expireTime; + }; + LIST m_arRequestsQueue; + CRITICAL_SECTION m_csRequestsQueue; + HANDLE m_evRequestsQueue; + HANDLE m_hWorkerThread; + bool m_bTerminated; + + void InitQueue(); + void UninitQueue(); + void ExecuteRequest(AsyncHttpRequest*); + bool PushAsyncHttpRequest(int iRequestType, LPCSTR szUrl, bool bSecure, VK_REQUEST_HANDLER pFunc, int iTimeout = 10000); + int SetupConnection(void); + void __cdecl WorkerThread(void*); + + void OnLoggedOut(); + void ShutdownSession(); + + void SetAllContactStatuses(int status); + int SetServerStatus(int); + + bool m_bOnline; + + HANDLE m_hNetlibUser, m_hNetlibConn; + HANDLE hAvatarFolder; + ptrA m_szAccessToken; }; diff --git a/protocols/VKontakte/src/vk_queue.cpp b/protocols/VKontakte/src/vk_queue.cpp new file mode 100644 index 0000000000..9dd6b8b382 --- /dev/null +++ b/protocols/VKontakte/src/vk_queue.cpp @@ -0,0 +1,137 @@ +/* +Copyright (C) 2013 Miranda NG Project (http://miranda-ng.org) + +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 version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "stdafx.h" + +void CVkProto::InitQueue() +{ + ::InitializeCriticalSection(&m_csRequestsQueue); + m_evRequestsQueue = CreateEvent(NULL, FALSE, FALSE, NULL); +} + +void CVkProto::UninitQueue() +{ + m_arRequestsQueue.destroy(); + CloseHandle(m_evRequestsQueue); + ::DeleteCriticalSection(&m_csRequestsQueue); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CVkProto::SetupConnection() +{ + if (m_hNetlibConn != NULL) + return TRUE; + + NETLIBOPENCONNECTION nloc = { sizeof(nloc) }; + nloc.flags = NLOCF_SSL | NLOCF_HTTP | NLOCF_V2; + nloc.szHost = VK_API_URL; + nloc.wPort = 443; + nloc.timeout = 5000; + m_hNetlibConn = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)m_hNetlibUser, (LPARAM)&nloc); + return m_hNetlibConn != NULL; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CVkProto::ExecuteRequest(AsyncHttpRequest *pReq) +{ + int bytesSent = CallService(MS_NETLIB_SENDHTTPREQUEST, (WPARAM)m_hNetlibConn, (LPARAM)pReq); + if (bytesSent > 0) { + NETLIBHTTPREQUEST *reply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_RECVHTTPHEADERS, (WPARAM)m_hNetlibConn, 0); + if (reply != NULL) { + (this->*(pReq->m_pFunc))(reply); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)reply); + } + } + delete pReq; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static NETLIBHTTPHEADER hdrs[3] = +{ + { "Connection", "keep-alive" }, + { "User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5)" }, + { "Host", VK_API_URL } +}; + +bool CVkProto::PushAsyncHttpRequest(int iRequestType, LPCSTR szUrl, bool bSecure, VK_REQUEST_HANDLER pFunc, int iTimeout) +{ + if ( !SetupConnection()) + return false; + + AsyncHttpRequest *pReq = new AsyncHttpRequest(); + pReq->cbSize = sizeof(NETLIBHTTPREQUEST); + pReq->requestType = iRequestType; + pReq->headers = hdrs; + pReq->headersCount = SIZEOF(hdrs); + pReq->szUrl = mir_strdup(szUrl); + pReq->nlc = m_hNetlibConn; + pReq->flags = NLHRF_PERSISTENT | NLHRF_HTTP11 | NLHRF_REDIRECT | NLHRF_NODUMP; + if (bSecure) + pReq->flags |= NLHRF_SSL; + pReq->m_expireTime = time(0) + iTimeout; + pReq->m_pFunc = pFunc; + { + mir_cslock lck(m_csRequestsQueue); + m_arRequestsQueue.insert(pReq); + } + SetEvent(m_evRequestsQueue); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CVkProto::WorkerThread(void*) +{ + m_szAccessToken = getStringA("AccessToken"); + if (m_szAccessToken != NULL) + RequestMyInfo(); + else { // Initialize new OAuth session + CMStringA szUrl; + szUrl.Format("/oauth/authorize?client_id=%d&scope=%s&redirect_uri=%s&display=wap&response_type=token", + VK_APP_ID, "friends,photos,audio,video,wall,messages,offline", VK_REDIRECT_URL); + PushAsyncHttpRequest(REQUEST_GET, szUrl, false, &CVkProto::OnOAuthAuthorize); + } + + while(true) { + DWORD dwRet = WaitForSingleObject(m_evRequestsQueue, 1000); + if (dwRet == WAIT_TIMEOUT) { + // check expiration; + continue; + } + + if (dwRet != WAIT_OBJECT_0) + continue; + + if (m_bTerminated) + break; + + AsyncHttpRequest *pReq; + { mir_cslock lck(m_csRequestsQueue); + if (m_arRequestsQueue.getCount() == 0) + continue; + + pReq = m_arRequestsQueue[0]; + m_arRequestsQueue.remove(0); + } + ExecuteRequest(pReq); + } + + OnLoggedOut(); +} diff --git a/protocols/VKontakte/src/vk_thread.cpp b/protocols/VKontakte/src/vk_thread.cpp index dea822ec83..71ee02c2a3 100644 --- a/protocols/VKontakte/src/vk_thread.cpp +++ b/protocols/VKontakte/src/vk_thread.cpp @@ -19,19 +19,27 @@ along with this program. If not, see . void CVkProto::ShutdownSession() { + if (m_hWorkerThread) { + m_bTerminated = true; + SetEvent(m_evRequestsQueue); + } + OnLoggedOut(); } void CVkProto::OnLoggedOut() { + m_hWorkerThread = 0; + ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, ID_STATUS_OFFLINE); m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; } -void CVkProto::WorkerThread(void*) +void CVkProto::OnOAuthAuthorize(NETLIBHTTPREQUEST *reply) { - m_hWorkerThread = GetCurrentThreadId(); +} - OnLoggedOut(); - m_hWorkerThread = 0; +void CVkProto::RequestMyInfo() +{ } + diff --git a/protocols/VKontakte/vk_10.vcxproj b/protocols/VKontakte/vk_10.vcxproj index 721069ae12..2897473b2e 100644 --- a/protocols/VKontakte/vk_10.vcxproj +++ b/protocols/VKontakte/vk_10.vcxproj @@ -176,6 +176,7 @@ + diff --git a/protocols/VKontakte/vk_10.vcxproj.filters b/protocols/VKontakte/vk_10.vcxproj.filters index eaf30d69b3..60d0e438ca 100644 --- a/protocols/VKontakte/vk_10.vcxproj.filters +++ b/protocols/VKontakte/vk_10.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files + diff --git a/protocols/VKontakte/vk_11.vcxproj b/protocols/VKontakte/vk_11.vcxproj index b4ec687c4c..afb873f2ed 100644 --- a/protocols/VKontakte/vk_11.vcxproj +++ b/protocols/VKontakte/vk_11.vcxproj @@ -179,6 +179,7 @@ + diff --git a/protocols/VKontakte/vk_11.vcxproj.filters b/protocols/VKontakte/vk_11.vcxproj.filters index de21e62729..408fc79322 100644 --- a/protocols/VKontakte/vk_11.vcxproj.filters +++ b/protocols/VKontakte/vk_11.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files + -- cgit v1.2.3