/*
Copyright (c) 2015 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"
#pragma warning(disable:4566)
bool CSkypeProto::IsOnline()
{
return m_iStatus > ID_STATUS_OFFLINE && m_hPollingThread;
}
void CSkypeProto::SetSrmmReadStatus(MCONTACT hContact)
{
time_t time = getDword(hContact, "LastMsgReadTime", 0);
if (!time)
return;
TCHAR ttime[64];
_locale_t locale = _create_locale(LC_ALL, "");
_tcsftime_l(ttime, SIZEOF(ttime), _T("%X - %x"), localtime(&time), locale);
_free_locale(locale);
StatusTextData st = { 0 };
st.cbSize = sizeof(st);
st.hIcon = LoadSkinnedIcon(SKINICON_OTHER_HISTORY);
mir_sntprintf(st.tszText, SIZEOF(st.tszText), TranslateT("Message read: %s"), ttime);
CallService(MS_MSG_SETSTATUSTEXT, (WPARAM)hContact, (LPARAM)&st);
}
time_t CSkypeProto::IsoToUnixTime(const char *stamp)
{
char date[9];
int i, y;
if (stamp == NULL)
return 0;
char *p = NEWSTR_ALLOCA(stamp);
// skip '-' chars
int si = 0, sj = 0;
while (true) {
if (p[si] == _T('-'))
si++;
else if (!(p[sj++] = p[si++]))
break;
}
// Get the date part
for (i = 0; *p != '\0' && i < 8 && isdigit(*p); p++, i++)
date[i] = *p;
// Parse year
if (i == 6) {
// 2-digit year (1970-2069)
y = (date[0] - '0') * 10 + (date[1] - '0');
if (y < 70) y += 100;
}
else if (i == 8) {
// 4-digit year
y = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + date[3] - '0';
y -= 1900;
}
else return 0;
struct tm timestamp;
timestamp.tm_year = y;
// Parse month
timestamp.tm_mon = (date[i - 4] - '0') * 10 + date[i - 3] - '0' - 1;
// Parse date
timestamp.tm_mday = (date[i - 2] - '0') * 10 + date[i - 1] - '0';
// Skip any date/time delimiter
for (; *p != '\0' && !isdigit(*p); p++);
// Parse time
if (sscanf(p, "%d:%d:%d", ×tamp.tm_hour, ×tamp.tm_min, ×tamp.tm_sec) != 3)
return (time_t)0;
timestamp.tm_isdst = 0; // DST is already present in _timezone below
time_t t = mktime(×tamp);
_tzset();
t -= _timezone;
return (t >= 0) ? t : 0;
}
struct HtmlEntity
{
const char *entity;
const char *symbol;
};
const HtmlEntity htmlEntities[] =
{
{ "AElig", "\u00C6" },
{ "Aacute", "\u00C1" },
{ "Acirc", "\u00C2" },
{ "Agrave", "\u00C0" },
{ "Alpha", "\u0391" },
{ "Aring", "\u00C5" },
{ "Atilde", "\u00C3" },
{ "Auml", "\u00C4" },
{ "Beta", "\u0392" },
{ "Ccedil", "\u00C7" },
{ "Chi", "\u03A7" },
{ "Dagger", "‡" },
{ "Delta", "\u0394" },
{ "ETH", "\u00D0" },
{ "Eacute", "\u00C9" },
{ "Ecirc", "\u00CA" },
{ "Egrave", "\u00C8" },
{ "Epsilon", "\u0395" },
{ "Eta", "\u0397" },
{ "Euml", "\u00CB" },
{ "Gamma", "\u0393" },
{ "Iacute", "\u00CD" },
{ "Icirc", "\u00CE" },
{ "Igrave", "\u00CC" },
{ "Iota", "\u0399" },
{ "Iuml", "\u00CF" },
{ "Kappa", "\u039A" },
{ "Lambda", "\u039B" },
{ "Mu", "\u039C" },
{ "Ntilde", "\u00D1" },
{ "Nu", "\u039D" },
{ "OElig", "\u0152" },
{ "Oacute", "\u00D3" },
{ "Ocirc", "\u00D4" },
{ "Ograve", "\u00D2" },
{ "Omega", "\u03A9" },
{ "Omicron", "\u039F" },
{ "Oslash", "\u00D8" },
{ "Otilde", "\u00D5" },
{ "Ouml", "\u00D6" },
{ "Phi", "\u03A6" },
{ "Pi", "\u03A0" },
{ "Prime", "\u2033" },
{ "Psi", "\u03A8" },
{ "Rho", "\u03A1" },
{ "Scaron", "Š" },
{ "Sigma", "Σ" },
{ "THORN", "Þ" },
{ "Tau", "Τ" },
{ "Theta", "Θ" },
{ "Uacute", "Ú" },
{ "Ucirc", "Û" },
{ "Ugrave", "Ù" },
{ "Upsilon", "Υ" },
{ "Uuml", "Ü" },
{ "Xi", "Ξ" },
{ "Yacute", "Ý" },
{ "Yuml", "Ÿ" },
{ "Zeta", "Ζ" },
{ "aacute", "á" },
{ "acirc", "â" },
{ "acute", "´" },
{ "aelig", "æ" },
{ "agrave", "à" },
{ "alefsym", "ℵ" },
{ "alpha", "α" },
{ "amp", "&" },
{ "and", "∧" },
{ "ang", "∠" },
{ "apos", "'" },
{ "aring", "å" },
{ "asymp", "≈" },
{ "atilde", "ã" },
{ "auml", "ä" },
{ "bdquo", "„" },
{ "beta", "β" },
{ "brvbar", "¦" },
{ "bull", "•" },
{ "cap", "∩" },
{ "ccedil", "ç" },
{ "cedil", "¸" },
{ "cent", "¢" },
{ "chi", "χ" },
{ "circ", "ˆ" },
{ "clubs", "♣" },
{ "cong", "≅" },
{ "copy", "©" },
{ "crarr", "↵" },
{ "cup", "∪" },
{ "curren", "¤" },
{ "dArr", "⇓" },
{ "dagger", "†" },
{ "darr", "↓" },
{ "deg", "°" },
{ "delta", "δ" },
{ "diams", "♦" },
{ "divide", "÷" },
{ "eacute", "é" },
{ "ecirc", "ê" },
{ "egrave", "è" },
{ "empty", "∅" },
{ "emsp", " " },
{ "ensp", " " },
{ "epsilon", "ε" },
{ "equiv", "≡" },
{ "eta", "η" },
{ "eth", "ð" },
{ "euml", "ë" },
{ "euro", "€" },
{ "exist", "∃" },
{ "fnof", "ƒ" },
{ "forall", "∀" },
{ "frac12", "½" },
{ "frac14", "¼" },
{ "frac34", "¾" },
{ "frasl", "⁄" },
{ "gamma", "γ" },
{ "ge", "≥" },
{ "gt", ">" },
{ "hArr", "⇔" },
{ "harr", "↔" },
{ "hearts", "♥" },
{ "hellip", "…" },
{ "iacute", "í" },
{ "icirc", "î" },
{ "iexcl", "¡" },
{ "igrave", "ì" },
{ "image", "ℑ" },
{ "infin", "∞" },
{ "int", "∫" },
{ "iota", "ι" },
{ "iquest", "¿" },
{ "isin", "∈" },
{ "iuml", "ï" },
{ "kappa", "κ" },
{ "lArr", "⇐" },
{ "lambda", "λ" },
{ "lang", "〈" },
{ "laquo", "«" },
{ "larr", "←" },
{ "lceil", "⌈" },
{ "ldquo", "“" },
{ "le", "≤" },
{ "lfloor", "⌊" },
{ "lowast", "∗" },
{ "loz", "◊" },
{ "lrm", "\xE2\x80\x8E" },
{ "lsaquo", "‹" },
{ "lsquo", "‘" },
{ "lt", "<" },
{ "macr", "¯" },
{ "mdash", "—" },
{ "micro", "µ" },
{ "middot", "·" },
{ "minus", "−" },
{ "mu", "μ" },
{ "nabla", "∇" },
{ "nbsp", " " },
{ "ndash", "–" },
{ "ne", "≠" },
{ "ni", "∋" },
{ "not", "¬" },
{ "notin", "∉" },
{ "nsub", "⊄" },
{ "ntilde", "ñ" },
{ "nu", "ν" },
{ "oacute", "ó" },
{ "ocirc", "ô" },
{ "oelig", "œ" },
{ "ograve", "ò" },
{ "oline", "‾" },
{ "omega", "ω" },
{ "omicron", "ο" },
{ "oplus", "⊕" },
{ "or", "∨" },
{ "ordf", "ª" },
{ "ordm", "º" },
{ "oslash", "ø" },
{ "otilde", "õ" },
{ "otimes", "⊗" },
{ "ouml", "ö" },
{ "para", "¶" },
{ "part", "∂" },
{ "permil", "‰" },
{ "perp", "⊥" },
{ "phi", "φ" },
{ "pi", "π" },
{ "piv", "ϖ" },
{ "plusmn", "±" },
{ "pound", "£" },
{ "prime", "′" },
{ "prod", "∏" },
{ "prop", "∝" },
{ "psi", "ψ" },
{ "quot", "\"" },
{ "rArr", "⇒" },
{ "radic", "√" },
{ "rang", "〉" },
{ "raquo", "»" },
{ "rarr", "→" },
{ "rceil", "⌉" },
{ "rdquo", "”" },
{ "real", "ℜ" },
{ "reg", "®" },
{ "rfloor", "⌋" },
{ "rho", "ρ" },
{ "rlm", "\xE2\x80\x8F" },
{ "rsaquo", "›" },
{ "rsquo", "’" },
{ "sbquo", "‚" },
{ "scaron", "š" },
{ "sdot", "⋅" },
{ "sect", "§" },
{ "shy", "\xC2\xAD" },
{ "sigma", "σ" },
{ "sigmaf", "ς" },
{ "sim", "∼" },
{ "spades", "♠" },
{ "sub", "⊂" },
{ "sube", "⊆" },
{ "sum", "∑" },
{ "sup", "⊃" },
{ "sup1", "¹" },
{ "sup2", "²" },
{ "sup3", "³" },
{ "supe", "⊇" },
{ "szlig", "ß" },
{ "tau", "τ" },
{ "there4", "∴" },
{ "theta", "θ" },
{ "thetasym", "ϑ" },
{ "thinsp", " " },
{ "thorn", "þ" },
{ "tilde", "˜" },
{ "times", "×" },
{ "trade", "™" },
{ "uArr", "⇑" },
{ "uacute", "ú" },
{ "uarr", "↑" },
{ "ucirc", "û" },
{ "ugrave", "ù" },
{ "uml", "¨" },
{ "upsih", "ϒ" },
{ "upsilon", "υ" },
{ "uuml", "ü" },
{ "weierp", "℘" },
{ "xi", "ξ" },
{ "yacute", "ý" },
{ "yen", "¥" },
{ "yuml", "ÿ" },
{ "zeta", "ζ" },
{ "zwj", "\xE2\x80\x8D" },
{ "zwnj", "\xE2\x80\x8C" }
};
char *CSkypeProto::RemoveHtml(const char *text)
{
std::string new_string = "";
std::string data = text;
if (data.find("\x1b\xe3\xac\x8d\x1d") != -1)
data = "CONVERSATION MEMBERS:" + data.substr(5, data.length() - 5);
for (std::string::size_type i = 0; i < data.length(); i++)
{
if (data.at(i) == '<')
{
i = data.find(">", i);
if (i == std::string::npos)
break;
continue;
}
if (data.at(i) == '&') {
std::string::size_type begin = i;
i = data.find(";", i);
if (i == std::string::npos) {
i = begin;
}
else {
std::string entity = data.substr(begin + 1, i - begin - 1);
bool found = false;
for (int j = 0; j < SIZEOF(htmlEntities); j++)
{
if (!mir_strcmpi(entity.c_str(), htmlEntities[j].entity))
{
new_string += htmlEntities[j].symbol;
found = true;
break;
}
}
if (found)
continue;
else
i = begin;
}
}
new_string += data.at(i);
}
return mir_strdup(new_string.c_str());
}
bool CSkypeProto::IsMe(const char *skypeName)
{
ptrA mySkypeName(getStringA(SKYPE_SETTINGS_ID));
ptrA SelfEndpointName(getStringA("SelfEndpointName"));
if (!mir_strcmp(skypeName, mySkypeName) || !mir_strcmp(skypeName, SelfEndpointName))
return true;
return false;
}
char *CSkypeProto::MirandaToSkypeStatus(int status)
{
switch (status)
{
case ID_STATUS_AWAY:
return "Away";
case ID_STATUS_DND:
return "Busy";
case ID_STATUS_IDLE:
return "Idle";
case ID_STATUS_INVISIBLE:
return "Hidden";
}
return "Online";
}
int CSkypeProto::SkypeToMirandaStatus(const char *status)
{
if (!mir_strcmpi(status, "Online"))
return ID_STATUS_ONLINE;
else if (!mir_strcmpi(status, "Hidden"))
return ID_STATUS_INVISIBLE;
else if (!mir_strcmpi(status, "Away"))
return ID_STATUS_AWAY;
else if (!mir_strcmpi(status, "Idle"))
return /*ID_STATUS_IDLE*/ID_STATUS_AWAY;
else if (!mir_strcmpi(status, "Busy"))
return ID_STATUS_DND;
else
return ID_STATUS_OFFLINE;
}
void CSkypeProto::ShowNotification(const TCHAR *caption, const TCHAR *message, MCONTACT hContact, int type)
{
if (Miranda_Terminated())
return;
if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) {
CMStringA className(FORMAT, "%s_", m_szModuleName);
switch (type)
{
case 1:
{
className.Append("Error");
break;
}
case SKYPE_DB_EVENT_TYPE_INCOMING_CALL:
{
className.Append("Call");
break;
}
default:
{
className.Append("Notification");
break;
}
}
POPUPDATACLASS ppd = { sizeof(ppd) };
ppd.ptszTitle = caption;
ppd.ptszText = message;
ppd.pszClassName = className.GetBuffer();
ppd.hContact = hContact;
CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&ppd);
}
else {
DWORD mtype = MB_OK | MB_SETFOREGROUND | MB_ICONSTOP;
MessageBox(NULL, message, caption, mtype);
}
}
LRESULT CSkypeProto::PopupDlgProcCall(HWND hPopup, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CONTEXTMENU:
PUDeletePopup(hPopup);
CallService(MODULE"/IncomingCallPP", 0, PUGetContact(hPopup));
break;
case WM_COMMAND:
PUDeletePopup(hPopup);
CallService(MODULE"/IncomingCallPP", 1, PUGetContact(hPopup));
break;
}
return DefWindowProc(hPopup, uMsg, wParam, lParam);
}
void CSkypeProto::ShowNotification(const TCHAR *message, MCONTACT hContact)
{
ShowNotification(_T(MODULE), message, hContact);
}
bool CSkypeProto::IsFileExists(std::tstring path)
{
return _taccess(path.c_str(), 0) == 0;
}
// url parsing
char *CSkypeProto::ParseUrl(const char *url, const char *token)
{
const char *start = strstr(url, token);
if (start == NULL)
return NULL;
start = start + mir_strlen(token);
const char *end = strchr(start, '/');
if (end == NULL)
return mir_strdup(start);
return mir_strndup(start, end - start);
}
char *CSkypeProto::ContactUrlToName(const char *url)
{
return ParseUrl(url, "/8:");
}
char *CSkypeProto::SelfUrlToName(const char *url)
{
return ParseUrl(url, "/1:");
}
char *CSkypeProto::ChatUrlToName(const char *url)
{
return ParseUrl(url, "/19:");
}
char *CSkypeProto::GetServerFromUrl(const char *url)
{
return ParseUrl(url, "://");
}
INT_PTR CSkypeProto::ParseSkypeUriService(WPARAM, LPARAM lParam)
{
TCHAR *arg = (TCHAR *)lParam;
if (arg == NULL)
return 1;
// skip leading prefix
TCHAR szUri[1024];
_tcsncpy_s(szUri, arg, _TRUNCATE);
TCHAR *szJid = _tcschr(szUri, _T(':'));
if (szJid == NULL)
return 1;
// skip //
for (++szJid; *szJid == _T('/'); ++szJid);
// empty jid?
if (!*szJid)
return 1;
// command code
TCHAR *szCommand = szJid;
szCommand = _tcschr(szCommand, _T('?'));
if (szCommand)
*(szCommand++) = 0;
// parameters
TCHAR *szSecondParam = szCommand ? _tcschr(szCommand, _T('&')) : NULL;
if (szSecondParam)
*(szSecondParam++) = 0;
// no command or message command
if (!szCommand || (szCommand && !mir_tstrcmpi(szCommand, _T("chat"))))
{
if (szSecondParam)
{
TCHAR *szChatId = _tcsstr(szSecondParam, _T("id="));
if (szChatId)
{
szChatId += 5;
StartChatRoom(szChatId, szChatId);
return 0;
}
}
MCONTACT hContact = AddContact(_T2A(szJid), true);
CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, NULL);
return 0;
}
else if (!mir_tstrcmpi(szCommand, _T("call")))
{
MCONTACT hContact = AddContact(_T2A(szJid), true);
NotifyEventHooks(m_hCallHook, (WPARAM)hContact, (LPARAM)0);
return 0;
}
else if (!mir_tstrcmpi(szCommand, _T("userinfo"))){ return 0; }
else if (!mir_tstrcmpi(szCommand, _T("add")))
{
MCONTACT hContact = FindContact(_T2A(szJid));
if (hContact == NULL)
{
PROTOSEARCHRESULT psr = { 0 };
psr.id.t = mir_tstrdup(szJid);
psr.nick.t = mir_tstrdup(szJid);
psr.flags = PSR_TCHAR;
ADDCONTACTSTRUCT acs;
acs.handleType = HANDLE_SEARCHRESULT;
acs.szProto = m_szModuleName;
acs.psr = &psr;
CallService(MS_ADDCONTACT_SHOW, 0, (LPARAM)&acs);
}
return 0;
}
if (!mir_tstrcmpi(szCommand, _T("sendfile")))
{
//CONTACT hContact = AddContact(_T2A(szJid), true);
//CallService(MS_FILE_SENDFILE, hContact, NULL);
return 1;
}
if (!mir_tstrcmpi(szCommand, _T("voicemail")))
{
return 1;
}
return 1; /* parse failed */
}
INT_PTR CSkypeProto::GlobalParseSkypeUriService(WPARAM wParam, LPARAM lParam)
{
for (int i = 0; i < Accounts.getCount(); i++)
if (Accounts[i]->IsOnline())
return Accounts[i]->ParseSkypeUriService(wParam, lParam);
return 1;
}
LPCTSTR CSkypeProto::ClearText(CMString &result, const TCHAR *message)
{
BSTR bstrHtml = SysAllocString(message), bstrRes = SysAllocString(_T(""));
HRESULT hr = TestMarkupServices(bstrHtml, &TestDocumentText, bstrRes);
if (SUCCEEDED(hr))
result = bstrRes;
else
result = message;
SysFreeString(bstrHtml);
SysFreeString(bstrRes);
return result;
}
HRESULT TestDocumentText(IHTMLDocument3 *pHtmlDoc, BSTR &message)
{
IHTMLDocument2 *pDoc = NULL;
IHTMLElement *pElem = NULL;
BSTR bstrId = SysAllocString(L"test");
HRESULT hr = pHtmlDoc->QueryInterface(IID_PPV_ARGS(&pDoc));
if (SUCCEEDED(hr) && pDoc) {
hr = pDoc->get_body(&pElem);
if (SUCCEEDED(hr) && pElem) {
BSTR bstrText = NULL;
pElem->get_innerText(&bstrText);
message = SysAllocString(bstrText);
SysFreeString(bstrText);
pElem->Release();
}
pDoc->Release();
}
SysFreeString(bstrId);
return hr;
}
HRESULT TestMarkupServices(BSTR bstrHtml, MarkupCallback *pCallback, BSTR &message)
{
IHTMLDocument3 *pHtmlDocRoot = NULL;
// Create the root document -- a "workspace" for parsing.
HRESULT hr = CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pHtmlDocRoot));
if (SUCCEEDED(hr) && pHtmlDocRoot) {
IPersistStreamInit *pPersistStreamInit = NULL;
HRESULT hr = pHtmlDocRoot->QueryInterface(IID_PPV_ARGS(&pPersistStreamInit));
if (SUCCEEDED(hr)) {
// Initialize the root document to a default state -- ready for parsing.
pPersistStreamInit->InitNew();
IMarkupServices *pMarkupServices = NULL;
hr = pHtmlDocRoot->QueryInterface(IID_PPV_ARGS(&pMarkupServices));
if (SUCCEEDED(hr)) {
IMarkupPointer *pMarkupBegin = NULL;
IMarkupPointer *pMarkupEnd = NULL;
// These markup pointers indicate the insertion point.
hr = pMarkupServices->CreateMarkupPointer(&pMarkupBegin);
if (SUCCEEDED(hr))
hr = pMarkupServices->CreateMarkupPointer(&pMarkupEnd);
if (SUCCEEDED(hr) && pMarkupBegin && pMarkupEnd) {
IMarkupContainer *pMarkupContainer = NULL;
// Parse the string -- the markup container contains the parsed HTML.
// Markup pointers are updated to point to begining and end of new container.
hr = pMarkupServices->ParseString(bstrHtml, 0, &pMarkupContainer, pMarkupBegin, pMarkupEnd);
if (SUCCEEDED(hr) && pMarkupContainer) {
IHTMLDocument3 *pHtmlDoc = NULL;
// Retrieve the document interface to the markup container.
hr = pMarkupContainer->QueryInterface(IID_PPV_ARGS(&pHtmlDoc));
if (SUCCEEDED(hr) && pHtmlDoc) {
// Invoke the user-defined action for this new fragment.
hr = pCallback(pHtmlDoc, message);
// Clean up.
pHtmlDoc->Release();
}
pMarkupContainer->Release();
}
pMarkupEnd->Release();
}
if (pMarkupBegin)
pMarkupBegin->Release();
pMarkupServices->Release();
}
pPersistStreamInit->Release();
}
pHtmlDocRoot->Release();
}
return hr;
}
void CSkypeProto::ProcessTimer()
{
if (IsOnline())
{
PushRequest(new GetContactListRequest(TokenSecret), &CSkypeProto::LoadContactList);
if (!m_hTrouterThread)
SendRequest(new CreateTrouterRequest(), &CSkypeProto::OnCreateTrouter);
}
}
static VOID CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD)
{
for (int i = 0; i < Accounts.getCount(); i++)
{
Accounts[i]->ProcessTimer();
}
}
void CSkypeProto::SkypeSetTimer(void*)
{
CSkypeProto::m_timer = SetTimer(NULL, 0, 600000, TimerProc);
}
void CSkypeProto::SkypeUnsetTimer(void*)
{
if (CSkypeProto::m_timer)
KillTimer(NULL, CSkypeProto::m_timer);
CSkypeProto::m_timer = 0;
}
std::string CSkypeProto::urlDecode(std::string SRC)
{
std::string ret;
char ch;
int i, ii;
for (i = 0; i < SRC.length(); i++)
{
if (int(SRC[i]) == 37)
{
sscanf(SRC.substr(i + 1, 2).c_str(), "%x", &ii);
ch = static_cast(ii);
ret += ch;
i = i + 2;
}
else
ret += SRC[i];
}
return (ret);
}