Esempio n. 1
0
int CJabberProto::OnContactDeleted( WPARAM wParam, LPARAM )
{
	if( !m_bJabberOnline )	// should never happen
		return 0;

	DBVARIANT dbv;
	if ( !JGetStringT(( HANDLE ) wParam, JGetByte( (HANDLE ) wParam, "ChatRoom", 0 )?(char*)"ChatRoomID":(char*)"jid", &dbv )) {
		if ( ListExist( LIST_ROSTER, dbv.ptszVal )) {
			if ( !_tcschr( dbv.ptszVal, _T( '@' ))) {
				TCHAR szStrippedJid[JABBER_MAX_JID_LEN];
				JabberStripJid( m_ThreadInfo->fullJID, szStrippedJid, SIZEOF(szStrippedJid) );
				TCHAR *szDog = _tcschr( szStrippedJid, _T('@'));
				if ( szDog && _tcsicmp( szDog + 1, dbv.ptszVal ))
					m_ThreadInfo->send( XmlNodeIq( _T("set"), SerialNext(), dbv.ptszVal ) << XQUERY( _T(JABBER_FEAT_REGISTER)) << XCHILD( _T("remove")));
			}

			// Remove from roster, server also handles the presence unsubscription process.
			m_ThreadInfo->send( XmlNodeIq( _T("set"), SerialNext()) << XQUERY( _T(JABBER_FEAT_IQ_ROSTER))
				<< XCHILD( _T("item")) << XATTR( _T("jid"), dbv.ptszVal ) << XATTR( _T("subscription"), _T("remove")));
		}

		JFreeVariant( &dbv );
	}
	return 0;
}
Esempio n. 2
0
int CJabberProto::OnContactDeleted(WPARAM wParam, LPARAM)
{
	if (!m_bJabberOnline)	// should never happen
		return 0;

	HANDLE hContact = (HANDLE)wParam;
	ptrT jid(getTStringA(hContact, isChatRoom(hContact) ? "ChatRoomID" : "jid"));
	if (jid == NULL)
		return 0;

	if (ListGetItemPtr(LIST_ROSTER, jid)) {
		if (!_tcschr(jid, _T('@'))) {
			TCHAR szStrippedJid[JABBER_MAX_JID_LEN];
			JabberStripJid(m_ThreadInfo->fullJID, szStrippedJid, SIZEOF(szStrippedJid));
			TCHAR *szDog = _tcschr(szStrippedJid, _T('@'));
			if (szDog && _tcsicmp(szDog + 1, jid))
				m_ThreadInfo->send(XmlNodeIq(_T("set"), SerialNext(), jid) << XQUERY(JABBER_FEAT_REGISTER) << XCHILD(_T("remove")));
		}

		// Remove from roster, server also handles the presence unsubscription process.
		m_ThreadInfo->send(XmlNodeIq(_T("set"), SerialNext()) << XQUERY(JABBER_FEAT_IQ_ROSTER)
			<< XCHILD(_T("item")) << XATTR(_T("jid"), jid) << XATTR(_T("subscription"), _T("remove")));
	}
	return 0;
}
Esempio n. 3
0
void __cdecl CJabberProto::IbbReceiveThread(JABBER_IBB_TRANSFER *jibb)
{
    debugLogA("Thread started: type=ibb_recv");

    filetransfer *ft = jibb->ft;

    jibb->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    jibb->bStreamClosed = FALSE;
    jibb->wPacketId = 0;
    jibb->dwTransferredSize = 0;
    jibb->state = JIBB_RECVING;

    WaitForSingleObject(jibb->hEvent, INFINITE);

    CloseHandle(jibb->hEvent);
    jibb->hEvent = NULL;

    if (jibb->state == JIBB_ERROR)
        m_ThreadInfo->send( XmlNodeIq(_T("set"), SerialNext(), jibb->dstJID) << XCHILDNS(_T("close"), JABBER_FEAT_IBB) << XATTR(_T("sid"), jibb->sid));

    if (jibb->bStreamClosed && jibb->dwTransferredSize == ft->dwExpectedRecvFileSize)
        jibb->state = JIBB_DONE;

    (this->*jibb->pfnFinal)((jibb->state==JIBB_DONE)?TRUE:FALSE, jibb->ft);
    jibb->ft = NULL;

    ListRemove(LIST_FTRECV, jibb->sid);

    JabberIbbFreeJibb(jibb);
}
Esempio n. 4
0
int CJabberProto::SearchRenewFields(HWND hwndDlg, JabberSearchData * dat)
{
	TCHAR szServerName[100];
	EnableWindow(GetDlgItem(hwndDlg, IDC_GO),FALSE);
	GetDlgItemText(hwndDlg,IDC_SERVER,szServerName,SIZEOF(szServerName));
	dat->CurrentHeight = 0;
	dat->curPos = 0;
	SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, 0, FALSE );

	JabberSearchFreeData( hwndDlg, dat );
	JabberSearchRefreshFrameScroll( hwndDlg, dat );

	if ( m_bJabberOnline )
		SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,TranslateT("Please wait...\r\nConnecting search server..."));
	else
		SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,TranslateT("You have to be connected to server"));

	if ( !m_bJabberOnline )
		return 0;

	searchHandleDlg = hwndDlg;

	int iqId = SerialNext();
	IqAdd( iqId, IQ_PROC_GETSEARCHFIELDS, &CJabberProto::OnIqResultGetSearchFields );
	m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId, szServerName ) << XQUERY( _T("jabber:iq:search")));
	return iqId;
}
Esempio n. 5
0
void CJabberProto::SetMucConfig( HXML node, void *from )
{
	if ( m_ThreadInfo && from ) {
		XmlNodeIq iq( _T("set"), SerialNext(), ( TCHAR* )from );
		HXML query = iq << XQUERY( xmlnsOwner );
		xmlAddChild( query, node );
		m_ThreadInfo->send( iq );
}	}
Esempio n. 6
0
void CJabberProto::AddContactToRoster(const TCHAR *jid, const TCHAR *nick, const TCHAR *grpName)
{
	XmlNodeIq iq(_T("set"), SerialNext());
	HXML query = iq << XQUERY(JABBER_FEAT_IQ_ROSTER)
		<< XCHILD(_T("item")) << XATTR(_T("jid"), jid) << XATTR(_T("name"), nick);
	if (grpName)
		query << XCHILD(_T("group"), grpName);
	m_ThreadInfo->send(iq);
}
Esempio n. 7
0
HWND __cdecl CJabberProto::SearchAdvanced( HWND hwndDlg )
{
	if ( !m_bJabberOnline || !hwndDlg )
		return 0;	//error

	JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
	if ( !dat )
		return 0; //error

	// check if server connected (at least one field exists)
	if ( dat->nJSInfCount == 0 )
		return 0;

	// formating request
	BOOL fRequestNotEmpty=FALSE;

	// get server name
	TCHAR szServerName[100];
	GetDlgItemText( hwndDlg, IDC_SERVER, szServerName, SIZEOF( szServerName ));

	// formating query
	int iqId = SerialNext();
	XmlNodeIq iq( _T("set"), iqId, szServerName );
	HXML query = iq << XQUERY( _T("jabber:iq:search"));
	
	if ( m_tszSelectedLang )
		iq << XATTR( _T("xml:lang"), m_tszSelectedLang ); // i'm sure :)

	// next can be 2 cases:
	// Forms: XEP-0055 Example 7
	if ( dat->fSearchRequestIsXForm ) {
		fRequestNotEmpty=TRUE;
		HXML n = JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode);
		xmlAddChild( query, n );
		xi.destroyNode( n );
    }
	else { //and Simple fields: XEP-0055 Example 3
		for ( int i=0; i<dat->nJSInfCount; i++ ) {
			TCHAR szFieldValue[100];
			GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, SIZEOF(szFieldValue));
			if ( szFieldValue[0] != _T('\0')) {
				xmlAddChild( query, dat->pJSInf[i].szFieldName, szFieldValue );
				fRequestNotEmpty=TRUE;
	}	}	}

	if ( fRequestNotEmpty ) {
		// register search request result handler
		IqAdd( iqId, IQ_PROC_GETSEARCH, &CJabberProto::OnIqResultAdvancedSearch );
		// send request
		m_ThreadInfo->send( iq );
		return ( HWND )iqId;
	}
	return 0;
}
Esempio n. 8
0
void CJabberProto::ProcessOutgoingNote(CNoteItem *pNote, bool ok)
{
	if (!ok || !pNote->IsNotEmpty())
	{
		delete pNote;
		return;
	}

	TCHAR buf[1024];
	mir_sntprintf(buf, SIZEOF(buf), _T("Incoming note: %s\n\n%s\nTags: %s"), 
		pNote->GetTitle(), pNote->GetText(), pNote->GetTagsStr());

	JabberCapsBits jcb = GetResourceCapabilites( pNote->GetFrom(), TRUE );

	if ( jcb & JABBER_RESOURCE_CAPS_ERROR )
		jcb = JABBER_RESOURCE_CAPS_NONE;

	int nMsgId = SerialNext();

	XmlNode m(_T("message"));
	m << XATTR(_T("type"), _T("chat")) << XATTR( _T("to"), pNote->GetFrom() ) << XATTRID( nMsgId );
	m << XCHILD(_T("body"), buf);
	HXML hXmlItem = m << XCHILDNS(_T("x"), _T(JABBER_FEAT_MIRANDA_NOTES)) << XCHILD(_T("note"));
	hXmlItem << XATTR(_T("tags"), pNote->GetTagsStr());
	hXmlItem << XCHILD(_T("title"), pNote->GetTitle());
	hXmlItem << XCHILD(_T("text"), pNote->GetText());

	// message receipts XEP priority
	if ( jcb & JABBER_CAPS_MESSAGE_RECEIPTS )
		m << XCHILDNS( _T("request"), _T(JABBER_FEAT_MESSAGE_RECEIPTS));
	else if ( jcb & JABBER_CAPS_MESSAGE_EVENTS ) {
		HXML x = m << XCHILDNS( _T("x"), _T(JABBER_FEAT_MESSAGE_EVENTS));
		x << XCHILD( _T("delivered")); x << XCHILD( _T("offline"));
	}
	else
		nMsgId = -1;

	m_ThreadInfo->send(m);
	delete pNote;
}
Esempio n. 9
0
void CJabberProto::EnableArchive(bool bEnable)
{
	m_ThreadInfo->send( XmlNodeIq(_T("set"), SerialNext())
		<< XCHILDNS( _T("auto"), JABBER_FEAT_ARCHIVE) << XATTR(_T("save"), (bEnable) ? _T("true") : _T("false")));
}
Esempio n. 10
0
void __cdecl CJabberProto::FileServerThread(filetransfer *ft)
{
	debugLogA("Thread started: type=file_send");

	ThreadData info(this, NULL);
	ft->type = FT_OOB;

	NETLIBBIND nlb = { 0 };
	nlb.cbSize = sizeof(NETLIBBIND);
	nlb.pfnNewConnectionV2 = JabberFileServerConnection;
	nlb.pExtra = this;
	nlb.wPort = 0;	// Use user-specified incoming port ranges, if available
	info.s = (HANDLE)CallService(MS_NETLIB_BINDPORT, (WPARAM)m_hNetlibUser, (LPARAM)&nlb);
	if (info.s == NULL) {
		debugLogA("Cannot allocate port to bind for file server thread, thread ended.");
		ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
		delete ft;
		return;
	}

	ft->s = info.s;
	debugLogA("ft->s = %d", info.s);

	HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	ft->hFileEvent = hEvent;

	TCHAR szPort[20];
	mir_sntprintf(szPort, _countof(szPort), _T("%d"), nlb.wPort);
	JABBER_LIST_ITEM *item = ListAdd(LIST_FILE, szPort);
	item->ft = ft;

	TCHAR *ptszResource = ListGetBestClientResourceNamePtr(ft->jid);
	if (ptszResource != NULL) {
		ft->state = FT_CONNECTING;
		for (int i = 0; i < ft->std.totalFiles && ft->state != FT_ERROR && ft->state != FT_DENIED; i++) {
			ft->std.currentFileNumber = i;
			ft->state = FT_CONNECTING;
			if (ft->httpPath) mir_free(ft->httpPath);
			ft->httpPath = NULL;

			TCHAR *p;
			if ((p = _tcschr(ft->std.ptszFiles[i], '\\')) != NULL)
				p++;
			else
				p = ft->std.ptszFiles[i];

			ptrA pFileName(mir_urlEncode(T2Utf(p)));
			if (pFileName != NULL) {
				ft->szId = JabberId2string(SerialNext());

				ptrA myAddr;
				if (m_options.BsDirect && m_options.BsDirectManual)
					myAddr = getStringA("BsDirectAddr");
				if (myAddr == NULL)
					myAddr = (char*)CallService(MS_NETLIB_ADDRESSTOSTRING, 1, nlb.dwExternalIP);

				char szAddr[256];
				mir_snprintf(szAddr, _countof(szAddr), "http://%s:%d/%s", myAddr, nlb.wPort, pFileName);

				size_t len = mir_tstrlen(ptszResource) + mir_tstrlen(ft->jid) + 2;
				TCHAR *fulljid = (TCHAR *)alloca(sizeof(TCHAR) * len);
				mir_sntprintf(fulljid, len, _T("%s/%s"), ft->jid, ptszResource);

				XmlNodeIq iq(_T("set"), ft->szId, fulljid);
				HXML query = iq << XQUERY(JABBER_FEAT_OOB);
				query << XCHILD(_T("url"), _A2T(szAddr));
				query << XCHILD(_T("desc"), ft->szDescription);
				m_ThreadInfo->send(iq);

				debugLogA("Waiting for the file to be sent...");
				WaitForSingleObject(hEvent, INFINITE);
			}
			debugLogA("File sent, advancing to the next file...");
			ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0);
		}
		CloseHandle(hEvent);
		ft->hFileEvent = NULL;
		debugLogA("Finish all files");
	}

	ft->s = NULL;
	debugLogA("ft->s is NULL");

	ListRemove(LIST_FILE, szPort);

	switch (ft->state) {
	case FT_DONE:
		debugLogA("Finish successfully");
		ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0);
		break;
	case FT_DENIED:
		ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DENIED, ft, 0);
		break;
	default: // FT_ERROR:
		debugLogA("Finish with errors");
		ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
		break;
	}

	debugLogA("Thread ended: type=file_send");
	delete ft;
}
Esempio n. 11
0
int CJabberProto::ByteSendProxyParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen )
{
	int num = datalen;

	switch ( jbt->state ) {
	case JBT_INIT:
		// received:
		// 00-00 ver ( 0x05 )
		// 01-01 selected method ( 0=no auth, 0xff=error )
		// send:
		// 00-00 ver ( 0x05 )
		// 01-01 cmd ( 1=connect )
		// 02-02 reserved ( 0 )
		// 03-03 address type ( 3 )
		// 04-44 dst.addr ( 41 bytes: 1-byte length, 40-byte SHA1 hash of [sid,srcJID,dstJID] )
		// 45-46 dst.port ( 0 )
		if ( datalen==2 && buffer[0]==5 && buffer[1]==0 ) {
			BYTE data[47];
			ZeroMemory( data, sizeof( data ));
			*(( DWORD* )data ) = 0x03000105;
			data[4] = 40;

			TCHAR text[256];

			TCHAR *szInitiatorJid = JabberPrepareJid(jbt->srcJID);
			TCHAR *szTargetJid = JabberPrepareJid(jbt->dstJID);
			mir_sntprintf( text, SIZEOF( text ), _T("%s%s%s"), jbt->sid, szInitiatorJid, szTargetJid );
			mir_free(szInitiatorJid);
			mir_free(szTargetJid);

			char* szAuthString = mir_utf8encodeT( text );
			Log( "Auth: '%s'", szAuthString );
			char* szHash = JabberSha1( szAuthString );
			strncpy(( char* )( data+5 ), szHash, 40 );
			mir_free( szHash );
			Netlib_Send( hConn, ( char* )data, 47, 0 );
			jbt->state = JBT_CONNECT;
			mir_free( szAuthString );
		}
		else jbt->state = JBT_SOCKSERR;
		break;

	case JBT_CONNECT:
		// received:
		// 00-00 ver ( 0x05 )
		// 01-01 reply ( 0=success,2=not allowed )
		// 02-02 reserved ( 0 )
		// 03-03 address type ( 1=IPv4 address,3=host address )
		// 04-mm bnd.addr server bound address ( 4-byte IP if IPv4, 1-byte length + n-byte host address string if host address )
		// nn-nn+1 bnd.port server bound port
		if ( datalen>=5 && buffer[0]==5 && buffer[1]==0 && ( buffer[3]==1 || buffer[3]==3 || buffer[3]==0 )) {
			if ( buffer[3]==1 && datalen>=10 )
				num = 10;
			else if ( buffer[3]==3 && datalen>=buffer[4]+7 )
				num = buffer[4] + 7;
			else if ( buffer[3]==0 && datalen>=6 )
				num = 6;
			else {
				jbt->state = JBT_SOCKSERR;
				break;
			}
			jbt->state = JBT_SENDING;

			jbt->hProxyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
			jbt->bStreamActivated = FALSE;

			int iqId = SerialNext();

			TCHAR listJid[256];
			mir_sntprintf(listJid, SIZEOF( listJid ), _T("ftproxy_%d"), iqId);

			JABBER_LIST_ITEM *item = ListAdd( LIST_FTIQID, listJid );
			item->jbt = jbt;

			IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::IqResultStreamActivate );
			m_ThreadInfo->send( 
				XmlNodeIq( _T("set"), iqId, jbt->streamhostJID ) << XQUERY( _T(JABBER_FEAT_BYTESTREAMS))
					<< XATTR( _T("sid"), jbt->sid ) << XCHILD( _T("activate"), jbt->dstJID ));

			WaitForSingleObject( jbt->hProxyEvent, INFINITE );

			CloseHandle( jbt->hProxyEvent );
			jbt->hProxyEvent = NULL;

			ListRemove( LIST_FTIQID, listJid );

			if ( jbt->bStreamActivated) 
				jbt->state = (this->*jbt->pfnSend)( hConn, jbt->ft ) ? JBT_DONE : JBT_ERROR;
			else
				jbt->state = JBT_ERROR;
		}
		else jbt->state = JBT_SOCKSERR;
		break;
	}

	return num;
}
Esempio n. 12
0
void __cdecl CJabberProto::FileServerThread( filetransfer* ft )
{
	Log( "Thread started: type=file_send" );

	ThreadData info( this, JABBER_SESSION_NORMAL );
	ft->type = FT_OOB;

	NETLIBBIND nlb = {0};
	nlb.cbSize = sizeof( NETLIBBIND );
	nlb.pfnNewConnectionV2 = JabberFileServerConnection;
	nlb.pExtra = this;
	nlb.wPort = 0;	// Use user-specified incoming port ranges, if available
	info.s = ( HANDLE ) JCallService( MS_NETLIB_BINDPORT, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nlb );
	if ( info.s == NULL ) {
		Log( "Cannot allocate port to bind for file server thread, thread ended." );
		JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
		delete ft;
		return;
	}

	ft->s = info.s;
	Log( "ft->s = %d", info.s );

	HANDLE hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
	ft->hFileEvent = hEvent;

	TCHAR szPort[20];
	mir_sntprintf( szPort, SIZEOF( szPort ), _T("%d"), nlb.wPort );
	JABBER_LIST_ITEM *item = ListAdd( LIST_FILE, szPort );
	item->ft = ft;

	TCHAR* ptszResource = ListGetBestClientResourceNamePtr( ft->jid );
	if ( ptszResource != NULL ) {
		ft->state = FT_CONNECTING;
		for ( int i=0; i < ft->std.totalFiles && ft->state != FT_ERROR && ft->state != FT_DENIED; i++ ) {
			ft->std.currentFileNumber = i;
			ft->state = FT_CONNECTING;
			if ( ft->httpPath ) mir_free( ft->httpPath );
			ft->httpPath = NULL;

			TCHAR* p;
			if (( p = _tcschr( ft->std.ptszFiles[i], '\\' )) != NULL )
				p++;
			else
				p = ft->std.ptszFiles[i];

			in_addr in;
			in.S_un.S_addr = m_dwJabberLocalIP;
		
			TCHAR* pFileName = JabberHttpUrlEncode( p );
			if ( pFileName != NULL ) {
				int id = SerialNext();
				if ( ft->iqId ) mir_free( ft->iqId );
				ft->iqId = ( TCHAR* )mir_alloc( sizeof(TCHAR)*( strlen( JABBER_IQID )+20 ));
				wsprintf( ft->iqId, _T(JABBER_IQID)_T("%d"), id );

				char *myAddr;
				DBVARIANT dbv;
				if (m_options.BsDirect && m_options.BsDirectManual) {
					if ( !DBGetContactSettingString( NULL, m_szModuleName, "BsDirectAddr", &dbv )) {
						myAddr = NEWSTR_ALLOCA( dbv.pszVal );
						JFreeVariant( &dbv );
					}
					else myAddr = inet_ntoa( in );
				}
				else myAddr = inet_ntoa( in );

				char szAddr[ 256 ];
				mir_snprintf( szAddr, sizeof(szAddr), "http://%s:%d/%s", myAddr, nlb.wPort, pFileName );

				int len = lstrlen(ptszResource) + lstrlen(ft->jid) + 2;
				TCHAR* fulljid = ( TCHAR* )alloca( sizeof( TCHAR )*len );
				wsprintf( fulljid, _T("%s/%s"), ft->jid, ptszResource );

				XmlNodeIq iq( _T("set"), id, fulljid );
				HXML query = iq << XQUERY( _T(JABBER_FEAT_OOB));
				query << XCHILD( _T("url"), _A2T(szAddr));
				query << XCHILD( _T("desc"), ft->szDescription);
				m_ThreadInfo->send( iq );

				Log( "Waiting for the file to be sent..." );
				WaitForSingleObject( hEvent, INFINITE );
				mir_free( pFileName );
			}
			Log( "File sent, advancing to the next file..." );
			JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0 );
		}
		CloseHandle( hEvent );
		ft->hFileEvent = NULL;
		Log( "Finish all files" );
	}

	ft->s = NULL;
	Log( "ft->s is NULL" );

	ListRemove( LIST_FILE, szPort );

	switch ( ft->state ) {
	case FT_DONE:
		Log( "Finish successfully" );
		JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0 );
		break;
	case FT_DENIED:
		JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DENIED, ft, 0 );
		break;
	default: // FT_ERROR:
		Log( "Finish with errors" );
		JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
		break;
	}

	Log( "Thread ended: type=file_send" );
	delete ft;
}
Esempio n. 13
0
int CJabberProto::OnProcessSrmmEvent(WPARAM, LPARAM lParam)
{
    MessageWindowEventData *event = (MessageWindowEventData *)lParam;

    if (event->uType == MSG_WINDOW_EVT_OPEN) {
        if (!hDialogsList)
            hDialogsList = WindowList_Create();
        WindowList_Add(hDialogsList, event->hwndWindow, event->hContact);

        ptrT jid(getTStringA(event->hContact, "jid"));
        if (jid != NULL) {
            JABBER_LIST_ITEM *pItem = ListGetItemPtr(LIST_ROSTER, jid);
            if (pItem && (m_ThreadInfo->jabberServerCaps & JABBER_CAPS_ARCHIVE_AUTO) && m_options.EnableMsgArchive)
                RetrieveMessageArchive(event->hContact, pItem);
        }
    }
    else if (event->uType == MSG_WINDOW_EVT_CLOSING) {
        if (hDialogsList)
            WindowList_Remove(hDialogsList, event->hwndWindow);

        DBVARIANT dbv;
        BOOL bSupportTyping = FALSE;
        if (!db_get(event->hContact, "SRMsg", "SupportTyping", &dbv)) {
            bSupportTyping = dbv.bVal == 1;
            db_free(&dbv);
        }
        else if (!db_get(NULL, "SRMsg", "DefaultTyping", &dbv)) {
            bSupportTyping = dbv.bVal == 1;
            db_free(&dbv);
        }
        if (!bSupportTyping || !m_bJabberOnline)
            return 0;

        TCHAR jid[JABBER_MAX_JID_LEN];
        if (GetClientJID(event->hContact, jid, _countof(jid))) {
            pResourceStatus r(ResourceInfoFromJID(jid));
            if (r && r->m_bMessageSessionActive) {
                r->m_bMessageSessionActive = FALSE;

                if (GetResourceCapabilites(jid, TRUE) & JABBER_CAPS_CHATSTATES)
                    m_ThreadInfo->send(
                        XmlNode(_T("message")) << XATTR(_T("to"), jid) << XATTR(_T("type"), _T("chat")) << XATTRID(SerialNext())
                        << XCHILDNS(_T("gone"), JABBER_FEAT_CHATSTATES));
            }
        }
    }

    return 0;
}