int CIcqProto::setSettingDouble(HANDLE hContact, const char *szSetting, double dValue) { return setSettingBlob(hContact, szSetting, (BYTE*)&dValue, sizeof(double)); }
// handle Owner's avatar hash changes void CIcqProto::handleAvatarOwnerHash(BYTE bFlags, BYTE *pData, size_t nDataLen) { if (nDataLen < 0x14 || !m_bAvatarsEnabled) return; switch (bFlags) { case 1: // our avatar is on the server setSettingBlob(NULL, "AvatarHash", pData, 0x14); /// TODO: properly handle multiple avatar items (more formats) setUserInfo(); { // here we need to find a file, check its hash, if invalid get avatar from server TCHAR *file = GetOwnAvatarFileName(); if (!file) { // we have no avatar file, download from server debugLogA("We have no avatar, requesting from server."); TCHAR szFile[MAX_PATH * 2 + 4]; GetAvatarFileName(0, NULL, szFile, MAX_PATH * 2); GetAvatarData(NULL, m_dwLocalUIN, NULL, pData, 0x14, szFile); } else { // we know avatar filename BYTE *hash = calcMD5HashOfFile(file); if (!hash) { // hash could not be calculated - probably missing file, get avatar from server debugLogA("We have no avatar, requesting from server."); TCHAR szFile[MAX_PATH * 2 + 4]; GetAvatarFileName(0, NULL, szFile, MAX_PATH * 2); GetAvatarData(NULL, m_dwLocalUIN, NULL, pData, 0x14, szFile); } // check if we had set any avatar if yes set our, if not download from server else if (memcmp(hash, pData + 4, 0x10)) { // we have different avatar, sync that if (m_bSsiEnabled && getByte("ForceOurAvatar", 1)) { // we want our avatar, update hash DWORD dwPaFormat = ::ProtoGetAvatarFileFormat(file); BYTE pHash[0x14]; debugLogA("Our avatar is different, setting our new hash."); pHash[0] = 0; pHash[1] = dwPaFormat == PA_FORMAT_XML ? AVATAR_HASH_FLASH : AVATAR_HASH_STATIC; pHash[2] = 1; // state of the hash pHash[3] = 0x10; // len of the hash memcpy((pHash + 4), hash, 0x10); updateServAvatarHash(pHash, 0x14); } else { // get avatar from server debugLogA("We have different avatar, requesting new from server."); TCHAR tszFile[MAX_PATH * 2 + 4]; GetAvatarFileName(0, NULL, tszFile, MAX_PATH * 2); GetAvatarData(NULL, m_dwLocalUIN, NULL, pData, 0x14, tszFile); } } SAFE_FREE((void**)&hash); SAFE_FREE(&file); } } break; case 0x41: // request to upload avatar data case 0x81: // request to re-upload avatar data if (m_bSsiEnabled) { // we could not change serv-list if it is disabled... TCHAR *file = GetOwnAvatarFileName(); if (!file) { // we have no file to upload, remove hash from server debugLogA("We do not have avatar, removing hash."); SetMyAvatar(0, 0); break; } DWORD dwPaFormat = ::ProtoGetAvatarFileFormat(file); BYTE *hash = calcMD5HashOfFile(file); if (!hash) { // the hash could not be calculated, remove from server debugLogA("We could not obtain hash, removing hash."); SetMyAvatar(0, 0); } else if (!memcmp(hash, pData + 4, 0x10)) { // we have the right file HANDLE hFile = NULL, hMap = NULL; BYTE *ppMap = NULL; long cbFileSize = 0; debugLogA("Uploading our avatar data."); if ((hFile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) if ((hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL) if ((ppMap = (BYTE*)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL) cbFileSize = GetFileSize(hFile, NULL); if (cbFileSize != 0) SetAvatarData(NULL, (WORD)(dwPaFormat == PA_FORMAT_XML ? AVATAR_HASH_FLASH : AVATAR_HASH_STATIC), ppMap, cbFileSize); if (ppMap != NULL) UnmapViewOfFile(ppMap); if (hMap != NULL) CloseHandle(hMap); if (hFile != NULL) CloseHandle(hFile); SAFE_FREE((void**)&hash); } else { BYTE pHash[0x14]; debugLogA("Our file is different, set our new hash."); pHash[0] = 0; pHash[1] = dwPaFormat == PA_FORMAT_XML ? AVATAR_HASH_FLASH : AVATAR_HASH_STATIC; pHash[2] = 1; // state of the hash pHash[3] = 0x10; // len of the hash memcpy((pHash + 4), hash, 0x10); updateServAvatarHash(pHash, 0x14); SAFE_FREE((void**)&hash); } SAFE_FREE(&file); } break; default: debugLogA("Received UNKNOWN Avatar Status."); } }
// handle Contact's avatar hash void CIcqProto::handleAvatarContactHash(DWORD dwUIN, char *szUID, MCONTACT hContact, BYTE *pHash, size_t nHashLen) { int bJob = FALSE; BOOL avatarInfoPresent = FALSE; int avatarType = -1; BYTE *pAvatarHash = NULL; size_t cbAvatarHash = 0; BYTE emptyItem[0x10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (!m_bAvatarsEnabled || nHashLen < 4) return; // only if enabled while (nHashLen >= 4) { // parse online message items one by one WORD itemType = pHash[0] << 8 | pHash[1]; size_t itemLen = pHash[3]; // just some validity check if (itemLen + 4 > nHashLen) itemLen = nHashLen - 4; if (itemLen && memcmp(pHash + 4, emptyItem, itemLen > 0x10 ? 0x10 : itemLen)) { // Item types // 0000: AIM mini avatar // 0001: AIM/ICQ avatar ID/hash (len 5 or 16 bytes) // 0002: iChat online message // 0008: ICQ Flash avatar hash (16 bytes) // 0009: iTunes music store link // 000C: ICQ contact photo (16 bytes) // 000D: Last update time of online message // 000E: Status mood if (itemType == AVATAR_HASH_MINI && itemLen == 0x05 && avatarType == -1) { // mini avatar pAvatarHash = pHash; cbAvatarHash = itemLen + 4; avatarType = itemType; } else if (itemType == AVATAR_HASH_STATIC && (itemLen == 0x05 || itemLen == 0x10) && (avatarType == -1 || avatarType == AVATAR_HASH_MINI)) { // normal avatar pAvatarHash = pHash; cbAvatarHash = itemLen + 4; avatarType = itemType; } else if (itemType == AVATAR_HASH_FLASH && itemLen == 0x10 && (avatarType == -1 || avatarType == AVATAR_HASH_MINI || avatarType == AVATAR_HASH_STATIC)) { // flash avatar pAvatarHash = pHash; cbAvatarHash = itemLen + 4; avatarType = itemType; } else if (itemType == AVATAR_HASH_PHOTO && itemLen == 0x10) { // big avatar (ICQ 6+) pAvatarHash = pHash; cbAvatarHash = itemLen + 4; avatarType = itemType; } } else if ((itemLen == 0) && (itemType == AVATAR_HASH_MINI || itemType == AVATAR_HASH_STATIC || itemType == AVATAR_HASH_FLASH || itemType == AVATAR_HASH_PHOTO)) // empty item - indicating that avatar of that type was removed avatarInfoPresent = TRUE; pHash += itemLen + 4; nHashLen -= itemLen + 4; } if (avatarType != -1) { // check settings, should we request avatar immediatelly? DBVARIANT dbv = { DBVT_DELETED }; TCHAR tszAvatar[MAX_PATH * 2 + 4]; BYTE bAutoLoad = getByte("AvatarsAutoLoad", DEFAULT_LOAD_AVATARS); if ((avatarType == AVATAR_HASH_STATIC || avatarType == AVATAR_HASH_MINI) && cbAvatarHash == 0x09 && !memcmp(pAvatarHash + 4, hashEmptyAvatar + 4, 0x05)) { // empty avatar - unlink image, clear hash if (!getSetting(hContact, "AvatarHash", &dbv)) { // contact had avatar, clear hash, notify UI db_free(&dbv); debugLogA("%s has removed Avatar.", strUID(dwUIN, szUID)); delSetting(hContact, "AvatarHash"); ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0); } else debugLogA("%s has empty Avatar.", strUID(dwUIN, szUID)); return; } if (getSetting(hContact, "AvatarHash", &dbv)) { // we did not find old avatar hash, i.e. get new avatar int avatarState = IsAvatarChanged(hContact, pAvatarHash, cbAvatarHash); // check saved hash and file, if equal only store hash if (!avatarState) { // hashes are the same int dwPaFormat = getByte(hContact, "AvatarType", PA_FORMAT_UNKNOWN); GetFullAvatarFileName(dwUIN, szUID, dwPaFormat, tszAvatar, MAX_PATH * 2); if (_taccess(tszAvatar, 0) == 0) { // the file is there, link to contactphoto, save hash debugLogA("%s has published Avatar. Image was found in the cache.", strUID(dwUIN, szUID)); setSettingBlob(hContact, "AvatarHash", pAvatarHash, cbAvatarHash); ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0); } else { // the file was lost, request avatar again debugLogA("%s has published Avatar.", strUID(dwUIN, szUID)); bJob = TRUE; } } else { // the hash is not the one we want, request avatar debugLogA("%s has published a new Avatar.", strUID(dwUIN, szUID)); bJob = TRUE; } } else { // we found hash check if it changed or not if ((dbv.cpbVal != cbAvatarHash) || memcmp(dbv.pbVal, pAvatarHash, cbAvatarHash)) { // the hash is different, request new avatar debugLogA("%s has changed Avatar.", strUID(dwUIN, szUID)); bJob = TRUE; } else { // the hash was not changed, check if we have the correct file int avatarState = IsAvatarChanged(hContact, pAvatarHash, cbAvatarHash); // we should have file, check if the file really exists if (!avatarState) { int dwPaFormat = getByte(hContact, "AvatarType", PA_FORMAT_UNKNOWN); if (dwPaFormat == PA_FORMAT_UNKNOWN) { // we do not know the format, get avatar again debugLogA("%s has Avatar. Image is missing.", strUID(dwUIN, szUID)); bJob = 2; } else { GetFullAvatarFileName(dwUIN, szUID, dwPaFormat, tszAvatar, MAX_PATH * 2); if (_taccess(tszAvatar, 0) != 0) { // the file was lost, get it again debugLogA("%s has Avatar. Image is missing.", strUID(dwUIN, szUID)); bJob = 2; } else debugLogA("%s has Avatar. Image was found in the cache.", strUID(dwUIN, szUID)); } } else { // the hash is not the one we want, request avatar debugLogA("%s has Avatar. Image was not retrieved yet.", strUID(dwUIN, szUID)); bJob = 2; } } db_free(&dbv); } if (bJob) { if (bJob == TRUE) { // Remove possible block - hash changed, try again. mir_cslock l(m_avatarsMutex); for (int i = 0; i < m_arAvatars.getCount(); i++) { avatars_request *ar = m_arAvatars[i]; if (ar->hContact == hContact && ar->type == ART_BLOCK) { // found one, remove m_arAvatars.remove(i); delete ar; break; } } } setSettingBlob(hContact, "AvatarHash", pAvatarHash, cbAvatarHash); ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0); if (bAutoLoad) { // auto-load is on, so request the avatar now, otherwise we are done GetAvatarFileName(dwUIN, szUID, tszAvatar, MAX_PATH * 2); GetAvatarData(hContact, dwUIN, szUID, pAvatarHash, cbAvatarHash, tszAvatar); } // avatar request sent or added to queue } } else if (avatarInfoPresent) { // hash was not found, clear the hash DBVARIANT dbv = { DBVT_DELETED }; if (!getSetting(hContact, "AvatarHash", &dbv)) { // contact had avatar, clear hash, notify UI db_free(&dbv); debugLogA("%s has removed Avatar.", strUID(dwUIN, szUID)); delSetting(hContact, "AvatarHash"); ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0); } else debugLogA("%s has no Avatar.", strUID(dwUIN, szUID)); } }
void CIcqProto::handleDirectoryUpdateResponse(BYTE *databuf, WORD wPacketLen, WORD wCookie, WORD wReplySubtype) { WORD wBytesRemaining = 0; snac_header requestSnac = {0}; BYTE requestResult; #ifdef _DEBUG debugLogA("Received directory update response"); #endif if (wPacketLen >= 2) unpackLEWord(&databuf, &wBytesRemaining); wPacketLen -= 2; _ASSERTE(wPacketLen == wBytesRemaining); if (!unpackSnacHeader(&requestSnac, &databuf, &wPacketLen) || !requestSnac.bValid) { debugLogA("Error: Failed to parse directory response"); return; } cookie_directory_data *pCookieData; MCONTACT hContact; // check request cookie if (!FindCookie(wCookie, &hContact, (void**)&pCookieData) || !pCookieData) { debugLogA("Warning: Ignoring unrequested directory reply type (x%x, x%x)", requestSnac.wFamily, requestSnac.wSubtype); return; } /// FIXME: we should really check the snac contents according to cookie data here ?? if (wPacketLen >= 3) unpackByte(&databuf, &requestResult); else { debugLogA("Error: Malformed directory response"); ReleaseCookie(wCookie); return; } if (requestResult != 1 && requestResult != 4) { debugLogA("Error: Directory request failed, status %u", requestResult); if (pCookieData->bRequestType == DIRECTORYREQUEST_UPDATEOWNER) ProtoBroadcastAck(NULL, ACKTYPE_SETINFO, ACKRESULT_FAILED, (HANDLE)wCookie, 0); ReleaseCookie(wCookie); return; } WORD wLen; unpackWord(&databuf, &wLen); wPacketLen -= 3; if (wLen) debugLogA("Warning: Data in error message present!"); if (pCookieData->bRequestType == DIRECTORYREQUEST_UPDATEOWNER) ProtoBroadcastAck(NULL, ACKTYPE_SETINFO, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); if (wPacketLen == 0x18) { DWORD64 qwMetaTime; BYTE pbEmptyMetaToken[0x10] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; unpackQWord(&databuf, &qwMetaTime); setSettingBlob(NULL, DBSETTING_METAINFO_TIME, (BYTE*)&qwMetaTime, 8); if (memcmp(databuf, pbEmptyMetaToken, 0x10)) setSettingBlob(NULL, DBSETTING_METAINFO_TOKEN, databuf, 0x10); } ReleaseCookie(wCookie); }