/* Copyright (c) 2013-16 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" UINT_PTR CVkProto::m_timer; mir_cs CVkProto::m_csTimer; char szBlankUrl[] = "https://oauth.vk.com/blank.html"; static char VK_TOKEN_BEG[] = "access_token="; static char VK_LOGIN_DOMAIN[] = "https://m.vk.com"; static char fieldsName[] = "id, first_name, last_name, photo_100, bdate, sex, timezone, " "contacts, last_seen, online, status, country, city, relation, interests, activities, " "music, movies, tv, books, games, quotes, about, domain"; ///////////////////////////////////////////////////////////////////////////////////////// void CVkProto::ShutdownSession() { debugLogA("CVkProto::ShutdownSession"); m_bTerminated = true; if (m_hWorkerThread) SetEvent(m_evRequestsQueue); OnLoggedOut(); } void CVkProto::ConnectionFailed(int iReason) { delSetting("AccessToken"); ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, iReason); debugLogA("CVkProto::ConnectionFailed ShutdownSession"); ShutdownSession(); } ///////////////////////////////////////////////////////////////////////////////////////// static VOID CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD) { mir_cslock lck(csInstances); for (int i = 0; i < vk_Instances.getCount(); i++) if (vk_Instances[i]->IsOnline()) { vk_Instances[i]->debugLogA("Tic timer for %i - %s", i, vk_Instances[i]->m_szModuleName); vk_Instances[i]->OnTimerTic(); } } static void CALLBACK VKSetTimer(void*) { mir_cslock lck(CVkProto::m_csTimer); if (CVkProto::m_timer) return; CVkProto::m_timer = SetTimer(NULL, 0, 60000, TimerProc); } static void CALLBACK VKUnsetTimer(void*) { mir_cslock lck(CVkProto::m_csTimer); if (CVkProto::m_timer) KillTimer(NULL, CVkProto::m_timer); CVkProto::m_timer = 0; } ///////////////////////////////////////////////////////////////////////////////////////// void CVkProto::OnTimerTic() { SetServerStatus(m_iDesiredStatus); RetrieveUsersInfo(true); RetrieveUnreadEvents(); } void CVkProto::OnLoggedIn() { debugLogA("CVkProto::OnLoggedIn"); m_bOnline = true; SetServerStatus(m_iDesiredStatus); // initialize online timer CallFunctionAsync(VKSetTimer, this); db_unset(NULL, m_szModuleName, "LastNewsReqTime"); db_unset(NULL, m_szModuleName, "LastNotificationsReqTime"); } void CVkProto::OnLoggedOut() { debugLogA("CVkProto::OnLoggedOut"); m_bOnline = false; if (m_hAPIConnection) Netlib_CloseHandle(m_hAPIConnection); if (m_pollingConn) CallService(MS_NETLIB_SHUTDOWN, (WPARAM)m_pollingConn); ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, ID_STATUS_OFFLINE); m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; bool bOnline = false; { mir_cslock lck(csInstances); for (int i = 0; i < vk_Instances.getCount(); i++) bOnline = bOnline || vk_Instances[i]->IsOnline(); } if (!bOnline) CallFunctionAsync(VKUnsetTimer, this); SetAllContactStatuses(ID_STATUS_OFFLINE); m_chats.destroy(); } ///////////////////////////////////////////////////////////////////////////////////////// void CVkProto::OnOAuthAuthorize(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq) { debugLogA("CVkProto::OnOAuthAuthorize %d", reply->resultCode); GrabCookies(reply); if (reply->resultCode == 302) { // manual redirect LPCSTR pszLocation = findHeader(reply, "Location"); if (pszLocation) { if (!_strnicmp(pszLocation, szBlankUrl, sizeof(szBlankUrl)-1)) { m_szAccessToken = NULL; LPCSTR p = strstr(pszLocation, VK_TOKEN_BEG); if (p) { p += sizeof(VK_TOKEN_BEG)-1; for (LPCSTR q = p+1; *q; q++) { if (*q == '&' || *q == '=' || *q == '\"') { m_szAccessToken = mir_strndup(p, q-p); break; } } if (m_szAccessToken == NULL) m_szAccessToken = mir_strdup(p); setString("AccessToken", m_szAccessToken); RetrieveMyInfo(); } else { delSetting("AccessToken"); ConnectionFailed(LOGINERR_NOSERVER); } } else { AsyncHttpRequest *pRedirectReq = new AsyncHttpRequest(); pRedirectReq->requestType = REQUEST_GET; pRedirectReq->flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11; pRedirectReq->m_pFunc = &CVkProto::OnOAuthAuthorize; pRedirectReq->AddHeader("Referer", m_prevUrl); pRedirectReq->Redirect(reply); if (!pRedirectReq->m_szUrl.IsEmpty()) { if (pRedirectReq->m_szUrl[0] == '/') pRedirectReq->m_szUrl = VK_LOGIN_DOMAIN + pRedirectReq->m_szUrl; ApplyCookies(pRedirectReq); m_prevUrl = pRedirectReq->m_szUrl; } pRedirectReq->m_bApiReq = false; pRedirectReq->bIsMainConn = true; Push(pRedirectReq); } } else ConnectionFailed(LOGINERR_NOSERVER); return; } if (reply->resultCode != 200 || !strstr(reply->pData, "form method=\"post\"")) { // something went wrong ConnectionFailed(LOGINERR_NOSERVER); return; } if (strstr(reply->pData, "service_msg_warning")) { ConnectionFailed(LOGINERR_WRONGPASSWORD); return; } CMStringA szAction, szBody; bool bSuccess = AutoFillForm(reply->pData, szAction, szBody); if (!bSuccess || szAction.IsEmpty() || szBody.IsEmpty()) { if (m_prevError) { ConnectionFailed(LOGINERR_NOSERVER); return; } m_prevError = true; } pReq = new AsyncHttpRequest(); pReq->requestType = REQUEST_POST; pReq->flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11; pReq->m_szParam = szBody; pReq->m_szUrl = szAction; if (!pReq->m_szUrl.IsEmpty() && pReq->m_szUrl[0] == '/') pReq->m_szUrl = VK_LOGIN_DOMAIN + pReq->m_szUrl; m_prevUrl = pReq->m_szUrl; pReq->m_pFunc = &CVkProto::OnOAuthAuthorize; pReq->AddHeader("Content-Type", "application/x-www-form-urlencoded"); pReq->Redirect(reply); ApplyCookies(pReq); pReq->m_bApiReq = false; pReq->bIsMainConn = true; Push(pReq); } ///////////////////////////////////////////////////////////////////////////////////////// void CVkProto::RetrieveMyInfo() { debugLogA("CVkProto::RetrieveMyInfo"); Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/users.get.json", true, &CVkProto::OnReceiveMyInfo, AsyncHttpRequest::rpHigh)); } void CVkProto::OnReceiveMyInfo(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq) { debugLogA("CVkProto::OnReceiveMyInfo %d", reply->resultCode); if (reply->resultCode != 200) { ConnectionFailed(LOGINERR_WRONGPASSWORD); return; } JSONNode jnRoot; const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot); if (!jnResponse) return; const JSONNode &jnUser = *(jnResponse.begin()); m_myUserId = jnUser["id"].as_int(); setDword("ID", m_myUserId); OnLoggedIn(); RetrieveUserInfo(m_myUserId); RetrieveUnreadMessages(); RetrieveFriends(); RetrievePollingInfo(); } MCONTACT CVkProto::SetContactInfo(const JSONNode &jnItem, bool flag, bool self) { if (!jnItem) { debugLogA("CVkProto::SetContactInfo pItem == NULL"); return INVALID_CONTACT_ID; } LONG userid = jnItem["id"].as_int(); debugLogA("CVkProto::SetContactInfo %d", userid); if (userid == 0 || userid == VK_FEED_USER) return NULL; MCONTACT hContact = FindUser(userid, flag); if (userid == m_myUserId) { if (hContact != NULL) if (self) hContact = NULL; else SetContactInfo(jnItem, flag, true); } else if (hContact == NULL) return NULL; CMString tszNick, tszValue; int iValue; tszValue = jnItem["first_name"].as_mstring(); if (!tszValue.IsEmpty()) { setTString(hContact, "FirstName", tszValue); tszNick.Append(tszValue); tszNick.AppendChar(' '); } tszValue = jnItem["last_name"].as_mstring(); if (!tszValue.IsEmpty()) { setTString(hContact, "LastName", tszValue); tszNick.Append(tszValue); } if (!tszNick.IsEmpty()) setTString(hContact, "Nick", tszNick); int sex = jnItem["sex"].as_int(); if (sex) setByte(hContact, "Gender", sex == 2 ? 'M' : 'F'); tszValue = jnItem["bdate"].as_mstring(); if (!tszValue.IsEmpty()) { int d, m, y, iReadCount; iReadCount = swscanf(tszValue, L"%d.%d.%d", &d, &m, &y); if (iReadCount > 1) { if (iReadCount == 3) setWord(hContact, "BirthYear", y); setByte(hContact, "BirthDay", d); setByte(hContact, "BirthMonth", m); } } tszValue = jnItem["photo_100"].as_mstring(); if (!tszValue.IsEmpty()) { SetAvatarUrl(hContact, tszValue); ReloadAvatarInfo(hContact); } const JSONNode &jnLastSeen = jnItem["last_seen"]; if (jnLastSeen) { int iLastSeen = jnLastSeen["time"].as_int(); int iOldLastSeen = db_get_dw(hContact, "BuddyExpectator", "LastSeen"); if (iLastSeen && iLastSeen > iOldLastSeen) { db_set_dw(hContact, "BuddyExpectator", "LastSeen", (DWORD)iLastSeen); db_set_w(hContact, "BuddyExpectator", "LastStatus", ID_STATUS_ONLINE); } } int iNewStatus = (jnItem["online"].as_int() == 0) ? ID_STATUS_OFFLINE : ID_STATUS_ONLINE; if (getWord(hContact, "Status", ID_STATUS_OFFLINE) != iNewStatus) setWord(hContact, "Status", iNewStatus); if (iNewStatus == ID_STATUS_ONLINE) { db_set_dw(hContact, "BuddyExpectator", "LastSeen", (DWORD)time(NULL)); db_set_dw(hContact, "BuddyExpectator", "LastStatus", ID_STATUS_ONLINE); int online_app = _wtoi(jnItem["online_app"].as_mstring()); int online_mobile = jnItem["online_mobile"].as_int(); if (online_app == 0 && online_mobile == 0) SetMirVer(hContact, 7); // vk.com else if (online_app != 0) SetMirVer(hContact, online_app); // App else SetMirVer(hContact, 1); // m.vk.com } else SetMirVer(hContact, -1); // unset MinVer if ((iValue = jnItem["timezone"].as_int()) != 0) setByte(hContact, "Timezone", iValue * -2); tszValue = jnItem["mobile_phone"].as_mstring(); if (!tszValue.IsEmpty()) setTString(hContact, "Cellular", tszValue); tszValue = jnItem["home_phone"].as_mstring(); if (!tszValue.IsEmpty()) setTString(hContact, "Phone", tszValue); tszValue = jnItem["status"].as_mstring(); CMString tszOldStatus(ptrW(db_get_tsa(hContact, hContact ? "CList" : m_szModuleName, "StatusMsg"))); if (tszValue != tszOldStatus) db_set_ts(hContact, hContact ? "CList" : m_szModuleName, "StatusMsg", tszValue); CMString tszOldListeningTo(ptrW(db_get_tsa(hContact, m_szModuleName, "ListeningTo"))); const JSONNode &jnAudio = jnItem["status_audio"]; if (jnAudio) { CMString tszListeningTo(FORMAT, L"%s - %s", jnAudio["artist"].as_mstring(), jnAudio["title"].as_mstring()); if (tszListeningTo != tszOldListeningTo) { setTString(hContact, "ListeningTo", tszListeningTo); setTString(hContact, "AudioUrl", jnAudio["url"].as_mstring()); } } else if (tszValue[0] == wchar_t(9835) && tszValue.GetLength() > 2) { setTString(hContact, "ListeningTo", &(tszValue.GetBuffer())[2]); db_unset(hContact, m_szModuleName, "AudioUrl"); } else { db_unset(hContact, m_szModuleName, "ListeningTo"); db_unset(hContact, m_szModuleName, "AudioUrl"); } const JSONNode &jnCountry = jnItem["country"]; if (jnCountry) { tszValue = jnCountry["title"].as_mstring(); if (!tszValue.IsEmpty()) setTString(hContact, "Country", tszValue); } const JSONNode &jnCity = jnItem["city"]; if (jnCity) { tszValue = jnCity["title"].as_mstring(); if (!tszValue.IsEmpty()) setTString(hContact, "City", tszValue); } // MaritalStatus BYTE cMaritalStatus[] = {0, 10, 11, 12, 20, 70, 50, 60}; if (jnItem["relation"] && jnItem["relation"].as_int() < _countof(cMaritalStatus)) setByte(hContact, "MaritalStatus", cMaritalStatus[jnItem["relation"].as_int()]); // interests, activities, music, movies, tv, books, games, quotes CVKInteres vkInteres[] = { { "interests", TranslateT("Interests") }, { "activities", TranslateT("Activities") }, { "music", TranslateT("Music") }, { "movies", TranslateT("Movies") }, { "tv", TranslateT("TV") }, { "books", TranslateT("Books") }, { "games", TranslateT("Games") }, { "quotes", TranslateT("Quotes") } }; int iInteres = 0; for (int i = 0; i < _countof(vkInteres); i++) { tszValue = jnItem[vkInteres[i].szField].as_mstring(); if (tszValue.IsEmpty()) continue; CMStringA InteresCat(FORMAT, "Interest%dCat", iInteres); CMStringA InteresText(FORMAT, "Interest%dText", iInteres); setTString(hContact, InteresCat, vkInteres[i].ptszTranslate); setTString(hContact, InteresText, tszValue); iInteres++; } for (int i = iInteres; iInteres > 0; i++) { CMStringA InteresCat(FORMAT, "Interest%dCat", iInteres); ptrW ptszCat(db_get_tsa(hContact, m_szModuleName, InteresCat)); if (!ptszCat) break; db_unset(hContact, m_szModuleName, InteresCat); CMStringA InteresText(FORMAT, "Interest%dText", iInteres); ptrW ptszText(db_get_tsa(hContact, m_szModuleName, InteresText)); if (!ptszText) break; db_unset(hContact, m_szModuleName, InteresText); } tszValue = jnItem["about"].as_mstring(); if (!tszValue.IsEmpty()) setTString(hContact, "About", tszValue); tszValue = jnItem["domain"].as_mstring(); if (!tszValue.IsEmpty()) { setTString(hContact, "domain", tszValue); CMString tszUrl("https://vk.com/"); tszUrl.Append(tszValue); setTString(hContact, "Homepage", tszUrl); } return hContact; } void CVkProto::RetrieveUserInfo(LONG userID) { debugLogA("CVkProto::RetrieveUserInfo (%d)", userID); if (userID == VK_FEED_USER || !IsOnline()) return; CMString code(FORMAT, L"var userIDs=\"%i\";var res=API.users.get({\"user_ids\":userIDs,\"fields\":\"%s\",\"name_case\":\"nom\"});return{\"freeoffline\":0,\"norepeat\":1,\"usercount\":res.length,\"users\":res};", userID, CMString(fieldsName)); Push(new AsyncHttpRequest(this, REQUEST_POST, "/method/execute.json", true, &CVkProto::OnReceiveUserInfo) << TCHAR_PARAM("code", code)); } void CVkProto::RetrieveUsersInfo(bool bFreeOffline, bool bRepeat) { debugLogA("CVkProto::RetrieveUsersInfo"); if (!IsOnline()) return; CMString userIDs, code; int i = 0; for (MCONTACT hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) { LONG userID = getDword(hContact, "ID", -1); if (userID == -1 || userID == VK_FEED_USER) continue; if (!userIDs.IsEmpty()) userIDs.AppendChar(','); userIDs.AppendFormat(L"%i", userID); if (i == MAX_CONTACTS_PER_REQUEST) break; i++; } CMString codeformat("var userIDs=\"%s\";var _fields=\"%s\";"); if (m_bNeedSendOnline) codeformat += L"API.account.setOnline();"; if (bFreeOffline && !m_vkOptions.bLoadFullCList) codeformat += CMString("var US=[];var res=[];var t=10;while(t>0){" "US=API.users.get({\"user_ids\":userIDs,\"fields\":_fields,\"name_case\":\"nom\"});" "var index=US.length;while(index>0){" "index=index-1;if(US[index].online!=0){res.push(US[index]);};};" "t=t-1;if(res.length>0)t=0;};" "return{\"freeoffline\":1,\"norepeat\":%d,\"usercount\":res.length,\"users\":res,\"requests\":API.friends.getRequests({\"extended\":0,\"need_mutual\":0,\"out\":0})};"); else codeformat += CMString("var res=API.users.get({\"user_ids\":userIDs,\"fields\":_fields,\"name_case\":\"nom\"});" "return{\"freeoffline\":0,\"norepeat\":%d,\"usercount\":res.length,\"users\":res,\"requests\":API.friends.getRequests({\"extended\":0,\"need_mutual\":0,\"out\":0})};"); code.AppendFormat(codeformat, userIDs, CMString(bFreeOffline ? "online,status" : fieldsName), (int)bRepeat); Push(new AsyncHttpRequest(this, REQUEST_POST, "/method/execute.json", true, &CVkProto::OnReceiveUserInfo) << TCHAR_PARAM("code", code)); } void CVkProto::OnReceiveUserInfo(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq) { debugLogA("CVkProto::OnReceiveUserInfo %d", reply->resultCode); if (reply->resultCode != 200 || !IsOnline()) return; JSONNode jnRoot; const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot); if (!jnResponse) return; const JSONNode &jnUsers = jnResponse["users"]; if (!jnUsers) return; if (!jnResponse["norepeat"].as_bool() && jnResponse["usercount"].as_int() == 0) { RetrieveUsersInfo(true, true); return; } MCONTACT hContact; LIST arContacts(10, PtrKeySortT); for (hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) if (!isChatRoom(hContact)) arContacts.insert((HANDLE)hContact); for (auto it = jnUsers.begin(); it != jnUsers.end(); ++it) { hContact = SetContactInfo((*it)); if (hContact) arContacts.remove((HANDLE)hContact); } if (jnResponse["freeoffline"].as_bool()) for (int i = 0; i < arContacts.getCount(); i++) { hContact = (UINT_PTR)arContacts[i]; LONG userID = getDword(hContact, "ID", -1); if (userID == m_myUserId || userID == VK_FEED_USER) continue; int iContactStatus = getWord(hContact, "Status", ID_STATUS_OFFLINE); if ((iContactStatus == ID_STATUS_ONLINE) || (iContactStatus == ID_STATUS_INVISIBLE && time(NULL) - getDword(hContact, "InvisibleTS", 0) >= m_vkOptions.iInvisibleInterval * 60LL)) { setWord(hContact, "Status", ID_STATUS_OFFLINE); SetMirVer(hContact, -1); db_unset(hContact, m_szModuleName, "ListeningTo"); } } arContacts.destroy(); AddFeedSpecialUser(); const JSONNode &jnRequests = jnResponse["requests"]; if (!jnRequests) return; int iCount = jnRequests["count"].as_int(); const JSONNode &jnItems = jnRequests["items"]; if (!iCount || !jnItems) return; debugLogA("CVkProto::OnReceiveUserInfo AuthRequests"); for (auto it = jnItems.begin(); it != jnItems.end(); ++it) { LONG userid = (*it).as_int(); if (userid == 0) break; hContact = FindUser(userid, true); if (!getBool(hContact, "ReqAuth")) { RetrieveUserInfo(userid); setByte(hContact, "ReqAuth", 1); CVkDBAddAuthRequestThreadParam *param = new CVkDBAddAuthRequestThreadParam(hContact, false); ForkThread(&CVkProto::DBAddAuthRequestThread, (void *)param); } } } void CVkProto::RetrieveFriends(bool bCleanNonFriendContacts) { debugLogA("CVkProto::RetrieveFriends"); if (!IsOnline()) return; Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/friends.get.json", true, &CVkProto::OnReceiveFriends) << INT_PARAM("count", m_vkOptions.iMaxFriendsCount > 5000 ? 1000 : m_vkOptions.iMaxFriendsCount) << CHAR_PARAM("fields", fieldsName))->pUserInfo = new CVkSendMsgParam(NULL, bCleanNonFriendContacts ? 1 : 0); } void CVkProto::OnReceiveFriends(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq) { debugLogA("CVkProto::OnReceiveFriends %d", reply->resultCode); if (reply->resultCode != 200 || !IsOnline()) return; JSONNode jnRoot; const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot); if (!jnResponse) return; CVkSendMsgParam *param = (CVkSendMsgParam *)pReq->pUserInfo; bool bCleanContacts = getBool("AutoClean") || (param->iMsgID != 0); delete param; LIST arContacts(10, PtrKeySortT); for (MCONTACT hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) { if (!isChatRoom(hContact)) setByte(hContact, "Auth", 1); db_unset(hContact, m_szModuleName, "ReqAuth"); SetMirVer(hContact, -1); if (bCleanContacts && !isChatRoom(hContact)) arContacts.insert((HANDLE)hContact); } const JSONNode &jnItems = jnResponse["items"]; if (jnItems) for (auto it = jnItems.begin(); it != jnItems.end(); ++it) { MCONTACT hContact = SetContactInfo((*it), true); if (hContact == NULL || hContact == INVALID_CONTACT_ID) continue; arContacts.remove((HANDLE)hContact); setByte(hContact, "Auth", 0); } if (bCleanContacts) for (int i = 0; i < arContacts.getCount(); i++) { MCONTACT hContact = (UINT_PTR)arContacts[i]; LONG userID = getDword(hContact, "ID", -1); if (userID == m_myUserId || userID == VK_FEED_USER) continue; CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact); } arContacts.destroy(); } ///////////////////////////////////////////////////////////////////////////////////////// INT_PTR __cdecl CVkProto::SvcAddAsFriend(WPARAM hContact, LPARAM) { debugLogA("CVkProto::SvcAddAsFriend"); LONG userID = getDword(hContact, "ID", -1); if (!IsOnline() || userID == -1 || userID == VK_FEED_USER) return 1; ProtoChainSend(hContact, PSS_AUTHREQUEST, 0, (LPARAM)TranslateT("Please authorize me to add you to my friend list.")); return 0; } INT_PTR CVkProto::SvcWipeNonFriendContacts(WPARAM, LPARAM) { debugLogA("CVkProto::SvcWipeNonFriendContacts"); if (IDNO == MessageBox(NULL, TranslateT("Are you sure to wipe local contacts missing in your friend list?"), TranslateT("Attention!"), MB_ICONWARNING | MB_YESNO)) return 0; RetrieveFriends(true); return 0; } INT_PTR __cdecl CVkProto::SvcDeleteFriend(WPARAM hContact, LPARAM flag) { debugLogA("CVkProto::SvcDeleteFriend"); LONG userID = getDword(hContact, "ID", -1); if (!IsOnline() || userID == -1 || userID == VK_FEED_USER) return 1; ptrW ptszNick(db_get_tsa(hContact, m_szModuleName, "Nick")); CMString ptszMsg; if (flag == 0) { ptszMsg.AppendFormat(TranslateT("Are you sure to delete %s from your friend list?"), IsEmpty(ptszNick) ? TranslateT("(Unknown contact)") : ptszNick); if (IDNO == MessageBox(NULL, ptszMsg, TranslateT("Attention!"), MB_ICONWARNING | MB_YESNO)) return 1; } Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/friends.delete.json", true, &CVkProto::OnReceiveDeleteFriend) << INT_PARAM("user_id", userID))->pUserInfo = new CVkSendMsgParam(hContact); return 0; } void CVkProto::OnReceiveDeleteFriend(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq) { debugLogA("CVkProto::OnReceiveDeleteFriend %d", reply->resultCode); CVkSendMsgParam *param = (CVkSendMsgParam*)pReq->pUserInfo; if (reply->resultCode == 200 && param) { JSONNode jnRoot; const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot); if (jnResponse) { CMString tszNick(ptrW(db_get_tsa(param->hContact, m_szModuleName, "Nick"))); if (tszNick.IsEmpty()) tszNick = TranslateT("(Unknown contact)"); CMString msgformat, msg; if (jnResponse["success"].as_bool()) { if (jnResponse["friend_deleted"].as_bool()) msgformat = TranslateT("User %s was deleted from your friend list"); else if (jnResponse["out_request_deleted"].as_bool()) msgformat = TranslateT("Your request to the user %s was deleted"); else if (jnResponse["in_request_deleted"].as_bool()) msgformat = TranslateT("Friend request from the user %s declined"); else if (jnResponse["suggestion_deleted"].as_bool()) msgformat = TranslateT("Friend request suggestion for the user %s deleted"); msg.AppendFormat(msgformat, tszNick); MsgPopup(param->hContact, msg, tszNick); setByte(param->hContact, "Auth", 1); } else { msg = TranslateT("User or request was not deleted"); MsgPopup(param->hContact, msg, tszNick); } } } if (param && (!pReq->bNeedsRestart || m_bTerminated)) { delete param; pReq->pUserInfo = NULL; } } INT_PTR __cdecl CVkProto::SvcBanUser(WPARAM hContact, LPARAM) { debugLogA("CVkProto::SvcBanUser"); LONG userID = getDword(hContact, "ID", -1); if (!IsOnline() || userID == -1 || userID == VK_FEED_USER) return 1; CMStringA code(FORMAT, "var userID=\"%d\";API.account.banUser({\"user_id\":userID});", userID); CMString tszVarWarning; if (m_vkOptions.bReportAbuse) { debugLogA("CVkProto::SvcBanUser m_vkOptions.bReportAbuse = true"); code += "API.users.report({\"user_id\":userID,type:\"spam\"});"; tszVarWarning = TranslateT(" report abuse on him/her"); } if (m_vkOptions.bClearServerHistory) { debugLogA("CVkProto::SvcBanUser m_vkOptions.bClearServerHistory = true"); code += "API.messages.deleteDialog({\"user_id\":userID,count:10000});"; if (!tszVarWarning.IsEmpty()) tszVarWarning.AppendChar(L','); tszVarWarning += TranslateT(" clear server history with him/her"); } if (m_vkOptions.bRemoveFromFrendlist) { debugLogA("CVkProto::SvcBanUser m_vkOptions.bRemoveFromFrendlist = true"); code += "API.friends.delete({\"user_id\":userID});"; if (!tszVarWarning.IsEmpty()) tszVarWarning.AppendChar(L','); tszVarWarning += TranslateT(" remove him/her from your friend list"); } if (m_vkOptions.bRemoveFromCList) { debugLogA("CVkProto::SvcBanUser m_vkOptions.bRemoveFromClist = true"); if (!tszVarWarning.IsEmpty()) tszVarWarning.AppendChar(L','); tszVarWarning += TranslateT(" remove him/her from your contact list"); } if (!tszVarWarning.IsEmpty()) tszVarWarning += ".\n"; code += "return 1;"; ptrW ptszNick(db_get_tsa(hContact, m_szModuleName, "Nick")); CMString ptszMsg(FORMAT, TranslateT("Are you sure to ban %s? %s%sContinue?"), IsEmpty(ptszNick) ? TranslateT("(Unknown contact)") : ptszNick, tszVarWarning.IsEmpty() ? L" " : TranslateT("\nIt will also"), tszVarWarning.IsEmpty() ? L"\n" : tszVarWarning); if (IDNO == MessageBox(NULL, ptszMsg, TranslateT("Attention!"), MB_ICONWARNING | MB_YESNO)) return 1; Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/execute.json", true, &CVkProto::OnReceiveSmth) << CHAR_PARAM("code", code)); if (m_vkOptions.bRemoveFromCList) CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact); return 0; } INT_PTR __cdecl CVkProto::SvcReportAbuse(WPARAM hContact, LPARAM) { debugLogA("CVkProto::SvcReportAbuse"); LONG userID = getDword(hContact, "ID", -1); if (!IsOnline() || userID == -1 || userID == VK_FEED_USER) return 1; CMString tszNick(ptrW(db_get_tsa(hContact, m_szModuleName, "Nick"))), ptszMsg(FORMAT, TranslateT("Are you sure to report abuse on %s?"), tszNick.IsEmpty() ? TranslateT("(Unknown contact)") : tszNick); if (IDNO == MessageBox(NULL, ptszMsg, TranslateT("Attention!"), MB_ICONWARNING | MB_YESNO)) return 1; Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/users.report.json", true, &CVkProto::OnReceiveSmth) << INT_PARAM("user_id", userID) << CHAR_PARAM("type", "spam")); return 0; } INT_PTR __cdecl CVkProto::SvcOpenBroadcast(WPARAM hContact, LPARAM) { debugLogA("CVkProto::SvcOpenBroadcast"); CMString tszAudio(ptrW(db_get_tsa(hContact, m_szModuleName, "AudioUrl"))); if (!tszAudio.IsEmpty()) Utils_OpenUrlT(tszAudio); return 0; } INT_PTR __cdecl CVkProto::SvcVisitProfile(WPARAM hContact, LPARAM) { debugLogA("CVkProto::SvcVisitProfile"); if (isChatRoom(hContact)) { ptrW tszHomepage(db_get_tsa(hContact, m_szModuleName, "Homepage")); if(!IsEmpty(tszHomepage)) Utils_OpenUrlT(tszHomepage); return 0; } LONG userID = getDword(hContact, "ID", -1); ptrW tszDomain(db_get_tsa(hContact, m_szModuleName, "domain")); CMString tszUrl("https://vk.com/"); if (tszDomain) tszUrl.Append(tszDomain); else tszUrl.AppendFormat(L"id%i", userID); Utils_OpenUrlT(tszUrl); return 0; }