/* Copyright (c) 2015-24 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 . */ #include "stdafx.h" void CSkypeProto::GetAvatarFileName(MCONTACT hContact, wchar_t *pszDest, size_t cbLen) { CMStringW wszPath(GetAvatarPath()); wszPath += '\\'; const wchar_t *szFileType = ProtoGetAvatarExtension(getByte(hContact, "AvatarType", PA_FORMAT_JPEG)); CMStringA username(getId(hContact)); username.Replace("live:", "__live_"); username.Replace("facebook:", "__facebook_"); wszPath.AppendFormat(L"%S%s", username.c_str(), szFileType); wcsncpy_s(pszDest, cbLen, wszPath, _TRUNCATE); } void CSkypeProto::ReloadAvatarInfo(MCONTACT hContact) { if (hContact == NULL) { ReportSelfAvatarChanged(); return; } PROTO_AVATAR_INFORMATION ai = { 0 }; ai.hContact = hContact; SvcGetAvatarInfo(0, (LPARAM)&ai); } void CSkypeProto::SetAvatarUrl(MCONTACT hContact, const CMStringW &tszUrl) { ptrW oldUrl(getWStringA(hContact, "AvatarUrl")); if (oldUrl != NULL) if (tszUrl == oldUrl) return; if (tszUrl.IsEmpty()) { delSetting(hContact, "AvatarUrl"); ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, nullptr); } else { setWString(hContact, "AvatarUrl", tszUrl); setByte(hContact, "NeedNewAvatar", 1); PROTO_AVATAR_INFORMATION ai = {}; ai.hContact = hContact; GetAvatarFileName(ai.hContact, ai.filename, _countof(ai.filename)); ai.format = ProtoGetAvatarFormat(ai.filename); ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &ai); } } ///////////////////////////////////////////////////////////////////////////////////////// // Avatar services for Miranda INT_PTR CSkypeProto::SvcGetAvatarCaps(WPARAM wParam, LPARAM lParam) { switch (wParam) { case AF_MAXSIZE: ((POINT*)lParam)->x = 98; ((POINT*)lParam)->y = 98; break; case AF_MAXFILESIZE: return 32000; case AF_PROPORTION: return PIP_SQUARE; case AF_FORMATSUPPORTED: case AF_ENABLED: case AF_DONTNEEDDELAYS: case AF_FETCHIFPROTONOTVISIBLE: case AF_FETCHIFCONTACTOFFLINE: return 1; } return 0; } INT_PTR CSkypeProto::SvcGetAvatarInfo(WPARAM, LPARAM lParam) { PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION *)lParam; pai->format = getByte(pai->hContact, "AvatarType", PA_FORMAT_JPEG); wchar_t tszFileName[MAX_PATH]; GetAvatarFileName(pai->hContact, tszFileName, _countof(tszFileName)); wcsncpy(pai->filename, tszFileName, _countof(pai->filename)); if (::_waccess(pai->filename, 0) == 0 && !getBool(pai->hContact, "NeedNewAvatar", 0)) return GAIR_SUCCESS; if (IsOnline()) if (ReceiveAvatar(pai->hContact)) return GAIR_WAITFOR; debugLogA("No avatar"); return GAIR_NOAVATAR; } INT_PTR CSkypeProto::SvcGetMyAvatar(WPARAM wParam, LPARAM lParam) { wchar_t path[MAX_PATH]; GetAvatarFileName(NULL, path, _countof(path)); wcsncpy((wchar_t *)wParam, path, (int)lParam); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // Avatars' receiving struct GetAvatarRequest : public AsyncHttpRequest { GetAvatarRequest(const char *url, MCONTACT hContact) : AsyncHttpRequest(REQUEST_GET, HOST_OTHER, url, &CSkypeProto::OnReceiveAvatar) { flags |= NLHRF_REDIRECT; pUserInfo = (void *)hContact; } }; void CSkypeProto::OnReceiveAvatar(MHttpResponse *response, AsyncHttpRequest *pRequest) { if (response == nullptr || response->body.IsEmpty()) return; MCONTACT hContact = (DWORD_PTR)pRequest->pUserInfo; if (response->resultCode != 200) return; PROTO_AVATAR_INFORMATION ai = { 0 }; ai.format = ProtoGetBufferFormat(response->body); setByte(hContact, "AvatarType", ai.format); GetAvatarFileName(hContact, ai.filename, _countof(ai.filename)); FILE *out = _wfopen(ai.filename, L"wb"); if (out == nullptr) { ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, &ai, 0); return; } fwrite(response->body, 1, response->body.GetLength(), out); fclose(out); setByte(hContact, "NeedNewAvatar", 0); ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &ai, 0); } bool CSkypeProto::ReceiveAvatar(MCONTACT hContact) { ptrA szUrl(getStringA(hContact, "AvatarUrl")); if (!mir_strlen(szUrl)) return false; PushRequest(new GetAvatarRequest(szUrl, hContact)); debugLogA("Requested to read an avatar from '%s'", szUrl.get()); return true; } ///////////////////////////////////////////////////////////////////////////////////////// // Setting my own avatar struct SetAvatarRequest : public AsyncHttpRequest { SetAvatarRequest(const uint8_t *data, int dataSize, const char *szMime, CSkypeProto *ppro) : AsyncHttpRequest(REQUEST_PUT, HOST_API, 0, &CSkypeProto::OnSentAvatar) { m_szUrl.AppendFormat("/users/%s/profile/avatar", ppro->m_szSkypename.MakeLower().c_str()); AddHeader("Content-Type", szMime); m_szParam.Truncate(dataSize); memcpy(m_szParam.GetBuffer(), data, dataSize); } }; void CSkypeProto::OnSentAvatar(MHttpResponse *response, AsyncHttpRequest*) { JsonReply root(response); if (root.error()) return; } INT_PTR CSkypeProto::SvcSetMyAvatar(WPARAM, LPARAM lParam) { wchar_t *path = (wchar_t*)lParam; wchar_t avatarPath[MAX_PATH]; GetAvatarFileName(NULL, avatarPath, _countof(avatarPath)); if (path != nullptr) { if (CopyFile(path, avatarPath, FALSE)) { FILE *hFile = _wfopen(path, L"rb"); if (hFile) { fseek(hFile, 0, SEEK_END); size_t length = ftell(hFile); if (length != -1) { rewind(hFile); mir_ptr data((uint8_t*)mir_alloc(length)); if (data != NULL && fread(data, sizeof(uint8_t), length, hFile) == length) { const char *szMime = FreeImage_GetFIFMimeType(FreeImage_GetFIFFromFilenameU(path)); PushRequest(new SetAvatarRequest(data, (int)length, szMime, this)); fclose(hFile); return 0; } } fclose(hFile); } } return -1; } else if (IsFileExists(avatarPath)) DeleteFile(avatarPath); return 0; }