void CIcqProto::handleServUINSettings(int nPort, serverthread_info *info) { setUserInfo(); /* SNAC 3,4: Tell server who's on our list (deprecated) */ /* SNAC 3,15: Try to add unauthorised contacts to temporary list */ sendEntireListServ(ICQ_BUDDY_FAMILY, ICQ_USER_ADDTOTEMPLIST, BUL_ALLCONTACTS); if (m_iDesiredStatus == ID_STATUS_INVISIBLE) { /* Tell server who's on our visible list (deprecated) */ if (!m_bSsiEnabled) sendEntireListServ(ICQ_BOS_FAMILY, ICQ_CLI_ADDVISIBLE, BUL_VISIBLE); else updateServVisibilityCode(3); } if (m_iDesiredStatus != ID_STATUS_INVISIBLE) { /* Tell server who's on our invisible list (deprecated) */ if (!m_bSsiEnabled) sendEntireListServ(ICQ_BOS_FAMILY, ICQ_CLI_ADDINVISIBLE, BUL_INVISIBLE); else updateServVisibilityCode(4); } // SNAC 1,1E: Set status icq_packet packet; { DWORD dwDirectCookie = rand() ^ (rand() << 16); // Get status WORD wStatus = MirandaStatusToIcq(m_iDesiredStatus); // Get status note & mood char *szStatusNote = PrepareStatusNote(m_iDesiredStatus); BYTE bXStatus = getContactXStatus(NULL); char szMoodData[32]; // prepare mood id if (m_bMoodsEnabled && bXStatus && moodXStatus[bXStatus - 1] != -1) mir_snprintf(szMoodData, SIZEOF(szMoodData), "icqmood%d", moodXStatus[bXStatus - 1]); else szMoodData[0] = '\0'; //! Tricky code, this ensures that the status note will be saved to the directory SetStatusNote(szStatusNote, m_bGatewayMode ? 5000 : 2500, TRUE); size_t wStatusNoteLen = mir_strlen(szStatusNote); size_t wStatusMoodLen = mir_strlen(szMoodData); size_t wSessionDataLen = (wStatusNoteLen ? wStatusNoteLen + 4 : 0) + 4 + wStatusMoodLen + 4; serverPacketInit(&packet, 71 + (wSessionDataLen ? wSessionDataLen + 4 : 0)); packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_SET_STATUS); packDWord(&packet, 0x00060004); // TLV 6: Status mode and security flags packWord(&packet, GetMyStatusFlags()); // Status flags packWord(&packet, wStatus); // Status packTLVWord(&packet, 0x0008, 0x0A06); // TLV 8: Independent Status Messages packDWord(&packet, 0x000c0025); // TLV C: Direct connection info packDWord(&packet, getDword("RealIP", 0)); packDWord(&packet, nPort); packByte(&packet, DC_TYPE); // TCP/FLAG firewall settings packWord(&packet, ICQ_VERSION); packDWord(&packet, dwDirectCookie); // DC Cookie packDWord(&packet, WEBFRONTPORT); // Web front port packDWord(&packet, CLIENTFEATURES); // Client features packDWord(&packet, 0x7fffffff); // Abused timestamp packDWord(&packet, ICQ_PLUG_VERSION); // Abused timestamp if (ServiceExists("SecureIM/IsContactSecured")) packDWord(&packet, 0x5AFEC0DE); // SecureIM Abuse else packDWord(&packet, 0x00000000); // Timestamp packWord(&packet, 0x0000); // Unknown packTLVWord(&packet, 0x001F, 0x0000); if (wSessionDataLen) { // Pack session data packWord(&packet, 0x1D); // TLV 1D packWord(&packet, WORD(wSessionDataLen)); // TLV length packWord(&packet, 0x02); // Item Type if (wStatusNoteLen) { packWord(&packet, 0x400 | WORD(wStatusNoteLen + 4)); // Flags + Item Length packWord(&packet, WORD(wStatusNoteLen)); // Text Length packBuffer(&packet, (LPBYTE)szStatusNote, wStatusNoteLen); packWord(&packet, 0); // Encoding not specified (utf-8 is default) } else packWord(&packet, 0); // Flags + Item Length packWord(&packet, 0x0E); // Item Type packWord(&packet, WORD(wStatusMoodLen)); // Flags + Item Length if (wStatusMoodLen) packBuffer(&packet, (LPBYTE)szMoodData, wStatusMoodLen); // Mood // Save current status note & mood db_set_utf(NULL, m_szModuleName, DBSETTING_STATUS_NOTE, szStatusNote); setString(DBSETTING_STATUS_MOOD, szMoodData); } // Release memory SAFE_FREE(&szStatusNote); sendServPacket(&packet); } /* SNAC 1,11 */ serverPacketInit(&packet, 14); packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_SET_IDLE); packDWord(&packet, 0x00000000); sendServPacket(&packet); m_bIdleAllow = 0; // Change status SetCurrentStatus(m_iDesiredStatus); // Finish Login sequence serverPacketInit(&packet, 98); packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_READY); packDWord(&packet, 0x00220001); // imitate ICQ 6 behaviour packDWord(&packet, 0x0110164f); packDWord(&packet, 0x00010004); packDWord(&packet, 0x0110164f); packDWord(&packet, 0x00130004); packDWord(&packet, 0x0110164f); packDWord(&packet, 0x00020001); packDWord(&packet, 0x0110164f); packDWord(&packet, 0x00030001); packDWord(&packet, 0x0110164f); packDWord(&packet, 0x00150001); packDWord(&packet, 0x0110164f); packDWord(&packet, 0x00040001); packDWord(&packet, 0x0110164f); packDWord(&packet, 0x00060001); packDWord(&packet, 0x0110164f); packDWord(&packet, 0x00090001); packDWord(&packet, 0x0110164f); packDWord(&packet, 0x000A0001); packDWord(&packet, 0x0110164f); packDWord(&packet, 0x000B0001); packDWord(&packet, 0x0110164f); sendServPacket(&packet); debugLogA(" *** Yeehah, login sequence complete"); // login sequence is complete enter logged-in mode info->bLoggedIn = true; m_bConnectionLost = false; // enable auto info-update routine icq_EnableUserLookup(true); if (!info->isMigrating) { // Get Offline Messages Reqeust cookie_offline_messages *ack = (cookie_offline_messages*)SAFE_MALLOC(sizeof(cookie_offline_messages)); if (ack) { DWORD dwCookie = AllocateCookie(CKT_OFFLINEMESSAGE, ICQ_MSG_CLI_REQ_OFFLINE, 0, ack); serverPacketInit(&packet, 10); packFNACHeader(&packet, ICQ_MSG_FAMILY, ICQ_MSG_CLI_REQ_OFFLINE, 0, dwCookie); sendServPacket(&packet); } else icq_LogMessage(LOG_WARNING, LPGEN("Failed to request offline messages. They may be received next time you log in.")); // Update our information from the server sendOwnerInfoRequest(); // Request info updates on all contacts icq_RescanInfoUpdate(); // Start sending Keep-Alive packets StartKeepAlive(info); if (m_bAvatarsEnabled) { // Send SNAC 1,4 - request avatar family 0x10 connection icq_requestnewfamily(ICQ_AVATAR_FAMILY, &CIcqProto::StartAvatarThread); m_avatarsConnectionPending = TRUE; debugLogA("Requesting Avatar family entry point."); } // Set last xstatus updateServerCustomStatus(TRUE); } info->isMigrating = false; if (m_bAimEnabled) { char **szAwayMsg = NULL; icq_lock l(m_modeMsgsMutex); szAwayMsg = MirandaStatusToAwayMsg(m_iStatus); if (szAwayMsg) icq_sendSetAimAwayMsgServ(*szAwayMsg); } }
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)); }