PBYTE icq_httpGatewayUnwrapRecv( NETLIBHTTPREQUEST *nlhr, PBYTE buf, int len, int *outBufLen, void *( *NetlibRealloc )( void *, size_t )) { WORD wLen, wType; PBYTE tbuf; int i, copyBytes; tbuf = buf; for( i = 0;; ) { if ( tbuf - buf + 2 > len ) break; unpackWord( &tbuf, &wLen ); if ( wLen < 12 ) break; if ( tbuf - buf + wLen > len ) break; tbuf += 2; /* version */ unpackWord( &tbuf, &wType ); tbuf += 8; /* flags & subtype */ if ( wType == HTTP_PACKETTYPE_FLAP ) { copyBytes = wLen - 12; if ( copyBytes > len - i ) { /* invalid data - do our best to get something out of it */ copyBytes = len - i; } memcpy( buf + i, tbuf, copyBytes ); i += copyBytes; } tbuf += wLen - 12; } *outBufLen = i; return buf; }
void CIcqProto::handleStatusFam(unsigned char *pBuffer, size_t wBufferLength, snac_header* pSnacHeader) { switch (pSnacHeader->wSubtype) { case ICQ_STATS_MINREPORTINTERVAL: { WORD wInterval; unpackWord(&pBuffer, &wInterval); debugLogA("Server sent SNAC(x0B,x02) - SRV_SET_MINREPORTINTERVAL (Value: %u hours)", wInterval); } break; case ICQ_ERROR: { WORD wError; if (wBufferLength >= 2) unpackWord(&pBuffer, &wError); else wError = 0; LogFamilyError(ICQ_STATS_FAMILY, wError); break; } default: debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_STATS_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef); break; } }
PBYTE icq_httpGatewayUnwrapRecv(NETLIBHTTPREQUEST* nlhr, PBYTE buf, int len, int* outBufLen, void *(*NetlibRealloc)(void *, size_t)) { WORD wLen, wType; DWORD dwPackSeq; PBYTE tbuf; int i, copyBytes; tbuf = buf; for(i = 0;;) { if (tbuf - buf + 2 > len) break; unpackWord(&tbuf, &wLen); if (wLen < 12) break; if (tbuf - buf + wLen > len) break; tbuf += 2; /* version */ unpackWord(&tbuf, &wType); tbuf += 4; /* flags */ unpackDWord(&tbuf, &dwPackSeq); if (wType == HTTP_PACKETTYPE_FLAP) { // it is normal data packet copyBytes = wLen - 12; if (copyBytes > len - i) { /* invalid data - do our best to get something out of it */ copyBytes = len - i; } memcpy(buf + i, tbuf, copyBytes); i += copyBytes; } else if (wType == HTTP_PACKETTYPE_LOGINREPLY) { // our "virtual connection" was established, good BYTE bRes; unpackByte(&tbuf, &bRes); wLen -= 1; if (!bRes) Netlib_Logf( NULL, "Gateway Connection #%d Established.", dwPackSeq); else Netlib_Logf( NULL, "Gateway Connection #%d Failed, error: %d", dwPackSeq, bRes); } else if (wType == HTTP_PACKETTYPE_CLOSEREPLY) { // "virtual connection" closed - only received if any other "virual connection" still active Netlib_Logf( NULL, "Gateway Connection #%d Closed.", dwPackSeq); } tbuf += wLen - 12; } *outBufLen = i; return buf; }
int JabberHttpGatewayInit( HANDLE /*hConn*/, NETLIBOPENCONNECTION* /*nloc*/, NETLIBHTTPREQUEST* /*nlhr*/ ) { #ifdef NNNN WORD wLen, wVersion, wType; WORD wIpLen; DWORD dwSid1, dwSid2, dwSid3, dwSid4; BYTE response[300], *buf; int responseBytes, recvResult; char szSid[33], szHttpServer[256], szHttpGetUrl[300], szHttpPostUrl[300]; NETLIBHTTPPROXYINFO nlhpi = {0}; for( responseBytes = 0; ; ) { recvResult = Netlib_Recv( hConn, response + responseBytes, sizeof( response ) - responseBytes, MSG_DUMPPROXY ); if( recvResult<=0 ) break; responseBytes += recvResult; if( responseBytes == sizeof( response )) break; } if( responseBytes < 31 ) { SetLastError( ERROR_INVALID_DATA ); return 0; } buf = response; unpackWord( &buf, &wLen ); unpackWord( &buf, &wVersion ); /* always 0x0443 */ unpackWord( &buf, &wType ); buf += 6; /* dunno */ unpackDWord( &buf, &dwSid1 ); unpackDWord( &buf, &dwSid2 ); unpackDWord( &buf, &dwSid3 ); unpackDWord( &buf, &dwSid4 ); sprintf( szSid, "%08x%08x%08x%08x", dwSid1, dwSid2, dwSid3, dwSid4 ); unpackWord( &buf, &wIpLen ); if( responseBytes < 30 + wIpLen || wIpLen == 0 || wIpLen > sizeof( szHttpServer ) - 1 ) { SetLastError( ERROR_INVALID_DATA ); return 0; } memcpy( szHttpServer, buf, wIpLen ); szHttpServer[wIpLen] = '\0'; nlhpi.cbSize = sizeof( nlhpi ); nlhpi.flags = NLHPIF_USEPOSTSEQUENCE; nlhpi.szHttpGetUrl = szHttpGetUrl; nlhpi.szHttpPostUrl = szHttpPostUrl; nlhpi.firstPostSequence = 1; sprintf( szHttpGetUrl, "http://%s/monitor?sid=%s", szHttpServer, szSid ); sprintf( szHttpPostUrl, "http://%s/data?sid=%s&seq=", szHttpServer, szSid ); return JCallService( MS_NETLIB_SETHTTPPROXYINFO, ( WPARAM )hConn, ( LPARAM )&nlhpi ); #endif return 1; }
/* set maxTlvs<=0 to get all TLVs in length, or a positive integer to get at most the first n */ oscar_tlv_chain* readIntoTLVChain(BYTE **buf, WORD wLen, int maxTlvs) { oscar_tlv_chain *now, *last, *chain = NULL; WORD now_tlv_len; int len = wLen; if (!buf || !wLen) return NULL; while (len > 0) { /* don't use unsigned variable for this check */ now = (oscar_tlv_chain *)SAFE_MALLOC(sizeof(oscar_tlv_chain)); if (!now) { disposeChain(&chain); return NULL; } unpackWord(buf, &(now->tlv.wType)); unpackWord(buf, &now_tlv_len); now->tlv.wLen = now_tlv_len; len -= 4; if (now_tlv_len < 1) now->tlv.pData = NULL; else if (now_tlv_len <= len) { now->tlv.pData = (BYTE *)SAFE_MALLOC(now_tlv_len); if (now->tlv.pData) memcpy(now->tlv.pData, *buf, now_tlv_len); } else { // the packet is shorter than it should be SAFE_FREE((void**)&now); return chain; // give at least the rest of chain } if (chain) // keep the original order last->next = now; else chain = now; last = now; len -= now_tlv_len; *buf += now_tlv_len; if (--maxTlvs == 0) break; } return chain; }
int icq_httpGatewayInit(HANDLE hConn, NETLIBOPENCONNECTION *nloc, NETLIBHTTPREQUEST *nlhr) { // initial response from ICQ http gateway WORD wLen, wVersion, wType; WORD wIpLen; DWORD dwSid1, dwSid2, dwSid3, dwSid4; BYTE *buf; char szSid[33], szHttpServer[256], szHttpGetUrl[300], szHttpPostUrl[300]; NETLIBHTTPPROXYINFO nlhpi = {0}; if (nlhr->dataLength < 31) { SetLastError(ERROR_INVALID_DATA); return 0; } buf = (PBYTE)nlhr->pData; unpackWord(&buf, &wLen); unpackWord(&buf, &wVersion); /* always 0x0443 */ unpackWord(&buf, &wType); /* hello reply */ buf += 6; /* dunno */ unpackDWord(&buf, &dwSid1); unpackDWord(&buf, &dwSid2); unpackDWord(&buf, &dwSid3); unpackDWord(&buf, &dwSid4); null_snprintf(szSid, 33, "%08x%08x%08x%08x", dwSid1, dwSid2, dwSid3, dwSid4); unpackWord(&buf, &wIpLen); if(nlhr->dataLength < 30 + wIpLen || wIpLen == 0 || wIpLen > sizeof(szHttpServer) - 1) { SetLastError(ERROR_INVALID_DATA); return 0; } SetGatewayIndex(hConn, 1); // new master connection begins here memcpy(szHttpServer, buf, wIpLen); szHttpServer[wIpLen] = '\0'; nlhpi.cbSize = sizeof(nlhpi); nlhpi.flags = NLHPIF_USEPOSTSEQUENCE; nlhpi.szHttpGetUrl = szHttpGetUrl; nlhpi.szHttpPostUrl = szHttpPostUrl; nlhpi.firstPostSequence = 1; null_snprintf(szHttpGetUrl, 300, "http://%s/monitor?sid=%s", szHttpServer, szSid); null_snprintf(szHttpPostUrl, 300, "http://%s/data?sid=%s&seq=", szHttpServer, szSid); return CallService(MS_NETLIB_SETHTTPPROXYINFO, (WPARAM)hConn, (LPARAM)&nlhpi); }
void CIcqProto::handleExtensionServerInfo(BYTE *buf, WORD wPackLen, WORD wFlags) { oscar_tlv_chain *chain; oscar_tlv *dataTlv; // The entire packet is encapsulated in a TLV type 1 chain = readIntoTLVChain(&buf, wPackLen, 0); if (chain == NULL) { debugLogA("Error: Broken snac 15/3 %d", 1); return; } dataTlv = chain->getTLV(0x0001, 1); if (dataTlv == NULL) { disposeChain(&chain); debugLogA("Error: Broken snac 15/3 %d", 2); return; } BYTE *databuf = dataTlv->pData; wPackLen -= 4; _ASSERTE(dataTlv->wLen == wPackLen); _ASSERTE(wPackLen >= 10); if ((dataTlv->wLen == wPackLen) && (wPackLen >= 10)) { WORD wBytesRemaining; WORD wRequestType; WORD wCookie; DWORD dwMyUin; unpackLEWord(&databuf, &wBytesRemaining); unpackLEDWord(&databuf, &dwMyUin); unpackLEWord(&databuf, &wRequestType); unpackWord(&databuf, &wCookie); _ASSERTE(wBytesRemaining == (wPackLen - 2)); if (wBytesRemaining == (wPackLen - 2)) { wPackLen -= 10; switch (wRequestType) { case SRV_META_INFO_REPLY: // SRV_META request replies handleExtensionMetaResponse(databuf, wPackLen, wCookie, wFlags); break; default: debugLogA("Warning: Ignoring Meta response - Unknown type %d", wRequestType); break; } } } else debugLogA("Error: Broken snac 15/3 %d", 3); if (chain) disposeChain(&chain); }
void CIcqProto::handleLookupFam(BYTE *pBuffer, size_t wBufferLength, snac_header* pSnacHeader) { switch (pSnacHeader->wSubtype) { case ICQ_LOOKUP_EMAIL_REPLY: // AIM search reply handleLookupEmailReply(pBuffer, wBufferLength, pSnacHeader->dwRef); break; case ICQ_ERROR: { WORD wError; if (wBufferLength >= 2) unpackWord(&pBuffer, &wError); else wError = 0; cookie_search *pCookie; if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&pCookie)) { if (wError == 0x14) debugLogA("Lookup: No results"); ReleaseLookupCookie(pSnacHeader->dwRef, pCookie); if (wError == 0x14) return; } LogFamilyError(ICQ_LOOKUP_FAMILY, wError); } break; default: debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_LOOKUP_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef); break; } }
oscar_tlv_record_list* readIntoTLVRecordList(BYTE **buf, WORD wLen, int nCount) { oscar_tlv_record_list *list = NULL, *last; while (wLen >= 2) { WORD wRecordSize; unpackWord(buf, &wRecordSize); wLen -= 2; if (wRecordSize && wRecordSize <= wLen) { oscar_tlv_record_list *pRecord = (oscar_tlv_record_list*)SAFE_MALLOC(sizeof(oscar_tlv_record_list)); BYTE *pData = *buf; *buf += wRecordSize; wLen -= wRecordSize; pRecord->item = readIntoTLVChain(&pData, wRecordSize, 0); if (pRecord->item) { // keep the order if (list) last->next = pRecord; else list = pRecord; last = pRecord; } else SAFE_FREE((void**)&pRecord); } if (--nCount == 0) break; } return list; }
void CIcqProto::handleAuthorizationFam(BYTE *pBuffer, size_t wBufferLength, snac_header *pSnacHeader, serverthread_info *info) { switch (pSnacHeader->wSubtype) { case ICQ_SIGNON_ERROR: { WORD wError; if (wBufferLength >= 2) unpackWord(&pBuffer, &wError); else wError = 0; LogFamilyError(ICQ_AUTHORIZATION_FAMILY, wError); break; } case ICQ_SIGNON_AUTH_KEY: handleAuthKeyResponse(pBuffer, wBufferLength, info); break; case ICQ_SIGNON_LOGIN_REPLY: handleLoginReply(pBuffer, wBufferLength, info); break; default: debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_AUTHORIZATION_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef); break; } }
void CIcqProto::handleBosFam(unsigned char *pBuffer, WORD wBufferLength, snac_header* pSnacHeader) { switch (pSnacHeader->wSubtype) { case ICQ_PRIVACY_RIGHTS_REPLY: // Reply to CLI_REQBOS handlePrivacyRightsReply(pBuffer, wBufferLength); break; case ICQ_ERROR: { WORD wError; if (wBufferLength >= 2) unpackWord(&pBuffer, &wError); else wError = 0; LogFamilyError(ICQ_BOS_FAMILY, wError); break; } default: NetLog_Server("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_BOS_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef); break; } }
WORD rates::getGroupFromPacket(icq_packet *pPacket) { if (this) { if (pPacket->nChannel == ICQ_DATA_CHAN && pPacket->wLen >= 0x10) { WORD wFamily, wCommand; BYTE *pBuf = pPacket->pData + 6; unpackWord(&pBuf, &wFamily); unpackWord(&pBuf, &wCommand); return getGroupFromSNAC(wFamily, wCommand); } } return 0; }
int CIcqProto::parseUserInfoRecord(MCONTACT hContact, oscar_tlv *pData, UserInfoRecordItem pRecordDef[], int nRecordDef, int nMaxRecords) { int nRecords = 0; if (pData && pData->wLen >= 2) { BYTE *pRecords = pData->pData; WORD wRecordCount; unpackWord(&pRecords, &wRecordCount); oscar_tlv_record_list *cData = readIntoTLVRecordList(&pRecords, pData->wLen - 2, nMaxRecords > wRecordCount ? wRecordCount : nMaxRecords); oscar_tlv_record_list *cDataItem = cData; while (cDataItem) { oscar_tlv_chain *cItem = cDataItem->item; for (int i = 0; i < nRecordDef; i++) { char szItemKey[MAX_PATH]; mir_snprintf(szItemKey, MAX_PATH, pRecordDef[i].szDbSetting, nRecords); switch (pRecordDef[i].dbType) { case DBVT_UTF8: writeDbInfoSettingTLVStringUtf(hContact, szItemKey, cItem, pRecordDef[i].wTLV); break; case DBVT_WORD: writeDbInfoSettingTLVWord(hContact, szItemKey, cItem, pRecordDef[i].wTLV); break; } } nRecords++; cDataItem = cDataItem->next; } // release memory disposeRecordList(&cData); } // remove old data from database if (!nRecords || nMaxRecords > 1) for (int i = nRecords; i <= nMaxRecords; i++) for (int j = 0; j < nRecordDef; j++) { char szItemKey[MAX_PATH]; mir_snprintf(szItemKey, MAX_PATH, pRecordDef[j].szDbSetting, i); delSetting(hContact, szItemKey); } return nRecords; }
void CIcqProto::handleLocationFam(BYTE *pBuffer, WORD wBufferLength, snac_header *pSnacHeader) { switch (pSnacHeader->wSubtype) { case ICQ_LOCATION_RIGHTS_REPLY: // Reply to CLI_REQLOCATION NetLog_Server("Server sent SNAC(x02,x03) - SRV_LOCATION_RIGHTS_REPLY"); break; case ICQ_LOCATION_USR_INFO_REPLY: // AIM user info reply handleLocationUserInfoReply(pBuffer, wBufferLength, pSnacHeader->dwRef); break; case ICQ_ERROR: { WORD wError; HANDLE hCookieContact; cookie_fam15_data *pCookieData; if (wBufferLength >= 2) unpackWord(&pBuffer, &wError); else wError = 0; if (wError == 4) { if (FindCookie(pSnacHeader->dwRef, &hCookieContact, (void**)&pCookieData) && !getContactUin(hCookieContact) && pCookieData->bRequestType == REQUESTTYPE_PROFILE) { BroadcastAck(hCookieContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0); ReleaseCookie(pSnacHeader->dwRef); } } LogFamilyError(ICQ_LOCATION_FAMILY, wError); break; } default: NetLog_Server("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_LOCATION_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef); break; } }
void CIcqProto::handleAuthKeyResponse(BYTE *buf, size_t wPacketLen, serverthread_info *info) { char szKey[64] = {0}; mir_md5_state_t state; BYTE digest[16]; debugLogA("Received %s", "ICQ_SIGNON_AUTH_KEY"); if (wPacketLen < 2) { debugLogA("Malformed %s", "ICQ_SIGNON_AUTH_KEY"); icq_LogMessage(LOG_FATAL, LPGEN("Secure login failed.\nInvalid server response.")); SetCurrentStatus(ID_STATUS_OFFLINE); return; } size_t wKeyLen; unpackWord(&buf, &wKeyLen); wPacketLen -= 2; if (!wKeyLen || wKeyLen > wPacketLen || wKeyLen > sizeof(szKey)) { debugLogA("Invalid length in %s: %u", "ICQ_SIGNON_AUTH_KEY", wKeyLen); icq_LogMessage(LOG_FATAL, LPGEN("Secure login failed.\nInvalid key length.")); SetCurrentStatus(ID_STATUS_OFFLINE); return; } unpackString(&buf, szKey, wKeyLen); mir_md5_init(&state); mir_md5_append(&state, info->szAuthKey, (int)info->wAuthKeyLen); mir_md5_finish(&state, digest); mir_md5_init(&state); mir_md5_append(&state, (LPBYTE)szKey, (int)wKeyLen); mir_md5_append(&state, digest, 16); mir_md5_append(&state, (LPBYTE)CLIENT_MD5_STRING, sizeof(CLIENT_MD5_STRING)-1); mir_md5_finish(&state, digest); debugLogA("Sending ICQ_SIGNON_LOGIN_REQUEST to login server"); sendClientAuth((char*)digest, 0x10, TRUE); }
int unpackSessionDataItem(oscar_tlv_chain *pChain, WORD wItemType, BYTE **ppItemData, WORD *pwItemSize, BYTE *pbItemFlags) { int len = 0; BYTE *data; oscar_tlv *tlv = pChain->getTLV(0x1D, 1); if (tlv) { len = tlv->wLen; data = tlv->pData; } while (len >= 4) { // parse session data items one by one WORD itemType; BYTE itemFlags, itemLen; unpackWord(&data, &itemType); unpackByte(&data, &itemFlags); unpackByte(&data, &itemLen); len -= 4; // just some validity check if (itemLen > len) itemLen = len; if (itemType == wItemType) { // found the requested item if (ppItemData) *ppItemData = data; if (pwItemSize) *pwItemSize = itemLen; if (pbItemFlags) *pbItemFlags = itemFlags; return 1; // Success } data += itemLen; len -= itemLen; } return 0; }
void CIcqProto::handleBuddyFam(BYTE *pBuffer, size_t wBufferLength, snac_header *pSnacHeader, serverthread_info *info) { switch (pSnacHeader->wSubtype) { case ICQ_USER_ONLINE: handleUserOnline(pBuffer, wBufferLength, info); break; case ICQ_USER_OFFLINE: handleUserOffline(pBuffer, wBufferLength); break; case ICQ_USER_SRV_REPLYBUDDY: handleReplyBuddy(pBuffer, wBufferLength); break; case ICQ_USER_NOTIFY_REJECTED: handleNotifyRejected(pBuffer, wBufferLength); break; case ICQ_ERROR: { WORD wError; if (wBufferLength >= 2) unpackWord(&pBuffer, &wError); else wError = 0; LogFamilyError(ICQ_BUDDY_FAMILY, wError); } break; default: debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_BUDDY_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef); break; } }
void CIcqProto::handleLocationUserInfoReply(BYTE* buf, WORD wLen, DWORD dwCookie) { HANDLE hContact; DWORD dwUIN; uid_str szUID; WORD wTLVCount; WORD wWarningLevel; HANDLE hCookieContact; WORD status; cookie_message_data *pCookieData; // Unpack the sender's user ID if (!unpackUID(&buf, &wLen, &dwUIN, &szUID)) return; // Syntax check if (wLen < 4) return; // Warning level? unpackWord(&buf, &wWarningLevel); wLen -= 2; // TLV count unpackWord(&buf, &wTLVCount); wLen -= 2; // Determine contact hContact = HContactFromUID(dwUIN, szUID, NULL); // Ignore away status if the user is not already on our list if (hContact == INVALID_HANDLE_VALUE) { #ifdef _DEBUG NetLog_Server("Ignoring away reply (%s)", strUID(dwUIN, szUID)); #endif return; } if (!FindCookie(dwCookie, &hCookieContact, (void**)&pCookieData)) { NetLog_Server("Error: Received unexpected away reply from %s", strUID(dwUIN, szUID)); return; } if (hContact != hCookieContact) { NetLog_Server("Error: Away reply Contact does not match Cookie Contact(0x%x != 0x%x)", hContact, hCookieContact); ReleaseCookie(dwCookie); // This could be a bad idea, but I think it is safe return; } switch (GetCookieType(dwCookie)) { case CKT_FAMILYSPECIAL: { ReleaseCookie(dwCookie); // Read user info TLVs { oscar_tlv_chain* pChain; BYTE *tmp; char *szMsg = NULL; // Syntax check if (wLen < 4) return; tmp = buf; // Get general chain if (!(pChain = readIntoTLVChain(&buf, wLen, wTLVCount))) return; disposeChain(&pChain); wLen -= (buf - tmp); // Get extra chain if (pChain = readIntoTLVChain(&buf, wLen, 2)) { oscar_tlv *pTLV; char *szEncoding = NULL; // Get Profile encoding TLV pTLV = pChain->getTLV(0x01, 1); if (pTLV && (pTLV->wLen >= 1)) { szEncoding = (char*)_alloca(pTLV->wLen + 1); memcpy(szEncoding, pTLV->pData, pTLV->wLen); szEncoding[pTLV->wLen] = '\0'; } // Get Profile info TLV pTLV = pChain->getTLV(0x02, 1); if (pTLV && (pTLV->wLen >= 1)) { szMsg = (char*)SAFE_MALLOC(pTLV->wLen + 2); memcpy(szMsg, pTLV->pData, pTLV->wLen); szMsg[pTLV->wLen] = '\0'; szMsg[pTLV->wLen + 1] = '\0'; szMsg = AimApplyEncoding(szMsg, szEncoding); szMsg = EliminateHtml(szMsg, pTLV->wLen); } // Free TLV chain disposeChain(&pChain); } setSettingString(hContact, "About", szMsg); BroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE)1 ,0); SAFE_FREE((void**)&szMsg); } break; } default: // away message { status = AwayMsgTypeToStatus(pCookieData->nAckType); if (status == ID_STATUS_OFFLINE) { NetLog_Server("SNAC(2.6) Ignoring unknown status message from %s", strUID(dwUIN, szUID)); ReleaseCookie(dwCookie); return; } ReleaseCookie(dwCookie); // Read user info TLVs { oscar_tlv_chain* pChain; oscar_tlv* pTLV; BYTE *tmp; char *szMsg = NULL; CCSDATA ccs; PROTORECVEVENT pre; // Syntax check if (wLen < 4) return; tmp = buf; // Get general chain if (!(pChain = readIntoTLVChain(&buf, wLen, wTLVCount))) return; disposeChain(&pChain); wLen -= (buf - tmp); // Get extra chain if (pChain = readIntoTLVChain(&buf, wLen, 2)) { char* szEncoding = NULL; // Get Away encoding TLV pTLV = pChain->getTLV(0x03, 1); if (pTLV && (pTLV->wLen >= 1)) { szEncoding = (char*)_alloca(pTLV->wLen + 1); memcpy(szEncoding, pTLV->pData, pTLV->wLen); szEncoding[pTLV->wLen] = '\0'; } // Get Away info TLV pTLV = pChain->getTLV(0x04, 1); if (pTLV && (pTLV->wLen >= 1)) { szMsg = (char*)SAFE_MALLOC(pTLV->wLen + 2); memcpy(szMsg, pTLV->pData, pTLV->wLen); szMsg[pTLV->wLen] = '\0'; szMsg[pTLV->wLen + 1] = '\0'; szMsg = AimApplyEncoding(szMsg, szEncoding); szMsg = EliminateHtml(szMsg, pTLV->wLen); } // Free TLV chain disposeChain(&pChain); } ccs.szProtoService = PSR_AWAYMSG; ccs.hContact = hContact; ccs.wParam = status; ccs.lParam = (LPARAM)⪯ pre.flags = 0; pre.szMessage = szMsg?szMsg:(char *)""; pre.timestamp = time(NULL); pre.lParam = dwCookie; CallService(MS_PROTO_CHAINRECV,0,(LPARAM)&ccs); SAFE_FREE((void**)&szMsg); } break; } } }
void CIcqProto::handleDirectoryQueryResponse(BYTE *databuf, WORD wPacketLen, WORD wCookie, WORD wReplySubtype, WORD wFlags) { WORD wBytesRemaining = 0; snac_header requestSnac = {0}; BYTE requestResult; #ifdef _DEBUG debugLogA("Received directory query 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 ?? // Check if this is the last packet for this request BOOL bMoreDataFollows = wFlags&0x0001 && requestSnac.wFlags&0x0001; if (wPacketLen >= 3) unpackByte(&databuf, &requestResult); else { debugLogA("Error: Malformed directory response"); if (!bMoreDataFollows) ReleaseCookie(wCookie); return; } if (requestResult != 1 && requestResult != 4) { debugLogA("Error: Directory request failed, status %u", requestResult); if (!bMoreDataFollows) { if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOUSER) ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0); else if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH) ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); // should report error here, but Find/Add module does not support that ReleaseCookie(wCookie); } return; } WORD wLen; unpackWord(&databuf, &wLen); wPacketLen -= 3; if (wLen) debugLogA("Warning: Data in error message present!"); if (wPacketLen <= 0x16) { // sanity check debugLogA("Error: Malformed directory response"); if (!bMoreDataFollows) { if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOUSER) ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0); else if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH) ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); // should report error here, but Find/Add module does not support that ReleaseCookie(wCookie); } return; } databuf += 0x10; // unknown stuff wPacketLen -= 0x10; DWORD dwItemCount; WORD wPageCount; /// FIXME: check itemcount, pagecount against the cookie data ??? unpackDWord(&databuf, &dwItemCount); unpackWord(&databuf, &wPageCount); wPacketLen -= 6; if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH && !bMoreDataFollows) debugLogA("Directory Search: %d contacts found (%u pages)", dwItemCount, wPageCount); if (wPacketLen <= 2) { // sanity check, block expected debugLogA("Error: Malformed directory response"); if (!bMoreDataFollows) { if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOUSER) ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0); else if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH) ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); // should report error here, but Find/Add module does not support that ReleaseCookie(wCookie); } return; } WORD wData; unpackWord(&databuf, &wData); // This probably the count of items following (a block) wPacketLen -= 2; if (wPacketLen >= 2 && wData >= 1) { unpackWord(&databuf, &wLen); // This is the size of the first item wPacketLen -= 2; } if (wData == 0 && pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH) { debugLogA("Directory Search: No contacts found"); ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); ReleaseCookie(wCookie); return; } _ASSERTE(wData == 1 && wPacketLen == wLen); if (wData != 1 || wPacketLen != wLen) { debugLogA("Error: Malformed directory response (missing data)"); if (!bMoreDataFollows) { if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOUSER) ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0); else if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH) ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); // should report error here, but Find/Add module does not support that ReleaseCookie(wCookie); } return; } oscar_tlv_chain *pDirectoryData = readIntoTLVChain(&databuf, wLen, -1); if (pDirectoryData) { switch (pCookieData->bRequestType) { case DIRECTORYREQUEST_INFOOWNER: parseDirectoryUserDetailsData(NULL, pDirectoryData, wCookie, pCookieData, wReplySubtype); break; case DIRECTORYREQUEST_INFOUSER: { DWORD dwUin = 0; char *szUid = pDirectoryData->getString(0x32, 1); if (!szUid) { debugLogA("Error: Received unrecognized data from the directory"); break; } if (IsStringUIN(szUid)) dwUin = atoi(szUid); if (hContact != HContactFromUID(dwUin, szUid, NULL)) { debugLogA("Error: Received data does not match cookie contact, ignoring."); SAFE_FREE(&szUid); break; } else SAFE_FREE(&szUid); } case DIRECTORYREQUEST_INFOMULTI: parseDirectoryUserDetailsData(hContact, pDirectoryData, wCookie, pCookieData, wReplySubtype); break; case DIRECTORYREQUEST_SEARCH: parseDirectorySearchData(pDirectoryData, wCookie, pCookieData, wReplySubtype); break; default: debugLogA("Error: Unknown cookie type %x for directory response!", pCookieData->bRequestType); } disposeChain(&pDirectoryData); } else debugLogA("Error: Failed parsing directory response"); // Release Memory if (!bMoreDataFollows) ReleaseCookie(wCookie); }
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); }
int CIcqProto::handleServerPackets(BYTE *buf, int len, serverthread_info *info) { BYTE channel; WORD sequence; WORD datalen; int bytesUsed = 0; while (len > 0) { if (info->bReinitRecver) break; // All FLAPS begin with 0x2a if (*buf++ != FLAP_MARKER) break; if (len < 6) break; unpackByte(&buf, &channel); unpackWord(&buf, &sequence); unpackWord(&buf, &datalen); if (len < 6 + datalen) break; #ifdef _DEBUG NetLog_Server("Server FLAP: Channel %u, Seq %u, Length %u bytes", channel, sequence, datalen); #endif switch (channel) { case ICQ_LOGIN_CHAN: handleLoginChannel(buf, datalen, info); break; case ICQ_DATA_CHAN: handleDataChannel(buf, datalen, info); break; case ICQ_ERROR_CHAN: handleErrorChannel(buf, datalen); break; case ICQ_CLOSE_CHAN: handleCloseChannel(buf, datalen, info); break; // we need this for walking thru proxy case ICQ_PING_CHAN: handlePingChannel(buf, datalen); break; default: NetLog_Server("Warning: Unhandled Server FLAP Channel: Channel %u, Seq %u, Length %u bytes", channel, sequence, datalen); break; } /* Increase pointers so we can check for more FLAPs */ buf += datalen; len -= (datalen + 6); bytesUsed += (datalen + 6); } return bytesUsed; }
void CIcqProto::handleExtensionError(BYTE *buf, WORD wPackLen) { WORD wErrorCode; if (wPackLen < 2) wErrorCode = 0; if (wPackLen >= 2 && wPackLen <= 6) unpackWord(&buf, &wErrorCode); else { // TODO: cookies need to be handled and freed here on error oscar_tlv_chain *chain = NULL; unpackWord(&buf, &wErrorCode); wPackLen -= 2; chain = readIntoTLVChain(&buf, wPackLen, 0); if (chain) { oscar_tlv* pTLV; pTLV = chain->getTLV(0x21, 1); // get meta error data if (pTLV && pTLV->wLen >= 8) { BYTE *pBuffer = pTLV->pData; WORD wData; pBuffer += 6; unpackLEWord(&pBuffer, &wData); // get request type switch (wData) { case CLI_META_INFO_REQ: if (pTLV->wLen >= 12) { WORD wSubType; WORD wCookie; unpackWord(&pBuffer, &wCookie); unpackLEWord(&pBuffer, &wSubType); // more sofisticated detection, send ack if (wSubType == META_REQUEST_FULL_INFO) { MCONTACT hContact; cookie_fam15_data *pCookieData = NULL; int foundCookie = FindCookie(wCookie, &hContact, (void**)&pCookieData); if (foundCookie && pCookieData) { ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0); ReleaseCookie(wCookie); // we do not leak cookie and memory } debugLogA("Full info request error 0x%02x received", wErrorCode); } else if (wSubType == META_SET_PASSWORD_REQ) { // failed to change user password, report to UI ProtoBroadcastAck(NULL, ACKTYPE_SETINFO, ACKRESULT_FAILED, (HANDLE)wCookie, 0); debugLogA("Meta change password request failed, error 0x%02x", wErrorCode); } else debugLogA("Meta request error 0x%02x received", wErrorCode); } else debugLogA("Meta request error 0x%02x received", wErrorCode); break; default: debugLogA("Unknown request 0x%02x error 0x%02x received", wData, wErrorCode); } disposeChain(&chain); return; } disposeChain(&chain); } } LogFamilyError(ICQ_EXTENSIONS_FAMILY, wErrorCode); }
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"); }
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::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::parseStatusNote(DWORD dwUin, char *szUid, MCONTACT hContact, oscar_tlv_chain *pChain) { DWORD dwStatusNoteTS = time(NULL); BYTE *pStatusNoteTS, *pStatusNote; WORD wStatusNoteTSLen, wStatusNoteLen; BYTE bStatusNoteFlags; if (unpackSessionDataItem(pChain, 0x0D, &pStatusNoteTS, &wStatusNoteTSLen, NULL) && wStatusNoteTSLen == sizeof(DWORD)) unpackDWord(&pStatusNoteTS, &dwStatusNoteTS); // Get Status Note session item if (unpackSessionDataItem(pChain, 0x02, &pStatusNote, &wStatusNoteLen, &bStatusNoteFlags)) { char *szStatusNote = NULL; if ((bStatusNoteFlags & 4) == 4 && wStatusNoteLen >= 4) { BYTE *buf = pStatusNote; WORD buflen = wStatusNoteLen - 2; WORD wTextLen; unpackWord(&buf, &wTextLen); if (wTextLen > buflen) wTextLen = buflen; if (wTextLen > 0) { szStatusNote = (char*)_alloca(wStatusNoteLen + 1); unpackString(&buf, szStatusNote, wTextLen); szStatusNote[wTextLen] = '\0'; buflen -= wTextLen; WORD wEncodingType = 0; char *szEncoding = NULL; if (buflen >= 2) unpackWord(&buf, &wEncodingType); if (wEncodingType == 1 && buflen > 6) { // Encoding specified buf += 2; buflen -= 2; unpackWord(&buf, &wTextLen); if (wTextLen > buflen) wTextLen = buflen; szEncoding = (char*)_alloca(wTextLen + 1); unpackString(&buf, szEncoding, wTextLen); szEncoding[wTextLen] = '\0'; } else if (UTF8_IsValid(szStatusNote)) szEncoding = "utf-8"; szStatusNote = ApplyEncoding(szStatusNote, szEncoding); } } // Check if the status note was changed if (dwStatusNoteTS > getDword(hContact, DBSETTING_STATUS_NOTE_TIME, 0)) { DBVARIANT dbv = {DBVT_DELETED}; if (mir_strlen(szStatusNote) || (!getString(hContact, DBSETTING_STATUS_NOTE, &dbv) && (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_UTF8) && mir_strlen(dbv.pszVal))) debugLogA("%s changed status note to \"%s\"", strUID(dwUin, szUid), szStatusNote ? szStatusNote : ""); db_free(&dbv); if (szStatusNote) db_set_utf(hContact, m_szModuleName, DBSETTING_STATUS_NOTE, szStatusNote); else delSetting(hContact, DBSETTING_STATUS_NOTE); setDword(hContact, DBSETTING_STATUS_NOTE_TIME, dwStatusNoteTS); if (getContactXStatus(hContact) != 0 || !CheckContactCapabilities(hContact, CAPF_STATUS_MESSAGES)) { setStatusMsgVar(hContact, szStatusNote, false); TCHAR *tszNote = mir_utf8decodeT(szStatusNote); ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, NULL, (LPARAM)tszNote); mir_free(tszNote); } } SAFE_FREE(&szStatusNote); } else if (getContactStatus(hContact) == ID_STATUS_OFFLINE) { setStatusMsgVar(hContact, NULL, false); delSetting(hContact, DBSETTING_STATUS_NOTE); setDword(hContact, DBSETTING_STATUS_NOTE_TIME, dwStatusNoteTS); } }
void CIcqProto::handleUserOffline(BYTE *buf, size_t wLen) { DWORD dwUIN; uid_str szUID; do { oscar_tlv_chain *pChain = NULL; // Unpack the sender's user ID if (!unpackUID(&buf, &wLen, &dwUIN, &szUID)) return; // Warning level? buf += 2; // TLV Count WORD wTLVCount; unpackWord(&buf, &wTLVCount); wLen -= 4; // Skip the TLV chain DWORD dwAwaySince = 0; while (wTLVCount && wLen >= 4) { WORD wTLVType; WORD wTLVLen; unpackWord(&buf, &wTLVType); unpackWord(&buf, &wTLVLen); wLen -= 4; // stop parsing overflowed packet if (wTLVLen > wLen) { disposeChain(&pChain); return; } if (wTLVType == 0x1D) { // read only TLV with Session data into chain BYTE *pTLV = buf - 4; disposeChain(&pChain); pChain = readIntoTLVChain(&pTLV, wLen + 4, 1); } else if (wTLVType == 0x29 && wTLVLen == sizeof(DWORD)) { // get Away Since value BYTE *pData = buf; unpackDWord(&pData, &dwAwaySince); } buf += wTLVLen; wLen -= wTLVLen; wTLVCount--; } // Determine contact MCONTACT hContact = HContactFromUID(dwUIN, szUID, NULL); // Skip contacts that are not already on our list or are already offline if (hContact != INVALID_CONTACT_ID) { WORD wOldStatus = getContactStatus(hContact); // Process Avatar Hash oscar_tlv *pAvatarTLV = pChain ? pChain->getTLV(0x1D, 1) : NULL; if (pAvatarTLV) handleAvatarContactHash(dwUIN, szUID, hContact, pAvatarTLV->pData, pAvatarTLV->wLen); else handleAvatarContactHash(dwUIN, szUID, hContact, NULL, 0); // Process Status Note (offline status note) parseStatusNote(dwUIN, szUID, hContact, pChain); // Update status times setDword(hContact, "IdleTS", 0); setDword(hContact, "AwayTS", dwAwaySince); // Clear custom status & mood char tmp = NULL; handleXStatusCaps(dwUIN, szUID, hContact, (BYTE*)&tmp, 0, &tmp, 0); if (wOldStatus != ID_STATUS_OFFLINE) { debugLogA("%s went offline.", strUID(dwUIN, szUID)); setWord(hContact, "Status", ID_STATUS_OFFLINE); // close Direct Connections to that user CloseContactDirectConns(hContact); // Reset DC status setByte(hContact, "DCStatus", 0); } else debugLogA("%s is offline.", strUID(dwUIN, szUID)); } // Release memory disposeChain(&pChain); } while (wLen >= 1); }
void CIcqProto::parseDirectoryUserDetailsData(MCONTACT hContact, oscar_tlv_chain *cDetails, DWORD dwCookie, cookie_directory_data *pCookieData, WORD wReplySubType) { oscar_tlv *pTLV; WORD wRecordCount; if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOMULTI && !hContact) { DWORD dwUin = 0; char *szUid = cDetails->getString(0x32, 1); if (!szUid) { debugLogA("Error: Received unrecognized data from the directory"); return; } if (IsStringUIN(szUid)) dwUin = atoi(szUid); hContact = HContactFromUID(dwUin, szUid, NULL); if (hContact == INVALID_CONTACT_ID) { debugLogA("Error: Received details for unknown contact \"%s\"", szUid); SAFE_FREE(&szUid); return; } #ifdef _DEBUG else debugLogA("Received user info for %s from directory", szUid); #endif SAFE_FREE(&szUid); } #ifdef _DEBUG else { char *szUid = cDetails->getString(0x32, 1); if (!hContact) debugLogA("Received owner user info from directory"); else debugLogA("Received user info for %s from directory", szUid); SAFE_FREE(&szUid); } #endif pTLV = cDetails->getTLV(0x50, 1); if (pTLV && pTLV->wLen > 0) writeDbInfoSettingTLVStringUtf(hContact, "e-mail", cDetails, 0x50); // Verified e-mail else writeDbInfoSettingTLVStringUtf(hContact, "e-mail", cDetails, 0x55); // Pending e-mail writeDbInfoSettingTLVStringUtf(hContact, "FirstName", cDetails, 0x64); writeDbInfoSettingTLVStringUtf(hContact, "LastName", cDetails, 0x6E); writeDbInfoSettingTLVStringUtf(hContact, "Nick", cDetails, 0x78); // Home Address parseUserInfoRecord(hContact, cDetails->getTLV(0x96, 1), rAddress, SIZEOF(rAddress), 1); // Origin Address parseUserInfoRecord(hContact, cDetails->getTLV(0xA0, 1), rOriginAddress, SIZEOF(rOriginAddress), 1); // Phones pTLV = cDetails->getTLV(0xC8, 1); if (pTLV && pTLV->wLen >= 2) { BYTE *pRecords = pTLV->pData; unpackWord(&pRecords, &wRecordCount); oscar_tlv_record_list *cPhones = readIntoTLVRecordList(&pRecords, pTLV->wLen - 2, wRecordCount); if (cPhones) { oscar_tlv_chain *cPhone; cPhone = cPhones->getRecordByTLV(0x6E, 1); writeDbInfoSettingTLVStringUtf(hContact, "Phone", cPhone, 0x64); cPhone = cPhones->getRecordByTLV(0x6E, 2); writeDbInfoSettingTLVStringUtf(hContact, "CompanyPhone", cPhone, 0x64); cPhone = cPhones->getRecordByTLV(0x6E, 3); writeDbInfoSettingTLVStringUtf(hContact, "Cellular", cPhone, 0x64); cPhone = cPhones->getRecordByTLV(0x6E, 4); writeDbInfoSettingTLVStringUtf(hContact, "Fax", cPhone, 0x64); cPhone = cPhones->getRecordByTLV(0x6E, 5); writeDbInfoSettingTLVStringUtf(hContact, "CompanyFax", cPhone, 0x64); disposeRecordList(&cPhones); } else { // Remove old data when phones not available delSetting(hContact, "Phone"); delSetting(hContact, "CompanyPhone"); delSetting(hContact, "Cellular"); delSetting(hContact, "Fax"); delSetting(hContact, "CompanyFax"); } } else { // Remove old data when phones not available delSetting(hContact, "Phone"); delSetting(hContact, "CompanyPhone"); delSetting(hContact, "Cellular"); delSetting(hContact, "Fax"); delSetting(hContact, "CompanyFax"); } // Emails parseUserInfoRecord(hContact, cDetails->getTLV(0x8C, 1), rEmail, SIZEOF(rEmail), 4); writeDbInfoSettingTLVByte(hContact, "Timezone", cDetails, 0x17C); // Company parseUserInfoRecord(hContact, cDetails->getTLV(0x118, 1), rCompany, SIZEOF(rCompany), 1); // Education parseUserInfoRecord(hContact, cDetails->getTLV(0x10E, 1), rEducation, SIZEOF(rEducation), 1); switch (cDetails->getNumber(0x82, 1)) { case 1: setByte(hContact, "Gender", 'F'); break; case 2: setByte(hContact, "Gender", 'M'); break; default: delSetting(hContact, "Gender"); } writeDbInfoSettingTLVStringUtf(hContact, "Homepage", cDetails, 0xFA); writeDbInfoSettingTLVDate(hContact, "BirthYear", "BirthMonth", "BirthDay", cDetails, 0x1A4); writeDbInfoSettingTLVByte(hContact, "Language1", cDetails, 0xAA); writeDbInfoSettingTLVByte(hContact, "Language2", cDetails, 0xB4); writeDbInfoSettingTLVByte(hContact, "Language3", cDetails, 0xBE); writeDbInfoSettingTLVByte(hContact, "MaritalStatus", cDetails, 0x12C); // Interests parseUserInfoRecord(hContact, cDetails->getTLV(0x122, 1), rInterest, SIZEOF(rInterest), 4); writeDbInfoSettingTLVStringUtf(hContact, "About", cDetails, 0x186); // if (hContact) // writeDbInfoSettingTLVStringUtf(hContact, DBSETTING_STATUS_NOTE, cDetails, 0x226); // else if (!hContact) { // Owner contact needs special processing, in the database is current status note for the client // We just received the last status note set on directory, if it differs call SetStatusNote() to // ensure the directory will be updated (it should be in process anyway) char *szClientStatusNote = getSettingStringUtf(hContact, DBSETTING_STATUS_NOTE, NULL); char *szDirectoryStatusNote = cDetails->getString(0x226, 1); if (strcmpnull(szClientStatusNote, szDirectoryStatusNote)) SetStatusNote(szClientStatusNote, 1000, TRUE); // Release memory SAFE_FREE(&szDirectoryStatusNote); SAFE_FREE(&szClientStatusNote); } writeDbInfoSettingTLVByte(hContact, "PrivacyLevel", cDetails, 0x1F9); if (!hContact) { setByte(hContact, "Auth", !cDetails->getByte(0x19A, 1)); writeDbInfoSettingTLVByte(hContact, "WebAware", cDetails, 0x212); writeDbInfoSettingTLVByte(hContact, "AllowSpam", cDetails, 0x1EA); } writeDbInfoSettingTLVWord(hContact, "InfoCP", cDetails, 0x1C2); if (hContact) { // Handle deprecated setting (Age & Birthdate are not separate fields anymore) int nAge = calcAgeFromBirthDate(cDetails->getDouble(0x1A4, 1)); if (nAge) setWord(hContact, "Age", nAge); else delSetting(hContact, "Age"); } else // we do not need to calculate age for owner delSetting(hContact, "Age"); { // Save user info last update time and privacy token double dInfoTime; BYTE pbEmptyMetaToken[0x10] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; int bHasMetaToken = FALSE; // Check if the details arrived with privacy token! if ((pTLV = cDetails->getTLV(0x3C, 1)) && pTLV->wLen == 0x10 && memcmp(pTLV->pData, pbEmptyMetaToken, 0x10)) bHasMetaToken = TRUE; // !Important, we need to save the MDir server-item time - it can be newer than the one from the directory if ((dInfoTime = getSettingDouble(hContact, DBSETTING_METAINFO_TIME, 0)) > 0) setSettingDouble(hContact, DBSETTING_METAINFO_SAVED, dInfoTime); else if (bHasMetaToken || !hContact) writeDbInfoSettingTLVDouble(hContact, DBSETTING_METAINFO_SAVED, cDetails, 0x1CC); else setDword(hContact, DBSETTING_METAINFO_SAVED, time(NULL)); } if (wReplySubType == META_DIRECTORY_RESPONSE) if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOUSER) ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE)1 ,0); // Remove user from info update queue. Removing is fast so we always call this // even if it is likely that the user is not queued at all. if (hContact) icq_DequeueUser(getContactUin(hContact)); }
void CIcqProto::handleUserOnline(BYTE *buf, size_t wLen, serverthread_info*) { DWORD dwPort = 0; DWORD dwRealIP = 0; DWORD dwUIN; uid_str szUID; DWORD dwDirectConnCookie = 0; DWORD dwWebPort = 0; DWORD dwFT1 = 0, dwFT2 = 0, dwFT3 = 0; const char *szClient = NULL; BYTE bClientId = 0; WORD wVersion = 0; WORD wTLVCount; WORD wWarningLevel; WORD wStatusFlags; WORD wStatus = 0, wOldStatus = 0; BYTE nTCPFlag = 0; char szStrBuf[MAX_PATH]; // Unpack the sender's user ID if (!unpackUID(&buf, &wLen, &dwUIN, &szUID)) return; // Syntax check if (wLen < 4) return; // Warning level? unpackWord(&buf, &wWarningLevel); wLen -= 2; // TLV count unpackWord(&buf, &wTLVCount); wLen -= 2; // Ignore status notification if the user is not already on our list MCONTACT hContact = HContactFromUID(dwUIN, szUID, NULL); if (hContact == INVALID_CONTACT_ID) { debugLogA("Ignoring user online (%s)", strUID(dwUIN, szUID)); return; } // Read user info TLVs oscar_tlv_chain *pChain; oscar_tlv *pTLV; // Syntax check if (wLen < 4) return; // Get chain if (!(pChain = readIntoTLVChain(&buf, wLen, wTLVCount))) return; // Get Class word WORD wClass = pChain->getWord(0x01, 1); int nIsICQ = wClass & CLASS_ICQ; if (dwUIN) { // Get DC info TLV pTLV = pChain->getTLV(0x0C, 1); if (pTLV && (pTLV->wLen >= 15)) { BYTE *pBuffer = pTLV->pData; nIsICQ = TRUE; unpackDWord(&pBuffer, &dwRealIP); unpackDWord(&pBuffer, &dwPort); unpackByte(&pBuffer, &nTCPFlag); unpackWord(&pBuffer, &wVersion); unpackDWord(&pBuffer, &dwDirectConnCookie); unpackDWord(&pBuffer, &dwWebPort); // Web front port pBuffer += 4; // Client features // Get faked time signatures, used to identify clients if (pTLV->wLen >= 0x23) { unpackDWord(&pBuffer, &dwFT1); unpackDWord(&pBuffer, &dwFT2); unpackDWord(&pBuffer, &dwFT3); } } // Get Status info TLV pTLV = pChain->getTLV(0x06, 1); if (pTLV && (pTLV->wLen >= 4)) { BYTE *pBuffer = pTLV->pData; unpackWord(&pBuffer, &wStatusFlags); unpackWord(&pBuffer, &wStatus); } else if (!nIsICQ) { // Connected thru AIM client, guess by user class if (wClass & CLASS_AWAY) wStatus = ID_STATUS_AWAY; else if (wClass & CLASS_WIRELESS) wStatus = ID_STATUS_ONTHEPHONE; else wStatus = ID_STATUS_ONLINE; wStatusFlags = 0; } else { // Huh? No status TLV? Lets guess then... wStatusFlags = 0; wStatus = ICQ_STATUS_ONLINE; } } else { nIsICQ = FALSE; if (wClass & CLASS_AWAY) wStatus = ID_STATUS_AWAY; else if (wClass & CLASS_WIRELESS) wStatus = ID_STATUS_ONTHEPHONE; else wStatus = ID_STATUS_ONLINE; wStatusFlags = 0; } debugLogA("Flags are %x", wStatusFlags); debugLogA("Status is %x", wStatus); // Get IP TLV DWORD dwIP = pChain->getDWord(0x0A, 1); // Get Online Since TLV DWORD dwOnlineSince = pChain->getDWord(0x03, 1); // Get Away Since TLV DWORD dwAwaySince = pChain->getDWord(0x29, 1); // Get Member Since TLV DWORD dwMemberSince = pChain->getDWord(0x05, 1); // Get Idle timer TLV WORD wIdleTimer = pChain->getWord(0x04, 1); time_t tIdleTS = 0; if (wIdleTimer) { time(&tIdleTS); tIdleTS -= (wIdleTimer*60); } if (wIdleTimer) debugLogA("Idle timer is %u.", wIdleTimer); debugLogA("Online since %s", time2text(dwOnlineSince)); if (dwAwaySince) debugLogA("Status was set on %s", time2text(dwAwaySince)); // Check client capabilities if (hContact != NULL) { wOldStatus = getContactStatus(hContact); // Collect all Capability info from TLV chain BYTE *capBuf = NULL; WORD capLen = 0; // Get Location Capability Info TLVs oscar_tlv *pFullTLV = pChain->getTLV(0x0D, 1); oscar_tlv *pShortTLV = pChain->getTLV(0x19, 1); if (pFullTLV && (pFullTLV->wLen >= BINARY_CAP_SIZE)) capLen += pFullTLV->wLen; if (pShortTLV && (pShortTLV->wLen >= 2)) capLen += (pShortTLV->wLen * 8); capBuf = (BYTE*)_alloca(capLen + BINARY_CAP_SIZE); if (capLen) { BYTE *pCapability = capBuf; capLen = 0; // we need to recount that if (pFullTLV && (pFullTLV->wLen >= BINARY_CAP_SIZE)) { // copy classic Capabilities BYTE *cData = pFullTLV->pData; int cLen = pFullTLV->wLen; while (cLen) { // be impervious to duplicates (AOL sends them sometimes) if (!capLen || !MatchCapability(capBuf, capLen, (capstr*)cData, BINARY_CAP_SIZE)) { // not present, add memcpy(pCapability, cData, BINARY_CAP_SIZE); capLen += BINARY_CAP_SIZE; pCapability += BINARY_CAP_SIZE; } cData += BINARY_CAP_SIZE; cLen -= BINARY_CAP_SIZE; } } if (pShortTLV && (pShortTLV->wLen >= 2)) { // copy short Capabilities capstr tmp; BYTE *cData = pShortTLV->pData; int cLen = pShortTLV->wLen; memcpy(tmp, capShortCaps, BINARY_CAP_SIZE); while (cLen) { // be impervious to duplicates (AOL sends them sometimes) tmp[2] = cData[0]; tmp[3] = cData[1]; if (!capLen || !MatchCapability(capBuf, capLen, &tmp, BINARY_CAP_SIZE)) { // not present, add memcpy(pCapability, tmp, BINARY_CAP_SIZE); capLen += BINARY_CAP_SIZE; pCapability += BINARY_CAP_SIZE; } cData += 2; cLen -= 2; } } debugLogA("Detected %d capability items.", capLen / BINARY_CAP_SIZE); } if (capLen) { // Update the contact's capabilies if present in packet SetCapabilitiesFromBuffer(hContact, capBuf, capLen, wOldStatus == ID_STATUS_OFFLINE); char *szCurrentClient = wOldStatus == ID_STATUS_OFFLINE ? NULL : getSettingStringUtf(hContact, "MirVer", NULL); szClient = detectUserClient(hContact, nIsICQ, wClass, dwOnlineSince, szCurrentClient, wVersion, dwFT1, dwFT2, dwFT3, dwDirectConnCookie, dwWebPort, capBuf, capLen, &bClientId, szStrBuf); // Check if the client changed, if not do not change if (szCurrentClient && !strcmpnull(szCurrentClient, szClient)) szClient = (const char*)-1; SAFE_FREE(&szCurrentClient); } else if (wOldStatus == ID_STATUS_OFFLINE) { // Remove the contact's capabilities if coming from offline ClearAllContactCapabilities(hContact); // no capability debugLogA("No capability info TLVs"); szClient = detectUserClient(hContact, nIsICQ, wClass, dwOnlineSince, NULL, wVersion, dwFT1, dwFT2, dwFT3, dwDirectConnCookie, dwWebPort, NULL, capLen, &bClientId, szStrBuf); } else // Capabilities not present in update packet, do not touch szClient = (const char*)-1; // we don't want to client be overwritten // handle Xtraz status char *moodData = NULL; WORD moodSize = 0; unpackSessionDataItem(pChain, 0x0E, (BYTE**)&moodData, &moodSize, NULL); if (capLen || wOldStatus == ID_STATUS_OFFLINE) handleXStatusCaps(dwUIN, szUID, hContact, capBuf, capLen, moodData, moodSize); else handleXStatusCaps(dwUIN, szUID, hContact, NULL, 0, moodData, moodSize); // Determine support for extended status messages if (pChain->getWord(0x08, 1) == 0x0A06) SetContactCapabilities(hContact, CAPF_STATUS_MESSAGES); else if (wOldStatus == ID_STATUS_OFFLINE) ClearContactCapabilities(hContact, CAPF_STATUS_MESSAGES); if (wOldStatus == ID_STATUS_OFFLINE) { if (CheckContactCapabilities(hContact, CAPF_SRV_RELAY)) debugLogA("Supports advanced messages"); else debugLogA("Does NOT support advanced messages"); } if (!nIsICQ) { // AIM clients does not advertise these, but do support them SetContactCapabilities(hContact, CAPF_UTF | CAPF_TYPING); // Server relayed messages are only supported by ICQ clients ClearContactCapabilities(hContact, CAPF_SRV_RELAY); if (dwUIN && wOldStatus == ID_STATUS_OFFLINE) debugLogA("Logged in with AIM client"); } if (nIsICQ && wVersion < 8) { ClearContactCapabilities(hContact, CAPF_SRV_RELAY); if (wOldStatus == ID_STATUS_OFFLINE) debugLogA("Forcing simple messages due to compability issues"); } // Process Avatar Hash pTLV = pChain->getTLV(0x1D, 1); if (pTLV) handleAvatarContactHash(dwUIN, szUID, hContact, pTLV->pData, pTLV->wLen); else handleAvatarContactHash(dwUIN, szUID, hContact, NULL, 0); // Process Status Note parseStatusNote(dwUIN, szUID, hContact, pChain); } // Free TLV chain disposeChain(&pChain); // Save contacts details in database if (hContact != NULL) { setDword(hContact, "LogonTS", dwOnlineSince); setDword(hContact, "AwayTS", dwAwaySince); setDword(hContact, "IdleTS", tIdleTS); if (dwMemberSince) setDword(hContact, "MemberTS", dwMemberSince); if (nIsICQ) { // on AIM these are not used setDword(hContact, "DirectCookie", dwDirectConnCookie); setByte(hContact, "DCType", (BYTE)nTCPFlag); setWord(hContact, "UserPort", (WORD)(dwPort & 0xffff)); setWord(hContact, "Version", wVersion); } else { delSetting(hContact, "DirectCookie"); delSetting(hContact, "DCType"); delSetting(hContact, "UserPort"); delSetting(hContact, "Version"); } // if no detection, set uknown if (!szClient) szClient = (nIsICQ ? "Unknown" : "Unknown AIM"); if (szClient != (char*)-1) { db_set_utf(hContact, m_szModuleName, "MirVer", szClient); setByte(hContact, "ClientID", bClientId); } if (wOldStatus == ID_STATUS_OFFLINE) { setDword(hContact, "IP", dwIP); setDword(hContact, "RealIP", dwRealIP); } else { // if not first notification only write significant information if (dwIP) setDword(hContact, "IP", dwIP); if (dwRealIP) setDword(hContact, "RealIP", dwRealIP); } setWord(hContact, "Status", (WORD)IcqStatusToMiranda(wStatus)); // Update info? if (dwUIN) { // check if the local copy of user details is up-to-date if (IsMetaInfoChanged(hContact)) icq_QueueUser(hContact); } } LPCTSTR ptszStatus = pcli->pfnGetStatusModeDescription(IcqStatusToMiranda(wStatus), 0); if (wOldStatus != IcqStatusToMiranda(wStatus)) { // And a small log notice... if status was changed if (nIsICQ) debugLogA("%u changed status to %S (v%d).", dwUIN, ptszStatus, wVersion); else debugLogA("%s changed status to %S.", strUID(dwUIN, szUID), ptszStatus); } if (szClient == cliSpamBot) { if (getByte("KillSpambots", DEFAULT_KILLSPAM_ENABLED) && db_get_b(hContact, "CList", "NotOnList", 0)) { // kill spammer icq_DequeueUser(dwUIN); icq_sendRemoveContact(dwUIN, NULL); AddToSpammerList(dwUIN); if (getByte("PopupsSpamEnabled", DEFAULT_SPAM_POPUPS_ENABLED)) ShowPopupMsg(hContact, LPGEN("Spambot Detected"), LPGEN("Contact deleted & further events blocked."), POPTYPE_SPAM); CallService(MS_DB_CONTACT_DELETE, hContact, 0); debugLogA("Contact %u deleted", dwUIN); } } }
rates::rates(CIcqProto *ppro, BYTE *pBuffer, WORD wLen) { nGroups = 0; memset(&groups, 0, MAX_RATES_GROUP_COUNT * sizeof(rates_group)); this->ppro = ppro; // Parse Rate Data Block WORD wCount; unpackWord(&pBuffer, &wCount); wLen -= 2; if (wCount > MAX_RATES_GROUP_COUNT) { // just sanity check ppro->NetLog_Server("Rates: Error: Data packet contains too many rate groups!"); wCount = MAX_RATES_GROUP_COUNT; } nGroups = wCount; // Parse Group details int i; for (i=0; i<wCount; i++) { rates_group *pGroup = &groups[i]; if (wLen >= 35) { pBuffer += 2; // Group ID unpackDWord(&pBuffer, &pGroup->dwWindowSize); unpackDWord(&pBuffer, &pGroup->dwClearLevel); unpackDWord(&pBuffer, &pGroup->dwAlertLevel); unpackDWord(&pBuffer, &pGroup->dwLimitLevel); pBuffer += 8; unpackDWord(&pBuffer, &pGroup->dwMaxLevel); pBuffer += 5; wLen -= 35; } else { // packet broken, put some basic defaults pGroup->dwWindowSize = 10; pGroup->dwMaxLevel = 5000; } pGroup->rCurrentLevel = pGroup->dwMaxLevel; } // Parse Group associated pairs for (i=0; i<wCount; i++) { rates_group *pGroup = &groups[i]; WORD wNum; if (wLen<4) break; pBuffer += 2; // Group ID unpackWord(&pBuffer, &wNum); wLen -= 4; if (wLen < wNum*4) break; pGroup->nPairs = wNum; pGroup->pPairs = (WORD*)SAFE_MALLOC(wNum*4); for (int n=0; n<wNum*2; n++) { WORD wItem; unpackWord(&pBuffer, &wItem); pGroup->pPairs[n] = wItem; } #ifdef _DEBUG ppro->NetLog_Server("Rates: %d# %d pairs.", i+1, wNum); #endif wLen -= wNum*4; } }