void CIcqProto::icq_LogUsingErrorCode(int level, DWORD dwError, const char *szMsg) { char szBuf[1024]; char str[1024]; char str2[64]; char szErrorMsg[512]; char *pszErrorMsg = NULL; int bNeedFree = FALSE; switch (dwError) { case ERROR_TIMEOUT: case WSAETIMEDOUT: pszErrorMsg = LPGEN("The server did not respond to the connection attempt within a reasonable time, it may be temporarily down. Try again later."); break; case ERROR_GEN_FAILURE: pszErrorMsg = LPGEN("The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection."); break; case WSAEHOSTUNREACH: case WSAENETUNREACH: pszErrorMsg = LPGEN("Miranda was unable to resolve the name of a server to its numeric address. This is most likely caused by a catastrophic loss of your network connection (for example, your modem has disconnected), but if you are behind a proxy, you may need to use the 'Resolve hostnames through proxy' option in M->Options->Network."); break; case WSAEHOSTDOWN: case WSAENETDOWN: case WSAECONNREFUSED: pszErrorMsg = LPGEN("Miranda was unable to make a connection with a server. It is likely that the server is down, in which case you should wait for a while and try again later."); break; case ERROR_ACCESS_DENIED: pszErrorMsg = LPGEN("Your proxy rejected the user name and password that you provided. Please check them in M->Options->Network."); break; case WSAHOST_NOT_FOUND: case WSANO_DATA: pszErrorMsg = LPGEN("The server to which you are trying to connect does not exist. Check your spelling in M->Options->Network->ICQ."); break; default: TCHAR err[512]; if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, err, _countof(err), NULL)) { pszErrorMsg = make_utf8_string(err); bNeedFree = TRUE; } break; } mir_snprintf(szBuf, _countof(szBuf), "%s%s%s (%s %d)", szMsg ? ICQTranslateUtfStatic(szMsg, str, 1024) : "", szMsg ? "\r\n\r\n" : "", ICQTranslateUtfStatic(pszErrorMsg, szErrorMsg, 512), ICQTranslateUtfStatic(LPGEN("error"), str2, 64), dwError); if (bNeedFree) SAFE_FREE(&pszErrorMsg); icq_LogMessage(level, szBuf); }
void CIcqProto::icq_LogFatalParam(const char *szMsg, WORD wError) { char str[MAX_PATH]; char buf[MAX_PATH]; mir_snprintf(buf, ICQTranslateUtfStatic(szMsg, str, _countof(str)), wError); icq_LogMessage(LOG_FATAL, buf); }
void CIcqProto::handleLoginReply(BYTE *buf, WORD datalen, serverthread_info *info) { oscar_tlv_chain *chain = NULL; icq_sendCloseConnection(); // imitate icq5 behaviour if (!(chain = readIntoTLVChain(&buf, datalen, 0))) { NetLog_Server("Error: Missing chain on close channel"); NetLib_CloseConnection(&hServerConn, TRUE); return; // Invalid data } // TLV 8 errors (signon errors?) WORD wError = chain->getWord(0x08, 1); if (wError) { handleSignonError(wError); // we return only if the server did not gave us cookie (possible to connect with soft error) if (!chain->getLength(0x06, 1)) { disposeChain(&chain); SetCurrentStatus(ID_STATUS_OFFLINE); icq_serverDisconnect(FALSE); return; // Failure } } // We are in the login phase and no errors were reported. // Extract communication server info. 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); // We dont need this anymore disposeChain(&chain); if (!info->newServer || !info->cookieData) { icq_LogMessage(LOG_FATAL, LPGEN("You could not sign on because the server returned invalid data. Try again.")); SAFE_FREE(&info->newServer); SAFE_FREE((void**)&info->cookieData); info->cookieDataLen = 0; SetCurrentStatus(ID_STATUS_OFFLINE); NetLib_CloseConnection(&hServerConn, TRUE); return; // Failure } NetLog_Server("Authenticated."); info->newServerReady = 1; return; }
void CIcqProto::StartAvatarThread(HANDLE hConn, char *cookie, size_t cookieLen) // called from event { if (!hConn) { mir_cslock l(m_avatarsMutex); // place avatars lock if (m_avatarsConnection && m_avatarsConnection->isPending()) { debugLogA("Avatar, Multiple start thread attempt, ignored."); SAFE_FREE((void**)&cookie); return; } debugLogA("Avatars: Connection failed"); m_avatarsConnectionPending = FALSE; // check if any upload request are waiting in the queue int bYet = 0; for (int i = 0; i < m_arAvatars.getCount();) { avatars_request *ar = m_arAvatars[i]; if (ar->type == ART_UPLOAD) { // we found it, return error if (!bYet) { icq_LogMessage(LOG_WARNING, LPGEN("Error uploading avatar to server, server temporarily unavailable.")); bYet = 1; } // remove upload request from queue m_arAvatars.remove(i); delete ar; } else i++; } SAFE_FREE((void**)&cookie); return; } mir_cslock l(m_avatarsMutex); if (m_avatarsConnection && m_avatarsConnection->isPending()) { debugLogA("Avatar, Multiple start thread attempt, ignored."); NetLib_CloseConnection(&hConn, false); SAFE_FREE((void**)&cookie); return; } if (m_avatarsConnection) m_avatarsConnection->closeConnection(); m_avatarsConnection = new avatars_server_connection(this, hConn, cookie, cookieLen); // the old connection should not be used anymore // connection object created, remove the connection request pending flag m_avatarsConnectionPending = false; }
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); }
void CIcqProto::handleMigration(serverthread_info *info) { // Check the data that was saved when the migration was announced debugLogA("Migrating to %s", info->newServer); if (!info->newServer || !info->cookieData) { icq_LogMessage(LOG_FATAL, LPGEN("You have been disconnected from the ICQ network because the current server shut down.")); SAFE_FREE(&info->newServer); SAFE_FREE((void**)&info->cookieData); info->isNewServerReady = info->isMigrating = false; } }
void CIcqProto::icq_sendFileResume(filetransfer *ft, int action, const char *szFilename) { if (ft->hConnection == NULL) return; directconnect *dc = FindFileTransferDC(ft); if (!dc) return; // something is broken... int openFlags; switch (action) { case FILERESUME_RESUME: openFlags = _O_BINARY | _O_WRONLY; break; case FILERESUME_OVERWRITE: openFlags = _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY; ft->dwFileBytesDone = 0; break; case FILERESUME_SKIP: openFlags = _O_BINARY | _O_WRONLY; ft->dwFileBytesDone = ft->dwThisFileSize; break; case FILERESUME_RENAME: openFlags = _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY; SAFE_FREE(&ft->szThisFile); ft->szThisFile = null_strdup(szFilename); ft->dwFileBytesDone = 0; break; } ft->fileId = OpenFileUtf(ft->szThisFile, openFlags, _S_IREAD | _S_IWRITE); if (ft->fileId == -1) { icq_LogMessage(LOG_ERROR, LPGEN("Your file receive has been aborted because Miranda could not open the destination file in order to write to it. You may be trying to save to a read-only folder.")); NetLib_CloseConnection(&ft->hConnection, FALSE); return; } if (action == FILERESUME_RESUME) ft->dwFileBytesDone = _lseek(ft->fileId, 0, SEEK_END); else _lseek(ft->fileId, ft->dwFileBytesDone, SEEK_SET); ft->dwBytesDone += ft->dwFileBytesDone; file_sendResume(this, dc); BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0); }
void CIcqProto::handleRuntimeError(WORD wError) { switch (wError) { case 0x01: ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_OTHERLOCATION); icq_LogMessage(LOG_FATAL, LPGEN("You have been disconnected from the ICQ network because you logged on from another location using the same ICQ number.")); break; default: icq_LogFatalParam(LPGEN("Unknown runtime error: 0x%02x"), wError); break; } }
void CIcqProto::handleSignonError(WORD wError) { switch (wError) { case 0x01: // Unregistered uin case 0x04: // Incorrect uin or password case 0x05: // Mismatch uin or password case 0x06: // Internal Client error (bad input to authorizer) case 0x07: // Invalid account BroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD); ZeroMemory(m_szPassword, sizeof(m_szPassword)); icq_LogFatalParam(LPGEN("Connection failed.\nYour ICQ number or password was rejected (%d)."), wError); break; case 0x02: // Service temporarily unavailable case 0x0D: // Bad database status case 0x10: // Service temporarily offline case 0x12: // Database send error case 0x14: // Reservation map error case 0x15: // Reservation link error case 0x1A: // Reservation timeout BroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NOSERVER); icq_LogFatalParam(LPGEN("Connection failed.\nThe server is temporarily unavailable (%d)."), wError); break; case 0x16: // The users num connected from this IP has reached the maximum case 0x17: // The users num connected from this IP has reached the maximum (reserved) icq_LogFatalParam(LPGEN("Connection failed.\nServer has too many connections from your IP (%d)."), wError); break; case 0x18: // Reservation rate limit exceeded case 0x1D: // Rate limit exceeded BroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NOSERVER); icq_LogFatalParam(LPGEN("Connection failed.\nYou have connected too quickly,\nplease wait and retry 10 to 20 minutes later (%d)."), wError); break; case 0x1B: // You are using an older version of ICQ. Upgrade required BroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPROTOCOL); icq_LogMessage(LOG_FATAL, LPGEN("Connection failed.\nThe server did not accept this client version.")); break; case 0x1C: // You are using an older version of ICQ. Upgrade recommended icq_LogMessage(LOG_WARNING, LPGEN("The server sent warning, this version is getting old.\nTry to look for a new one.")); break; case 0x1E: // Can't register on the ICQ network icq_LogMessage(LOG_FATAL, LPGEN("Connection failed.\nYou were rejected by the server for an unknown reason.\nThis can happen if the UIN is already connected.")); break; case 0x0C: // Invalid database fields, MD5 login not supported icq_LogMessage(LOG_FATAL, LPGEN("Connection failed.\nSecure (MD5) login is not supported on this account.")); break; case 0: // No error break; case 0x08: // Deleted account case 0x09: // Expired account case 0x0A: // No access to database case 0x0B: // No access to resolver case 0x0E: // Bad resolver status case 0x0F: // Internal error case 0x11: // Suspended account case 0x13: // Database link error case 0x19: // User too heavily warned case 0x1F: // Token server timeout case 0x20: // Invalid SecureID number case 0x21: // MC error case 0x22: // Age restriction case 0x23: // RequireRevalidation case 0x24: // Link rule rejected case 0x25: // Missing information or bad SNAC format case 0x26: // Link broken case 0x27: // Invalid client IP case 0x28: // Partner rejected case 0x29: // SecureID missing case 0x2A: // Blocked account | Bump user default: icq_LogFatalParam(LPGEN("Connection failed.\nUnknown error during sign on: 0x%02x"), wError); break; } }
void CIcqProto::StartAvatarThread(HANDLE hConn, char *cookie, WORD cookieLen) // called from event { if (!hConn) { icq_lock l(m_avatarsMutex); // place avatars lock if (m_avatarsConnection && m_avatarsConnection->isPending()) { debugLogA("Avatar, Multiple start thread attempt, ignored."); SAFE_FREE((void**)&cookie); return; } debugLogA("Avatars: Connection failed"); m_avatarsConnectionPending = FALSE; { // check if any upload request are waiting in the queue avatars_request *ar = m_avatarsQueue; int bYet = 0; while (ar) { if (ar->type == ART_UPLOAD) { // we found it, return error if (!bYet) { icq_LogMessage(LOG_WARNING, LPGEN("Error uploading avatar to server, server temporarily unavailable.")); bYet = 1; } // remove upload request from queue ar = ReleaseAvatarRequestInQueue(ar); continue; } ar = ar->pNext; } } SAFE_FREE((void**)&cookie); return; } icq_lock l(m_avatarsMutex); if (m_avatarsConnection && m_avatarsConnection->isPending()) { debugLogA("Avatar, Multiple start thread attempt, ignored."); NetLib_CloseConnection(&hConn, FALSE); SAFE_FREE((void**)&cookie); return; } else if (m_avatarsConnection) m_avatarsConnection->closeConnection(); m_avatarsConnection = new avatars_server_connection(this, hConn, cookie, cookieLen); // the old connection should not be used anymore // connection object created, remove the connection request pending flag m_avatarsConnectionPending = FALSE; }
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); } }
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::handleFileTransferPacket(directconnect* dc, PBYTE buf, WORD wLen) { if (wLen < 1) return; NetLog_Direct("Handling file packet"); switch (buf[0]) { case PEER_FILE_INIT: /* first packet of a file transfer */ if (dc->initialised) return; if (wLen < 19) return; buf += 5; /* id, and unknown 0 */ dc->type = DIRECTCONN_FILE; { DWORD dwFileCount; DWORD dwTotalSize; DWORD dwTransferSpeed; WORD wNickLength; int bAdded; unpackLEDWord(&buf, &dwFileCount); unpackLEDWord(&buf, &dwTotalSize); unpackLEDWord(&buf, &dwTransferSpeed); unpackLEWord(&buf, &wNickLength); dc->ft = FindExpectedFileRecv(dc->dwRemoteUin, dwTotalSize); if (dc->ft == NULL) { NetLog_Direct("Unexpected file receive"); CloseDirectConnection(dc); return; } dc->ft->dwFileCount = dwFileCount; dc->ft->dwTransferSpeed = dwTransferSpeed; dc->ft->hContact = HContactFromUIN(dc->ft->dwUin, &bAdded); dc->ft->dwBytesDone = 0; dc->ft->iCurrentFile = -1; dc->ft->fileId = -1; dc->ft->hConnection = dc->hConnection; dc->ft->dwLastNotify = GetTickCount(); dc->initialised = 1; file_sendTransferSpeed(this, dc); file_sendNick(this, dc); } BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, dc->ft, 0); break; case PEER_FILE_INIT_ACK: if (wLen < 8) return; buf++; unpackLEDWord(&buf, &dc->ft->dwTransferSpeed); /* followed by nick */ file_sendNextFile(this, dc); break; case PEER_FILE_NEXTFILE: if (wLen < 20) return; buf++; /* id */ { char *szAnsi; WORD wThisFilenameLen, wSubdirLen; BYTE isDirectory; unpackByte(&buf, &isDirectory); unpackLEWord(&buf, &wThisFilenameLen); if (wLen < 19 + wThisFilenameLen) return; SAFE_FREE(&dc->ft->szThisFile); szAnsi = (char *)_malloca(wThisFilenameLen + 1); memcpy(szAnsi, buf, wThisFilenameLen); szAnsi[wThisFilenameLen] = '\0'; dc->ft->szThisFile = ansi_to_utf8(szAnsi); buf += wThisFilenameLen; unpackLEWord(&buf, &wSubdirLen); if (wLen < 18 + wThisFilenameLen + wSubdirLen) return; SAFE_FREE(&dc->ft->szThisSubdir); szAnsi = (char *)_malloca(wSubdirLen + 1); memcpy(szAnsi, buf, wSubdirLen); szAnsi[wSubdirLen] = '\0'; dc->ft->szThisSubdir = ansi_to_utf8(szAnsi); buf += wSubdirLen; unpackLEDWord(&buf, &dc->ft->dwThisFileSize); unpackLEDWord(&buf, &dc->ft->dwThisFileDate); unpackLEDWord(&buf, &dc->ft->dwTransferSpeed); /* no cheating with paths */ if (!IsValidRelativePath(dc->ft->szThisFile) || !IsValidRelativePath(dc->ft->szThisSubdir)) { NetLog_Direct("Invalid path information"); break; } char *szFullPath = (char*)SAFE_MALLOC(strlennull(dc->ft->szSavePath)+strlennull(dc->ft->szThisSubdir)+strlennull(dc->ft->szThisFile)+3); strcpy(szFullPath, dc->ft->szSavePath); NormalizeBackslash(szFullPath); strcat(szFullPath, dc->ft->szThisSubdir); NormalizeBackslash(szFullPath); // _chdir(szFullPath); // set current dir - not very useful strcat(szFullPath, dc->ft->szThisFile); // we joined the full path to dest file SAFE_FREE(&dc->ft->szThisFile); dc->ft->szThisFile = szFullPath; dc->ft->dwFileBytesDone = 0; dc->ft->iCurrentFile++; if (isDirectory) { MakeDirUtf(dc->ft->szThisFile); dc->ft->fileId = -1; } else { /* file resume */ PROTOFILETRANSFERSTATUS pfts = {0}; file_buildProtoFileTransferStatus(dc->ft, &pfts); if (BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, dc->ft, (LPARAM)&pfts)) break; /* UI supports resume: it will call PS_FILERESUME */ dc->ft->fileId = OpenFileUtf(dc->ft->szThisFile, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE); if (dc->ft->fileId == -1) { icq_LogMessage(LOG_ERROR, LPGEN("Your file receive has been aborted because Miranda could not open the destination file in order to write to it. You may be trying to save to a read-only folder.")); CloseDirectConnection(dc); dc->ft->hConnection = NULL; break; } } } file_sendResume(this, dc); BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, dc->ft, 0); break; case PEER_FILE_RESUME: if (dc->ft->fileId == -1 && !dc->ft->currentIsDir) return; if (wLen < 13) return; if (wLen < 17) NetLog_Direct("Warning: Received short PEER_FILE_RESUME"); buf++; { DWORD dwRestartFrom; unpackLEDWord(&buf, &dwRestartFrom); if (dwRestartFrom > dc->ft->dwThisFileSize) return; buf += 4; /* unknown. 0 */ unpackLEDWord(&buf, &dc->ft->dwTransferSpeed); buf += 4; /* unknown. 1 */ if (!dc->ft->currentIsDir) _lseek(dc->ft->fileId, dwRestartFrom, 0); dc->wantIdleTime = 1; dc->ft->dwBytesDone += dwRestartFrom; dc->ft->dwFileBytesDone += dwRestartFrom; } break; case PEER_FILE_SPEED: if (wLen < 5) return; buf++; unpackLEDWord(&buf, &dc->ft->dwTransferSpeed); dc->ft->dwLastNotify = GetTickCount(); break; case PEER_FILE_DATA: if (!dc->ft->currentIsDir) { if (dc->ft->fileId == -1) break; buf++; wLen--; _write(dc->ft->fileId, buf, wLen); } else wLen = 0; dc->ft->dwBytesDone += wLen; dc->ft->dwFileBytesDone += wLen; if (GetTickCount() > dc->ft->dwLastNotify + 500 || wLen < 2048) { PROTOFILETRANSFERSTATUS pfts; file_buildProtoFileTransferStatus(dc->ft, &pfts); BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_DATA, dc->ft, (LPARAM)&pfts); dc->ft->dwLastNotify = GetTickCount(); } if (wLen < 2048) { /* EOF */ if (!dc->ft->currentIsDir) _close(dc->ft->fileId); dc->ft->fileId = -1; if ((DWORD)dc->ft->iCurrentFile == dc->ft->dwFileCount - 1) { dc->type = DIRECTCONN_CLOSING; /* this guarantees that we won't accept any more data but that the sender is still free to closesocket() neatly */ BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dc->ft, 0); } } break; default: NetLog_Direct("Unknown file transfer packet ignored."); break; } }
void __cdecl CIcqProto::ServerThread(serverthread_start_info *infoParam) { serverthread_info info = {0}; info.isLoginServer = 1; info.wAuthKeyLen = infoParam->wPassLen; null_strcpy((char*)info.szAuthKey, infoParam->szPass, info.wAuthKeyLen); // store server port info.wServerPort = infoParam->nloc.wPort; srand(time(NULL)); ResetSettingsOnConnect(); // Connect to the login server NetLog_Server("Authenticating to server"); { NETLIBOPENCONNECTION nloc = infoParam->nloc; nloc.timeout = 6; if (m_bGatewayMode) nloc.flags |= NLOCF_HTTPGATEWAY; hServerConn = NetLib_OpenConnection(m_hServerNetlibUser, NULL, &nloc); SAFE_FREE((void**)&nloc.szHost); SAFE_FREE((void**)&infoParam); if (hServerConn && m_bSecureConnection) { if (!CallService(MS_NETLIB_STARTSSL, (WPARAM)hServerConn, 0)) { icq_LogMessage(LOG_ERROR, LPGEN("Unable to connect to ICQ login server, SSL could not be negotiated")); SetCurrentStatus(ID_STATUS_OFFLINE); NetLib_CloseConnection(&hServerConn, TRUE); return; } } } // Login error if (hServerConn == NULL) { DWORD dwError = GetLastError(); SetCurrentStatus(ID_STATUS_OFFLINE); icq_LogUsingErrorCode(LOG_ERROR, dwError, LPGEN("Unable to connect to ICQ login server")); return; } // Initialize direct connection ports { DWORD dwInternalIP; BYTE bConstInternalIP = getSettingByte(NULL, "ConstRealIP", 0); info.hDirectBoundPort = NetLib_BindPort(icq_newConnectionReceived, this, &wListenPort, &dwInternalIP); if (!info.hDirectBoundPort) { icq_LogUsingErrorCode(LOG_WARNING, GetLastError(), LPGEN("Miranda was unable to allocate a port to listen for direct peer-to-peer connections between clients. You will be able to use most of the ICQ network without problems but you may be unable to send or receive files.\n\nIf you have a firewall this may be blocking Miranda, in which case you should configure your firewall to leave some ports open and tell Miranda which ports to use in M->Options->ICQ->Network.")); wListenPort = 0; if (!bConstInternalIP) deleteSetting(NULL, "RealIP"); } else if (!bConstInternalIP) setSettingDword(NULL, "RealIP", dwInternalIP); } // Initialize rate limiting queues { icq_lock l(m_ratesMutex); m_ratesQueue_Request = new rates_queue(this, "request", RML_IDLE_30, RML_IDLE_50, 1); m_ratesQueue_Response = new rates_queue(this, "response", RML_IDLE_10, RML_IDLE_30, -1); } // This is the "infinite" loop that receives the packets from the ICQ server { int recvResult; NETLIBPACKETRECVER packetRecv = {0}; info.hPacketRecver = (HANDLE)CallService(MS_NETLIB_CREATEPACKETRECVER, (WPARAM)hServerConn, 0x2400); packetRecv.cbSize = sizeof(packetRecv); packetRecv.dwTimeout = INFINITE; while (serverThreadHandle) { if (info.bReinitRecver) { // we reconnected, reinit struct info.bReinitRecver = 0; ZeroMemory(&packetRecv, sizeof(packetRecv)); packetRecv.cbSize = sizeof(packetRecv); packetRecv.dwTimeout = INFINITE; } recvResult = CallService(MS_NETLIB_GETMOREPACKETS, (WPARAM)info.hPacketRecver, (LPARAM)&packetRecv); if (recvResult == 0) { NetLog_Server("Clean closure of server socket"); break; } if (recvResult == SOCKET_ERROR) { NetLog_Server("Abortive closure of server socket, error: %d", GetLastError()); break; } if (m_iDesiredStatus == ID_STATUS_OFFLINE) { // Disconnect requested, send disconnect packet icq_sendCloseConnection(); // disconnected upon request m_bConnectionLost = FALSE; SetCurrentStatus(ID_STATUS_OFFLINE); NetLog_Server("Logged off."); break; } // Deal with the packet packetRecv.bytesUsed = handleServerPackets(packetRecv.buffer, packetRecv.bytesAvailable, &info); } serverThreadHandle = NULL; // Time to shutdown NetLib_CloseConnection(&hServerConn, TRUE); // Close the packet receiver (connection may still be open) NetLib_SafeCloseHandle(&info.hPacketRecver); // Close DC port NetLib_SafeCloseHandle(&info.hDirectBoundPort); } // disable auto info-update thread icq_EnableUserLookup(FALSE); if (m_iStatus != ID_STATUS_OFFLINE && m_iDesiredStatus != ID_STATUS_OFFLINE) { if (!info.bLoggedIn) icq_LogMessage(LOG_FATAL, LPGEN("Connection failed.\nLogin sequence failed for unknown reason.\nTry again later.")); // set flag indicating we were kicked out m_bConnectionLost = TRUE; SetCurrentStatus(ID_STATUS_OFFLINE); } // signal keep-alive thread to stop StopKeepAlive(&info); // Close all open DC connections CloseContactDirectConns(NULL); // Close avatar connection if any StopAvatarThread(); // Offline all contacts HANDLE hContact = FindFirstContact(); while (hContact) { DWORD dwUIN; uid_str szUID; if (!getContactUid(hContact, &dwUIN, &szUID)) { if (getContactStatus(hContact) != ID_STATUS_OFFLINE) { char tmp = 0; setSettingWord(hContact, "Status", ID_STATUS_OFFLINE); handleXStatusCaps(dwUIN, szUID, hContact, (BYTE*)&tmp, 0, &tmp, 0); } } hContact = FindNextContact(hContact); } setSettingDword(NULL, "LogonTS", 0); // clear logon time servlistPendingFlushOperations(); // clear pending operations list { // release rates queues icq_lock l(m_ratesMutex); SAFE_DELETE((void_struct**)&m_ratesQueue_Request); SAFE_DELETE((void_struct**)&m_ratesQueue_Response); SAFE_DELETE((void_struct**)&m_rates); } FlushServerIDs(); // clear server IDs list NetLog_Server("%s thread ended.", "Server"); }