void CIcqProto::handleNotifyRejected(BYTE *buf, size_t wPackLen) { DWORD dwUIN; uid_str szUID; while (wPackLen) if (unpackUID(&buf, &wPackLen, &dwUIN, &szUID)) debugLogA("%s status notification rejected.", strUID(dwUIN, szUID)); }
void CIcqProto::makeContactTemporaryVisible(MCONTACT hContact) { if (getByte(hContact, "TemporaryVisible", 0)) return; // already there DWORD dwUin; uid_str szUid; if (getContactUid(hContact, &dwUin, &szUid)) return; // Invalid contact icq_sendGenericContact(dwUin, szUid, ICQ_BOS_FAMILY, ICQ_CLI_ADDTEMPVISIBLE); setByte(hContact, "TemporaryVisible", 1); debugLogA("Added contact %s to temporary visible list", strUID(dwUin, szUid)); }
void CIcqProto::sendClientAuth(const char *szKey, size_t wKeyLen, BOOL bSecure) { char szUin[UINMAXLEN]; icq_packet packet; size_t wUinLen = mir_strlen(strUID(m_dwLocalUIN, szUin)); packet.wLen = WORD((m_bLegacyFix ? 65 : 70) + sizeof(CLIENT_ID_STRING) + wUinLen + wKeyLen + (m_bSecureConnection ? 4 : 0)); if (bSecure) { serverPacketInit(&packet, packet.wLen + 10); packFNACHeader(&packet, ICQ_AUTHORIZATION_FAMILY, ICQ_SIGNON_LOGIN_REQUEST, 0, 0); } else { write_flap(&packet, ICQ_LOGIN_CHAN); packDWord(&packet, 0x00000001); } packTLV(&packet, 0x0001, wUinLen, (LPBYTE)szUin); if (bSecure) { // Pack MD5 auth digest packTLV(&packet, 0x0025, wKeyLen, (BYTE*)szKey); packDWord(&packet, 0x004C0000); // empty TLV(0x4C): unknown } else { // Pack old style password hash BYTE hash[20]; icq_encryptPassword(szKey, hash); packTLV(&packet, 0x0002, wKeyLen, hash); } // Pack client identification details. packTLV(&packet, 0x0003, (WORD)sizeof(CLIENT_ID_STRING)-1, (LPBYTE)CLIENT_ID_STRING); packTLVWord(&packet, 0x0017, CLIENT_VERSION_MAJOR); packTLVWord(&packet, 0x0018, CLIENT_VERSION_MINOR); packTLVWord(&packet, 0x0019, CLIENT_VERSION_LESSER); packTLVWord(&packet, 0x001a, CLIENT_VERSION_BUILD); packTLVWord(&packet, 0x0016, CLIENT_ID_CODE); packTLVDWord(&packet, 0x0014, CLIENT_DISTRIBUTION); packTLV(&packet, 0x000f, 0x0002, (LPBYTE)CLIENT_LANGUAGE); packTLV(&packet, 0x000e, 0x0002, (LPBYTE)CLIENT_COUNTRY); if (!m_bLegacyFix) packTLV(&packet, 0x0094, 0x0001, &m_bConnectionLost); // CLIENT_RECONNECT flag if (m_bSecureConnection) packDWord(&packet, 0x008C0000); // empty TLV(0x8C): use SSL sendServPacket(&packet); }
void CIcqProto::makeContactTemporaryVisible(HANDLE hContact) { DWORD dwUin; uid_str szUid; if (getSettingByte(hContact, "TemporaryVisible", 0)) return; // already there if (getContactUid(hContact, &dwUin, &szUid)) return; // Invalid contact icq_sendGenericContact(dwUin, szUid, ICQ_BOS_FAMILY, ICQ_CLI_ADDTEMPVISIBLE); setSettingByte(hContact, "TemporaryVisible", 1); #ifdef _DEBUG NetLog_Server("Added contact %s to temporary visible list", strUID(dwUin, szUid)); #endif }
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; } } }
DWORD avatars_server_connection::sendGetAvatarRequest(MCONTACT hContact, DWORD dwUin, char *szUid, const BYTE *hash, size_t hashlen, const TCHAR *file) { int i; DWORD dwNow = GetTickCount(); mir_cslockfull alck(ppro->m_avatarsMutex); for (i = 0; i < runCount;) { // look for timeouted requests if (runTime[i] < dwNow) { // found outdated, remove runContact[i] = runContact[runCount - 1]; runTime[i] = runTime[runCount - 1]; runCount--; } else i++; } for (i = 0; i < runCount; i++) { if (runContact[i] == hContact) { ppro->debugLogA("Ignoring duplicate get %s image request.", strUID(dwUin, szUid)); return -1; // Success: request ignored } } if (runCount < 4) { // 4 concurent requests at most int bSendNow = TRUE; { // rate management mir_cslock l(m_ratesMutex); WORD wGroup = m_rates->getGroupFromSNAC(ICQ_AVATAR_FAMILY, ICQ_AVATAR_GET_REQUEST); if (m_rates->getNextRateLevel(wGroup) < m_rates->getLimitLevel(wGroup, RML_ALERT)) { // we will be over quota if we send the request now, add to queue instead bSendNow = FALSE; ppro->debugLogA("Rates: Delay avatar request."); } } if (bSendNow) { runContact[runCount] = hContact; runTime[runCount] = GetTickCount() + 30000; // 30sec to complete request runCount++; alck.unlock(); int nUinLen = getUIDLen(dwUin, szUid); cookie_avatar *ack = (cookie_avatar*)SAFE_MALLOC(sizeof(cookie_avatar)); if (!ack) return 0; // Failure: out of memory ack->dwUin = 1; //dwUin; // I should be damned for this - only to identify get request ack->hContact = hContact; ack->hash = (BYTE*)SAFE_MALLOC(hashlen); memcpy(ack->hash, hash, hashlen); // copy the data ack->hashlen = hashlen; ack->szFile = null_strdup(file); // duplicate the string DWORD dwCookie = ppro->AllocateCookie(CKT_AVATAR, ICQ_AVATAR_GET_REQUEST, hContact, ack); icq_packet packet; serverPacketInit(&packet, 12 + nUinLen + hashlen); packFNACHeader(&packet, ICQ_AVATAR_FAMILY, ICQ_AVATAR_GET_REQUEST, 0, dwCookie); packUID(&packet, dwUin, szUid); packByte(&packet, 1); // unknown, probably type of request: 1 = get icon :) packBuffer(&packet, hash, hashlen); if (sendServerPacket(&packet)) { ppro->debugLogA("Request to get %s image sent.", strUID(dwUin, szUid)); return dwCookie; } ppro->FreeCookie(dwCookie); // sending failed, free resources SAFE_FREE(&ack->szFile); SAFE_FREE((void**)&ack->hash); SAFE_FREE((void**)&ack); } } return 0; // Failure }
// request avatar data from server int CIcqProto::GetAvatarData(MCONTACT hContact, DWORD dwUin, const char *szUid, const BYTE *hash, size_t hashlen, const TCHAR *file) { uid_str szUidData; char *pszUid = NULL; if (!dwUin && szUid) { // create a copy in local writable buffer mir_strcpy(szUidData, szUid); pszUid = szUidData; } mir_cslockfull alck(m_avatarsMutex); if (m_avatarsConnection && m_avatarsConnection->isReady()) { // check if we are ready // check if requests for this user are not blocked for (int i = 0; i < m_arAvatars.getCount();) { avatars_request *ar = m_arAvatars[i]; if (ar->hContact == hContact && ar->type == ART_BLOCK) { // found a block item if (GetTickCount() > ar->timeOut) { // remove timeouted block m_arAvatars.remove(i); delete ar; continue; } debugLogA("Avatars: Requests for %s avatar are blocked.", strUID(dwUin, pszUid)); return 0; } i++; } mir_cslock l(m_avatarsConnection->getLock()); alck.unlock(); DWORD dwCookie = m_avatarsConnection->sendGetAvatarRequest(hContact, dwUin, pszUid, hash, hashlen, file); if (dwCookie) // return now if the request was sent successfully return dwCookie; alck.lock(); } // we failed to send request, or avatar thread not ready // check if any request for this user is not already in the queue for (int i = 0; i < m_arAvatars.getCount();) { avatars_request *ar = m_arAvatars[i]; if (ar->hContact == hContact) { // we found it, return error if (ar->type == ART_BLOCK && GetTickCount() > ar->timeOut) { // remove timeouted block m_arAvatars.remove(i); delete ar; continue; } alck.unlock(); debugLogA("Avatars: Ignoring duplicate get %s avatar request.", strUID(dwUin, pszUid)); // make sure avatar connection is in progress requestAvatarConnection(); return 0; } i++; } // add request to queue, processed after successful login avatars_request *ar = new avatars_request(ART_GET); // get avatar ar->hContact = hContact; ar->dwUin = dwUin; if (!dwUin) mir_strcpy(ar->szUid, szUid); ar->hash = (BYTE*)SAFE_MALLOC(hashlen); if (!ar->hash) { // alloc failed delete ar; return 0; } memcpy(ar->hash, hash, hashlen); // copy the data ar->hashlen = hashlen; ar->szFile = null_strdup(file); // duplicate the string m_arAvatars.insert(ar); alck.unlock(); debugLogA("Avatars: Request to get %s image added to queue.", strUID(dwUin, pszUid)); // make sure avatar connection is in progress requestAvatarConnection(); return -1; // we added to queue }
// 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::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::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); } } }
// request avatar data from server int CIcqProto::GetAvatarData(HANDLE hContact, DWORD dwUin, const char *szUid, const BYTE *hash, unsigned int hashlen, const TCHAR *file) { uid_str szUidData; char *pszUid = NULL; if (!dwUin && szUid) { // create a copy in local writable buffer strcpy(szUidData, szUid); pszUid = szUidData; } m_avatarsMutex->Enter(); if (m_avatarsConnection && m_avatarsConnection->isReady()) // check if we are ready { // check if requests for this user are not blocked DWORD dwNow = GetTickCount(); avatars_request *ar = m_avatarsQueue; while (ar) { if (ar->hContact == hContact && ar->type == ART_BLOCK) { // found a block item if (GetTickCount() > ar->timeOut) { // remove timeouted block ar = ReleaseAvatarRequestInQueue(ar); continue; } m_avatarsMutex->Leave(); debugLogA("Avatars: Requests for %s avatar are blocked.", strUID(dwUin, pszUid)); return 0; } ar = ar->pNext; } avatars_server_connection *pConnection = m_avatarsConnection; pConnection->_Lock(); m_avatarsMutex->Leave(); DWORD dwCookie = pConnection->sendGetAvatarRequest(hContact, dwUin, pszUid, hash, hashlen, file); m_avatarsMutex->Enter(); pConnection->_Release(); if (dwCookie) { // return now if the request was sent successfully m_avatarsMutex->Leave(); return dwCookie; } } // we failed to send request, or avatar thread not ready // check if any request for this user is not already in the queue avatars_request *ar = m_avatarsQueue; while (ar) { if (ar->hContact == hContact) { // we found it, return error if (ar->type == ART_BLOCK && GetTickCount() > ar->timeOut) { // remove timeouted block ar = ReleaseAvatarRequestInQueue(ar); continue; } m_avatarsMutex->Leave(); debugLogA("Avatars: Ignoring duplicate get %s avatar request.", strUID(dwUin, pszUid)); // make sure avatar connection is in progress requestAvatarConnection(); return 0; } ar = ar->pNext; } // add request to queue, processed after successful login ar = new avatars_request(ART_GET); // get avatar if (!ar) { // out of memory, go away m_avatarsMutex->Leave(); return 0; } ar->hContact = hContact; ar->dwUin = dwUin; if (!dwUin) strcpy(ar->szUid, szUid); ar->hash = (BYTE*)SAFE_MALLOC(hashlen); if (!ar->hash) { // alloc failed m_avatarsMutex->Leave(); delete ar; return 0; } memcpy(ar->hash, hash, hashlen); // copy the data ar->hashlen = hashlen; ar->szFile = null_strdup(file); // duplicate the string ar->pNext = m_avatarsQueue; m_avatarsQueue = ar; m_avatarsMutex->Leave(); debugLogA("Avatars: Request to get %s image added to queue.", strUID(dwUin, pszUid)); // make sure avatar connection is in progress requestAvatarConnection(); return -1; // we added to queue }
void avatars_server_connection::checkRequestQueue() { #ifdef _DEBUG ppro->debugLogA("Checking request queue..."); #endif ppro->m_avatarsMutex->Enter(); while (ppro->m_avatarsQueue && runCount < 3) // pick up an request and send it - happens immediatelly after login { // do not fill queue to top, leave one place free avatars_request *pRequest = ppro->m_avatarsQueue; { // rate management icq_lock l(m_ratesMutex); WORD wGroup = m_rates->getGroupFromSNAC(ICQ_AVATAR_FAMILY, (WORD)(pRequest->type == ART_UPLOAD ? ICQ_AVATAR_GET_REQUEST : ICQ_AVATAR_UPLOAD_REQUEST)); if (m_rates->getNextRateLevel(wGroup) < m_rates->getLimitLevel(wGroup, RML_ALERT)) { // we are over rate, leave queue and wait #ifdef _DEBUG ppro->debugLogA("Rates: Leaving avatar queue processing"); #endif break; } } if (pRequest->type == ART_BLOCK) { // block contact processing avatars_request **ppRequest = &ppro->m_avatarsQueue; while (pRequest) { if (GetTickCount() > pRequest->timeOut) { // expired contact block, remove *ppRequest = pRequest->pNext; delete pRequest; } else // it is not time, move to next request ppRequest = &pRequest->pNext; pRequest = *ppRequest; } // end queue processing (only block requests follows) break; } else ppro->m_avatarsQueue = pRequest->pNext; ppro->m_avatarsMutex->Leave(); #ifdef _DEBUG ppro->debugLogA("Picked up the %s request from queue.", strUID(pRequest->dwUin, pRequest->szUid)); #endif switch (pRequest->type) { case ART_GET: // get avatar sendGetAvatarRequest(pRequest->hContact, pRequest->dwUin, pRequest->szUid, pRequest->hash, pRequest->hashlen, pRequest->szFile); break; case ART_UPLOAD: // set avatar sendUploadAvatarRequest(pRequest->hContact, pRequest->wRef, pRequest->pData, pRequest->cbData); break; } delete pRequest; ppro->m_avatarsMutex->Enter(); } ppro->m_avatarsMutex->Leave(); }
char* CIcqProto::buildUinList(int subtype, size_t wMaxLen, MCONTACT *hContactResume) { MCONTACT hContact; WORD wCurrentLen = 0; int add; char *szList = (char*)SAFE_MALLOC(CallService(MS_DB_CONTACT_GETCOUNT, 0, 0) * UINMAXLEN); char szLen[2]; szLen[1] = '\0'; if (*hContactResume) hContact = *hContactResume; else hContact = db_find_first(m_szModuleName); while (hContact != NULL) { DWORD dwUIN; uid_str szUID; if (!getContactUid(hContact, &dwUIN, &szUID)) { szLen[0] = (char)mir_strlen(strUID(dwUIN, szUID)); switch (subtype) { case BUL_VISIBLE: add = ID_STATUS_ONLINE == getWord(hContact, "ApparentMode", 0); break; case BUL_INVISIBLE: add = ID_STATUS_OFFLINE == getWord(hContact, "ApparentMode", 0); break; case BUL_TEMPVISIBLE: add = getByte(hContact, "TemporaryVisible", 0); // clear temporary flag // Here we assume that all temporary contacts will be in one packet setByte(hContact, "TemporaryVisible", 0); break; default: add = 1; // If we are in SS mode, we only add those contacts that are // not in our SS list, or are awaiting authorization, to our // client side list if (m_bSsiEnabled && getWord(hContact, DBSETTING_SERVLIST_ID, 0) && !getByte(hContact, "Auth", 0)) add = 0; // Never add hidden contacts to CS list if (db_get_b(hContact, "CList", "Hidden", 0)) add = 0; break; } if (add) { wCurrentLen += szLen[0] + 1; if (wCurrentLen > wMaxLen) { *hContactResume = hContact; return szList; } mir_strcat(szList, szLen); mir_strcat(szList, szUID); } } hContact = db_find_next(hContact, m_szModuleName); } *hContactResume = NULL; return szList; }
void CIcqProto::handleLoginChannel(BYTE *buf, WORD datalen, serverthread_info *info) { icq_packet packet; #ifdef _DEBUG NetLog_Server("Received SRV_HELLO from %s", info->isLoginServer ? "login server" : "communication server"); #endif // isLoginServer is "1" if we just received SRV_HELLO if (info->isLoginServer) { if (m_bSecureLogin) { char szUin[UINMAXLEN]; WORD wUinLen; #ifdef _DEBUG NetLog_Server("Sending %s to %s", "CLI_HELLO", "login server"); #endif packet.wLen = 12; write_flap(&packet, ICQ_LOGIN_CHAN); packDWord(&packet, 0x00000001); packTLVDWord(&packet, 0x8003, 0x00100000); // unknown sendServPacket(&packet); // greet login server wUinLen = strlennull(strUID(m_dwLocalUIN, szUin)); #ifdef _DEBUG NetLog_Server("Sending %s to %s", "ICQ_SIGNON_AUTH_REQUEST", "login server"); #endif serverPacketInit(&packet, (WORD)(14 + wUinLen)); packFNACHeader(&packet, ICQ_AUTHORIZATION_FAMILY, ICQ_SIGNON_AUTH_REQUEST, 0, 0); packTLV(&packet, 0x0001, wUinLen, (LPBYTE)szUin); sendServPacket(&packet); // request login digest } else { sendClientAuth((char*)info->szAuthKey, info->wAuthKeyLen, FALSE); #ifdef _DEBUG NetLog_Server("Sent CLI_IDENT to %s", "login server"); #endif } info->isLoginServer = 0; if (info->cookieDataLen) { SAFE_FREE((void**)&info->cookieData); info->cookieDataLen = 0; } } else { if (info->cookieDataLen) { wLocalSequence = generate_flap_sequence(); serverCookieInit(&packet, info->cookieData, (WORD)info->cookieDataLen); sendServPacket(&packet); #ifdef _DEBUG NetLog_Server("Sent CLI_IDENT to %s", "communication server"); #endif SAFE_FREE((void**)&info->cookieData); info->cookieDataLen = 0; } else { // We need a cookie to identify us to the communication server NetLog_Server("Error: Connected to %s without a cookie!", "communication server"); } } }