void CIcqProto::ReleaseLookupCookie(DWORD dwCookie, cookie_search *pCookie) { FreeCookie(dwCookie); SAFE_FREE(&pCookie->szObject); if (pCookie->dwMainId && !pCookie->dwStatus) // we need to wait for main search pCookie->dwStatus = 1; else { // finish everything if (pCookie->dwMainId) ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)pCookie->dwMainId, 0); else // we are single ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)dwCookie, 0); SAFE_FREE((void**)&pCookie); } }
void CIcqProto::icq_CancelFileTransfer(MCONTACT hContact, filetransfer* ft) { DWORD dwCookie; if (FindCookieByData(ft, &dwCookie, NULL)) FreeCookie(dwCookie); /* this bit stops a send that's waiting for acceptance */ if (IsValidFileTransfer(ft)) { // Transfer still out there, end it NetLib_CloseConnection(&ft->hConnection, FALSE); ProtoBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0); if (!FindFileTransferDC(ft)) { // Release orphan structure only SafeReleaseFileTransfer((void**)&ft); } } }
void CIcqProto::parseUserInfoUpdateAck(unsigned char *databuf, WORD wPacketLen, WORD wCookie, WORD wReplySubtype, BYTE bResultCode) { switch (wReplySubtype) { case META_SET_PASSWORD_ACK: // Set user password server ack if (bResultCode == 0xA) ProtoBroadcastAck(NULL, ACKTYPE_SETINFO, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); else ProtoBroadcastAck(NULL, ACKTYPE_SETINFO, ACKRESULT_FAILED, (HANDLE)wCookie, 0); FreeCookie(wCookie); break; default: debugLogA("Warning: Ignored 15/03 user info update ack type x%x", wReplySubtype); break; } }
void CIcqProto::ReleaseSearchCookie(DWORD dwCookie, cookie_search *pCookie) { if (pCookie) { FreeCookie(dwCookie); if (pCookie->dwMainId) { if (pCookie->dwStatus) { SAFE_FREE((void**)&pCookie); ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)dwCookie, 0); } else pCookie->dwStatus = 1; } else { SAFE_FREE((void**)&pCookie); ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)dwCookie, 0); } } else ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)dwCookie, 0); }
void CIcqProto::handleFileAck(PBYTE buf, WORD wLen, DWORD dwUin, DWORD dwCookie, WORD wStatus, char* pszText) { char* pszFileName = NULL; DWORD dwFileSize; HANDLE hCookieContact; WORD wPort; WORD wFilenameLength; filetransfer* ft; // Find the filetransfer that belongs to this response if (!FindCookie(dwCookie, &hCookieContact, (void**)&ft)) { NetLog_Direct("Error: Received unexpected file transfer request response"); return; } FreeCookie(dwCookie); if (hCookieContact != HContactFromUIN(dwUin, NULL)) { NetLog_Direct("Error: UINs do not match in file transfer request response"); return; } // If status != 0, a request has been denied if (wStatus != 0) { NetLog_Direct("File transfer denied by %u.", dwUin); ProtoBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_DENIED, (HANDLE)ft, 0); FreeCookie(dwCookie); return; } if (wLen < 6) { // sanity check NetLog_Direct("Ignoring malformed file transfer request response"); return; } // Port to connect to unpackWord(&buf, &wPort); ft->dwRemotePort = wPort; wLen -= 2; // Unknown buf += 2; wLen -= 2; // Filename unpackLEWord(&buf, &wFilenameLength); if (wFilenameLength > 0) { if (wFilenameLength > wLen - 2) wFilenameLength = wLen - 2; pszFileName = (char*)_alloca(wFilenameLength+1); unpackString(&buf, pszFileName, wFilenameLength); pszFileName[wFilenameLength] = '\0'; } wLen = wLen - 2 - wFilenameLength; if (wLen >= 4) { // Total filesize unpackLEDWord(&buf, &dwFileSize); wLen -= 4; } else dwFileSize = 0; NetLog_Direct("File transfer ack from %u, port %u, name %s, size %u", dwUin, ft->dwRemotePort, pszFileName, dwFileSize); ProtoBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_CONNECTING, (HANDLE)ft, 0); OpenDirectConnection(ft->hContact, DIRECTCONN_FILE, ft); }
void CIcqProto::handleServiceFam(BYTE *pBuffer, size_t wBufferLength, snac_header *pSnacHeader, serverthread_info *info) { icq_packet packet; switch (pSnacHeader->wSubtype) { case ICQ_SERVER_READY: debugLogA("Server is ready and is requesting my Family versions"); debugLogA("Sending my Families"); // This packet is a response to SRV_FAMILIES SNAC(1,3). // This tells the server which SNAC families and their corresponding // versions which the client understands. This also seems to identify // the client as an ICQ vice AIM client to the server. // Miranda mimics the behaviour of ICQ 6 serverPacketInit(&packet, 54); packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_FAMILIES); packDWord(&packet, 0x00220001); packDWord(&packet, 0x00010004); packDWord(&packet, 0x00130004); packDWord(&packet, 0x00020001); packDWord(&packet, 0x00030001); packDWord(&packet, 0x00150001); packDWord(&packet, 0x00040001); packDWord(&packet, 0x00060001); packDWord(&packet, 0x00090001); packDWord(&packet, 0x000a0001); packDWord(&packet, 0x000b0001); sendServPacket(&packet); break; case ICQ_SERVER_FAMILIES2: /* This is a reply to CLI_FAMILIES and it tells the client which families and their versions that this server understands. * We send a rate request packet */ debugLogA("Server told me his Family versions"); debugLogA("Requesting Rate Information"); serverPacketInit(&packet, 10); packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_REQ_RATE_INFO); sendServPacket(&packet); break; case ICQ_SERVER_RATE_INFO: debugLogA("Server sent Rate Info"); /* init rates management */ m_rates = new rates(this, pBuffer, wBufferLength); /* ack rate levels */ debugLogA("Sending Rate Info Ack"); m_rates->initAckPacket(&packet); sendServPacket(&packet); /* CLI_REQINFO - This command requests from the server certain information about the client that is stored on the server. */ debugLogA("Sending CLI_REQINFO"); serverPacketInit(&packet, 10); packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_REQINFO); sendServPacket(&packet); if (m_bSsiEnabled) { cookie_servlist_action* ack; DWORD dwCookie; DWORD dwLastUpdate = getDword("SrvLastUpdate", 0); WORD wRecordCount = getWord("SrvRecordCount", 0); // CLI_REQLISTS - we want to use SSI debugLogA("Requesting roster rights"); serverPacketInit(&packet, 16); packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_REQLISTS); packTLVWord(&packet, 0x0B, 0x000F); // mimic ICQ 6 sendServPacket(&packet); if (!wRecordCount) { // CLI_REQROSTER // we do not have any data - request full list debugLogA("Requesting full roster"); serverPacketInit(&packet, 10); ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action)); if (ack) { // we try to use standalone cookie if available ack->dwAction = SSA_CHECK_ROSTER; // loading list dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_CLI_REQUEST, 0, ack); } else // if not use that old fake dwCookie = ICQ_LISTS_CLI_REQUEST << 0x10; packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_REQUEST, 0, dwCookie); sendServPacket(&packet); } else { // CLI_CHECKROSTER debugLogA("Requesting roster check"); serverPacketInit(&packet, 16); ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action)); if (ack) { // TODO: rewrite - use get list service for empty list // we try to use standalone cookie if available ack->dwAction = SSA_CHECK_ROSTER; // loading list dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_CLI_CHECK, 0, ack); } else // if not use that old fake dwCookie = ICQ_LISTS_CLI_CHECK << 0x10; packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_CHECK, 0, dwCookie); // check if it was not changed elsewhere (force reload, set that setting to zero) if (IsServerGroupsDefined()) { packDWord(&packet, dwLastUpdate); // last saved time packWord(&packet, wRecordCount); // number of records saved } else { // we need to get groups info into DB, force receive list packDWord(&packet, 0); // last saved time packWord(&packet, 0); // number of records saved } sendServPacket(&packet); } } // CLI_REQLOCATION debugLogA("Requesting Location rights"); serverPacketInit(&packet, 10); packFNACHeader(&packet, ICQ_LOCATION_FAMILY, ICQ_LOCATION_CLI_REQ_RIGHTS); sendServPacket(&packet); // CLI_REQBUDDY debugLogA("Requesting Client-side contactlist rights"); serverPacketInit(&packet, 16); packFNACHeader(&packet, ICQ_BUDDY_FAMILY, ICQ_USER_CLI_REQBUDDY); // Query flags: 1 = Enable Avatars // 2 = Enable offline status message notification // 4 = Enable Avatars for offline contacts // 8 = Use reject for not authorized contacts packTLVWord(&packet, 0x05, 0x0007); sendServPacket(&packet); // CLI_REQICBM debugLogA("Sending CLI_REQICBM"); serverPacketInit(&packet, 10); packFNACHeader(&packet, ICQ_MSG_FAMILY, ICQ_MSG_CLI_REQICBM); sendServPacket(&packet); // CLI_REQBOS debugLogA("Sending CLI_REQBOS"); serverPacketInit(&packet, 10); packFNACHeader(&packet, ICQ_BOS_FAMILY, ICQ_PRIVACY_REQ_RIGHTS); sendServPacket(&packet); break; case ICQ_SERVER_PAUSE: debugLogA("Server is going down in a few seconds... (Flags: %u)", pSnacHeader->wFlags); // This is the list of groups that we want to have on the next server serverPacketInit(&packet, 30); packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_PAUSE_ACK); packWord(&packet, ICQ_SERVICE_FAMILY); packWord(&packet, ICQ_LISTS_FAMILY); packWord(&packet, ICQ_LOCATION_FAMILY); packWord(&packet, ICQ_BUDDY_FAMILY); packWord(&packet, ICQ_EXTENSIONS_FAMILY); packWord(&packet, ICQ_MSG_FAMILY); packWord(&packet, 0x06); packWord(&packet, ICQ_BOS_FAMILY); packWord(&packet, ICQ_LOOKUP_FAMILY); packWord(&packet, ICQ_STATS_FAMILY); sendServPacket(&packet); debugLogA("Sent server pause ack"); break; case ICQ_SERVER_MIGRATIONREQ: debugLogA("Server migration requested (Flags: %u)", pSnacHeader->wFlags); pBuffer += 2; // Unknown, seen: 0 wBufferLength -= 2; { oscar_tlv_chain *chain = readIntoTLVChain(&pBuffer, wBufferLength, 0); if (info->cookieDataLen > 0) SAFE_FREE((void**)&info->cookieData); info->newServer = chain->getString(0x05, 1); info->newServerSSL = chain->getNumber(0x8E, 1); info->cookieData = (BYTE*)chain->getString(0x06, 1); info->cookieDataLen = chain->getLength(0x06, 1); disposeChain(&chain); if (!info->newServer || !info->cookieData) { icq_LogMessage(LOG_FATAL, LPGEN("A server migration has failed because the server returned invalid data. You must reconnect manually.")); SAFE_FREE(&info->newServer); SAFE_FREE((void**)&info->cookieData); info->cookieDataLen = 0; info->isNewServerReady = false; return; } debugLogA("Migration has started. New server will be %s", info->newServer); m_iDesiredStatus = m_iStatus; SetCurrentStatus(ID_STATUS_CONNECTING); // revert to connecting state info->isNewServerReady = info->isMigrating = true; } break; case ICQ_SERVER_NAME_INFO: // This is the reply to CLI_REQINFO debugLogA("Received self info"); { BYTE bUinLen; unpackByte(&pBuffer, &bUinLen); pBuffer += bUinLen; pBuffer += 4; /* warning level & user class */ wBufferLength -= 5 + bUinLen; // This is during the login sequence if (pSnacHeader->dwRef == ICQ_CLIENT_REQINFO << 0x10) { // TLV(x01) User type? // TLV(x0C) Empty CLI2CLI Direct connection info // TLV(x0A) External IP // TLV(x0F) Number of seconds that user has been online // TLV(x03) The online since time. // TLV(x0A) External IP again // TLV(x22) Unknown // TLV(x1E) Unknown: empty. // TLV(x05) Member of ICQ since. // TLV(x14) Unknown oscar_tlv_chain *chain = readIntoTLVChain(&pBuffer, wBufferLength, 0); // Save external IP DWORD dwValue = chain->getDWord(0x0A, 1); setDword("IP", dwValue); // Save member since timestamp dwValue = chain->getDWord(0x05, 1); if (dwValue) setDword("MemberTS", dwValue); dwValue = chain->getDWord(0x03, 1); setDword("LogonTS", dwValue ? dwValue : time(NULL)); disposeChain(&chain); // If we are in SSI mode, this is sent after the list is acked instead // to make sure that we don't set status before seing the visibility code if (!m_bSsiEnabled || info->isMigrating) handleServUINSettings(wListenPort, info); } } break; case ICQ_SERVER_RATE_CHANGE: if (wBufferLength >= 2) { WORD wStatus, wClass; DWORD dwLevel; // We now have global rate management, although controlled are only some // areas. This should not arrive in most cases. If it does, update our // local rate levels & issue broadcast. unpackWord(&pBuffer, &wStatus); unpackWord(&pBuffer, &wClass); pBuffer += 20; unpackDWord(&pBuffer, &dwLevel); { mir_cslock l(m_ratesMutex); m_rates->updateLevel(wClass, dwLevel); } if (wStatus == 2 || wStatus == 3) { // this is only the simplest solution, needs rate management to every section ProtoBroadcastAck(NULL, ICQACKTYPE_RATEWARNING, ACKRESULT_STATUS, (HANDLE)wClass, wStatus); if (wStatus == 2) debugLogA("Rates #%u: Alert", wClass); else debugLogA("Rates #%u: Limit", wClass); } else if (wStatus == 4) { ProtoBroadcastAck(NULL, ICQACKTYPE_RATEWARNING, ACKRESULT_STATUS, (HANDLE)wClass, wStatus); debugLogA("Rates #%u: Clear", wClass); } } break; case ICQ_SERVER_REDIRECT_SERVICE: // reply to family request, got new connection point { oscar_tlv_chain *pChain = NULL; cookie_family_request *pCookieData; if (!(pChain = readIntoTLVChain(&pBuffer, wBufferLength, 0))) { debugLogA("Received Broken Redirect Service SNAC(1,5)."); break; } // pick request data WORD wFamily = pChain->getWord(0x0D, 1); if ((!FindCookie(pSnacHeader->dwRef, NULL, (void**)&pCookieData)) || (pCookieData->wFamily != wFamily)) { disposeChain(&pChain); debugLogA("Received unexpected SNAC(1,5), skipping."); break; } FreeCookie(pSnacHeader->dwRef); // new family entry point received char *pServer = pChain->getString(0x05, 1); BYTE bServerSSL = pChain->getNumber(0x8E, 1); char *pCookie = pChain->getString(0x06, 1); WORD wCookieLen = pChain->getLength(0x06, 1); if (!pServer || !pCookie) { debugLogA("Server returned invalid data, family unavailable."); SAFE_FREE(&pServer); SAFE_FREE(&pCookie); SAFE_FREE((void**)&pCookieData); disposeChain(&pChain); break; } // Get new family server ip and port WORD wPort = info->wServerPort; // get default port parseServerAddress(pServer, &wPort); // establish connection NETLIBOPENCONNECTION nloc = { 0 }; if (m_bGatewayMode) nloc.flags |= NLOCF_HTTPGATEWAY; nloc.szHost = pServer; nloc.wPort = wPort; HANDLE hConnection = NetLib_OpenConnection(m_hNetlibUser, wFamily == ICQ_AVATAR_FAMILY ? "Avatar " : NULL, &nloc); if (hConnection == NULL) debugLogA("Unable to connect to ICQ new family server."); // we want the handler to be called even if the connecting failed else if (bServerSSL) { /* Start SSL session if requested */ debugLogA("(%p) Starting SSL negotiation", CallService(MS_NETLIB_GETSOCKET, (WPARAM)hConnection, 0)); if (!CallService(MS_NETLIB_STARTSSL, (WPARAM)hConnection, 0)) { debugLogA("Unable to connect to ICQ new family server, SSL could not be negotiated"); NetLib_CloseConnection(&hConnection, FALSE); } } (this->*pCookieData->familyHandler)(hConnection, pCookie, wCookieLen); // Free allocated memory // NOTE: "cookie" will get freed when we have connected to the avatar server. disposeChain(&pChain); SAFE_FREE(&pServer); SAFE_FREE((void**)&pCookieData); break; } case ICQ_SERVER_EXTSTATUS: // our session data debugLogA("Received owner session data."); while (wBufferLength > 4) { // loop thru all items WORD itemType = pBuffer[0] * 0x10 | pBuffer[1]; BYTE itemFlags = pBuffer[2]; size_t itemLen = pBuffer[3]; if (itemType == AVATAR_HASH_PHOTO) { /// TODO: handle photo item // skip photo item debugLogA("Photo item recognized"); } else if ((itemType == AVATAR_HASH_STATIC || itemType == AVATAR_HASH_FLASH) && (itemLen >= 0x10)) { debugLogA("Avatar item recognized"); if (m_bAvatarsEnabled && !info->bMyAvatarInited) { // signal the server after login // this refreshes avatar state - it used to work automatically, but now it does not if (getByte("ForceOurAvatar", 0)) { // keep our avatar TCHAR *file = GetOwnAvatarFileName(); SetMyAvatar(0, (LPARAM)file); SAFE_FREE(&file); } else { // only change avatar hash to the same one BYTE hash[0x14]; memcpy(hash, pBuffer, 0x14); hash[2] = 1; // update image status updateServAvatarHash(hash, 0x14); } info->bMyAvatarInited = true; break; } // process owner avatar hash changed notification handleAvatarOwnerHash(itemFlags, pBuffer, itemLen + 4); } else if (itemType == 0x02) { debugLogA("Status message item recognized"); } else if (itemType == 0x0E) { debugLogA("Status mood item recognized"); } // move to next item if (wBufferLength >= itemLen + 4) { wBufferLength -= itemLen + 4; pBuffer += itemLen + 4; } else { pBuffer += wBufferLength; wBufferLength = 0; } } break; case ICQ_ERROR: // Something went wrong, probably the request for avatar family failed { WORD wError; if (wBufferLength >= 2) unpackWord(&pBuffer, &wError); else wError = 0; LogFamilyError(ICQ_SERVICE_FAMILY, wError); } break; // Stuff we don't care about case ICQ_SERVER_MOTD: debugLogA("Server message of the day"); break; default: debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_SERVICE_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef); break; } }
void CIcqProto::handleExtensionMetaResponse(BYTE *databuf, WORD wPacketLen, WORD wCookie, WORD wFlags) { WORD wReplySubtype; BYTE bResultCode; _ASSERTE(wPacketLen >= 3); if (wPacketLen >= 3) { // Reply subtype unpackLEWord(&databuf, &wReplySubtype); wPacketLen -= 2; // Success byte unpackByte(&databuf, &bResultCode); wPacketLen -= 1; switch (wReplySubtype) { case META_SET_PASSWORD_ACK: parseUserInfoUpdateAck(databuf, wPacketLen, wCookie, wReplySubtype, bResultCode); break; case SRV_RANDOM_FOUND: case SRV_USER_FOUND: case SRV_LAST_USER_FOUND: parseSearchReplies(databuf, wPacketLen, wCookie, wReplySubtype, bResultCode); break; case META_PROCESSING_ERROR: // Meta processing error server reply // Todo: We only use this as an SMS ack, that will have to change { // Terminate buffer char *pszInfo = (char *)_alloca(wPacketLen + 1); if (wPacketLen > 0) memcpy(pszInfo, databuf, wPacketLen); pszInfo[wPacketLen] = 0; ProtoBroadcastAck(NULL, ICQACKTYPE_SMS, ACKRESULT_FAILED, (HANDLE)wCookie, (LPARAM)pszInfo); FreeCookie(wCookie); break; } break; case META_SMS_DELIVERY_RECEIPT: // Todo: This overlaps with META_SET_AFFINFO_ACK. // Todo: Check what happens if result != A if (wPacketLen > 8) { WORD wNetworkNameLen; WORD wAckLen; char *pszInfo; databuf += 6; // Some unknowns wPacketLen -= 6; unpackWord(&databuf, &wNetworkNameLen); if (wPacketLen >= (wNetworkNameLen + 2)) { databuf += wNetworkNameLen; wPacketLen -= wNetworkNameLen; unpackWord(&databuf, &wAckLen); if (pszInfo = (char *)_alloca(wAckLen + 1)) { // Terminate buffer if (wAckLen > 0) memcpy(pszInfo, databuf, wAckLen); pszInfo[wAckLen] = 0; ProtoBroadcastAck(NULL, ICQACKTYPE_SMS, ACKRESULT_SENTREQUEST, (HANDLE)wCookie, (LPARAM)pszInfo); FreeCookie(wCookie); // Parsing success break; } } } // Parsing failure debugLogA("Error: Failure parsing META_SMS_DELIVERY_RECEIPT"); break; case META_DIRECTORY_DATA: case META_DIRECTORY_RESPONSE: if (bResultCode == 0x0A) handleDirectoryQueryResponse(databuf, wPacketLen, wCookie, wReplySubtype, wFlags); else debugLogA("Error: Directory request failed, code %u", bResultCode); break; case META_DIRECTORY_UPDATE_ACK: if (bResultCode == 0x0A) handleDirectoryUpdateResponse(databuf, wPacketLen, wCookie, wReplySubtype); else debugLogA("Error: Directory request failed, code %u", bResultCode); break; case META_BASIC_USERINFO: case META_WORK_USERINFO: case META_MORE_USERINFO: case META_NOTES_USERINFO: case META_EMAIL_USERINFO: case META_INTERESTS_USERINFO: case META_AFFILATIONS_USERINFO: case META_SHORT_USERINFO: case META_HPAGECAT_USERINFO: debugLogA("Warning: Ignored 15/03 (legacy user info) replysubtype x%x", wReplySubtype); break; default: debugLogA("Warning: Ignored 15/03 replysubtype x%x", wReplySubtype); // _ASSERTE(0); break; } // Success return; } // Failure debugLogA("Warning: Broken 15/03 ExtensionMetaResponse"); }