void rates::initAckPacket(icq_packet *pPacket) { serverPacketInit(pPacket, 10 + nGroups * (int)sizeof(WORD)); packFNACHeader(pPacket, ICQ_SERVICE_FAMILY, ICQ_CLIENT_RATE_ACK); for (WORD wGroup = 1; wGroup <= nGroups; wGroup++) packWord(pPacket, wGroup); }
DWORD avatars_server_connection::sendUploadAvatarRequest(MCONTACT hContact, WORD wRef, const BYTE *data, size_t datalen) { cookie_avatar *ack = (cookie_avatar*)SAFE_MALLOC(sizeof(cookie_avatar)); if (!ack) return 0; // Failure: out of memory ack->hContact = hContact; DWORD dwCookie = ppro->AllocateCookie(CKT_AVATAR, ICQ_AVATAR_UPLOAD_REQUEST, 0, ack); icq_packet packet; serverPacketInit(&packet, 14 + datalen); packFNACHeader(&packet, ICQ_AVATAR_FAMILY, ICQ_AVATAR_UPLOAD_REQUEST, 0, dwCookie); packWord(&packet, wRef); // unknown, probably reference packWord(&packet, (WORD)datalen); packBuffer(&packet, data, datalen); if (sendServerPacket(&packet)) { ppro->debugLogA("Upload image packet sent."); return dwCookie; } ppro->ReleaseCookie(dwCookie); // failed to send, free resources return 0; }
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::sendEntireListServ(WORD wFamily, WORD wSubtype, int listType) { MCONTACT hResumeContact = NULL; do { // server doesn't seem to be able to cope with packets larger than 8k // send only about 100contacts per packet char *szList = buildUinList(listType, 0x3E8, &hResumeContact); size_t nListLen = mir_strlen(szList); if (nListLen) { icq_packet packet; serverPacketInit(&packet, nListLen + 10); packFNACHeader(&packet, wFamily, wSubtype); packBuffer(&packet, (LPBYTE)szList, nListLen); sendServPacket(&packet); } SAFE_FREE((void**)&szList); } while (hResumeContact); }
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 }
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, "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; mir_cslock l(m_modeMsgsMutex); szAwayMsg = MirandaStatusToAwayMsg(m_iStatus); if (szAwayMsg) icq_sendSetAimAwayMsgServ(*szAwayMsg); } }
// CLI_SETUSERINFO void CIcqProto::setUserInfo() { icq_packet packet; size_t wAdditionalData = 0; BYTE bXStatus = getContactXStatus(NULL); if (m_bAimEnabled) wAdditionalData += 16; #ifdef DBG_CAPMTN wAdditionalData += 16; #endif wAdditionalData += 16; // unicode #ifdef DBG_NEWCAPS wAdditionalData += 16; #endif #ifdef DBG_CAPXTRAZ wAdditionalData += 16; #endif #ifdef DBG_OSCARFT wAdditionalData += 16; #endif if (m_bAvatarsEnabled) wAdditionalData += 16; if (m_bXStatusEnabled && bXStatus != 0) wAdditionalData += 16; #ifdef DBG_CAPHTML wAdditionalData += 16; #endif #ifdef DBG_AIMCONTACTSEND wAdditionalData += 16; #endif wAdditionalData += CustomCapList.getCount() * 16; //MIM/PackName bool bHasPackName = false; DBVARIANT dbv; if (!db_get_s(NULL, "ICQCaps", "PackName", &dbv)) { //MIM/PackName bHasPackName = true; wAdditionalData += 16; } serverPacketInit(&packet, 62 + wAdditionalData); packFNACHeader(&packet, ICQ_LOCATION_FAMILY, ICQ_LOCATION_SET_USER_INFO); /* TLV(5): capability data */ packWord(&packet, 0x0005); packWord(&packet, WORD(48 + wAdditionalData)); #ifdef DBG_CAPMTN packDWord(&packet, 0x563FC809); // CAP_TYPING packDWord(&packet, 0x0B6F41BD); packDWord(&packet, 0x9F794226); packDWord(&packet, 0x09DFA2F3); #endif packShortCapability(&packet, 0x1349); // AIM_CAPS_ICQSERVERRELAY // Broadcasts the capability to receive UTF8 encoded messages packShortCapability(&packet, 0x134E); // CAP_UTF8MSGS #ifdef DBG_NEWCAPS // Tells server we understand to new format of caps packShortCapability(&packet, 0x0000); // CAP_SHORTCAPS #endif #ifdef DBG_CAPXTRAZ packDWord(&packet, 0x1a093c6c); // CAP_XTRAZ packDWord(&packet, 0xd7fd4ec5); // Broadcasts the capability to handle packDWord(&packet, 0x9d51a647); // Xtraz packDWord(&packet, 0x4e34f5a0); #endif if (m_bAvatarsEnabled) packShortCapability(&packet, 0x134C); // CAP_DEVILS #ifdef DBG_OSCARFT // Broadcasts the capability to receive Oscar File Transfers packShortCapability(&packet, 0x1343); // CAP_AIM_FILE #endif // Tells the server we can speak to AIM if (m_bAimEnabled) packShortCapability(&packet, 0x134D); // CAP_AIM_COMPATIBLE #ifdef DBG_AIMCONTACTSEND packShortCapability(&packet, 0x134B); // CAP_SENDBUDDYLIST #endif if (m_bXStatusEnabled && bXStatus != 0) packBuffer(&packet, capXStatus[bXStatus - 1], BINARY_CAP_SIZE); packShortCapability(&packet, 0x1344); // CAP_ICQDIRECT #ifdef DBG_CAPHTML packShortCapability(&packet, 0x0002); // CAP_HTMLMSGS #endif packDWord(&packet, 0x4D697261); // Miranda Signature packDWord(&packet, 0x6E64614E); WORD v[4]; CallService(MS_SYSTEM_GETFILEVERSION, 0, (LPARAM)v); packWord(&packet, v[0]); packWord(&packet, v[1]); packWord(&packet, v[2]); packWord(&packet, v[3]); //MIM/PackName if (bHasPackName) { packBuffer(&packet, (BYTE*)dbv.pszVal, 0x10); db_free(&dbv); } if (CustomCapList.getCount()) for (int i = 0; i < CustomCapList.getCount(); i++) packBuffer(&packet, (PBYTE)CustomCapList[i].caps, 0x10); sendServPacket(&packet); }
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::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"); } } }