summaryrefslogtreecommitdiff
path: root/protocols/GTalkExt/inbox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/GTalkExt/inbox.cpp')
-rw-r--r--protocols/GTalkExt/inbox.cpp402
1 files changed, 402 insertions, 0 deletions
diff --git a/protocols/GTalkExt/inbox.cpp b/protocols/GTalkExt/inbox.cpp
new file mode 100644
index 0000000000..9317fbc6cf
--- /dev/null
+++ b/protocols/GTalkExt/inbox.cpp
@@ -0,0 +1,402 @@
+//***************************************************************************************
+//
+// Google Extension plugin for the Miranda IM's Jabber protocol
+// Copyright (c) 2011 bems@jabber.org, George Hazan (ghazan@jabber.ru)
+//
+// 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, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+//***************************************************************************************
+
+#include "stdafx.h"
+#include "inbox.h"
+#include "notifications.h"
+#include "db.h"
+#include "options.h"
+
+static const LPTSTR COMMON_GMAIL_HOST1 = _T("gmail.com");
+static const LPTSTR COMMON_GMAIL_HOST2 = _T("googlemail.com");
+
+static const LPSTR AUTH_REQUEST_URL = "https://www.google.com/accounts/ClientAuth";
+static const LPSTR AUTH_REQUEST_PARAMS = "Email=%s&Passwd=%s&"
+ "accountType=HOSTED_OR_GOOGLE&"
+ "skipvpage=true&"
+ "PersistentCookie=false";
+
+static const LPSTR ISSUE_TOKEN_REQUEST_URL = "https://www.google.com/accounts/IssueAuthToken";
+static const LPSTR ISSUE_TOKEN_REQUEST_PARAMS = "SID=%s&LSID=%s&"
+ "Session=true&"
+ "skipvpage=true&"
+ "service=gaia";
+
+static const LPSTR TOKEN_AUTH_URL = "https://www.google.com/accounts/TokenAuth?"\
+ "auth=%s&"
+ "service=mail&"
+ "continue=%s&"
+ "source=googletalk";
+
+
+static const NETLIBHTTPHEADER HEADER_URL_ENCODED = {"Content-Type", "application/x-www-form-urlencoded"};
+static const int HTTP_OK = 200;
+
+static const LPSTR SID_KEY_NAME = "SID=";
+static const LPSTR LSID_KEY_NAME = "LSID=";
+
+static const LPSTR LOGIN_PASS_SETTING_NAME = "LoginPassword";
+
+static const LPTSTR INBOX_URL_FORMAT = _T("https://mail.google.com/%s%s/#inbox");
+
+static const DWORD SIZE_OF_JABBER_OPTIONS = 243 * sizeof(DWORD);
+
+// 3 lines from netlib.h
+#define GetNetlibHandleType(h) (h?*(int*)h:NLH_INVALID)
+#define NLH_INVALID 0
+#define NLH_USER 'USER'
+
+char to_hex(char code) {
+ static char hex[] = "0123456789abcdef";
+ return hex[code & 15];
+}
+
+char *url_encode(char *str) {
+ char *pstr = str, *buf = (char*)malloc(strlen(str) * 3 + 1), *pbuf = buf;
+ while (*pstr) {
+ if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == ',' || *pstr == '~')
+ *pbuf++ = *pstr;
+ else if (*pstr == ' ')
+ *pbuf++ = '+';
+ else
+ *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
+ pstr++;
+ }
+ *pbuf = '\0';
+ return buf;
+}
+
+LPSTR HttpPost(HANDLE hUser, LPSTR reqUrl, LPSTR reqParams)
+{
+ NETLIBHTTPREQUEST nlhr = {0};
+ nlhr.cbSize = sizeof(nlhr);
+ nlhr.requestType = REQUEST_POST;
+ nlhr.flags = NLHRF_GENERATEHOST | NLHRF_SMARTAUTHHEADER | NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP | NLHRF_NODUMPHEADERS;
+ nlhr.szUrl = reqUrl;
+ nlhr.headers = (NETLIBHTTPHEADER*)&HEADER_URL_ENCODED;
+ nlhr.headersCount = 1;
+ nlhr.pData = reqParams;
+ nlhr.dataLength = lstrlenA(reqParams);
+
+ NETLIBHTTPREQUEST *pResp = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hUser, (LPARAM)&nlhr);
+ if (!pResp) return NULL;
+ __try {
+ if (HTTP_OK == pResp->resultCode)
+ return _strdup(pResp->pData);
+ else
+ return NULL;
+ }
+ __finally {
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)pResp);
+ }
+}
+
+LPSTR MakeRequest(HANDLE hUser, LPSTR reqUrl, LPSTR reqParamsFormat, LPSTR p1, LPSTR p2)
+{
+ LPSTR encodedP1 = url_encode(p1);
+ __try {
+ LPSTR encodedP2 = url_encode(p2);
+ __try {
+ LPSTR reqParams = (LPSTR)malloc(lstrlenA(reqParamsFormat) + 1 + lstrlenA(encodedP1) + lstrlenA(encodedP2));
+ __try {
+ sprintf(reqParams, reqParamsFormat, encodedP1, encodedP2);
+ return HttpPost(hUser, reqUrl, reqParams);
+ }
+ __finally {
+ free(reqParams);
+ }
+ }
+ __finally {
+ free(encodedP2);
+ }
+ }
+ __finally {
+ free(encodedP1);
+ }
+}
+
+LPSTR FindSid(LPSTR resp, LPSTR *LSID)
+{
+ LPSTR SID = strstr(resp, SID_KEY_NAME);
+ *LSID = strstr(resp, LSID_KEY_NAME);
+ if (!SID || !*LSID) return NULL;
+
+ if (SID - 1 == *LSID) SID = strstr(SID + 1, SID_KEY_NAME);
+ if (!SID) return NULL;
+
+ SID += lstrlenA(SID_KEY_NAME);
+ LPSTR term = strstr(SID, "\n");
+ if (term) term[0] = 0;
+
+ *LSID += lstrlenA(LSID_KEY_NAME);
+ term = strstr(*LSID, "\n");
+ if (term) term[0] = 0;
+
+ return SID;
+}
+
+void DoOpenUrl(LPSTR tokenResp, LPSTR url)
+{
+ LPSTR encodedUrl = url_encode(url);
+ __try {
+ LPSTR encodedToken = url_encode(tokenResp);
+ __try {
+ LPSTR composedUrl = (LPSTR)malloc(lstrlenA(TOKEN_AUTH_URL) + 1 + lstrlenA(encodedToken) + lstrlenA(encodedUrl));
+ __try {
+ sprintf(composedUrl, TOKEN_AUTH_URL, encodedToken, encodedUrl);
+ ShellExecuteA(0, NULL, composedUrl, NULL, NULL, SW_SHOW);
+ }
+ __finally {
+ free(composedUrl);
+ }
+ }
+ __finally {
+ free(encodedToken);
+ }
+ }
+ __finally {
+ free(encodedUrl);
+ }
+}
+
+BOOL AuthAndOpen(HANDLE hUser, LPSTR url, LPSTR mailbox, LPSTR pwd)
+{
+ LPSTR authResp = MakeRequest(hUser, AUTH_REQUEST_URL, AUTH_REQUEST_PARAMS, mailbox, pwd);
+ if (!authResp) return FALSE;
+
+ __try {
+ LPSTR LSID;
+ LPSTR SID = FindSid(authResp, &LSID);
+ LPSTR tokenResp = MakeRequest(hUser, ISSUE_TOKEN_REQUEST_URL, ISSUE_TOKEN_REQUEST_PARAMS, SID, LSID);
+ if (!tokenResp) return FALSE;
+
+ __try {
+ DoOpenUrl(tokenResp, url);
+ return TRUE;
+ }
+ __finally {
+ free(tokenResp);
+ }
+ }
+ __finally {
+ free(authResp);
+ }
+}
+
+struct OPEN_URL_HEADER {
+ LPSTR url;
+ LPSTR mailbox;
+ LPSTR pwd;
+ LPCSTR acc;
+};
+
+HANDLE FindNetUserHandle(LPCSTR acc)
+{
+ IJabberInterface *ji = getJabberApi(acc);
+ if (!ji) return NULL;
+
+ PBYTE m_psProto = *(PBYTE*)((PBYTE)ji + sizeof(*ji)); // see CJabberInterface in jabber_proto.h
+
+ PHANDLE pResult = (PHANDLE)(m_psProto + // see CJabberProto in jabber_proto.h
+ sizeof(PVOID) + // skip vtable ptr
+ sizeof(PVOID) + // skip m_ThreadInfo
+ SIZE_OF_JABBER_OPTIONS); // skip m_options
+
+ for (int i=0; i < 100; i++) {
+ __try {
+ if (GetNetlibHandleType(*pResult) == NLH_USER)
+ break;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER){
+ }
+ pResult++;
+ }
+
+ assert(GetNetlibHandleType(*pResult) == NLH_USER);
+ return *pResult;
+}
+
+unsigned __stdcall OpenUrlThread(OPEN_URL_HEADER* data)
+{
+ __try {
+ HANDLE hUser = FindNetUserHandle(data->acc);
+ if (!hUser || !AuthAndOpen(hUser, data->url, data->mailbox, data->pwd))
+ ShellExecuteA(0, NULL, data->url, NULL, NULL, SW_SHOW);
+ }
+ __finally {
+ free(data);
+ }
+ return 0;
+}
+
+void __forceinline DecryptString(LPSTR str, int len)
+{
+ for (--len; len >= 0; len--)
+ {
+ const char c = str[len] ^ 0xc3;
+ if (c) str[len] = c;
+ }
+}
+
+int GetMailboxPwd(LPCSTR acc, LPCTSTR mailbox, LPSTR *pwd, int buffSize)
+{
+ char buff[256];
+
+ DBCONTACTGETSETTING cgs;
+ DBVARIANT dbv;
+ cgs.szModule = acc;
+ cgs.szSetting = LOGIN_PASS_SETTING_NAME;
+ cgs.pValue = &dbv;
+ dbv.type = DBVT_ASCIIZ;
+ dbv.pszVal = &buff[0];
+ dbv.cchVal = sizeof(buff);
+ if (CallService(MS_DB_CONTACT_GETSETTINGSTATIC, 0, (LPARAM)&cgs))
+ return 0;
+
+ int result = dbv.cchVal;
+
+ if (pwd) {
+ if (buffSize < result + 1) result = buffSize - 1;
+ memcpy(*pwd, &buff, result + 1);
+ DecryptString(*pwd, result);
+ }
+
+ return result;
+}
+
+BOOL OpenUrlWithAuth(LPCSTR acc, LPCTSTR mailbox, LPCTSTR url)
+{
+ int pwdLen = GetMailboxPwd(acc, mailbox, NULL, 0);
+ if (!pwdLen++) return FALSE;
+
+ int urlLen = lstrlen(url) + 1;
+ int mailboxLen = lstrlen(mailbox) + 1;
+
+ OPEN_URL_HEADER *data = (OPEN_URL_HEADER*)malloc(sizeof(OPEN_URL_HEADER) + urlLen + mailboxLen + pwdLen);
+ __try {
+ data->url = (LPSTR)data + sizeof(OPEN_URL_HEADER);
+ LPSTR ansi = WtoA(url);
+ __try {
+ memcpy(data->url, ansi, urlLen);
+ }
+ __finally {
+ free(ansi);
+ }
+
+ data->mailbox = data->url + urlLen;
+ ansi = WtoA(mailbox);
+ __try {
+ memcpy(data->mailbox, ansi, mailboxLen);
+ }
+ __finally {
+ free(ansi);
+ }
+
+ data->pwd = data->mailbox + mailboxLen;
+ if (!GetMailboxPwd(acc, mailbox, &data->pwd, pwdLen)) return FALSE;
+
+ data->acc = acc;
+
+ if (HANDLE h = mir_forkthreadex((pThreadFuncEx)OpenUrlThread, data, 0, NULL)) {
+ CloseHandle(h);
+ data = NULL;
+ }
+ else return FALSE;
+ }
+ __finally {
+ free(data);
+ }
+
+ return TRUE;
+}
+
+unsigned __stdcall ShellExecuteThread(PVOID param)
+{
+ __try {
+ ShellExecute(0, NULL, (LPTSTR)param, NULL, NULL, SW_SHOW);
+ }
+ __finally {
+ free(param);
+ }
+ return 0;
+}
+
+void StartShellExecuteThread(LPCTSTR url)
+{
+ LPTSTR urlCopy = _tcsdup(url);
+ __try {
+ if (HANDLE h = mir_forkthreadex(ShellExecuteThread, urlCopy, 0, NULL)) {
+ CloseHandle(h);
+ urlCopy = NULL;
+ }
+ }
+ __finally {
+ free(urlCopy);
+ }
+}
+
+void OpenUrl(LPCSTR acc, LPCTSTR mailbox, LPCTSTR url)
+{
+ extern DWORD itlsSettings;
+ if (!ReadCheckbox(0, IDC_AUTHONMAILBOX, (DWORD)TlsGetValue(itlsSettings)) ||
+ !OpenUrlWithAuth(acc, mailbox, url))
+ StartShellExecuteThread(url);
+}
+
+LPTSTR CraftInboxUrl(LPTSTR jid)
+{
+ LPTSTR host = _tcsstr(jid, _T("@")) + 1;
+
+ LPTSTR result = (LPTSTR)malloc((lstrlen(INBOX_URL_FORMAT) + 1 + lstrlen(jid)) * sizeof(TCHAR));
+ __try {
+ if (lstrcmpi(host, COMMON_GMAIL_HOST1) && lstrcmpi(host, COMMON_GMAIL_HOST2))
+ wsprintf(result, INBOX_URL_FORMAT, _T("a/"), host); // hosted
+ else
+ wsprintf(result, INBOX_URL_FORMAT, NULL, _T("mail")); // common
+ }
+ __except (
+ free(result),
+ EXCEPTION_CONTINUE_SEARCH
+ ) {}
+
+ return result;
+}
+
+void OpenContactInbox(HANDLE hContact)
+{
+ LPSTR acc = (LPSTR)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (!acc) return;
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(0, acc, "jid", &dbv))
+ __try {
+ LPTSTR url = CraftInboxUrl(dbv.ptszVal);
+ __try {
+ OpenUrl(acc, dbv.ptszVal, url);
+ }
+ __finally {
+ free(url);
+ }
+ }
+ __finally {
+ DBFreeVariant(&dbv);
+ }
+} \ No newline at end of file