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::handleCloseChannel(BYTE *buf, WORD datalen, serverthread_info *info)
{
    oscar_tlv_chain *chain = NULL;

    // Parse server reply, prepare reconnection
    if (!info->bLoggedIn && datalen && !info->newServerReady)
        handleLoginReply(buf, datalen, info);

    if (info->isMigrating)
        handleMigration(info);

    if ((!info->bLoggedIn || info->isMigrating) && info->newServerReady)
    {
        if (!connectNewServer(info))
        {   // Connecting failed
            if (info->isMigrating)
                icq_LogUsingErrorCode(LOG_ERROR, GetLastError(), LPGEN("Unable to connect to migrated ICQ communication server"));
            else
                icq_LogUsingErrorCode(LOG_ERROR, GetLastError(), LPGEN("Unable to connect to ICQ communication server"));

            SetCurrentStatus(ID_STATUS_OFFLINE);

            info->isMigrating = 0;
        }
        info->newServerReady = 0;

        return;
    }

    if (chain = readIntoTLVChain(&buf, datalen, 0))
    {
        // TLV 9 errors (runtime errors?)
        WORD wError = chain->getWord(0x09, 1);
        if (wError)
        {
            SetCurrentStatus(ID_STATUS_OFFLINE);

            handleRuntimeError(wError);
        }

        disposeChain(&chain);
    }
    // Server closed connection on error, or sign off
    NetLib_CloseConnection(&hServerConn, TRUE);
}
Exemple #3
0
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);
}
Exemple #4
0
void CSListView::OnSelchangeWorktab(NMHDR* pNMHDR, LRESULT* pResult) 
{
	int nCur = m_wndWorkTab.GetCurtab();
	CRect	rect = m_wndWorkTab.GetTabRect( nCur );
	CMenu	menu;
	switch( nCur )
	{
	case SL_WORKTAB_CLASS:
		/*
		if( menu.LoadMenu( IDR_MENU_SLISTBARCLASS ) )
		{
			CMenu* pPopupMenu = menu.GetSubMenu(0);
			if( pPopupMenu )
				pPopupMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_LEFTBUTTON , rect.left+3, rect.top+6, AfxGetMainFrame() );
		}*/
		break;
	case SL_WORKTAB_A:
		SetCurrentStatus( CStockContainer::typeA, NULL, -1 );
		break;
	case SL_WORKTAB_SZNB:
		SetCurrentStatus( CStockContainer::typeClassSznb, NULL, -1 );
		break;
	case SL_WORKTAB_SHB:
		SetCurrentStatus( CStockContainer::typeClassShab, NULL, -1 );
		break;
	case SL_WORKTAB_BOND:
		SetCurrentStatus( CStockContainer::typeBond, NULL, -1 );
		break;
	case SL_WORKTAB_FUND:
		SetCurrentStatus( CStockContainer::typeFund, NULL, -1 );
		break;
	case SL_WORKTAB_SELF:
		{
			CString	strSelf;
			strSelf.LoadString( IDS_DOMAIN_SELF );
			SetCurrentStatus( CStockContainer::typeGroup, strSelf, -1 );
		}
		break;
	case SL_WORKTAB_GROUP:
		if( menu.LoadMenu( IDR_MENU_SLISTBARGROUP ) )
		{
			CMenu* pPopupMenu = menu.GetSubMenu(0);
			if( pPopupMenu )
				pPopupMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_LEFTBUTTON , rect.left+3, rect.top+6, AfxGetMainFrame() );
		}
		break;
	case SL_WORKTAB_DOMAIN:
		if( menu.LoadMenu( IDR_MENU_SLISTBARDOMAIN ) )
		{
			CMenu* pPopupMenu = menu.GetSubMenu(0);
			if( pPopupMenu )
				pPopupMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_LEFTBUTTON , rect.left+3, rect.top+6, AfxGetMainFrame() );
		}
		break;
	default:;
	}
	
	*pResult = 0;
}
Exemple #5
0
void CIcqProto::sendServPacket(icq_packet *pPacket)
{
	// make sure to have the connection handle
	connectionHandleMutex->Enter();

	if (hServerConn)
	{
		int nSendResult;

		// This critsec makes sure that the sequence order doesn't get screwed up
		localSeqMutex->Enter();

		// :IMPORTANT:
		// The FLAP sequence must be a WORD. When it reaches 0xFFFF it should wrap to
		// 0x0000, otherwise we'll get kicked by server.
		wLocalSequence++;

		// Pack sequence number
		pPacket->pData[2] = ((wLocalSequence & 0xff00) >> 8);
		pPacket->pData[3] = (wLocalSequence & 0x00ff);

		nSendResult = Netlib_Send(hServerConn, (const char *)pPacket->pData, pPacket->wLen, 0);

		localSeqMutex->Leave();
		connectionHandleMutex->Leave();

		// Send error
		if (nSendResult == SOCKET_ERROR)
		{
			icq_LogUsingErrorCode(LOG_ERROR, GetLastError(), LPGEN("Your connection with the ICQ server was abortively closed"));
			icq_serverDisconnect(FALSE);

			if (m_iStatus != ID_STATUS_OFFLINE)
			{
				SetCurrentStatus(ID_STATUS_OFFLINE);
			}
		}
		else
		{ // Rates management
			icq_lock l(m_ratesMutex);
			m_rates->packetSent(pPacket);
		}

	}
	else
	{
Exemple #6
0
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);
	}
}
Exemple #7
0
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;
	}
}
Exemple #8
0
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");
}
//------------------------------------------------------------------------------
// Name: OnPlay()
// Desc: Take actions when the Play button is clicked.
//------------------------------------------------------------------------------
BOOL OnPlay()
{
	HRESULT hr = S_OK;

    if( NULL == g_pAudioplay )
    {
        return( E_UNEXPECTED );
    }

	//
	// Check previous status of the player
	//
	switch( g_Status )
	{
	case PAUSE:
		//
		// Player was PAUSEed, now resume it
		//
		hr = g_pAudioplay->Resume();
		if( FAILED( hr ) )
		{
			TCHAR tszErrMesg[ 128 ];
            (void)StringCchPrintf( tszErrMesg, ARRAYSIZE(tszErrMesg), _T( "Unable to resume (hr=%#X)" ), hr );
			MessageBox( g_hwndDialog, tszErrMesg, ERROR_DIALOG_TITLE, MB_OK );
		}
		else
		{
			SetCurrentStatus( PLAY );
		}
		
		break;
	
	case STOP:
        //
        // Player was STOPped, now start it
        //
        hr = g_pAudioplay->Start();
        if( FAILED( hr ) )
        {
            TCHAR tszErrMesg[ 128 ];
            (void)StringCchPrintf( tszErrMesg, ARRAYSIZE(tszErrMesg), _T("Unable to start (hr=%#X)"), hr );
            MessageBox( g_hwndDialog, tszErrMesg, ERROR_DIALOG_TITLE, MB_OK );
        }
        else
        {
            SetCurrentStatus( OPENING );
        }

        break;

    case CLOSED:
        //
        // The play is being called for the current file for the first time.
        // Start playing the file
        //
        SetCurrentStatus( OPENING );
        
        //
        // Get the file name
        //
        GetDlgItemText( g_hwndDialog, IDC_FILENAME, g_ptszFileName, MAX_PATH );

        //
        // Remove leading spaces from the file name        
        //
        TCHAR *ptszTemp = g_ptszFileName;
        while( *ptszTemp == _T(' ') )
        {
            ptszTemp++;
        }

        if( g_ptszFileName != ptszTemp )
        {
            memmove( g_ptszFileName, ptszTemp, sizeof( TCHAR ) * ( _tcslen( ptszTemp ) + 1 ) );
            SendDlgItemMessage( g_hwndDialog, IDC_FILENAME, WM_SETTEXT, 0, ( WPARAM )g_ptszFileName );
        }

        //
        // Open the file. We may need to convert the filename string from multibytes to wide characters
        //
#ifndef UNICODE
		{
			WCHAR pwszFileName[ MAX_PATH ];

			if( 0 == MultiByteToWideChar( CP_ACP, 0, g_ptszFileName, -1, pwszFileName, MAX_PATH ) )
			{
                //
                // Convertion failed
                //
				SetCurrentStatus( CLOSED );
				SetCurrentStatus( READY );
				break;
			}

			hr = g_pAudioplay->Open( pwszFileName );
		}
#else
		hr = g_pAudioplay->Open( g_ptszFileName );
#endif // UNICODE

		if( FAILED( hr ) )
		{
			SetCurrentStatus( CLOSED );
			SetCurrentStatus( READY );
		}
		else
		{
            //
            // Start to play from the beginning
            //
            hr = g_pAudioplay->Start();
            if( FAILED( hr ) )
            {
                TCHAR tszErrMesg[ 128 ];
                (void)StringCchPrintf( tszErrMesg, ARRAYSIZE(tszErrMesg), _T("Unable to start (hr=%#X)"), hr );
                MessageBox( g_hwndDialog, tszErrMesg, ERROR_DIALOG_TITLE, MB_OK );
            }
            else
            {
                //
                // Set the max range of the slider to be the value of the file's duration in milliseconds
                //
			    SendDlgItemMessage( g_hwndDialog, IDC_SLIDER, TBM_SETRANGEMAX, TRUE,
								    ( DWORD )( g_pAudioplay->GetFileDuration() / 10000 ) );
    			
                //
                // Update the window title with the file's name
                //
			    LPTSTR ptszFile = _tcsrchr( g_ptszFileName, _T( '\\' ) );
                if( NULL != ptszFile )
                {
			        SetWindowText( g_hwndDialog, ptszFile + 1 );
                }
                else
                {
			        SetWindowText( g_hwndDialog, g_ptszFileName );
                }
            }
		}

		break;
	}
	
	return TRUE;
}
//------------------------------------------------------------------------------
// Name: DlgProc()
// Desc: Dialog box procedure.
//------------------------------------------------------------------------------
INT_PTR CALLBACK DlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	HRESULT	hr		= S_OK;
	HICON	hIcon	= NULL;
	RECT	rect;

	switch( uMsg )
	{
	case WM_INITDIALOG:
		
		g_hwndDialog = hwndDlg;

		//
		// Load the application icon
		//
		hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_WMAICON ) );
		if( hIcon )
		{
			SendMessage( hwndDlg, WM_SETICON, ICON_SMALL, ( LPARAM )hIcon );
			SendMessage( hwndDlg, WM_SETICON, ICON_BIG, ( LPARAM )hIcon );
		}

		GetWindowRect( hwndDlg, &rect );

		//
		// Store the Window height in a global variable for future reference
		//
		g_iDlgHeight = rect.bottom - rect.top;

        //
        // Ready to open and play a file
        //
		SetCurrentStatus( READY );
		
        //
        // Create and initialize the audio player
        //
        g_pAudioplay = new CAudioPlay;
        if( NULL == g_pAudioplay )
        {
			//
			// Creation has failed. Close the application.
			//
			SendMessage( hwndDlg, WM_CLOSE, 0,0 );
            return TRUE;
        }

		hr = g_pAudioplay->Init();
		if( FAILED(hr) )
		{
			//
			// Init has failed. Close the application.
			//
			SendMessage( hwndDlg, WM_CLOSE, 0,0 );
		}

		return TRUE;

	case WM_COMMAND:

		switch( LOWORD( wParam ) ) 
		{
		case IDC_FILENAME:

			if( EN_CHANGE == HIWORD( wParam ) )
			{
				//
                // Filename has been changed
				// Use this notification for enabling or disabling the Play button
				//
				TCHAR tszFileName[ MAX_PATH ];

				GetDlgItemText( hwndDlg, IDC_FILENAME, tszFileName, MAX_PATH );

                //
                // If filename is not empty, enable the Play button
                //
				if( _tcslen( tszFileName) > 0 )
				{
					EnableWindow( GetDlgItem( hwndDlg, IDC_PLAY ), TRUE );

					SetCurrentStatus( CLOSED );
				}
				else
				{
					EnableWindow( GetDlgItem( hwndDlg, IDC_PLAY ), FALSE );
				}
				
				SetCurrentStatus( READY );
				
				return TRUE;
			}
			
			return FALSE;

		case IDC_OPEN:

            //
            // Show the OpenFile dialog
            //
			if( ShowOpenFileDialog() )
			{
				//
				// Display the file name
				//
				SetDlgItemText( hwndDlg, IDC_FILENAME, g_ptszFileName );

				SetFocus( GetDlgItem( hwndDlg, IDC_PLAY ) );
			}

			return TRUE;
		
		case IDC_STOP:
			
			SetCurrentStatus( STOPPING );

            //
            // Stop the audio player
            //
            if( NULL != g_pAudioplay )
            {
			    hr = g_pAudioplay->Stop();
			    if( FAILED( hr ) )
			    {
				    SetCurrentStatus( g_Status );

				    TCHAR tszErrMesg[128];
                    (void)StringCchPrintf( tszErrMesg, ARRAYSIZE(tszErrMesg), _T( "Unable to Stop (hr=%#X)" ), hr );
				    MessageBox( hwndDlg, tszErrMesg, ERROR_DIALOG_TITLE, MB_OK );
			    }
            }

			return TRUE;
			
		case IDC_PAUSE:
			
            //
            // Pause the audio player
            //
            if( NULL != g_pAudioplay )
            {
			    hr = g_pAudioplay->Pause();
			    if( FAILED( hr ) )
			    {
				    TCHAR tszErrMesg[128];
                    (void)StringCchPrintf( tszErrMesg, ARRAYSIZE(tszErrMesg), _T("Unable to Pause (hr=%#X)"), hr );
				    MessageBox( hwndDlg, tszErrMesg, ERROR_DIALOG_TITLE, MB_OK );
			    }
			    else
			    {
				    SetCurrentStatus( PAUSE );
			    }
            }

			break;
		
		case IDC_PLAY:
			
			return( OnPlay() );

		case IDCANCEL:
			//
			// Close the player before exiting application
			//
            if( NULL != g_pAudioplay )
            {
			    g_pAudioplay->Exit();
			    g_pAudioplay->Release();
            }

			EndDialog( hwndDlg, wParam );

			return TRUE;
		}
		break;

	case WM_HSCROLL:

        if( NULL == g_pAudioplay )
        {
            break;
        }

		//
		// Seek only when the file is seekable
		//
		if( ( LOWORD( wParam ) == TB_THUMBTRACK || 
			  LOWORD( wParam ) == TB_BOTTOM ||
			  LOWORD( wParam ) == TB_PAGEDOWN ||
			  LOWORD( wParam ) == TB_PAGEUP ||
			  LOWORD( wParam ) == TB_TOP ) &&
            g_pAudioplay->IsSeekable() )
		{
			//
			// Set g_IsSeeking, to be referenced when thumb tracking is over
			//
			g_IsSeeking = TRUE;
		}
		else if( LOWORD( wParam ) == TB_ENDTRACK && g_pAudioplay->IsSeekable() && g_IsSeeking )
		{
			DWORD_PTR dwPos = SendDlgItemMessage( hwndDlg, IDC_SLIDER, TBM_GETPOS, 0, 0 );

			// 
			// Start the file from the new position
			//
			hr = g_pAudioplay->Start( ( QWORD )dwPos * 10000 );
			if( FAILED ( hr ) )
			{
				g_IsSeeking = FALSE;
			}
		}

		break;
	}

    return FALSE;
}