/*
Copyright (c) 2015-25 Miranda NG team (https://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 ");
bUsed = true;
}
else if (!strncmp(p, "/s]", 3)) {
p += 2;
ret.Append("");
bUsed = true;
}
else if (!strncmp(p, "code]", 5)) {
p += 4;
ret.Append("
");
bUsed = true;
}
else if (!strncmp(p, "/code]", 6)) {
p += 5;
ret.Append("");
bUsed = true;
}
}
else ret.AppendChar(*p);
}
if (bUsed)
str = ret;
return bUsed;
}
//////////////////////////////////////////////////////////////////////////////////////////
void Utf32toUtf16(uint32_t c, CMStringW &dest)
{
if (c < 0x10000)
dest.AppendChar(c);
else {
unsigned int t = c - 0x10000;
dest.AppendChar((((t << 12) >> 22) + 0xD800));
dest.AppendChar((((t << 22) >> 22) + 0xDC00));
}
}
bool is_surrogate(wchar_t uc) { return (uc - 0xd800u) < 2048u; }
bool is_high_surrogate(wchar_t uc) { return (uc & 0xfffffc00) == 0xd800; }
bool is_low_surrogate(wchar_t uc) { return (uc & 0xfffffc00) == 0xdc00; }
uint32_t Utf16toUtf32(const wchar_t *str)
{
if (!is_surrogate(str[0]))
return str[0];
if (is_high_surrogate(str[0]) && is_low_surrogate(str[1]))
return (str[0] << 10) + str[1] - 0x35fdc00;
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////
const 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_AWAY;
else if (!mir_strcmpi(status, "Busy"))
return ID_STATUS_DND;
else
return ID_STATUS_OFFLINE;
}
//////////////////////////////////////////////////////////////////////////////////////////
bool CSkypeProto::IsMe(const wchar_t *str)
{
return (!mir_wstrcmpi(str, Utf2T(m_szMyname)) || !mir_wstrcmp(str, Utf2T(m_szOwnSkypeId)));
}
bool CSkypeProto::IsMe(const char *str)
{
return (!mir_strcmpi(str, m_szMyname) || !mir_strcmp(str, m_szOwnSkypeId));
}
//////////////////////////////////////////////////////////////////////////////////////////
int64_t CSkypeProto::getLastTime(MCONTACT hContact)
{
ptrA szLastTime(getStringA(hContact, "LastMsgTime"));
return (szLastTime) ? _atoi64(szLastTime) : 0;
}
void CSkypeProto::setLastTime(MCONTACT hContact, int64_t iValue)
{
char buf[100];
_i64toa(iValue, buf, 10);
setString(hContact, "LastMsgTime", buf);
}
//////////////////////////////////////////////////////////////////////////////////////////
bool CSkypeProto::IsFileExists(std::wstring path)
{
return _waccess(path.c_str(), 0) == 0;
}
int64_t getRandomId()
{
int64_t ret;
Utils_GetRandom(&ret, sizeof(ret));
if (ret < 0)
ret = -ret;
return ret;
}
CMStringA getMessageId(const JSONNode &node)
{
CMStringA ret(node["skypeeditedid"].as_mstring());
if (ret.IsEmpty())
ret = node["clientmessageid"].as_mstring();
return ret;
}
const char* GetSkypeNick(const char *szSkypeId)
{
if (auto *p = strchr(szSkypeId, ':'))
return p + 1;
return szSkypeId;
}
const wchar_t* GetSkypeNick(const wchar_t *szSkypeId)
{
if (auto *p = wcsrchr(szSkypeId, ':'))
return p + 1;
return szSkypeId;
}
void CSkypeProto::SetString(MCONTACT hContact, const char *pszSetting, const JSONNode &node)
{
CMStringW str = node.as_mstring();
if (str.IsEmpty() || str == L"null")
delSetting(hContact, pszSetting);
else
setWString(hContact, pszSetting, str);
}
/////////////////////////////////////////////////////////////////////////////////////////
// url parsing
CMStringA ParseUrl(const char *url, const char *token)
{
if (url == nullptr)
return CMStringA();
auto *start = strstr(url, token);
if (start == nullptr)
return CMStringA();
auto *end = strchr(++start, '/');
if (end == nullptr)
return CMStringA(start);
return CMStringA(start, end - start);
}
CMStringW ParseUrl(const wchar_t *url, const wchar_t *token)
{
if (url == nullptr)
return CMStringW();
auto *start = wcsstr(url, token);
if (start == nullptr)
return CMStringW();
auto *end = wcschr(++start, '/');
if (end == nullptr)
return CMStringW(start);
return CMStringW(start, end - start);
}
/////////////////////////////////////////////////////////////////////////////////////////
static int possibleTypes[] = { 1, 2, 4, 8, 19, 28 };
bool IsPossibleUserType(const char *pszUserId)
{
int iType = atoi(pszUserId);
// we don't process 28 and other shit
for (auto &it : possibleTypes)
if (it == iType)
return true;
return false;
}
CMStringA UrlToSkypeId(const char *url, int *pUserType)
{
int userType = -1;
CMStringA szResult;
if (url != nullptr) {
for (auto &it : possibleTypes) {
char tmp[10];
sprintf_s(tmp, "/%d:", it);
if (strstr(url, tmp)) {
userType = it;
szResult = ParseUrl(url, tmp);
break;
}
}
}
if (pUserType)
*pUserType = userType;
return szResult;
}
CMStringW UrlToSkypeId(const wchar_t *url, int *pUserType)
{
int userType = -1;
CMStringW szResult;
if (url != nullptr) {
for (auto &it : possibleTypes) {
wchar_t tmp[10];
swprintf_s(tmp, L"/%d:", it);
if (wcsstr(url, tmp)) {
userType = it;
szResult = ParseUrl(url, tmp);
break;
}
}
}
if (pUserType)
*pUserType = userType;
return szResult;
}
/////////////////////////////////////////////////////////////////////////////////////////
INT_PTR CSkypeProto::ParseSkypeUriService(WPARAM, LPARAM lParam)
{
wchar_t *arg = (wchar_t *)lParam;
if (arg == nullptr)
return 1;
// skip leading prefix
wchar_t szUri[1024];
wcsncpy_s(szUri, arg, _TRUNCATE);
wchar_t *szJid = wcschr(szUri, ':');
if (szJid == nullptr)
return 1;
// empty jid?
if (!*szJid)
return 1;
// command code
wchar_t *szCommand = szJid;
szCommand = wcschr(szCommand, '?');
if (szCommand)
*(szCommand++) = 0;
// parameters
wchar_t *szSecondParam = szCommand ? wcschr(szCommand, '&') : nullptr;
if (szSecondParam)
*(szSecondParam++) = 0;
// no command or message command
if (!szCommand || !mir_wstrcmpi(szCommand, L"chat")) {
if (szSecondParam) {
wchar_t *szChatId = wcsstr(szSecondParam, L"id=");
if (szChatId) {
szChatId += 5;
StartChatRoom(szChatId, szChatId);
return 0;
}
}
MCONTACT hContact = AddContact(_T2A(szJid), nullptr, true);
CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, NULL);
return 0;
}
if (!mir_wstrcmpi(szCommand, L"call")) {
MCONTACT hContact = AddContact(_T2A(szJid), nullptr, true);
NotifyEventHooks(g_hCallEvent, (WPARAM)hContact, (LPARAM)0);
return 0;
}
if (!mir_wstrcmpi(szCommand, L"userinfo"))
return 0;
if (!mir_wstrcmpi(szCommand, L"add")) {
MCONTACT hContact = FindContact(_T2A(szJid));
if (hContact == NULL) {
PROTOSEARCHRESULT psr = { 0 };
psr.cbSize = sizeof(psr);
psr.id.w = mir_wstrdup(szJid);
psr.nick.w = mir_wstrdup(szJid);
psr.flags = PSR_UNICODE;
Contact::AddBySearch(m_szModuleName, &psr);
}
return 0;
}
if (!mir_wstrcmpi(szCommand, L"sendfile")) {
MCONTACT hContact = AddContact(_T2A(szJid), nullptr, true);
File::Send(hContact);
return 1;
}
if (!mir_wstrcmpi(szCommand, L"voicemail"))
return 1;
return 1;
}
INT_PTR CSkypeProto::GlobalParseSkypeUriService(WPARAM wParam, LPARAM lParam)
{
for (auto &it : CMPlugin::g_arInstances)
if (it->IsOnline())
return it->ParseSkypeUriService(wParam, lParam);
return 1;
}