diff options
author | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-10-12 14:53:57 +0000 |
---|---|---|
committer | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-10-12 14:53:57 +0000 |
commit | 3b55a62fdcb1f8222de3c2c8fbed530792c419a0 (patch) | |
tree | 5b2f628e847f61bb3e16f95ecaed6e187963362f /protocols/JabberG/src | |
parent | 1f9c986d82657f965462d289bf94aa012cf026fc (diff) |
GTalkExt, ICQ, IRC, Jabber: folders restructurization
git-svn-id: http://svn.miranda-ng.org/main/trunk@1890 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/JabberG/src')
80 files changed, 53122 insertions, 0 deletions
diff --git a/protocols/JabberG/src/MString.cpp b/protocols/JabberG/src/MString.cpp new file mode 100644 index 0000000000..773871d1a5 --- /dev/null +++ b/protocols/JabberG/src/MString.cpp @@ -0,0 +1,201 @@ +#include "jabber.h"
+#include "MString.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CMBaseString
+
+CNilMStringData CMBaseString::m_nil;
+
+CMStringData* CMBaseString::Allocate( int nChars, int nCharSize )
+{
+ CMStringData* pData;
+ nChars++; // nil char
+ size_t nDataBytes = nCharSize * nChars;
+ size_t nTotalSize = nDataBytes + sizeof(CMStringData);
+
+ pData = static_cast<CMStringData*>(malloc(nTotalSize));
+ if (pData == NULL)
+ return NULL;
+
+ pData->nRefs = 1;
+ pData->nAllocLength = nChars - 1;
+ pData->nDataLength = 0;
+ return pData;
+}
+
+void CMBaseString::Free(CMStringData* pData)
+{
+ free(pData);
+}
+
+CMStringData* CMBaseString::Realloc(CMStringData* pData, int nChars, int nCharSize)
+{
+ CMStringData* pNewData;
+ nChars++; // nil char
+ ULONG nDataBytes = nCharSize * nChars;
+ ULONG nTotalSize = nDataBytes + sizeof(CMStringData);
+
+ pNewData = static_cast<CMStringData*>(realloc(pData, nTotalSize));
+ if (pNewData == NULL)
+ return NULL;
+
+ pNewData->nAllocLength = nChars - 1;
+ return pNewData;
+}
+
+CMStringData* CMBaseString::GetNilString()
+{
+ m_nil.AddRef();
+ return &m_nil;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CMStringData
+
+void* CMStringData::data()
+{
+ return (this + 1);
+}
+
+void CMStringData::AddRef()
+{
+ InterlockedIncrement(&nRefs);
+}
+
+bool CMStringData::IsLocked() const
+{
+ return nRefs < 0;
+}
+
+bool CMStringData::IsShared() const
+{
+ return (nRefs > 1);
+}
+
+void CMStringData::Lock()
+{
+ nRefs--; // Locked buffers can't be shared, so no interlocked operation necessary
+ if ( nRefs == 0 )
+ nRefs = -1;
+}
+
+void CMStringData::Release()
+{
+ if (InterlockedDecrement(&nRefs) <= 0)
+ CMBaseString::Free(this);
+}
+
+void CMStringData::Unlock()
+{
+ if (IsLocked())
+ {
+ nRefs++; // Locked buffers can't be shared, so no interlocked operation necessary
+ if (nRefs == 0)
+ nRefs = 1;
+ }
+}
+
+CNilMStringData::CNilMStringData()
+{
+ nRefs = 2; // Never gets freed
+ nDataLength = 0;
+ nAllocLength = 0;
+ achNil[0] = 0;
+ achNil[1] = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// ChTraitsCRT<wchar_t>
+
+#if _MSC_VER < 1400
+static HINSTANCE hCrt = NULL;
+
+typedef int ( __cdecl *_vscprintf_func )( LPCSTR pszFormat, va_list args );
+static _vscprintf_func _vscprintf_ptr = NULL;
+
+typedef int ( __cdecl *_vscwprintf_func )( LPCWSTR pszFormat, va_list args );
+static _vscwprintf_func _vscwprintf_ptr = NULL;
+
+typedef int ( __cdecl *_vsnprintf_func )( char*, size_t, const char*, va_list );
+static _vsnprintf_func _vsnprintf_ptr = NULL;
+
+typedef int ( __cdecl *_vsnwprintf_func )( wchar_t *, size_t, const wchar_t *, va_list );
+static _vsnwprintf_func _vsnwprintf_ptr = NULL;
+
+typedef int ( __cdecl *vswprintf_func )( wchar_t *, size_t, const wchar_t *, va_list );
+static vswprintf_func vswprintf_ptr = NULL;
+
+typedef int ( __cdecl *vsprintf_func )( char*, size_t, const char*, va_list );
+static vsprintf_func vsprintf_ptr = NULL;
+
+static void checkCrt( void )
+{
+ if ( hCrt == NULL ) {
+ hCrt = GetModuleHandleA( "msvcrt.dll" );
+ _vscprintf_ptr = (_vscprintf_func)GetProcAddress( hCrt, "_vscprintf" );
+ _vscwprintf_ptr = (_vscwprintf_func)GetProcAddress( hCrt, "_vscwprintf" );
+ _vsnprintf_ptr = (_vsnprintf_func)GetProcAddress( hCrt, "_vsnprintf" );
+ _vsnwprintf_ptr = (_vsnwprintf_func)GetProcAddress( hCrt, "_vsnwprintf" );
+ vswprintf_ptr = (vswprintf_func)GetProcAddress( hCrt, "vswprintf" );
+ vsprintf_ptr = (vsprintf_func)GetProcAddress( hCrt, "vsprintf" );
+} }
+#endif
+
+int __stdcall ChTraitsCRT<wchar_t>::GetFormattedLength( LPCWSTR pszFormat, va_list args )
+{
+ #if _MSC_VER < 1400
+ checkCrt();
+
+ if ( _vscwprintf_ptr != NULL )
+ return _vscwprintf_ptr( pszFormat, args );
+
+ WCHAR buf[ 4000 ];
+ return vswprintf_ptr( buf, SIZEOF(buf), pszFormat, args );
+ #else
+ return _vscwprintf( pszFormat, args );
+ #endif
+}
+
+int __stdcall ChTraitsCRT<wchar_t>::Format( LPWSTR pszBuffer, size_t nLength, LPCWSTR pszFormat, va_list args)
+{
+ #if _MSC_VER < 1400
+ checkCrt();
+
+ if ( _vsnwprintf_ptr != NULL )
+ return _vsnwprintf_ptr( pszBuffer, nLength, pszFormat, args );
+
+ return vswprintf_ptr( pszBuffer, nLength, pszFormat, args );
+ #else
+ return _vsnwprintf( pszBuffer, nLength, pszFormat, args );
+ #endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// ChTraitsCRT<char>
+
+int __stdcall ChTraitsCRT<char>::GetFormattedLength( LPCSTR pszFormat, va_list args )
+{
+ #if _MSC_VER < 1400
+ checkCrt();
+
+ if ( _vscprintf_ptr != NULL )
+ return _vscprintf_ptr( pszFormat, args );
+
+ char buf[4000];
+ return vsprintf_ptr( buf, sizeof(buf), pszFormat, args );
+ #else
+ return _vscprintf( pszFormat, args );
+ #endif
+}
+
+int __stdcall ChTraitsCRT<char>::Format( LPSTR pszBuffer, size_t nlength, LPCSTR pszFormat, va_list args )
+{
+ #if _MSC_VER < 1400
+ checkCrt();
+
+ return _vsnprintf( pszBuffer, nlength, pszFormat, args );
+ #else
+ return vsprintf_s( pszBuffer, nlength, pszFormat, args );
+ #endif
+}
+
diff --git a/protocols/JabberG/src/MString.h b/protocols/JabberG/src/MString.h new file mode 100644 index 0000000000..7cc16ff4a3 --- /dev/null +++ b/protocols/JabberG/src/MString.h @@ -0,0 +1,2300 @@ +#pragma once
+
+#include <string.h>
+#include <mbstring.h>
+#include <wchar.h>
+
+#ifdef __MINGW32__
+#include <limits.h>
+
+__inline size_t strnlen(const char *string, size_t maxlen)
+{
+ const char *end = (const char *)memchr ((const void *)string, '\0', maxlen);
+ return end ? (size_t) (end - string) : maxlen;
+}
+__inline size_t wcsnlen(const wchar_t *string, size_t maxlen)
+{
+ const wchar_t *end = wmemchr (string, L'\0', maxlen);
+ return end ? (size_t) (end - string) : maxlen;
+}
+
+/* FIXME: This may be wrong assumption about _AtlGetConversionACP */
+#define _AtlGetConversionACP() CP_THREAD_ACP
+/* FIXME: This is unsafe */
+#define memcpy_s(dest,size,src,count) memcpy(dest,src,count)
+/* FIXME: This is quite silly implementation of _mbsstr */
+#define _mbsstr(str,search) strstr((const char *)str,(const char *)search)
+#define __max(x,y) (((x)<(y))?(y):(x))
+#endif /* __MINGW32__ */
+
+struct CMStringData
+{
+ int nDataLength; // Length of currently used data in XCHARs (not including terminating null)
+ int nAllocLength; // Length of allocated data in XCHARs (not including terminating null)
+ long nRefs; // Reference count: negative == locked
+ // XCHAR data[nAllocLength+1] // A CStringData is always followed in memory by the actual array of character data
+ void* data();
+ void AddRef();
+ bool IsLocked() const;
+ bool IsShared() const;
+ void Lock();
+ void Release();
+ void Unlock();
+};
+
+class CNilMStringData : public CMStringData
+{
+public:
+ CNilMStringData();
+
+public:
+ wchar_t achNil[2];
+};
+
+template< typename BaseType = char >
+class ChTraitsBase
+{
+public:
+ typedef char XCHAR;
+ typedef LPSTR PXSTR;
+ typedef LPCSTR PCXSTR;
+ typedef wchar_t YCHAR;
+ typedef LPWSTR PYSTR;
+ typedef LPCWSTR PCYSTR;
+};
+
+template<>
+class ChTraitsBase< wchar_t >
+{
+public:
+ typedef wchar_t XCHAR;
+ typedef LPWSTR PXSTR;
+ typedef LPCWSTR PCXSTR;
+ typedef char YCHAR;
+ typedef LPSTR PYSTR;
+ typedef LPCSTR PCYSTR;
+};
+
+class CMBaseString
+{
+public:
+ static CMStringData* Allocate(int nChars, int nCharSize);
+ static void Free(CMStringData* pData);
+ static CMStringData* Realloc(CMStringData* pData, int nChars, int nCharSize);
+
+protected:
+ static CMStringData* GetNilString();
+ static CNilMStringData m_nil;
+};
+
+template< typename BaseType >
+class CMSimpleStringT : public CMBaseString
+{
+public:
+ typedef typename ChTraitsBase< BaseType >::XCHAR XCHAR;
+ typedef typename ChTraitsBase< BaseType >::PXSTR PXSTR;
+ typedef typename ChTraitsBase< BaseType >::PCXSTR PCXSTR;
+ typedef typename ChTraitsBase< BaseType >::YCHAR YCHAR;
+ typedef typename ChTraitsBase< BaseType >::PYSTR PYSTR;
+ typedef typename ChTraitsBase< BaseType >::PCYSTR PCYSTR;
+
+public:
+ explicit CMSimpleStringT()
+ {
+ CMStringData* pData = GetNilString();
+ Attach(pData);
+ }
+
+ CMSimpleStringT(const CMSimpleStringT& strSrc)
+ {
+ CMStringData* pSrcData = strSrc.GetData();
+ CMStringData* pNewData = CloneData( pSrcData );
+ Attach( pNewData );
+ }
+
+ CMSimpleStringT(PCXSTR pszSrc)
+ {
+ int nLength = StringLength( pszSrc );
+ CMStringData* pData = Allocate( nLength, sizeof( XCHAR ));
+ if (pData != NULL)
+ {
+ Attach( pData );
+ SetLength( nLength );
+ CopyChars( m_pszData, nLength, pszSrc, nLength );
+ }
+ }
+ CMSimpleStringT(const XCHAR* pchSrc, int nLength)
+ {
+ CMStringData* pData = Allocate( nLength, sizeof( XCHAR ));
+ if ( pData != NULL )
+ {
+ Attach( pData );
+ SetLength( nLength );
+ CopyChars( m_pszData, nLength, pchSrc, nLength );
+ }
+ }
+ ~CMSimpleStringT()
+ {
+ CMStringData* pData = GetData();
+ pData->Release();
+ }
+
+ operator CMSimpleStringT<BaseType>&()
+ {
+ return *(CMSimpleStringT<BaseType>*)this;
+ }
+
+ CMSimpleStringT& operator=(const CMSimpleStringT& strSrc )
+ {
+ CMStringData* pSrcData = strSrc.GetData();
+ CMStringData* pOldData = GetData();
+ if ( pSrcData != pOldData)
+ {
+ if ( pOldData->IsLocked())
+ SetString( strSrc.GetString(), strSrc.GetLength());
+ else
+ {
+ CMStringData* pNewData = CloneData( pSrcData );
+ pOldData->Release();
+ Attach( pNewData );
+ }
+ }
+
+ return *this;
+ }
+
+ CMSimpleStringT& operator=(PCXSTR pszSrc)
+ {
+ SetString( pszSrc );
+ return *this;
+ }
+
+ CMSimpleStringT& operator+=( const CMSimpleStringT& strSrc )
+ {
+ Append( strSrc );
+
+ return *this;
+ }
+
+ CMSimpleStringT& operator+=( PCXSTR pszSrc )
+ {
+ Append( pszSrc );
+
+ return *this;
+ }
+ CMSimpleStringT& operator+=( char ch )
+ {
+ AppendChar(XCHAR(ch));
+
+ return *this;
+ }
+ CMSimpleStringT& operator+=( unsigned char ch )
+ {
+ AppendChar(XCHAR(ch));
+
+ return *this;
+ }
+ CMSimpleStringT& operator+=( wchar_t ch )
+ {
+ AppendChar(XCHAR(ch));
+
+ return *this;
+ }
+
+ XCHAR operator[]( int iChar ) const
+ {
+ return m_pszData[iChar];
+ }
+
+ operator PCXSTR() const
+ {
+ return m_pszData;
+ }
+
+ PCXSTR c_str() const
+ {
+ return m_pszData;
+ }
+
+ void Append( PCXSTR pszSrc )
+ {
+ Append( pszSrc, StringLength( pszSrc ));
+ }
+ void Append( PCXSTR pszSrc, int nLength )
+ {
+ // See comment in SetString() about why we do this
+ UINT_PTR nOffset = pszSrc - GetString();
+
+ UINT nOldLength = GetLength();
+ if (nOldLength < 0)
+ {
+ // protects from underflow
+ nOldLength = 0;
+ }
+
+ //Make sure we don't read pass end of the terminating NULL
+ int nSrcLength = StringLength(pszSrc);
+ nLength = nLength > nSrcLength ? nSrcLength: nLength;
+
+ int nNewLength = nOldLength+nLength;
+ PXSTR pszBuffer = GetBuffer( nNewLength );
+ if ( nOffset <= nOldLength )
+ {
+ pszSrc = pszBuffer+nOffset;
+ // No need to call CopyCharsOverlapped, since the destination is
+ // beyond the end of the original buffer
+ }
+ CopyChars( pszBuffer+nOldLength, nLength, pszSrc, nLength );
+ ReleaseBufferSetLength( nNewLength );
+ }
+ void AppendChar( XCHAR ch )
+ {
+ UINT nOldLength = GetLength();
+ int nNewLength = nOldLength+1;
+ PXSTR pszBuffer = GetBuffer( nNewLength );
+ pszBuffer[nOldLength] = ch;
+ ReleaseBufferSetLength( nNewLength );
+ }
+ void Append( const CMSimpleStringT& strSrc )
+ {
+ Append( strSrc.GetString(), strSrc.GetLength());
+ }
+ void Empty()
+ {
+ CMStringData* pOldData = GetData();
+ if ( pOldData->nDataLength == 0 )
+ return;
+
+ if ( pOldData->IsLocked())
+ {
+ // Don't reallocate a locked buffer that's shrinking
+ SetLength( 0 );
+ }
+ else
+ {
+ pOldData->Release();
+ CMStringData* pNewData = GetNilString();
+ Attach( pNewData );
+ }
+ }
+ void FreeExtra()
+ {
+ CMStringData* pOldData = GetData();
+ int nLength = pOldData->nDataLength;
+ if ( pOldData->nAllocLength == nLength )
+ return;
+
+ if ( !pOldData->IsLocked()) // Don't reallocate a locked buffer that's shrinking
+ {
+ CMStringData* pNewData = Allocate( nLength, sizeof( XCHAR ));
+ if ( pNewData == NULL ) {
+ SetLength( nLength );
+ return;
+ }
+
+ CopyChars( PXSTR( pNewData->data()), nLength, PCXSTR( pOldData->data()), nLength );
+
+ pOldData->Release();
+ Attach( pNewData );
+ SetLength( nLength );
+ }
+ }
+
+ int GetAllocLength() const
+ {
+ return GetData()->nAllocLength;
+ }
+ XCHAR GetAt( int iChar ) const
+ {
+ return m_pszData[iChar];
+ }
+ PXSTR GetBuffer()
+ {
+ CMStringData* pData = GetData();
+ if ( pData->IsShared())
+ Fork( pData->nDataLength );
+
+ return m_pszData;
+ }
+ PXSTR GetBuffer( int nMinBufferLength )
+ {
+ return PrepareWrite( nMinBufferLength );
+ }
+ PXSTR GetBufferSetLength( int nLength )
+ {
+ PXSTR pszBuffer = GetBuffer( nLength );
+ SetLength( nLength );
+
+ return pszBuffer;
+ }
+ int GetLength() const
+ {
+ return GetData()->nDataLength;
+ }
+
+ PCXSTR GetString() const
+ {
+ return m_pszData;
+ }
+ bool IsEmpty() const
+ {
+ return GetLength() == 0;
+ }
+ PXSTR LockBuffer()
+ {
+ CMStringData* pData = GetData();
+ if ( pData->IsShared())
+ {
+ Fork( pData->nDataLength );
+ pData = GetData(); // Do it again, because the fork might have changed it
+ }
+ pData->Lock();
+
+ return m_pszData;
+ }
+ void UnlockBuffer()
+ {
+ CMStringData* pData = GetData();
+ pData->Unlock();
+ }
+ void Preallocate( int nLength )
+ {
+ PrepareWrite( nLength );
+ }
+ void ReleaseBuffer( int nNewLength = -1 )
+ {
+ if ( nNewLength == -1 )
+ {
+ int nAlloc = GetData()->nAllocLength;
+ nNewLength = StringLengthN( m_pszData, nAlloc);
+ }
+ SetLength( nNewLength );
+ }
+ void ReleaseBufferSetLength( int nNewLength )
+ {
+ SetLength( nNewLength );
+ }
+ void Truncate( int nNewLength )
+ {
+ GetBuffer( nNewLength );
+ ReleaseBufferSetLength( nNewLength );
+ }
+ void SetAt( int iChar, XCHAR ch )
+ {
+ int nLength = GetLength();
+ PXSTR pszBuffer = GetBuffer();
+ pszBuffer[iChar] = ch;
+ ReleaseBufferSetLength( nLength );
+
+ }
+ void SetString( PCXSTR pszSrc )
+ {
+ SetString( pszSrc, StringLength( pszSrc ));
+ }
+ void SetString( PCXSTR pszSrc, int nLength )
+ {
+ if ( nLength == 0 )
+ {
+ Empty();
+ }
+ else
+ {
+
+ UINT nOldLength = GetLength();
+ UINT_PTR nOffset = pszSrc - GetString();
+
+ PXSTR pszBuffer = GetBuffer( nLength );
+ if ( nOffset <= nOldLength )
+ {
+ CopyCharsOverlapped( pszBuffer, GetAllocLength(),
+ pszBuffer+nOffset, nLength );
+ }
+ else
+ {
+ CopyChars( pszBuffer, GetAllocLength(), pszSrc, nLength );
+ }
+ ReleaseBufferSetLength( nLength );
+ }
+ }
+public:
+ friend CMSimpleStringT __stdcall operator+(const CMSimpleStringT& str1, const CMSimpleStringT& str2)
+ {
+ CMSimpleStringT s;
+
+ Concatenate( s, str1, str1.GetLength(), str2, str2.GetLength());
+
+ return s;
+ }
+
+ friend CMSimpleStringT __stdcall operator+(const CMSimpleStringT& str1, PCXSTR psz2)
+ {
+ CMSimpleStringT s;
+
+ Concatenate( s, str1, str1.GetLength(), psz2, StringLength( psz2 ));
+
+ return s;
+ }
+
+ friend CMSimpleStringT __stdcall operator+(PCXSTR psz1, const CMSimpleStringT& str2)
+ {
+ CMSimpleStringT s;
+
+ Concatenate( s, psz1, StringLength( psz1 ), str2, str2.GetLength());
+
+ return s;
+ }
+
+ static void __stdcall CopyChars(XCHAR* pchDest, const XCHAR* pchSrc, int nChars )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ memcpy(pchDest, pchSrc, nChars * sizeof(XCHAR));
+#pragma warning (pop)
+ }
+ static void __stdcall CopyChars(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars )
+ {
+ #if _MSC_VER >= 1400
+ memcpy_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR));
+ #else
+ memcpy(pchDest, pchSrc, nDestLen * sizeof(XCHAR));
+ #endif
+ }
+
+ static void __stdcall CopyCharsOverlapped(XCHAR* pchDest, const XCHAR* pchSrc, int nChars )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ memmove(pchDest, pchSrc, nChars * sizeof(XCHAR));
+#pragma warning (pop)
+ }
+ static void __stdcall CopyCharsOverlapped(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars)
+ {
+ #if _MSC_VER >= 1400
+ memmove_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR));
+ #else
+ memmove(pchDest, pchSrc, nDestLen * sizeof(XCHAR));
+ #endif
+ }
+ static int __stdcall StringLength(const char* psz)
+ {
+ if (psz == NULL)
+ {
+ return(0);
+ }
+ return (int(strlen(psz)));
+ }
+ static int __stdcall StringLength(const wchar_t* psz)
+ {
+ if (psz == NULL)
+ return 0;
+
+ return int(wcslen(psz));
+ }
+ static int __stdcall StringLengthN(const char* psz, size_t sizeInXChar )
+ {
+ if ( psz == NULL )
+ return 0;
+
+ return int( strnlen( psz, sizeInXChar ));
+ }
+ static int __stdcall StringLengthN(const wchar_t* psz, size_t sizeInXChar )
+ {
+ if ( psz == NULL )
+ return 0;
+
+ return int( wcsnlen( psz, sizeInXChar ));
+ }
+protected:
+ static void __stdcall Concatenate(CMSimpleStringT& strResult, PCXSTR psz1, int nLength1, PCXSTR psz2, int nLength2)
+ {
+ int nNewLength = nLength1+nLength2;
+ PXSTR pszBuffer = strResult.GetBuffer(nNewLength);
+ CopyChars(pszBuffer, nLength1, psz1, nLength1 );
+ CopyChars(pszBuffer + nLength1, nLength2, psz2, nLength2);
+ strResult.ReleaseBufferSetLength(nNewLength);
+ }
+ // Implementation
+private:
+ void Attach(CMStringData* pData)
+ {
+ m_pszData = static_cast<PXSTR>(pData->data());
+ }
+ void Fork(int nLength)
+ {
+ CMStringData* pOldData = GetData();
+ int nOldLength = pOldData->nDataLength;
+ CMStringData* pNewData = Allocate(nLength, sizeof(XCHAR));
+ if (pNewData != NULL)
+ {
+ int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength)+1; // Copy '\0'
+ CopyChars( PXSTR( pNewData->data()), nCharsToCopy, PCXSTR( pOldData->data()), nCharsToCopy );
+ pNewData->nDataLength = nOldLength;
+ pOldData->Release();
+ Attach(pNewData);
+ }
+ }
+ CMStringData* GetData() const
+ {
+ return (reinterpret_cast<CMStringData *>(m_pszData) - 1);
+ }
+ PXSTR PrepareWrite( int nLength )
+ {
+ CMStringData* pOldData = GetData();
+ int nShared = 1 - pOldData->nRefs; // nShared < 0 means true, >= 0 means false
+ int nTooShort = pOldData->nAllocLength-nLength; // nTooShort < 0 means true, >= 0 means false
+ if ((nShared | nTooShort) < 0 ) // If either sign bit is set (i.e. either is less than zero), we need to copy data
+ PrepareWrite2(nLength);
+
+ return m_pszData;
+ }
+ void PrepareWrite2(int nLength)
+ {
+ CMStringData* pOldData = GetData();
+ if (pOldData->nDataLength > nLength)
+ nLength = pOldData->nDataLength;
+
+ if (pOldData->IsShared())
+ {
+ Fork(nLength);
+ }
+ else if (pOldData->nAllocLength < nLength)
+ {
+ // Grow exponentially, until we hit 1K.
+ int nNewLength = pOldData->nAllocLength;
+ if ( nNewLength > 1024 )
+ nNewLength += 1024;
+ else
+ nNewLength *= 2;
+
+ if ( nNewLength < nLength )
+ nNewLength = nLength;
+
+ Reallocate( nNewLength );
+ }
+ }
+ void Reallocate( int nLength )
+ {
+ CMStringData* pOldData = GetData();
+ if ( pOldData->nAllocLength >= nLength || nLength <= 0)
+ return;
+
+ CMStringData* pNewData = Realloc( pOldData, nLength, sizeof( XCHAR ));
+ if ( pNewData != NULL )
+ Attach( pNewData );
+ }
+
+ void SetLength( int nLength )
+ {
+ GetData()->nDataLength = nLength;
+ m_pszData[nLength] = 0;
+ }
+
+ static CMStringData* __stdcall CloneData(CMStringData* pData)
+ {
+ CMStringData* pNewData = NULL;
+
+ if (!pData->IsLocked()) {
+ pNewData = pData;
+ pNewData->AddRef();
+ }
+
+ return pNewData;
+ }
+
+public :
+ // typedef CStrBufT<BaseType> CStrBuf;
+private:
+ PXSTR m_pszData;
+};
+
+
+template< typename _CharType = char >
+class ChTraitsCRT : public ChTraitsBase< _CharType >
+{
+public:
+ static char* __stdcall CharNext( const char* p )
+ {
+ return reinterpret_cast< char* >( _mbsinc( reinterpret_cast< const unsigned char* >( p )));
+ }
+
+ static int __stdcall IsDigit( char ch )
+ {
+ return _ismbcdigit( ch );
+ }
+
+ static int __stdcall IsSpace( char ch )
+ {
+ return _ismbcspace( ch );
+ }
+
+ static int __stdcall StringCompare( LPCSTR pszA, LPCSTR pszB )
+ {
+ return _mbscmp( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
+ }
+
+ static int __stdcall StringCompareIgnore( LPCSTR pszA, LPCSTR pszB )
+ {
+ return _mbsicmp( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
+ }
+
+ static int __stdcall StringCollate( LPCSTR pszA, LPCSTR pszB )
+ {
+ return _mbscoll( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
+ }
+
+ static int __stdcall StringCollateIgnore( LPCSTR pszA, LPCSTR pszB )
+ {
+ return _mbsicoll( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
+ }
+
+ static LPCSTR __stdcall StringFindString( LPCSTR pszBlock, LPCSTR pszMatch )
+ {
+ return reinterpret_cast< LPCSTR >( _mbsstr( reinterpret_cast< const unsigned char* >( pszBlock ),
+ reinterpret_cast< const unsigned char* >( pszMatch )));
+ }
+
+ static LPSTR __stdcall StringFindString( LPSTR pszBlock, LPCSTR pszMatch )
+ {
+ return const_cast< LPSTR >( StringFindString( const_cast< LPCSTR >( pszBlock ), pszMatch ));
+ }
+
+ static LPCSTR __stdcall StringFindChar( LPCSTR pszBlock, char chMatch )
+ {
+ return reinterpret_cast< LPCSTR >( _mbschr( reinterpret_cast< const unsigned char* >( pszBlock ), (unsigned char)chMatch ));
+ }
+
+ static LPCSTR __stdcall StringFindCharRev( LPCSTR psz, char ch )
+ {
+ return reinterpret_cast< LPCSTR >( _mbsrchr( reinterpret_cast< const unsigned char* >( psz ), (unsigned char)ch ));
+ }
+
+ static LPCSTR __stdcall StringScanSet( LPCSTR pszBlock, LPCSTR pszMatch )
+ {
+ return reinterpret_cast< LPCSTR >( _mbspbrk( reinterpret_cast< const unsigned char* >( pszBlock ),
+ reinterpret_cast< const unsigned char* >( pszMatch )));
+ }
+
+ static int __stdcall StringSpanIncluding( LPCSTR pszBlock, LPCSTR pszSet )
+ {
+ return (int)_mbsspn( reinterpret_cast< const unsigned char* >( pszBlock ), reinterpret_cast< const unsigned char* >( pszSet ));
+ }
+
+ static int __stdcall StringSpanExcluding( LPCSTR pszBlock, LPCSTR pszSet )
+ {
+ return (int)_mbscspn( reinterpret_cast< const unsigned char* >( pszBlock ), reinterpret_cast< const unsigned char* >( pszSet ));
+ }
+
+ static LPSTR __stdcall StringUppercase( LPSTR psz )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return reinterpret_cast< LPSTR >( _mbsupr( reinterpret_cast< unsigned char* >( psz )) );
+#pragma warning (pop)
+ }
+
+ static LPSTR __stdcall StringLowercase( LPSTR psz )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return reinterpret_cast< LPSTR >( _mbslwr( reinterpret_cast< unsigned char* >( psz )) );
+#pragma warning (pop)
+ }
+
+ static LPSTR __stdcall StringUppercase( LPSTR psz, size_t size )
+ {
+ #if _MSC_VER >= 1400
+ _mbsupr_s(reinterpret_cast< unsigned char* >( psz ), size);
+ #else
+ _mbsupr(reinterpret_cast< unsigned char* >( psz ));
+ #endif
+ return psz;
+ }
+
+ static LPSTR __stdcall StringLowercase( LPSTR psz, size_t size )
+ {
+ #if _MSC_VER >= 1400
+ _mbslwr_s( reinterpret_cast< unsigned char* >( psz ), size );
+ #else
+ _mbslwr(reinterpret_cast< unsigned char* >( psz ));
+ #endif
+ return psz;
+ }
+
+ static LPSTR __stdcall StringReverse( LPSTR psz )
+ {
+ return reinterpret_cast< LPSTR >( _mbsrev( reinterpret_cast< unsigned char* >( psz )) );
+ }
+
+ static int __stdcall GetFormattedLength( LPCSTR pszFormat, va_list args );
+
+ static int __stdcall Format( LPSTR pszBuffer, LPCSTR pszFormat, va_list args )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return vsprintf( pszBuffer, pszFormat, args );
+#pragma warning (pop)
+
+ }
+
+ static int __stdcall Format( LPSTR pszBuffer, size_t nlength, LPCSTR pszFormat, va_list args );
+
+ static int __stdcall GetBaseTypeLength( LPCSTR pszSrc )
+ {
+ // Returns required buffer length in XCHARs
+ return int( strlen( pszSrc ));
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCSTR pszSrc, int nLength )
+ {
+ (void)pszSrc;
+ // Returns required buffer length in XCHARs
+ return nLength;
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCWSTR pszSource )
+ {
+ // Returns required buffer length in XCHARs
+ return ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSource, -1, NULL, 0, NULL, NULL )-1;
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCWSTR pszSource, int nLength )
+ {
+ // Returns required buffer length in XCHARs
+ return ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0, NULL, NULL );
+ }
+
+ static void __stdcall ConvertToBaseType( LPSTR pszDest, int nDestLength, LPCSTR pszSrc, int nSrcLength = -1 )
+ {
+ if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); }
+ // nLen is in XCHARs
+ memcpy_s( pszDest, nDestLength*sizeof( char ),
+ pszSrc, nSrcLength*sizeof( char ));
+ }
+
+ static void __stdcall ConvertToBaseType( LPSTR pszDest, int nDestLength, LPCWSTR pszSrc, int nSrcLength = -1)
+ {
+ // nLen is in XCHARs
+ ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL );
+ }
+
+ static void ConvertToOem( _CharType* pstrString)
+ {
+ BOOL fSuccess=::CharToOemA(pstrString, pstrString);
+ }
+
+ static void ConvertToAnsi( _CharType* pstrString)
+ {
+ BOOL fSuccess=::OemToCharA(pstrString, pstrString);
+ }
+
+ static void ConvertToOem( _CharType* pstrString, size_t size)
+ {
+ if(size>UINT_MAX)
+ {
+ return;
+ }
+ DWORD dwSize=static_cast<DWORD>(size);
+ BOOL fSuccess=::CharToOemBuffA(pstrString, pstrString, dwSize);
+ }
+
+ static void ConvertToAnsi( _CharType* pstrString, size_t size)
+ {
+ if(size>UINT_MAX)
+ return;
+
+ DWORD dwSize=static_cast<DWORD>(size);
+ BOOL fSuccess=::OemToCharBuffA(pstrString, pstrString, dwSize);
+ }
+
+ static void __stdcall FloodCharacters( char ch, int nLength, char* pch )
+ {
+ // nLength is in XCHARs
+ memset( pch, ch, nLength );
+ }
+
+ static BSTR __stdcall AllocSysString( const char* pchData, int nDataLength )
+ {
+ int nLen = ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, NULL, NULL );
+ BSTR bstr = ::SysAllocStringLen( NULL, nLen );
+ if ( bstr != NULL )
+ ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, bstr, nLen );
+
+ return bstr;
+ }
+
+ static BOOL __stdcall ReAllocSysString( const char* pchData, BSTR* pbstr, int nDataLength )
+ {
+ int nLen = ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, NULL, NULL );
+ BOOL bSuccess = ::SysReAllocStringLen( pbstr, NULL, nLen );
+ if ( bSuccess )
+ ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, *pbstr, nLen );
+
+ return bSuccess;
+ }
+
+ static int __stdcall SafeStringLen( LPCSTR psz )
+ {
+ // returns length in bytes
+ return (psz != NULL) ? int( strlen( psz )) : 0;
+ }
+
+ static int __stdcall SafeStringLen( LPCWSTR psz )
+ {
+ // returns length in wchar_ts
+ return (psz != NULL) ? int( wcslen( psz )) : 0;
+ }
+
+ static int __stdcall GetCharLen( const wchar_t* pch )
+ {
+ // returns char length
+ return 1;
+ }
+
+ static int __stdcall GetCharLen( const char* pch )
+ {
+ // returns char length
+ return int( _mbclen( reinterpret_cast< const unsigned char* >( pch )) );
+ }
+
+ static DWORD __stdcall GetEnvironmentVariable( LPCSTR pszVar, LPSTR pszBuffer, DWORD dwSize )
+ {
+ return ::GetEnvironmentVariableA( pszVar, pszBuffer, dwSize );
+ }
+};
+
+// specialization for wchar_t
+template<>
+class ChTraitsCRT< wchar_t > : public ChTraitsBase< wchar_t >
+{
+ static DWORD __stdcall _GetEnvironmentVariableW( LPCWSTR pszName, LPWSTR pszBuffer, DWORD nSize )
+ {
+ return ::GetEnvironmentVariableW( pszName, pszBuffer, nSize );
+ }
+
+public:
+ static LPWSTR __stdcall CharNext( LPCWSTR psz )
+ {
+ return const_cast< LPWSTR >( psz+1 );
+ }
+
+ static int __stdcall IsDigit( wchar_t ch )
+ {
+ return iswdigit( static_cast<unsigned short>(ch));
+ }
+
+ static int __stdcall IsSpace( wchar_t ch )
+ {
+ return iswspace( static_cast<unsigned short>(ch));
+ }
+
+ static int __stdcall StringCompare( LPCWSTR pszA, LPCWSTR pszB )
+ {
+ return wcscmp( pszA, pszB );
+ }
+
+ static int __stdcall StringCompareIgnore( LPCWSTR pszA, LPCWSTR pszB )
+ {
+ return _wcsicmp( pszA, pszB );
+ }
+
+ static int __stdcall StringCollate( LPCWSTR pszA, LPCWSTR pszB )
+ {
+ return wcscoll( pszA, pszB );
+ }
+
+ static int __stdcall StringCollateIgnore( LPCWSTR pszA, LPCWSTR pszB )
+ {
+ return _wcsicoll( pszA, pszB );
+ }
+
+ static LPCWSTR __stdcall StringFindString( LPCWSTR pszBlock, LPCWSTR pszMatch )
+ {
+ return wcsstr( pszBlock, pszMatch );
+ }
+
+ static LPWSTR __stdcall StringFindString( LPWSTR pszBlock, LPCWSTR pszMatch )
+ {
+ return const_cast< LPWSTR >( StringFindString( const_cast< LPCWSTR >( pszBlock ), pszMatch ));
+ }
+
+ static LPCWSTR __stdcall StringFindChar( LPCWSTR pszBlock, wchar_t chMatch )
+ {
+ return wcschr( pszBlock, chMatch );
+ }
+
+ static LPCWSTR __stdcall StringFindCharRev( LPCWSTR psz, wchar_t ch )
+ {
+ return wcsrchr( psz, ch );
+ }
+
+ static LPCWSTR __stdcall StringScanSet( LPCWSTR pszBlock, LPCWSTR pszMatch )
+ {
+ return wcspbrk( pszBlock, pszMatch );
+ }
+
+ static int __stdcall StringSpanIncluding( LPCWSTR pszBlock, LPCWSTR pszSet )
+ {
+ return (int)wcsspn( pszBlock, pszSet );
+ }
+
+ static int __stdcall StringSpanExcluding( LPCWSTR pszBlock, LPCWSTR pszSet )
+ {
+ return (int)wcscspn( pszBlock, pszSet );
+ }
+
+ static LPWSTR __stdcall StringUppercase( LPWSTR psz )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return _wcsupr( psz );
+#pragma warning (pop)
+ }
+
+ static LPWSTR __stdcall StringLowercase( LPWSTR psz )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return _wcslwr( psz );
+#pragma warning (pop)
+ }
+
+ static LPWSTR __stdcall StringUppercase( LPWSTR psz, size_t )
+ {
+ return _wcsupr( psz );
+ }
+
+ static LPWSTR __stdcall StringLowercase( LPWSTR psz, size_t )
+ {
+ return _wcslwr( psz );
+ }
+
+ static LPWSTR __stdcall StringReverse( LPWSTR psz )
+ {
+ return _wcsrev( psz );
+ }
+
+ static int __stdcall GetFormattedLength( LPCWSTR pszFormat, va_list args);
+
+ static int __stdcall Format( LPWSTR pszBuffer, LPCWSTR pszFormat, va_list args)
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return vswprintf( pszBuffer, pszFormat, args );
+#pragma warning (pop)
+ }
+
+ static int __stdcall Format( LPWSTR pszBuffer, size_t nLength, LPCWSTR pszFormat, va_list args);
+
+ static int __stdcall GetBaseTypeLength( LPCSTR pszSrc )
+ {
+ // Returns required buffer size in wchar_ts
+ return ::MultiByteToWideChar( CP_ACP, 0, pszSrc, -1, NULL, 0 )-1;
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCSTR pszSrc, int nLength )
+ {
+ // Returns required buffer size in wchar_ts
+ return ::MultiByteToWideChar( CP_ACP, 0, pszSrc, nLength, NULL, 0 );
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCWSTR pszSrc )
+ {
+ // Returns required buffer size in wchar_ts
+ return (int)wcslen( pszSrc );
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCWSTR pszSrc, int nLength )
+ {
+ (void)pszSrc;
+ // Returns required buffer size in wchar_ts
+ return nLength;
+ }
+
+ static void __stdcall ConvertToBaseType( LPWSTR pszDest, int nDestLength, LPCSTR pszSrc, int nSrcLength = -1)
+ {
+ // nLen is in wchar_ts
+ ::MultiByteToWideChar( CP_ACP, 0, pszSrc, nSrcLength, pszDest, nDestLength );
+ }
+
+ static void __stdcall ConvertToBaseType( LPWSTR pszDest, int nDestLength, LPCWSTR pszSrc, int nSrcLength = -1 )
+ {
+ if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); }
+ // nLen is in wchar_ts
+ #if _MSC_VER >= 1400
+ wmemcpy_s(pszDest, nDestLength, pszSrc, nSrcLength);
+ #else
+ wmemcpy(pszDest, pszSrc, nDestLength);
+ #endif
+ }
+
+ static void __stdcall FloodCharacters( wchar_t ch, int nLength, LPWSTR psz )
+ {
+ // nLength is in XCHARs
+ for ( int i = 0; i < nLength; i++ )
+ {
+ psz[i] = ch;
+ }
+ }
+
+ static BSTR __stdcall AllocSysString( const wchar_t* pchData, int nDataLength )
+ {
+ return ::SysAllocStringLen( pchData, nDataLength );
+ }
+
+ static BOOL __stdcall ReAllocSysString( const wchar_t* pchData, BSTR* pbstr, int nDataLength )
+ {
+ return ::SysReAllocStringLen( pbstr, pchData, nDataLength );
+ }
+
+ static int __stdcall SafeStringLen( LPCSTR psz )
+ {
+ // returns length in bytes
+ return (psz != NULL) ? (int)strlen( psz ) : 0;
+ }
+
+ static int __stdcall SafeStringLen( LPCWSTR psz )
+ {
+ // returns length in wchar_ts
+ return (psz != NULL) ? (int)wcslen( psz ) : 0;
+ }
+
+ static int __stdcall GetCharLen( const wchar_t* pch )
+ {
+ (void)pch;
+ // returns char length
+ return 1;
+ }
+
+ static int __stdcall GetCharLen( const char* pch )
+ {
+ // returns char length
+ return (int)( _mbclen( reinterpret_cast< const unsigned char* >( pch )) );
+ }
+
+ static DWORD __stdcall GetEnvironmentVariable( LPCWSTR pszVar, LPWSTR pszBuffer, DWORD dwSize )
+ {
+ return _GetEnvironmentVariableW( pszVar, pszBuffer, dwSize );
+ }
+
+ static void __stdcall ConvertToOem( LPWSTR /*psz*/ )
+ {
+ }
+
+ static void __stdcall ConvertToAnsi( LPWSTR /*psz*/ )
+ {
+ }
+
+ static void __stdcall ConvertToOem( LPWSTR /*psz*/, size_t )
+ {
+ }
+
+ static void __stdcall ConvertToAnsi( LPWSTR /*psz*/, size_t )
+ {
+ }
+};
+
+template< typename BaseType, class StringTraits >
+class CMStringT : public CMSimpleStringT< BaseType >
+{
+public:
+ typedef CMSimpleStringT< BaseType> CThisSimpleString;
+ typedef typename CThisSimpleString::XCHAR XCHAR;
+ typedef typename CThisSimpleString::PXSTR PXSTR;
+ typedef typename CThisSimpleString::PCXSTR PCXSTR;
+ typedef typename CThisSimpleString::YCHAR YCHAR;
+ typedef typename CThisSimpleString::PYSTR PYSTR;
+ typedef typename CThisSimpleString::PCYSTR PCYSTR;
+
+public:
+ CMStringT() : CThisSimpleString()
+ {
+ }
+
+ static void __stdcall Construct( CMStringT* pString )
+ {
+ new( pString ) CMStringT;
+ }
+
+ // Copy constructor
+ CMStringT( const CMStringT& strSrc ) :
+ CThisSimpleString( strSrc )
+ {
+ }
+
+ CMStringT( const XCHAR* pszSrc ) :
+ CThisSimpleString()
+ {
+ // nDestLength is in XCHARs
+ *this = pszSrc;
+ }
+
+ CMStringT( const YCHAR* pszSrc ) :
+ CThisSimpleString()
+ {
+ *this = pszSrc;
+ }
+
+
+ CMStringT( const unsigned char* pszSrc ) :
+ CThisSimpleString()
+ {
+ *this = reinterpret_cast< const char* >( pszSrc );
+ }
+
+ CMStringT( char ch, int nLength = 1 ) :
+ CThisSimpleString()
+ {
+ if ( nLength > 0 )
+ {
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::FloodCharacters( XCHAR( ch ), nLength, pszBuffer );
+ this->ReleaseBufferSetLength( nLength );
+ }
+ }
+
+ CMStringT( wchar_t ch, int nLength = 1 ) :
+ CThisSimpleString()
+ {
+ if ( nLength > 0 )
+ {
+ //Convert ch to the BaseType
+ wchar_t pszCh[2] = { ch , 0 };
+ int nBaseTypeCharLen = 1;
+
+ if(ch != L'\0')
+ {
+ nBaseTypeCharLen = StringTraits::GetBaseTypeLength(pszCh);
+ }
+
+ XCHAR *buffBaseTypeChar = new XCHAR[nBaseTypeCharLen+1];
+ StringTraits::ConvertToBaseType( buffBaseTypeChar, nBaseTypeCharLen+1, pszCh, 1 );
+ //Allocate enough characters in String and flood (replicate) with the (converted character)*nLength
+ PXSTR pszBuffer = this->GetBuffer( nLength*nBaseTypeCharLen );
+ if (nBaseTypeCharLen == 1)
+ { //Optimization for a common case - wide char translates to 1 ansi/wide char.
+ StringTraits::FloodCharacters( buffBaseTypeChar[0], nLength, pszBuffer );
+ } else
+ {
+ XCHAR* p=pszBuffer;
+ for (int i=0 ; i < nLength ;++i)
+ {
+ for (int j=0 ; j < nBaseTypeCharLen ;++j)
+ {
+ *p=buffBaseTypeChar[j];
+ ++p;
+ }
+ }
+ }
+ this->ReleaseBufferSetLength( nLength*nBaseTypeCharLen );
+ delete [] buffBaseTypeChar;
+ }
+ }
+
+ CMStringT( const XCHAR* pch, int nLength ) :
+ CThisSimpleString( pch, nLength )
+ {
+ }
+
+ CMStringT( const YCHAR* pch, int nLength ) :
+ CThisSimpleString()
+ {
+ if ( nLength > 0 )
+ {
+ int nDestLength = StringTraits::GetBaseTypeLength( pch, nLength );
+ PXSTR pszBuffer = this->GetBuffer( nDestLength );
+ StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pch, nLength );
+ this->ReleaseBufferSetLength( nDestLength );
+ }
+ }
+
+ // Destructor
+ ~CMStringT()
+ {
+ }
+
+ // Assignment operators
+ CMStringT& operator=( const CMStringT& strSrc )
+ {
+ CThisSimpleString::operator=( strSrc );
+
+ return *this;
+ }
+
+ CMStringT& operator=( PCXSTR pszSrc )
+ {
+ CThisSimpleString::operator=( pszSrc );
+
+ return *this;
+ }
+
+ CMStringT& operator=( PCYSTR pszSrc )
+ {
+ // nDestLength is in XCHARs
+ int nDestLength = (pszSrc != NULL) ? StringTraits::GetBaseTypeLength( pszSrc ) : 0;
+ if ( nDestLength > 0 )
+ {
+ PXSTR pszBuffer = this->GetBuffer( nDestLength );
+ StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pszSrc);
+ this->ReleaseBufferSetLength( nDestLength );
+ }
+ else
+ {
+ this->Empty();
+ }
+
+ return *this;
+ }
+
+ CMStringT& operator=( const unsigned char* pszSrc )
+ {
+ return operator=( reinterpret_cast< const char* >( pszSrc ));
+ }
+
+ CMStringT& operator=( char ch )
+ {
+ char ach[2] = { ch, 0 };
+
+ return operator=( ach );
+ }
+
+ CMStringT& operator=( wchar_t ch )
+ {
+ wchar_t ach[2] = { ch, 0 };
+
+ return operator=( ach );
+ }
+
+// CMStringT& operator=( const VARIANT& var );
+
+ CMStringT& operator+=( const CMStringT& str )
+ {
+ CThisSimpleString::operator+=( str );
+ return *this;
+ }
+
+ CMStringT& operator+=( const CThisSimpleString& str )
+ {
+ CThisSimpleString::operator+=( str );
+ return *this;
+ }
+
+ CMStringT& operator+=( PCXSTR pszSrc )
+ {
+ CThisSimpleString::operator+=( pszSrc );
+ return *this;
+ }
+// template< int t_nSize >
+// CMStringT& operator+=( const CStaticString< XCHAR, t_nSize >& strSrc )
+// {
+// CThisSimpleString::operator+=( strSrc );
+//
+// return *this;
+// }
+ CMStringT& operator+=( PCYSTR pszSrc )
+ {
+ CMStringT str( pszSrc );
+
+ return operator+=( str );
+ }
+
+ CMStringT& operator+=( char ch )
+ {
+ CThisSimpleString::operator+=( ch );
+
+ return *this;
+ }
+
+ CMStringT& operator+=( unsigned char ch )
+ {
+ CThisSimpleString::operator+=( ch );
+
+ return *this;
+ }
+
+ CMStringT& operator+=( wchar_t ch )
+ {
+ CThisSimpleString::operator+=( ch );
+
+ return *this;
+ }
+
+ // Comparison
+
+ int Compare( PCXSTR psz ) const
+ {
+ return StringTraits::StringCompare( this->GetString(), psz );
+ }
+
+ int CompareNoCase( PCXSTR psz ) const
+ {
+ return StringTraits::StringCompareIgnore( this->GetString(), psz );
+ }
+
+ int Collate( PCXSTR psz ) const
+ {
+ return StringTraits::StringCollate( this->GetString(), psz );
+ }
+
+ int CollateNoCase( PCXSTR psz ) const
+ {
+ return StringTraits::StringCollateIgnore( this->GetString(), psz );
+ }
+
+ // Advanced manipulation
+
+ // Delete 'nCount' characters, starting at index 'iIndex'
+ int Delete( int iIndex, int nCount = 1 )
+ {
+ if ( iIndex < 0 )
+ iIndex = 0;
+
+ if ( nCount < 0 )
+ nCount = 0;
+
+ int nLength = this->GetLength();
+ if ( nCount + iIndex > nLength )
+ {
+ nCount = nLength-iIndex;
+ }
+ if ( nCount > 0 )
+ {
+ int nNewLength = nLength-nCount;
+ int nXCHARsToCopy = nLength-(iIndex+nCount)+1;
+ PXSTR pszBuffer = this->GetBuffer();
+ #if _MSC_VER >= 1400
+ memmove_s( pszBuffer+iIndex, nXCHARsToCopy*sizeof( XCHAR ), pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof( XCHAR ));
+ #else
+ memmove( pszBuffer+iIndex, pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof( XCHAR ));
+ #endif
+ this->ReleaseBufferSetLength( nNewLength );
+ }
+
+ return this->GetLength();
+ }
+
+ // Insert character 'ch' before index 'iIndex'
+ int Insert( int iIndex, XCHAR ch )
+ {
+ if ( iIndex < 0 )
+ iIndex = 0;
+
+ if ( iIndex > this->GetLength())
+ iIndex = this->GetLength();
+
+ int nNewLength = this->GetLength()+1;
+
+ PXSTR pszBuffer = this->GetBuffer( nNewLength );
+
+ // move existing bytes down
+ #if _MSC_VER >= 1400
+ memmove_s( pszBuffer+iIndex+1, (nNewLength-iIndex)*sizeof( XCHAR ), pszBuffer+iIndex, (nNewLength-iIndex)*sizeof( XCHAR ));
+ #else
+ memmove( pszBuffer+iIndex+1, pszBuffer+iIndex, (nNewLength-iIndex)*sizeof( XCHAR ));
+ #endif
+ pszBuffer[iIndex] = ch;
+
+ this->ReleaseBufferSetLength( nNewLength );
+ return nNewLength;
+ }
+
+ // Insert string 'psz' before index 'iIndex'
+ int Insert( int iIndex, PCXSTR psz )
+ {
+ if ( iIndex < 0 )
+ iIndex = 0;
+
+ if ( iIndex > this->GetLength())
+ {
+ iIndex = this->GetLength();
+ }
+
+ // nInsertLength and nNewLength are in XCHARs
+ int nInsertLength = StringTraits::SafeStringLen( psz );
+ int nNewLength = this->GetLength();
+ if ( nInsertLength > 0 )
+ {
+ nNewLength += nInsertLength;
+
+ PXSTR pszBuffer = this->GetBuffer( nNewLength );
+ // move existing bytes down
+ #if _MSC_VER >= 1400
+ memmove_s( pszBuffer+iIndex+nInsertLength, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ), pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ));
+ memcpy_s( pszBuffer+iIndex, nInsertLength*sizeof( XCHAR ), psz, nInsertLength*sizeof( XCHAR ));
+ #else
+ memmove( pszBuffer+iIndex+nInsertLength, pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ));
+ memcpy( pszBuffer+iIndex, psz, nInsertLength*sizeof( XCHAR ));
+ #endif
+ this->ReleaseBufferSetLength( nNewLength );
+ }
+
+ return nNewLength;
+ }
+
+ // Replace all occurrences of character 'chOld' with character 'chNew'
+ int Replace( XCHAR chOld, XCHAR chNew )
+ {
+ int nCount = 0;
+
+ // short-circuit the nop case
+ if ( chOld != chNew )
+ {
+ // otherwise modify each character that matches in the string
+ bool bCopied = false;
+ PXSTR pszBuffer = const_cast< PXSTR >( this->GetString()); // We don't actually write to pszBuffer until we've called GetBuffer().
+
+ int nLength = this->GetLength();
+ int iChar = 0;
+ while( iChar < nLength )
+ {
+ // replace instances of the specified character only
+ if ( pszBuffer[iChar] == chOld )
+ {
+ if ( !bCopied )
+ {
+ bCopied = true;
+ pszBuffer = this->GetBuffer( nLength );
+ }
+ pszBuffer[iChar] = chNew;
+ nCount++;
+ }
+ iChar = int( StringTraits::CharNext( pszBuffer+iChar )-pszBuffer );
+ }
+ if ( bCopied )
+ {
+ this->ReleaseBufferSetLength( nLength );
+ }
+ }
+
+ return nCount;
+ }
+
+ // Replace all occurrences of string 'pszOld' with string 'pszNew'
+ int Replace( PCXSTR pszOld, PCXSTR pszNew )
+ {
+ // can't have empty or NULL lpszOld
+
+ // nSourceLen is in XCHARs
+ int nSourceLen = StringTraits::SafeStringLen( pszOld );
+ if ( nSourceLen == 0 )
+ return 0;
+ // nReplacementLen is in XCHARs
+ int nReplacementLen = StringTraits::SafeStringLen( pszNew );
+
+ // loop once to figure out the size of the result string
+ int nCount = 0;
+ {
+ PCXSTR pszStart = this->GetString();
+ PCXSTR pszEnd = pszStart+this->GetLength();
+ while( pszStart < pszEnd )
+ {
+ PCXSTR pszTarget;
+ while( (pszTarget = StringTraits::StringFindString( pszStart, pszOld )) != NULL)
+ {
+ nCount++;
+ pszStart = pszTarget+nSourceLen;
+ }
+ pszStart += StringTraits::SafeStringLen( pszStart )+1;
+ }
+ }
+
+ // if any changes were made, make them
+ if ( nCount > 0 )
+ {
+ // if the buffer is too small, just
+ // allocate a new buffer (slow but sure)
+ int nOldLength = this->GetLength();
+ int nNewLength = nOldLength+(nReplacementLen-nSourceLen)*nCount;
+
+ PXSTR pszBuffer = this->GetBuffer( __max( nNewLength, nOldLength ));
+
+ PXSTR pszStart = pszBuffer;
+ PXSTR pszEnd = pszStart+nOldLength;
+
+ // loop again to actually do the work
+ while( pszStart < pszEnd )
+ {
+ PXSTR pszTarget;
+ while( (pszTarget = StringTraits::StringFindString( pszStart, pszOld )) != NULL )
+ {
+ int nBalance = nOldLength-int(pszTarget-pszBuffer+nSourceLen);
+ memmove_s( pszTarget+nReplacementLen, nBalance*sizeof( XCHAR ),
+ pszTarget+nSourceLen, nBalance*sizeof( XCHAR ));
+ memcpy_s( pszTarget, nReplacementLen*sizeof( XCHAR ),
+ pszNew, nReplacementLen*sizeof( XCHAR ));
+ pszStart = pszTarget+nReplacementLen;
+ pszTarget[nReplacementLen+nBalance] = 0;
+ nOldLength += (nReplacementLen-nSourceLen);
+ }
+ pszStart += StringTraits::SafeStringLen( pszStart )+1;
+ }
+ this->ReleaseBufferSetLength( nNewLength );
+ }
+
+ return nCount;
+ }
+
+ // Remove all occurrences of character 'chRemove'
+ int Remove( XCHAR chRemove )
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+
+ PXSTR pszSource = pszBuffer;
+ PXSTR pszDest = pszBuffer;
+ PXSTR pszEnd = pszBuffer+nLength;
+
+ while( pszSource < pszEnd )
+ {
+ PXSTR pszNewSource = StringTraits::CharNext( pszSource );
+ if ( *pszSource != chRemove )
+ {
+ // Copy the source to the destination. Remember to copy all bytes of an MBCS character
+ // Copy the source to the destination. Remember to copy all bytes of an MBCS character
+ size_t NewSourceGap = (pszNewSource-pszSource);
+ PXSTR pszNewDest = pszDest + NewSourceGap;
+ size_t i = 0;
+ for (i = 0; pszDest != pszNewDest && i < NewSourceGap; i++)
+ {
+ *pszDest = *pszSource;
+ pszSource++;
+ pszDest++;
+ }
+ }
+ pszSource = pszNewSource;
+ }
+ *pszDest = 0;
+ int nCount = int( pszSource-pszDest );
+ this->ReleaseBufferSetLength( nLength-nCount );
+
+ return nCount;
+ }
+
+ CMStringT Tokenize( PCXSTR pszTokens, int& iStart ) const
+ {
+ if ( (pszTokens == NULL) || (*pszTokens == (XCHAR)0))
+ {
+ if (iStart < this->GetLength())
+ return CMStringT( this->GetString()+iStart );
+ }
+ else
+ {
+ PCXSTR pszPlace = this->GetString()+iStart;
+ PCXSTR pszEnd = this->GetString()+this->GetLength();
+ if ( pszPlace < pszEnd )
+ {
+ int nIncluding = StringTraits::StringSpanIncluding( pszPlace, pszTokens );
+
+ if ( (pszPlace+nIncluding) < pszEnd )
+ {
+ pszPlace += nIncluding;
+ int nExcluding = StringTraits::StringSpanExcluding( pszPlace, pszTokens );
+
+ int iFrom = iStart+nIncluding;
+ int nUntil = nExcluding;
+ iStart = iFrom+nUntil+1;
+
+ return Mid( iFrom, nUntil );
+ }
+ }
+ }
+
+ // return empty string, done tokenizing
+ iStart = -1;
+
+ return CMStringT();
+ }
+
+ // find routines
+
+ // Find the first occurrence of character 'ch', starting at index 'iStart'
+ int Find( XCHAR ch, int iStart = 0 ) const
+ {
+ // nLength is in XCHARs
+ int nLength = this->GetLength();
+ if ( iStart < 0 || iStart >= nLength)
+ return -1;
+
+ // find first single character
+ PCXSTR psz = StringTraits::StringFindChar( this->GetString()+iStart, ch );
+
+ // return -1 if not found and index otherwise
+ return (psz == NULL) ? -1 : int( psz-this->GetString());
+ }
+
+ // look for a specific sub-string
+
+ // Find the first occurrence of string 'pszSub', starting at index 'iStart'
+ int Find( PCXSTR pszSub, int iStart = 0 ) const
+ {
+ // iStart is in XCHARs
+ if(pszSub == NULL)
+ return -1;
+
+ // nLength is in XCHARs
+ int nLength = this->GetLength();
+ if ( iStart < 0 || iStart > nLength )
+ return -1;
+
+ // find first matching substring
+ PCXSTR psz = StringTraits::StringFindString( this->GetString()+iStart, pszSub );
+
+ // return -1 for not found, distance from beginning otherwise
+ return (psz == NULL) ? -1 : int( psz-this->GetString());
+ }
+
+ // Find the first occurrence of any of the characters in string 'pszCharSet'
+ int FindOneOf( PCXSTR pszCharSet ) const
+ {
+ PCXSTR psz = StringTraits::StringScanSet( this->GetString(), pszCharSet );
+ return (psz == NULL) ? -1 : int( psz-this->GetString());
+ }
+
+ // Find the last occurrence of character 'ch'
+ int ReverseFind( XCHAR ch ) const
+ {
+ // find last single character
+ PCXSTR psz = StringTraits::StringFindCharRev( this->GetString(), ch );
+
+ // return -1 if not found, distance from beginning otherwise
+ return (psz == NULL) ? -1 : int( psz-this->GetString());
+ }
+
+ // manipulation
+
+ // Convert the string to uppercase
+ CMStringT& MakeUpper()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::StringUppercase( pszBuffer, nLength+1 );
+ this->ReleaseBufferSetLength( nLength );
+
+ return *this;
+ }
+
+ // Convert the string to lowercase
+ CMStringT& MakeLower()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::StringLowercase( pszBuffer, nLength+1 );
+ this->ReleaseBufferSetLength( nLength );
+
+ return *this;
+ }
+
+ // Reverse the string
+ CMStringT& MakeReverse()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::StringReverse( pszBuffer );
+ this->ReleaseBufferSetLength( nLength );
+
+ return *this;
+ }
+
+ // trimming
+
+ // Remove all trailing whitespace
+ CMStringT& TrimRight()
+ {
+ // find beginning of trailing spaces by starting
+ // at beginning (DBCS aware)
+
+ PCXSTR psz = this->GetString();
+ PCXSTR pszLast = NULL;
+
+ while( *psz != 0 )
+ {
+ if ( StringTraits::IsSpace( *psz ))
+ {
+ if ( pszLast == NULL )
+ pszLast = psz;
+ }
+ else
+ {
+ pszLast = NULL;
+ }
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if ( pszLast != NULL )
+ {
+ // truncate at trailing space start
+ int iLast = int( pszLast-this->GetString());
+
+ this->Truncate( iLast );
+ }
+
+ return *this;
+ }
+
+ // Remove all leading whitespace
+ CMStringT& TrimLeft()
+ {
+ // find first non-space character
+
+ PCXSTR psz = this->GetString();
+
+ while( StringTraits::IsSpace( *psz ))
+ {
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if ( psz != this->GetString())
+ {
+ // fix up data and length
+ int iFirst = int( psz-this->GetString());
+ PXSTR pszBuffer = this->GetBuffer( this->GetLength());
+ psz = pszBuffer+iFirst;
+ int nDataLength = this->GetLength()-iFirst;
+ memmove_s( pszBuffer, (this->GetLength()+1)*sizeof( XCHAR ),
+ psz, (nDataLength+1)*sizeof( XCHAR ));
+ this->ReleaseBufferSetLength( nDataLength );
+ }
+
+ return *this;
+ }
+
+ // Remove all leading and trailing whitespace
+ CMStringT& Trim()
+ {
+ return TrimRight().TrimLeft();
+ }
+
+ // Remove all leading and trailing occurrences of character 'chTarget'
+ CMStringT& Trim( XCHAR chTarget )
+ {
+ return TrimRight( chTarget ).TrimLeft( chTarget );
+ }
+
+ // Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets'
+ CMStringT& Trim( PCXSTR pszTargets )
+ {
+ return TrimRight( pszTargets ).TrimLeft( pszTargets );
+ }
+
+ // trimming anything (either side)
+
+ // Remove all trailing occurrences of character 'chTarget'
+ CMStringT& TrimRight( XCHAR chTarget )
+ {
+ // find beginning of trailing matches
+ // by starting at beginning (DBCS aware)
+
+ PCXSTR psz = this->GetString();
+ PCXSTR pszLast = NULL;
+
+ while( *psz != 0 )
+ {
+ if ( *psz == chTarget )
+ {
+ if ( pszLast == NULL )
+ {
+ pszLast = psz;
+ }
+ }
+ else
+ {
+ pszLast = NULL;
+ }
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if ( pszLast != NULL )
+ {
+ // truncate at left-most matching character
+ int iLast = int( pszLast-this->GetString());
+ this->Truncate( iLast );
+ }
+
+ return *this;
+ }
+
+ // Remove all trailing occurrences of any of the characters in string 'pszTargets'
+ CMStringT& TrimRight( PCXSTR pszTargets )
+ {
+ // if we're not trimming anything, we're not doing any work
+ if ( (pszTargets == NULL) || (*pszTargets == 0))
+ {
+ return *this;
+ }
+
+ // find beginning of trailing matches
+ // by starting at beginning (DBCS aware)
+
+ PCXSTR psz = this->GetString();
+ PCXSTR pszLast = NULL;
+
+ while( *psz != 0 )
+ {
+ if ( StringTraits::StringFindChar( pszTargets, *psz ) != NULL )
+ {
+ if ( pszLast == NULL )
+ {
+ pszLast = psz;
+ }
+ }
+ else
+ {
+ pszLast = NULL;
+ }
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if ( pszLast != NULL )
+ {
+ // truncate at left-most matching character
+ int iLast = int( pszLast-this->GetString());
+ this->Truncate( iLast );
+ }
+
+ return *this;
+ }
+
+ // Remove all leading occurrences of character 'chTarget'
+ CMStringT& TrimLeft( XCHAR chTarget )
+ {
+ // find first non-matching character
+ PCXSTR psz = this->GetString();
+
+ while( chTarget == *psz )
+ {
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if ( psz != this->GetString())
+ {
+ // fix up data and length
+ int iFirst = int( psz-this->GetString());
+ PXSTR pszBuffer = this->GetBuffer( this->GetLength());
+ psz = pszBuffer+iFirst;
+ int nDataLength = this->GetLength()-iFirst;
+ memmove_s( pszBuffer, (this->GetLength()+1)*sizeof( XCHAR ),
+ psz, (nDataLength+1)*sizeof( XCHAR ));
+ this->ReleaseBufferSetLength( nDataLength );
+ }
+
+ return *this;
+ }
+
+ // Remove all leading occurrences of any of the characters in string 'pszTargets'
+ CMStringT& TrimLeft( PCXSTR pszTargets )
+ {
+ // if we're not trimming anything, we're not doing any work
+ if ( (pszTargets == NULL) || (*pszTargets == 0))
+ {
+ return *this;
+ }
+
+ PCXSTR psz = this->GetString();
+ while( (*psz != 0) && (StringTraits::StringFindChar( pszTargets, *psz ) != NULL))
+ {
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if ( psz != this->GetString())
+ {
+ // fix up data and length
+ int iFirst = int( psz-this->GetString());
+ PXSTR pszBuffer = this->GetBuffer( this->GetLength());
+ psz = pszBuffer+iFirst;
+ int nDataLength = this->GetLength()-iFirst;
+ memmove_s( pszBuffer, (this->GetLength()+1)*sizeof( XCHAR ),
+ psz, (nDataLength+1)*sizeof( XCHAR ));
+ this->ReleaseBufferSetLength( nDataLength );
+ }
+
+ return *this;
+ }
+
+ // Convert the string to the OEM character set
+ void AnsiToOem()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::ConvertToOem( pszBuffer, nLength+1 );
+ this->ReleaseBufferSetLength( nLength );
+ }
+
+ // Convert the string to the ANSI character set
+ void OemToAnsi()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::ConvertToAnsi( pszBuffer, nLength+1 );
+ this->ReleaseBufferSetLength( nLength );
+ }
+
+ // Very simple sub-string extraction
+
+ // Return the substring starting at index 'iFirst'
+ CMStringT Mid( int iFirst ) const
+ {
+ return Mid( iFirst, this->GetLength()-iFirst );
+ }
+
+ // Return the substring starting at index 'iFirst', with length 'nCount'
+ CMStringT Mid( int iFirst, int nCount ) const
+ {
+ // nCount is in XCHARs
+
+ // out-of-bounds requests return sensible things
+ if (iFirst < 0)
+ iFirst = 0;
+ if (nCount < 0)
+ nCount = 0;
+
+ if ( (iFirst + nCount) > this->GetLength())
+ nCount = this->GetLength()-iFirst;
+
+ if ( iFirst > this->GetLength())
+ nCount = 0;
+
+ // optimize case of returning entire string
+ if ( (iFirst == 0) && ((iFirst+nCount) == this->GetLength()))
+ return *this;
+
+ return CMStringT( this->GetString()+iFirst, nCount );
+ }
+
+ // Return the substring consisting of the rightmost 'nCount' characters
+ CMStringT Right( int nCount ) const
+ {
+ // nCount is in XCHARs
+ if (nCount < 0)
+ nCount = 0;
+
+ int nLength = this->GetLength();
+ if ( nCount >= nLength )
+ {
+ return *this;
+ }
+
+ return CMStringT( this->GetString()+nLength-nCount, nCount );
+ }
+
+ // Return the substring consisting of the leftmost 'nCount' characters
+ CMStringT Left( int nCount ) const
+ {
+ // nCount is in XCHARs
+ if (nCount < 0)
+ nCount = 0;
+
+ int nLength = this->GetLength();
+ if ( nCount >= nLength )
+ return *this;
+
+ return CMStringT( this->GetString(), nCount );
+ }
+
+ // Return the substring consisting of the leftmost characters in the set 'pszCharSet'
+ CMStringT SpanIncluding( PCXSTR pszCharSet ) const
+ {
+ return Left( StringTraits::StringSpanIncluding( this->GetString(), pszCharSet ));
+ }
+
+ // Return the substring consisting of the leftmost characters not in the set 'pszCharSet'
+ CMStringT SpanExcluding( PCXSTR pszCharSet ) const
+ {
+ return Left( StringTraits::StringSpanExcluding( this->GetString(), pszCharSet ));
+ }
+
+ // Format data using format string 'pszFormat'
+ void Format( PCXSTR pszFormat, ... );
+
+ // Append formatted data using format string 'pszFormat'
+ void AppendFormat( PCXSTR pszFormat, ... );
+
+ void AppendFormatV( PCXSTR pszFormat, va_list args )
+ {
+ int nCurrentLength = this->GetLength();
+ int nAppendLength = StringTraits::GetFormattedLength( pszFormat, args );
+ PXSTR pszBuffer = this->GetBuffer( nCurrentLength+nAppendLength );
+ StringTraits::Format( pszBuffer+nCurrentLength,
+ nAppendLength+1, pszFormat, args );
+ this->ReleaseBufferSetLength( nCurrentLength+nAppendLength );
+ }
+
+ void FormatV( PCXSTR pszFormat, va_list args )
+ {
+ int nLength = StringTraits::GetFormattedLength( pszFormat, args );
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::Format( pszBuffer, nLength+1, pszFormat, args );
+ this->ReleaseBufferSetLength( nLength );
+ }
+
+ // OLE BSTR support
+
+ // Allocate a BSTR containing a copy of the string
+ BSTR AllocSysString() const
+ {
+ BSTR bstrResult = StringTraits::AllocSysString( this->GetString(), this->GetLength());
+ return bstrResult;
+ }
+
+ BSTR SetSysString( BSTR* pbstr ) const
+ {
+ StringTraits::ReAllocSysString( this->GetString(), pbstr, this->GetLength());
+ return *pbstr;
+ }
+
+ // Set the string to the value of environment variable 'pszVar'
+ BOOL GetEnvironmentVariable( PCXSTR pszVar )
+ {
+ ULONG nLength = StringTraits::GetEnvironmentVariable( pszVar, NULL, 0 );
+ BOOL bRetVal = FALSE;
+
+ if ( nLength == 0 )
+ {
+ this->Empty();
+ }
+ else
+ {
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::GetEnvironmentVariable( pszVar, pszBuffer, nLength );
+ this->ReleaseBuffer();
+ bRetVal = TRUE;
+ }
+
+ return bRetVal;
+ }
+
+ // Load the string from resource 'nID'
+ BOOL LoadString( UINT nID )
+ {
+ HINSTANCE hInst = StringTraits::FindStringResourceInstance( nID );
+ if ( hInst == NULL )
+ return FALSE;
+
+ return LoadString( hInst, nID );
+ }
+
+ friend CMStringT __stdcall operator+( const CMStringT& str1, const CMStringT& str2 )
+ {
+ CMStringT strResult;
+
+ Concatenate( strResult, str1, str1.GetLength(), str2, str2.GetLength());
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( const CMStringT& str1, PCXSTR psz2 )
+ {
+ CMStringT strResult;
+
+ Concatenate( strResult, str1, str1.GetLength(), psz2, StringLength( psz2 ));
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( PCXSTR psz1, const CMStringT& str2 )
+ {
+ CMStringT strResult;
+
+ Concatenate( strResult, psz1, StringLength( psz1 ), str2, str2.GetLength());
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( const CMStringT& str1, wchar_t ch2 )
+ {
+ CMStringT strResult;
+ XCHAR chTemp = XCHAR( ch2 );
+
+ Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( const CMStringT& str1, char ch2 )
+ {
+ CMStringT strResult;
+ XCHAR chTemp = XCHAR( ch2 );
+
+ Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( wchar_t ch1, const CMStringT& str2 )
+ {
+ CMStringT strResult;
+ XCHAR chTemp = XCHAR( ch1 );
+
+ Concatenate( strResult, &chTemp, 1, str2, str2.GetLength());
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( char ch1, const CMStringT& str2 )
+ {
+ CMStringT strResult;
+ XCHAR chTemp = XCHAR( ch1 );
+
+ Concatenate( strResult, &chTemp, 1, str2, str2.GetLength());
+
+ return strResult;
+ }
+
+ friend bool __stdcall operator==( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) == 0;
+ }
+
+ friend bool __stdcall operator==( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) == 0;
+ }
+
+ friend bool __stdcall operator==( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) == 0;
+ }
+
+ friend bool __stdcall operator==( const CMStringT& str1, PCYSTR psz2 )
+ {
+ CMStringT str2( psz2 );
+
+ return str1 == str2;
+ }
+
+ friend bool __stdcall operator==( PCYSTR psz1, const CMStringT& str2 )
+ {
+ CMStringT str1( psz1 );
+
+ return str1 == str2;
+ }
+
+ friend bool __stdcall operator!=( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) != 0;
+ }
+
+ friend bool __stdcall operator!=( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) != 0;
+ }
+
+ friend bool __stdcall operator!=( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) != 0;
+ }
+
+ friend bool __stdcall operator!=( const CMStringT& str1, PCYSTR psz2 )
+ {
+ CMStringT str2( psz2 );
+
+ return str1 != str2;
+ }
+
+ friend bool __stdcall operator!=( PCYSTR psz1, const CMStringT& str2 )
+ {
+ CMStringT str1( psz1 );
+
+ return str1 != str2;
+ }
+
+ friend bool __stdcall operator<( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) < 0;
+ }
+
+ friend bool __stdcall operator<( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) < 0;
+ }
+
+ friend bool __stdcall operator<( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) > 0;
+ }
+
+ friend bool __stdcall operator>( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) > 0;
+ }
+
+ friend bool __stdcall operator>( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) > 0;
+ }
+
+ friend bool __stdcall operator>( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) < 0;
+ }
+
+ friend bool __stdcall operator<=( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) <= 0;
+ }
+
+ friend bool __stdcall operator<=( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) <= 0;
+ }
+
+ friend bool __stdcall operator<=( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) >= 0;
+ }
+
+ friend bool __stdcall operator>=( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) >= 0;
+ }
+
+ friend bool __stdcall operator>=( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) >= 0;
+ }
+
+ friend bool __stdcall operator>=( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) <= 0;
+ }
+
+ friend bool __stdcall operator==( XCHAR ch1, const CMStringT& str2 )
+ {
+ return (str2.GetLength() == 1) && (str2[0] == ch1);
+ }
+
+ friend bool __stdcall operator==( const CMStringT& str1, XCHAR ch2 )
+ {
+ return (str1.GetLength() == 1) && (str1[0] == ch2);
+ }
+
+ friend bool __stdcall operator!=( XCHAR ch1, const CMStringT& str2 )
+ {
+ return (str2.GetLength() != 1) || (str2[0] != ch1);
+ }
+
+ friend bool __stdcall operator!=( const CMStringT& str1, XCHAR ch2 )
+ {
+ return (str1.GetLength() != 1) || (str1[0] != ch2);
+ }
+};
+
+template< typename BaseType, class StringTraits >
+inline void CMStringT<BaseType, StringTraits>::Format(PCXSTR pszFormat, ... )
+{
+ va_list argList;
+ va_start( argList, pszFormat );
+ FormatV( pszFormat, argList );
+ va_end( argList );
+}
+
+template< typename BaseType, class StringTraits >
+inline void CMStringT<BaseType, StringTraits>::AppendFormat(PCXSTR pszFormat, ... )
+{
+ va_list argList;
+ va_start( argList, pszFormat );
+ AppendFormatV( pszFormat, argList );
+ va_end( argList );
+}
+
+typedef CMStringT< wchar_t, ChTraitsCRT< wchar_t > > CMStringW;
+typedef CMStringT< char, ChTraitsCRT< char > > CMStringA;
+typedef CMStringT< TCHAR, ChTraitsCRT< TCHAR > > CMString;
diff --git a/protocols/JabberG/src/jabber.cpp b/protocols/JabberG/src/jabber.cpp new file mode 100644 index 0000000000..a3dd31a3fa --- /dev/null +++ b/protocols/JabberG/src/jabber.cpp @@ -0,0 +1,279 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+#include "jabber_rc.h"
+
+#include <locale.h>
+
+#include <m_fontservice.h>
+#include <m_icolib.h>
+
+#include "m_assocmgr.h"
+#include "m_folders.h"
+#include "m_toptoolbar.h"
+#include "m_extraicons.h"
+
+HINSTANCE hInst;
+
+int hLangpack;
+
+int g_cbCountries;
+struct CountryListEntry* g_countries;
+
+TCHAR szCoreVersion[100];
+
+PLUGININFOEX pluginInfo = {
+ sizeof( PLUGININFOEX ),
+ "Jabber Protocol",
+ __VERSION_DWORD,
+ "Jabber protocol plugin for Miranda NG.",
+ "George Hazan, Maxim Mluhov, Victor Pavlychko, Artem Shpynov, Michael Stepura",
+ "ghazan@miranda-im.org",
+ "(c) 2005-2012 George Hazan, Maxim Mluhov, Victor Pavlychko, Artem Shpynov, Michael Stepura",
+ "http://miranda-ng.org/",
+ UNICODE_AWARE,
+ {0x144e80a2, 0xd198, 0x428b, {0xac, 0xbe, 0x9d, 0x55, 0xda, 0xcc, 0x7f, 0xde}} // {144E80A2-D198-428b-ACBE-9D55DACC7FDE}
+};
+
+XML_API xi;
+TIME_API tmi;
+
+CLIST_INTERFACE* pcli;
+
+/////////////////////////////////////////////////////////////////////////////
+// Theme API
+BOOL (WINAPI *JabberAlphaBlend)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION) = NULL;
+BOOL (WINAPI *JabberIsThemeActive)() = NULL;
+HRESULT (WINAPI *JabberDrawThemeParentBackground)(HWND, HDC, RECT *) = NULL;
+/////////////////////////////////////////////////////////////////////////////
+
+BOOL jabberChatDllPresent = FALSE;
+HANDLE hModulesLoaded, hModulesLoadedTB;
+
+HANDLE hExtraActivity = NULL;
+HANDLE hExtraMood = NULL;
+
+void JabberUserInfoInit(void);
+
+int bSecureIM;
+
+/////////////////////////////////////////////////////////////////////////////
+// Protocol instances
+static int sttCompareProtocols(const CJabberProto *p1, const CJabberProto *p2)
+{
+ return lstrcmp(p1->m_tszUserName, p2->m_tszUserName);
+}
+
+LIST<CJabberProto> g_Instances(1, sttCompareProtocols);
+/////////////////////////////////////////////////////////////////////////////
+
+BOOL WINAPI DllMain( HINSTANCE hModule, DWORD, LPVOID )
+{
+ hInst = hModule;
+ return TRUE;
+}
+
+extern "C" __declspec( dllexport ) PLUGININFOEX *MirandaPluginInfoEx( DWORD mirandaVersion )
+{
+ return &pluginInfo;
+}
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PROTOCOL, MIID_LAST};
+
+///////////////////////////////////////////////////////////////////////////////
+// OnPreShutdown - prepares Miranda to be shut down
+
+int __cdecl CJabberProto::OnPreShutdown( WPARAM, LPARAM )
+{
+ UI_SAFE_CLOSE_HWND(m_hwndAgentRegInput);
+ UI_SAFE_CLOSE_HWND(m_hwndRegProgress);
+ UI_SAFE_CLOSE_HWND(m_hwndMucVoiceList);
+ UI_SAFE_CLOSE_HWND(m_hwndMucMemberList);
+ UI_SAFE_CLOSE_HWND(m_hwndMucModeratorList);
+ UI_SAFE_CLOSE_HWND(m_hwndMucBanList);
+ UI_SAFE_CLOSE_HWND(m_hwndMucAdminList);
+ UI_SAFE_CLOSE_HWND(m_hwndMucOwnerList);
+ UI_SAFE_CLOSE_HWND(m_hwndJabberChangePassword);
+ UI_SAFE_CLOSE_HWND(m_hwndJabberAddBookmark);
+ UI_SAFE_CLOSE_HWND(m_hwndPrivacyRule);
+
+ UI_SAFE_CLOSE(m_pDlgPrivacyLists);
+ UI_SAFE_CLOSE(m_pDlgBookmarks);
+ UI_SAFE_CLOSE(m_pDlgServiceDiscovery);
+ UI_SAFE_CLOSE(m_pDlgJabberJoinGroupchat);
+ UI_SAFE_CLOSE(m_pDlgNotes);
+
+ m_iqManager.ExpireAll();
+ m_iqManager.Shutdown();
+ m_messageManager.Shutdown();
+ m_presenceManager.Shutdown();
+ m_sendManager.Shutdown();
+ ConsoleUninit();
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OnModulesLoaded - execute some code when all plugins are initialized
+
+static INT_PTR g_SvcParseXmppUri(WPARAM w, LPARAM l)
+{
+ if (CJabberProto *ppro = JabberChooseInstance(true))
+ return ppro->JabberServiceParseXmppURI(w, l);
+ return 0;
+}
+
+static int OnModulesLoaded( WPARAM, LPARAM )
+{
+ hModulesLoadedTB = HookEvent(ME_TTB_MODULELOADED, g_OnToolbarInit);
+
+ bSecureIM = (ServiceExists("SecureIM/IsContactSecured"));
+
+ // file associations manager plugin support
+ if ( ServiceExists( MS_ASSOCMGR_ADDNEWURLTYPE )) {
+ CreateServiceFunction("JABBER/*" JS_PARSE_XMPP_URI, g_SvcParseXmppUri );
+ AssocMgr_AddNewUrlTypeT( "xmpp:", TranslateT("Jabber Link Protocol"), hInst, IDI_JABBER, "JABBER/*" JS_PARSE_XMPP_URI, 0 );
+ }
+
+ // init fontservice for info frame
+ FontID fontid = {0};
+ fontid.cbSize = sizeof(fontid);
+ strcpy(fontid.group, "Jabber");
+ strcpy(fontid.dbSettingsGroup, GLOBAL_SETTING_MODULE);
+ strcpy(fontid.backgroundGroup, "Jabber");
+ strcpy(fontid.backgroundName,"Background");
+ fontid.flags = FIDF_DEFAULTVALID;
+
+ fontid.deffontsettings.charset = DEFAULT_CHARSET;
+ fontid.deffontsettings.colour = GetSysColor(COLOR_WINDOWTEXT);
+ fontid.deffontsettings.size = -11;
+ lstrcpyA(fontid.deffontsettings.szFace, "MS Shell Dlg");
+ fontid.deffontsettings.style = 0;
+
+ strcpy(fontid.name, "Frame title");
+ strcpy(fontid.prefix, "fntFrameTitle");
+ fontid.deffontsettings.style = DBFONTF_BOLD;
+ FontRegister(&fontid);
+
+ strcpy(fontid.name, "Frame text");
+ strcpy(fontid.prefix, "fntFrameClock");
+ fontid.deffontsettings.style = 0;
+ FontRegister(&fontid);
+
+ ColourID colourid = {0};
+ colourid.cbSize = sizeof(colourid);
+ strcpy(colourid.group, "Jabber");
+ strcpy(colourid.dbSettingsGroup, GLOBAL_SETTING_MODULE);
+
+ strcpy(colourid.name, "Background");
+ strcpy(colourid.setting, "clFrameBack");
+ colourid.defcolour = GetSysColor(COLOR_WINDOW);
+ ColourRegister(&colourid);
+
+ // Init extra icons
+ hExtraActivity = ExtraIcon_Register("activity", "Jabber Activity" /* No icons registered, "working" */);
+ hExtraMood = ExtraIcon_Register("mood", "Jabber Mood" /* No icons registered, "amazed" */);
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OnLoad - initialize the plugin instance
+
+static CJabberProto* jabberProtoInit( const char* pszProtoName, const TCHAR* tszUserName )
+{
+ CJabberProto *ppro = new CJabberProto( pszProtoName, tszUserName );
+ g_Instances.insert(ppro);
+ return ppro;
+}
+
+static int jabberProtoUninit( CJabberProto* ppro )
+{
+ g_Instances.remove(ppro);
+ delete ppro;
+ return 0;
+}
+
+extern "C" int __declspec( dllexport ) Load()
+{
+ // set the memory, lists & utf8 managers
+ mir_getXI( &xi );
+ mir_getTMI( &tmi );
+ mir_getLP( &pluginInfo );
+
+ WORD v[4];
+ CallService(MS_SYSTEM_GETFILEVERSION, 0, (LPARAM)v);
+ mir_sntprintf(szCoreVersion, SIZEOF(szCoreVersion), _T("%d.%d.%d.%d"), v[0], v[1], v[2], v[3]);
+
+ CallService( MS_UTILS_GETCOUNTRYLIST, ( WPARAM )&g_cbCountries, ( LPARAM )&g_countries );
+
+ setlocale(LC_ALL, "");
+
+ pcli = ( CLIST_INTERFACE* )CallService(MS_CLIST_RETRIEVE_INTERFACE, 0, (LPARAM)hInst);
+
+ // Register protocol module
+ PROTOCOLDESCRIPTOR pd;
+ ZeroMemory( &pd, sizeof( PROTOCOLDESCRIPTOR ));
+ pd.cbSize = sizeof( PROTOCOLDESCRIPTOR );
+ pd.szName = "JABBER";
+ pd.fnInit = ( pfnInitProto )jabberProtoInit;
+ pd.fnUninit = ( pfnUninitProto )jabberProtoUninit;
+ pd.type = PROTOTYPE_PROTOCOL;
+ CallService( MS_PROTO_REGISTERMODULE, 0, ( LPARAM )&pd );
+
+ // Load some fuctions
+ HMODULE hDll;
+ if ( hDll = GetModuleHandleA( "gdi32.dll" ))
+ JabberAlphaBlend = (BOOL (WINAPI *)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION)) GetProcAddress(hDll, "GdiAlphaBlend");
+ if ( JabberAlphaBlend == NULL && ( hDll = LoadLibraryA("msimg32.dll" )))
+ JabberAlphaBlend = (BOOL (WINAPI *)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION)) GetProcAddress(hDll, "AlphaBlend");
+
+ if ( IsWinVerXPPlus()) {
+ if ( hDll = GetModuleHandleA("uxtheme")) {
+ JabberDrawThemeParentBackground = (HRESULT (WINAPI *)(HWND,HDC,RECT *))GetProcAddress(hDll, "DrawThemeParentBackground");
+ JabberIsThemeActive = (BOOL (WINAPI *)())GetProcAddress(hDll, "IsThemeActive");
+ } }
+
+ g_IconsInit();
+ g_MenuInit();
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ JabberUserInfoInit();
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Unload - destroy the plugin instance
+
+extern "C" int __declspec( dllexport ) Unload( void )
+{
+ UnhookEvent(hModulesLoaded);
+ UnhookEvent(hModulesLoadedTB);
+
+ g_MenuUninit();
+
+ g_Instances.destroy();
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber.h b/protocols/JabberG/src/jabber.h new file mode 100644 index 0000000000..957b7d66bf --- /dev/null +++ b/protocols/JabberG/src/jabber.h @@ -0,0 +1,767 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_H_
+#define _JABBER_H_
+
+#ifdef _MSC_VER
+ #pragma warning(disable:4706 4121 4127)
+#endif
+
+#define MIRANDA_VER 0x0A00
+
+#include "m_stdhdr.h"
+
+#define LISTFOREACH(var__, obj__, list__) \
+ for (int var__ = 0; (var__ = obj__->ListFindNext(list__, var__)) >= 0; ++var__)
+#define LISTFOREACH_NODEF(var__, obj__, list__) \
+ for (var__ = 0; (var__ = obj__->ListFindNext(list__, var__)) >= 0; ++var__)
+
+/*******************************************************************
+ * Global header files
+ *******************************************************************/
+#define _WIN32_WINNT 0x501
+#define _WIN32_IE 0x501
+#include <windows.h>
+#include <commctrl.h>
+#include <uxtheme.h>
+#include <process.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_netlib.h>
+#include <m_protomod.h>
+#include <m_protosvc.h>
+#include <m_protoint.h>
+#include <m_clist.h>
+#include <m_clui.h>
+#include <m_options.h>
+#include <m_userinfo.h>
+#include <m_database.h>
+#include <m_langpack.h>
+#include <m_utils.h>
+#include <m_message.h>
+#include <m_skin.h>
+#include <m_chat.h>
+#include <m_clc.h>
+#include <m_clistint.h>
+#include <m_button.h>
+#include <m_avatars.h>
+#include <m_idle.h>
+#include <m_timezones.h>
+#include <m_jabber.h>
+#include <m_fingerprint.h>
+#include <win2k.h>
+
+#include "../../plugins/zlib/zlib.h"
+
+#include "resource.h"
+#include "version.h"
+
+#include "MString.h"
+
+#include "jabber_xml.h"
+#include "jabber_byte.h"
+#include "jabber_ibb.h"
+#include "jabber_db_utils.h"
+#include "ui_utils.h"
+
+struct CJabberProto;
+
+class CJabberDlgBase: public CProtoDlgBase<CJabberProto>
+{
+ typedef CProtoDlgBase<CJabberProto> CSuper;
+protected:
+ __inline CJabberDlgBase(CJabberProto *proto, int idDialog, HWND parent, bool show_label=true ) :
+ CSuper( proto, idDialog, parent, show_label )
+ {
+ }
+
+ int Resizer(UTILRESIZECONTROL *urc)
+ {
+ switch (urc->wId) {
+ case IDC_HEADERBAR:
+ urc->rcItem.right = urc->dlgNewSize.cx;
+ return 0;
+ }
+
+ return CSuper::Resizer(urc);
+ }
+};
+
+#if !defined(OPENFILENAME_SIZE_VERSION_400)
+ #define OPENFILENAME_SIZE_VERSION_400 sizeof(OPENFILENAME)
+#endif
+
+/*******************************************************************
+ * Global constants
+ *******************************************************************/
+
+#define GLOBAL_SETTING_PREFIX "JABBER"
+#define GLOBAL_SETTING_MODULE "JABBER"
+
+#define JABBER_DEFAULT_PORT 5222
+#define JABBER_IQID "mir_"
+#define JABBER_MAX_JID_LEN 1024
+
+#define JABBER_GC_MSG_QUIT LPGENT("I'm happy Miranda NG user. Get it at http://miranda-ng.org/.")
+#define JABBER_GC_MSG_SLAP LPGENT("/me slaps %s around a bit with a large trout")
+
+// registered db event types
+#define JABBER_DB_EVENT_TYPE_CHATSTATES 2000
+#define JS_DB_GETEVENTTEXT_CHATSTATES "/GetEventText2000"
+#define JABBER_DB_EVENT_CHATSTATES_GONE 1
+#define JABBER_DB_EVENT_TYPE_PRESENCE 2001
+#define JS_DB_GETEVENTTEXT_PRESENCE "/GetEventText2001"
+#define JABBER_DB_EVENT_PRESENCE_SUBSCRIBE 1
+#define JABBER_DB_EVENT_PRESENCE_SUBSCRIBED 2
+#define JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBE 3
+#define JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBED 4
+#define JABBER_DB_EVENT_PRESENCE_ERROR 5
+
+// User-defined message
+#define WM_JABBER_REGDLG_UPDATE (WM_PROTO_LAST + 100)
+#define WM_JABBER_AGENT_REFRESH (WM_PROTO_LAST + 101)
+#define WM_JABBER_TRANSPORT_REFRESH (WM_PROTO_LAST + 102)
+#define WM_JABBER_REGINPUT_ACTIVATE (WM_PROTO_LAST + 103)
+#define WM_JABBER_REFRESH WM_PROTO_REFRESH
+#define WM_JABBER_CHECK_ONLINE WM_PROTO_CHECK_ONLINE
+#define WM_JABBER_ACTIVATE WM_PROTO_ACTIVATE
+#define WM_JABBER_CHANGED (WM_PROTO_LAST + 106)
+#define WM_JABBER_SET_FONT (WM_PROTO_LAST + 108)
+#define WM_JABBER_FLASHWND (WM_PROTO_LAST + 109)
+#define WM_JABBER_GC_MEMBER_ADD (WM_PROTO_LAST + 110)
+#define WM_JABBER_GC_FORCE_QUIT (WM_PROTO_LAST + 111)
+#define WM_JABBER_SHUTDOWN (WM_PROTO_LAST + 112)
+#define WM_JABBER_SMILEY (WM_PROTO_LAST + 113)
+#define WM_JABBER_JOIN (WM_PROTO_LAST + 114)
+#define WM_JABBER_ADD_TO_ROSTER (WM_PROTO_LAST + 115)
+#define WM_JABBER_ADD_TO_BOOKMARKS (WM_PROTO_LAST + 116)
+#define WM_JABBER_REFRESH_VCARD (WM_PROTO_LAST + 117)
+
+
+// Error code
+#define JABBER_ERROR_REDIRECT 302
+#define JABBER_ERROR_BAD_REQUEST 400
+#define JABBER_ERROR_UNAUTHORIZED 401
+#define JABBER_ERROR_PAYMENT_REQUIRED 402
+#define JABBER_ERROR_FORBIDDEN 403
+#define JABBER_ERROR_NOT_FOUND 404
+#define JABBER_ERROR_NOT_ALLOWED 405
+#define JABBER_ERROR_NOT_ACCEPTABLE 406
+#define JABBER_ERROR_REGISTRATION_REQUIRED 407
+#define JABBER_ERROR_REQUEST_TIMEOUT 408
+#define JABBER_ERROR_CONFLICT 409
+#define JABBER_ERROR_INTERNAL_SERVER_ERROR 500
+#define JABBER_ERROR_NOT_IMPLEMENTED 501
+#define JABBER_ERROR_REMOTE_SERVER_ERROR 502
+#define JABBER_ERROR_SERVICE_UNAVAILABLE 503
+#define JABBER_ERROR_REMOTE_SERVER_TIMEOUT 504
+
+// Vcard flags
+#define JABBER_VCEMAIL_HOME 1
+#define JABBER_VCEMAIL_WORK 2
+#define JABBER_VCEMAIL_INTERNET 4
+#define JABBER_VCEMAIL_X400 8
+
+#define JABBER_VCTEL_HOME 0x0001
+#define JABBER_VCTEL_WORK 0x0002
+#define JABBER_VCTEL_VOICE 0x0004
+#define JABBER_VCTEL_FAX 0x0008
+#define JABBER_VCTEL_PAGER 0x0010
+#define JABBER_VCTEL_MSG 0x0020
+#define JABBER_VCTEL_CELL 0x0040
+#define JABBER_VCTEL_VIDEO 0x0080
+#define JABBER_VCTEL_BBS 0x0100
+#define JABBER_VCTEL_MODEM 0x0200
+#define JABBER_VCTEL_ISDN 0x0400
+#define JABBER_VCTEL_PCS 0x0800
+
+// File transfer setting
+#define JABBER_OPTION_FT_DIRECT 0 // Direct connection
+#define JABBER_OPTION_FT_PASS 1 // Use PASS server
+#define JABBER_OPTION_FT_PROXY 2 // Use proxy with local port forwarding
+
+// Font style saved in DB
+#define JABBER_FONT_BOLD 1
+#define JABBER_FONT_ITALIC 2
+
+// Font for groupchat log dialog
+#define JABBER_GCLOG_NUM_FONT 6 // 6 fonts ( 0:send, 1:msg, 2:time, 3:nick, 4:sys, 5:/me )
+
+// Old SDK don't have this
+#ifndef SPI_GETSCREENSAVERRUNNING
+#define SPI_GETSCREENSAVERRUNNING 114
+#endif
+
+// Icon list
+enum {
+ JABBER_IDI_GCOWNER = 0,
+ JABBER_IDI_GCADMIN,
+ JABBER_IDI_GCMODERATOR,
+ JABBER_IDI_GCVOICE,
+ JABBER_ICON_TOTAL
+};
+
+// Services and Events
+#define JS_PARSE_XMPP_URI "/ParseXmppURI"
+
+// XEP-0224 support (Attention/Nudge)
+#define JS_SEND_NUDGE "/SendNudge"
+#define JE_NUDGE "/Nudge"
+
+// Called when contact changes custom status and extra icon is set to clist_mw
+//wParam = hContact // contact changing status
+//lParam = hIcon // HANDLE to clist extra icon set as custom status
+#define JE_CUSTOMSTATUS_EXTRAICON_CHANGED "/XStatusExtraIconChanged"
+#define JE_CUSTOMSTATUS_CHANGED "/XStatusChanged"
+
+#define LR_BIGICON 0x40
+
+#define JS_SENDXML "/SendXML" // Warning: This service is obsolete. Use IJabberNetInterface::SendXmlNode() instead.
+#define JS_GETADVANCEDSTATUSICON "/GetAdvancedStatusIcon"
+#define JS_GETCUSTOMSTATUSICON "/GetXStatusIcon"
+#define JS_GETXSTATUS "/GetXStatus"
+#define JS_SETXSTATUS "/SetXStatus"
+
+#define JS_HTTP_AUTH "/HttpAuthRequest"
+#define JS_INCOMING_NOTE_EVENT "/IncomingNoteEvent"
+
+#define DBSETTING_DISPLAY_UID "display_uid"
+#define DBSETTING_XSTATUSID "XStatusId"
+#define DBSETTING_XSTATUSNAME "XStatusName"
+#define DBSETTING_XSTATUSMSG "XStatusMsg"
+
+#define ADVSTATUS_MOOD "mood"
+#define ADVSTATUS_ACTIVITY "activity"
+#define ADVSTATUS_TUNE "tune"
+
+#define ADVSTATUS_VAL_ID "id"
+#define ADVSTATUS_VAL_ICON "icon"
+#define ADVSTATUS_VAL_TITLE "title"
+#define ADVSTATUS_VAL_TEXT "text"
+
+struct CJabberHttpAuthParams
+{
+ enum {IQ = 1, MSG = 2} m_nType;
+ TCHAR *m_szFrom;
+ TCHAR *m_szIqId;
+ TCHAR *m_szThreadId;
+ TCHAR *m_szId;
+ TCHAR *m_szMethod;
+ TCHAR *m_szUrl;
+ CJabberHttpAuthParams()
+ {
+ ZeroMemory(this, sizeof(CJabberHttpAuthParams));
+ }
+ ~CJabberHttpAuthParams()
+ {
+ Free();
+ }
+ void Free()
+ {
+ mir_free(m_szFrom);
+ mir_free(m_szIqId);
+ mir_free(m_szThreadId);
+ mir_free(m_szId);
+ mir_free(m_szMethod);
+ mir_free(m_szUrl);
+ ZeroMemory(this, sizeof(CJabberHttpAuthParams));
+ }
+};
+
+/*******************************************************************
+ * Global data structures and data type definitions
+ *******************************************************************/
+typedef HANDLE JABBER_SOCKET;
+
+enum JABBER_SESSION_TYPE
+{
+ JABBER_SESSION_NORMAL,
+ JABBER_SESSION_REGISTER
+};
+
+#define CAPS_BOOKMARK 0x0001
+#define CAPS_BOOKMARKS_LOADED 0x8000
+
+#define ZLIB_CHUNK_SIZE 2048
+
+#include "jabber_caps.h"
+
+#define JABBER_LOGIN_ROSTER 0x0001
+#define JABBER_LOGIN_BOOKMARKS 0x0002
+#define JABBER_LOGIN_SERVERINFO 0x0004
+#define JABBER_LOGIN_BOOKMARKS_AJ 0x0008
+
+struct ThreadData
+{
+ ThreadData( CJabberProto* _ppro, JABBER_SESSION_TYPE parType );
+ ~ThreadData();
+
+ HANDLE hThread;
+ JABBER_SESSION_TYPE type;
+
+ // network support
+ JABBER_SOCKET s;
+ BOOL useSSL;
+ HANDLE iomutex; // protects i/o operations
+ CJabberProto* proto;
+
+ // XEP-0138 (Compression support)
+ BOOL useZlib;
+ z_stream zStreamIn,zStreamOut;
+ bool zRecvReady;
+ int zRecvDatalen;
+ char* zRecvData;
+
+ void xmpp_client_query( void );
+
+ BOOL zlibInit( void );
+ void zlibUninit();
+ int zlibSend( char* data, int datalen );
+ int zlibRecv( char* data, long datalen );
+
+ // for nick names resolving
+ int resolveID;
+ HANDLE resolveContact;
+
+ // features & registration
+ HWND reg_hwndDlg;
+ BOOL reg_done, bIsSessionAvailable;
+ class TJabberAuth* auth;
+ JabberCapsBits jabberServerCaps;
+ BOOL bBookmarksLoaded;
+ DWORD dwLoginRqs;
+
+ // connection & login data
+ TCHAR username[512];
+ TCHAR password[512];
+ char server[128];
+ char manualHost[128];
+ TCHAR resource[128];
+ TCHAR fullJID[JABBER_MAX_JID_LEN];
+ WORD port;
+ TCHAR newPassword[512];
+
+ void close( void );
+ void shutdown( void );
+ int recv( char* buf, size_t len );
+ int send( char* buffer, int bufsize );
+ int send( const char* fmt, ... );
+ int send( HXML node );
+
+ int recvws( char* buffer, size_t bufsize, int flags );
+ int sendws( char* buffer, size_t bufsize, int flags );
+};
+
+struct JABBER_MODEMSGS
+{
+ TCHAR* szOnline;
+ TCHAR* szAway;
+ TCHAR* szNa;
+ TCHAR* szDnd;
+ TCHAR* szFreechat;
+};
+
+struct JABBER_REG_ACCOUNT
+{
+ TCHAR username[512];
+ TCHAR password[512];
+ char server[128];
+ char manualHost[128];
+ WORD port;
+ BOOL useSSL;
+};
+
+typedef enum { FT_SI, FT_OOB, FT_BYTESTREAM, FT_IBB } JABBER_FT_TYPE;
+typedef enum { FT_CONNECTING, FT_INITIALIZING, FT_RECEIVING, FT_DONE, FT_ERROR, FT_DENIED } JABBER_FILE_STATE;
+
+struct filetransfer
+{
+ filetransfer( CJabberProto* proto );
+ ~filetransfer();
+
+ void close();
+ void complete();
+ int create();
+
+ PROTOFILETRANSFERSTATUS std;
+
+// HANDLE hContact;
+ JABBER_FT_TYPE type;
+ JABBER_SOCKET s;
+ JABBER_FILE_STATE state;
+ TCHAR* jid;
+ int fileId;
+ TCHAR* iqId;
+ TCHAR* sid;
+ int bCompleted;
+ HANDLE hWaitEvent;
+
+ // For type == FT_BYTESTREAM
+ JABBER_BYTE_TRANSFER *jbt;
+
+ JABBER_IBB_TRANSFER *jibb;
+
+ // Used by file receiving only
+ char* httpHostName;
+ WORD httpPort;
+ TCHAR* httpPath;
+ unsigned __int64 dwExpectedRecvFileSize;
+
+ // Used by file sending only
+ HANDLE hFileEvent;
+ unsigned __int64 *fileSize;
+ TCHAR* szDescription;
+
+ CJabberProto* ppro;
+};
+
+struct JABBER_SEARCH_RESULT
+{
+ PROTOSEARCHRESULT hdr;
+ TCHAR jid[JABBER_MAX_JID_LEN];
+};
+
+struct JABBER_GCLOG_FONT
+{
+ char face[LF_FACESIZE]; // LF_FACESIZE is from LOGFONT struct
+ BYTE style;
+ char size; // signed
+ BYTE charset;
+ COLORREF color;
+};
+
+struct JABBER_FIELD_MAP
+{
+ int id;
+ char* name;
+};
+
+enum JABBER_MUC_JIDLIST_TYPE
+{
+ MUC_VOICELIST,
+ MUC_MEMBERLIST,
+ MUC_MODERATORLIST,
+ MUC_BANLIST,
+ MUC_ADMINLIST,
+ MUC_OWNERLIST
+};
+
+struct JABBER_MUC_JIDLIST_INFO
+{
+ ~JABBER_MUC_JIDLIST_INFO();
+
+ JABBER_MUC_JIDLIST_TYPE type;
+ TCHAR* roomJid; // filled-in by the WM_JABBER_REFRESH code
+ HXML iqNode;
+ CJabberProto* ppro;
+
+ TCHAR* type2str( void ) const;
+};
+
+typedef void ( CJabberProto::*JABBER_FORM_SUBMIT_FUNC )( HXML values, void *userdata );
+
+//---- jabber_treelist.c ------------------------------------------------
+
+typedef struct TTreeList_ItemInfo *HTREELISTITEM;
+enum { TLM_TREE, TLM_REPORT };
+
+//---- proto frame ------------------------------------------------
+
+class CJabberInfoFrameItem;
+
+struct CJabberInfoFrame_Event
+{
+ enum { CLICK, DESTROY } m_event;
+ const char *m_pszName;
+ LPARAM m_pUserData;
+};
+
+class CJabberInfoFrame
+{
+public:
+ CJabberInfoFrame(CJabberProto *proto);
+ ~CJabberInfoFrame();
+
+ void CreateInfoItem(char *pszName, bool bCompact=false, LPARAM pUserData=0);
+ void SetInfoItemCallback(char *pszName, void (CJabberProto::*onEvent)(CJabberInfoFrame_Event *));
+ void UpdateInfoItem(char *pszName, HANDLE hIcolibItem, TCHAR *pszText);
+ void ShowInfoItem(char *pszName, bool bShow);
+ void RemoveInfoItem(char *pszName);
+
+ void LockUpdates();
+ void Update();
+
+private:
+ CJabberProto *m_proto;
+ HWND m_hwnd;
+ int m_frameId;
+ bool m_compact;
+ OBJLIST<CJabberInfoFrameItem> m_pItems;
+ int m_hiddenItemCount;
+ int m_clickedItem;
+ bool m_bLocked;
+ int m_nextTooltipId;
+ HWND m_hwndToolTip;
+
+ HANDLE m_hhkFontsChanged;
+ HFONT m_hfntTitle, m_hfntText;
+ COLORREF m_clTitle, m_clText, m_clBack;
+
+ static void InitClass();
+ static LRESULT CALLBACK GlobalWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ LRESULT WndProc(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ void ReloadFonts();
+ void UpdateSize();
+
+ void RemoveTooltip(int id);
+ void SetToolTip(int id, RECT *rc, TCHAR *pszText);
+
+ void PaintSkinGlyph(HDC hdc, RECT *rc, char **glyphs, COLORREF fallback);
+ void PaintCompact(HDC hdc);
+ void PaintNormal(HDC hdc);
+
+ enum
+ {
+ SZ_FRAMEPADDING = 2, // padding inside frame
+ SZ_LINEPADDING = 0, // line height will be incremented by this value
+ SZ_LINESPACING = 0, // between lines
+ SZ_ICONSPACING = 2, // between icon and text
+ };
+};
+
+#include "jabber_list.h"
+#include "jabber_proto.h"
+
+/*******************************************************************
+ * Global variables
+ *******************************************************************/
+extern HINSTANCE hInst;
+extern BOOL jabberChatDllPresent;
+
+extern HANDLE hExtraMood;
+extern HANDLE hExtraActivity;
+
+// Theme API
+extern BOOL (WINAPI *JabberAlphaBlend)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION);
+extern BOOL (WINAPI *JabberIsThemeActive)();
+extern HRESULT (WINAPI *JabberDrawThemeParentBackground)(HWND, HDC, RECT *);
+
+extern const TCHAR xmlnsOwner[];
+extern TCHAR szCoreVersion[];
+
+extern int g_cbCountries;
+extern struct CountryListEntry* g_countries;
+
+/*******************************************************************
+ * Function declarations
+ *******************************************************************/
+
+//---- jabber_treelist.c ------------------------------------------------
+
+void TreeList_Create(HWND hwnd);
+void TreeList_Destroy(HWND hwnd);
+void TreeList_Reset(HWND hwnd);
+void TreeList_SetMode(HWND hwnd, int mode);
+HTREELISTITEM TreeList_GetActiveItem(HWND hwnd);
+void TreeList_SetSortMode(HWND hwnd, int col, BOOL descending);
+void TreeList_SetFilter(HWND hwnd, TCHAR *filter);
+HTREELISTITEM TreeList_AddItem(HWND hwnd, HTREELISTITEM hParent, TCHAR *text, LPARAM data);
+void TreeList_ResetItem(HWND hwnd, HTREELISTITEM hParent);
+void TreeList_MakeFakeParent(HTREELISTITEM hItem, BOOL flag);
+void TreeList_AppendColumn(HTREELISTITEM hItem, TCHAR *text);
+int TreeList_AddIcon(HWND hwnd, HICON hIcon, int iOverlay);
+void TreeList_SetIcon(HTREELISTITEM hItem, int iIcon, int iOverlay);
+LPARAM TreeList_GetData(HTREELISTITEM hItem);
+HTREELISTITEM TreeList_GetRoot(HWND hwnd);
+int TreeList_GetChildrenCount(HTREELISTITEM hItem);
+HTREELISTITEM TreeList_GetChild(HTREELISTITEM hItem, int i);
+void sttTreeList_RecursiveApply(HTREELISTITEM hItem, void (*func)(HTREELISTITEM, LPARAM), LPARAM data);
+void TreeList_Update(HWND hwnd);
+BOOL TreeList_ProcessMessage(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT idc, BOOL *result);
+
+//---- jabber_form.c ------------------------------------------------
+
+enum TJabberFormControlType
+{
+ JFORM_CTYPE_NONE, JFORM_CTYPE_TEXT_PRIVATE, JFORM_CTYPE_TEXT_MULTI,
+ JFORM_CTYPE_BOOLEAN, JFORM_CTYPE_LIST_SINGLE, JFORM_CTYPE_LIST_MULTI,
+ JFORM_CTYPE_FIXED, JFORM_CTYPE_HIDDEN, JFORM_CTYPE_TEXT_SINGLE
+};
+
+typedef struct TJabberFormControlInfo *HJFORMCTRL;
+typedef struct TJabberFormLayoutInfo *HJFORMLAYOUT;
+
+void JabberFormCreateUI( HWND hwndStatic, HXML xNode, int *formHeight, BOOL bCompact = FALSE );
+void JabberFormDestroyUI(HWND hwndStatic);
+void JabberFormSetInstruction( HWND hwndForm, const TCHAR *text );
+HJFORMLAYOUT JabberFormCreateLayout(HWND hwndStatic); // use mir_free to destroy
+HJFORMCTRL JabberFormAppendControl(HWND hwndStatic, HJFORMLAYOUT layout_info, TJabberFormControlType type, const TCHAR *labelStr, const TCHAR *valueStr);
+void JabberFormAddListItem(HJFORMCTRL item, const TCHAR *text, bool selected);
+void JabberFormLayoutControls(HWND hwndStatic, HJFORMLAYOUT layout_info, int *formHeight);
+
+void JabberFormCreateDialog( HXML xNode, TCHAR* defTitle, JABBER_FORM_SUBMIT_FUNC pfnSubmit, void *userdata );
+
+HXML JabberFormGetData( HWND hwndStatic, HXML xNode );
+
+//---- jabber_icolib.c ----------------------------------------------
+
+void g_IconsInit();
+HANDLE g_GetIconHandle( int iconId );
+HICON g_LoadIconEx( const char* name, bool big = false );
+void g_ReleaseIcon( HICON hIcon );
+
+void ImageList_AddIcon_Icolib( HIMAGELIST hIml, HICON hIcon );
+void WindowSetIcon(HWND hWnd, CJabberProto *proto, const char* name);
+void WindowFreeIcon(HWND hWnd);
+
+int ReloadIconsEventHook(WPARAM wParam, LPARAM lParam);
+
+//---- jabber_libstr.c ----------------------------------------------
+
+int lstrcmp_null(const TCHAR *s1, const TCHAR *s2);
+
+//---- jabber_menu.c ------------------------------------------------
+
+void g_MenuInit();
+void g_MenuUninit();
+int g_OnToolbarInit(WPARAM, LPARAM);
+
+//---- jabber_misc.c ------------------------------------------------
+
+void JabberChatDllError( void );
+int JabberCompareJids( const TCHAR* jid1, const TCHAR* jid2 );
+void JabberContactListCreateGroup( TCHAR* groupName );
+TCHAR* EscapeChatTags(TCHAR* pszText);
+TCHAR* UnEscapeChatTags(TCHAR* str_in);
+
+//---- jabber_adhoc.cpp ---------------------------------------------
+
+struct CJabberAdhocStartupParams
+{
+ TCHAR* m_szJid;
+ TCHAR* m_szNode;
+ CJabberProto* m_pProto;
+
+ CJabberAdhocStartupParams( CJabberProto* proto, TCHAR* szJid, TCHAR* szNode = NULL )
+ {
+ m_pProto = proto;
+ m_szJid = mir_tstrdup( szJid );
+ m_szNode = szNode ? mir_tstrdup( szNode ) : NULL;
+ }
+ ~CJabberAdhocStartupParams()
+ {
+ if ( m_szJid )
+ mir_free( m_szJid );
+ if ( m_szNode )
+ mir_free( m_szNode );
+ }
+};
+
+struct JabberAdHocData
+{
+ CJabberProto* proto;
+ int CurrentHeight;
+ int curPos;
+ int frameHeight;
+ RECT frameRect;
+ HXML AdHocNode;
+ HXML CommandsNode;
+ TCHAR* ResponderJID;
+};
+
+//---- jabber_std.cpp -------------------------------------------------------------------
+
+void __fastcall JFreeVariant( DBVARIANT* dbv );
+char* __fastcall JTranslate( const char* str );
+
+//---- jabber_util.cpp ------------------------------------------------------------------
+
+struct TStringPairsElem
+{
+ const char *name, *value;
+};
+
+struct TStringPairs
+{
+ TStringPairs( char* );
+ ~TStringPairs();
+
+ const char* operator[]( const char* name ) const;
+
+ int numElems;
+ TStringPairsElem* elems;
+};
+
+TCHAR* __stdcall JabberNickFromJID( const TCHAR* jid );
+TCHAR* JabberPrepareJid( LPCTSTR jid );
+char* __stdcall JabberUrlDecode( char* str );
+void __stdcall JabberUrlDecodeW( WCHAR* str );
+char* __stdcall JabberUrlEncode( const char* str );
+char* __stdcall JabberSha1( char* str );
+TCHAR* __stdcall JabberStrFixLines( const TCHAR* str );
+char* __stdcall JabberUnixToDos( const char* str );
+WCHAR* __stdcall JabberUnixToDosW( const WCHAR* str );
+void __stdcall JabberHttpUrlDecode( TCHAR* str );
+TCHAR* __stdcall JabberHttpUrlEncode( const TCHAR* str );
+int __stdcall JabberCombineStatus( int status1, int status2 );
+TCHAR* __stdcall JabberErrorStr( int errorCode );
+TCHAR* __stdcall JabberErrorMsg( HXML errorNode, int* errorCode = NULL );
+void __stdcall JabberUtfToTchar( const char* str, size_t cbLen, LPTSTR& dest );
+char* __stdcall JabberBase64Encode( const char* buffer, int bufferLen );
+char* __stdcall JabberBase64Decode( const char* buffer, int *resultLen );
+char* __stdcall JabberBase64DecodeW( const WCHAR* buffer, int *resultLen );
+time_t __stdcall JabberIsoToUnixTime( const TCHAR* stamp );
+void __stdcall JabberStringAppend( char* *str, int *sizeAlloced, const char* fmt, ... );
+TCHAR* __stdcall JabberStripJid( const TCHAR* jid, TCHAR* dest, size_t destLen );
+int __stdcall JabberGetPictureType( const char* buf );
+int __stdcall JabberGetPacketID( HXML n );
+
+#define JabberUnixToDosT JabberUnixToDosW
+#define JabberBase64DecodeT JabberBase64DecodeW
+
+const TCHAR *JabberStrIStr( const TCHAR *str, const TCHAR *substr);
+void JabberCopyText(HWND hwnd, TCHAR *text);
+void JabberBitmapPremultiplyChannels(HBITMAP hBitmap);
+CJabberProto *JabberChooseInstance(bool bIsLink=false);
+
+//---- jabber_xml.cpp -------------------------------------------------------------------
+
+void strdel( char* parBuffer, int len );
+
+//---- jabber_userinfo.cpp --------------------------------------------------------------
+
+void JabberUserInfoUpdate( HANDLE hContact );
+
+//---- jabber_iq_handlers.cpp
+BOOL GetOSDisplayString(LPTSTR pszOS, int BUFSIZE);
+
+#endif
diff --git a/protocols/JabberG/src/jabber_adhoc.cpp b/protocols/JabberG/src/jabber_adhoc.cpp new file mode 100644 index 0000000000..09884e79df --- /dev/null +++ b/protocols/JabberG/src/jabber_adhoc.cpp @@ -0,0 +1,609 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2007 Artem Shpynov
+Copyright ( C ) 2005-12 George Hazan
+
+Module implements an XMPP protocol extension for reporting and executing ad-hoc,
+human-oriented commands according to XEP-0050: Ad-Hoc Commands
+http://www.xmpp.org/extensions/xep-0050.html
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include <CommCtrl.h>
+#include "jabber_iq.h"
+#include "m_clui.h"
+#include "jabber_caps.h"
+
+
+#define ShowDlgItem( a, b, c ) ShowWindow( GetDlgItem( a, b ), c )
+#define EnableDlgItem( a, b, c ) EnableWindow( GetDlgItem( a, b ), c )
+
+enum
+{
+ JAHM_COMMANDLISTRESULT = WM_USER+1,
+ JAHM_PROCESSRESULT
+};
+
+//Declarations
+static INT_PTR CALLBACK JabberAdHoc_CommandDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam );
+
+//implementations
+
+// convert iqID to dialog hwnd
+HWND CJabberProto::GetWindowFromIq( HXML iqNode )
+{
+ const TCHAR* id = xmlGetAttrValue( iqNode, _T( "id" ));
+ if (_tcslen(id)>4)
+ return (HWND)_tcstol(id+4,NULL,10);
+ return m_hwndCommandWindow;
+
+}
+// Callback to clear form content
+static BOOL CALLBACK sttDeleteChildWindowsProc( HWND hwnd, LPARAM )
+{
+ DestroyWindow( hwnd );
+ return TRUE;
+}
+
+static void sttEnableControls( HWND hwndDlg, BOOL bEnable, const int * controlsID )
+{
+ int i=0;
+ while ( controlsID[i]!=0 )
+ EnableDlgItem( hwndDlg, controlsID[i++], bEnable );
+}
+
+static void sttShowControls( HWND hwndDlg, BOOL bShow, int * controlsID )
+{
+ int i=0;
+ while ( controlsID[i]!=0 )
+ ShowDlgItem( hwndDlg, controlsID[i++], (bShow) ? SW_SHOW : SW_HIDE );
+}
+
+static void JabberAdHoc_RefreshFrameScroll(HWND hwndDlg, JabberAdHocData * dat)
+{
+ HWND hFrame = GetDlgItem( hwndDlg, IDC_FRAME );
+ HWND hwndScroll = GetDlgItem( hwndDlg, IDC_VSCROLL );
+ RECT rc;
+ RECT rcScrollRc;
+ GetClientRect( hFrame, &rc );
+ GetClientRect( hFrame, &dat->frameRect );
+ GetWindowRect( hwndScroll, &rcScrollRc );
+ dat->frameRect.right-=(rcScrollRc.right-rcScrollRc.left);
+ dat->frameHeight = rc.bottom-rc.top;
+ if ( dat->frameHeight < dat->CurrentHeight) {
+ ShowWindow( hwndScroll, SW_SHOW );
+ EnableWindow( hwndScroll, TRUE );
+ }
+ else ShowWindow( hwndScroll, SW_HIDE );
+
+ SetScrollRange( hwndScroll, SB_CTL, 0, dat->CurrentHeight-dat->frameHeight, FALSE );
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Iq handlers
+// Forwards to dialog window procedure
+
+void CJabberProto::OnIqResult_ListOfCommands( HXML iqNode )
+{
+ SendMessage( GetWindowFromIq( iqNode ), JAHM_COMMANDLISTRESULT, 0, (LPARAM)xi.copyNode( iqNode ));
+}
+
+void CJabberProto::OnIqResult_CommandExecution( HXML iqNode )
+{
+ SendMessage( GetWindowFromIq( iqNode ), JAHM_PROCESSRESULT, (WPARAM)xi.copyNode( iqNode ), 0 );
+}
+
+int CJabberProto::AdHoc_RequestListOfCommands( TCHAR * szResponder, HWND hwndDlg )
+{
+ int iqId = (int)hwndDlg;
+ IqAdd( iqId, IQ_PROC_DISCOCOMMANDS, &CJabberProto::OnIqResult_ListOfCommands );
+ m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId, szResponder ) << XQUERY( _T(JABBER_FEAT_DISCO_ITEMS))
+ << XATTR( _T("node"), _T(JABBER_FEAT_COMMANDS)));
+ return iqId;
+}
+
+int CJabberProto::AdHoc_ExecuteCommand( HWND hwndDlg, TCHAR*, JabberAdHocData* dat )
+{
+ for ( int i = 1; ; i++ ) {
+ HXML itemNode = xmlGetNthChild( dat->CommandsNode, _T("item"), i );
+ if ( !itemNode)
+ break;
+ if ( !IsDlgButtonChecked( GetDlgItem( hwndDlg, IDC_FRAME ), i ))
+ continue;
+ const TCHAR* node = xmlGetAttrValue( itemNode, _T("node"));
+ if ( node ) {
+ const TCHAR *jid2 = xmlGetAttrValue( itemNode, _T("jid"));
+
+ int iqId = (int)hwndDlg;
+ IqAdd( iqId, IQ_PROC_EXECCOMMANDS, &CJabberProto::OnIqResult_CommandExecution );
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("set"), iqId, jid2 )
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), node ) << XATTR( _T("action"), _T("execute")));
+
+ EnableDlgItem( hwndDlg, IDC_SUBMIT, FALSE );
+ SetDlgItemText( hwndDlg, IDC_SUBMIT, TranslateT( "OK" ));
+ } }
+
+ xi.destroyNode( dat->CommandsNode ); dat->CommandsNode = NULL;
+ return TRUE;
+}
+
+//Messages handlers
+int CJabberProto::AdHoc_OnJAHMCommandListResult( HWND hwndDlg, HXML iqNode, JabberAdHocData* dat )
+{
+ int nodeIdx = 0;
+ const TCHAR * type = xmlGetAttrValue( iqNode, _T("type"));
+ if ( !type || !_tcscmp( type, _T( "error" )) ) {
+ // error occurred here
+ TCHAR buff[255];
+ const TCHAR* code = NULL;
+ const TCHAR* description = NULL;
+
+ HXML errorNode = xmlGetChild( iqNode, "error" );
+ if ( errorNode ) {
+ code = xmlGetAttrValue( errorNode, _T("code"));
+ description = xmlGetText( errorNode );
+ }
+ _sntprintf( buff, SIZEOF(buff), TranslateT( "Error %s %s" ), (code) ? code : _T(""), (description) ? description : _T(""));
+ JabberFormSetInstruction( hwndDlg, buff );
+ }
+ else if ( !_tcscmp( type, _T("result")) ) {
+ BOOL validResponse = FALSE;
+ EnumChildWindows( GetDlgItem( hwndDlg, IDC_FRAME ), sttDeleteChildWindowsProc, 0 );
+ dat->CurrentHeight = 0;
+ dat->curPos = 0;
+ SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, 0, FALSE );
+ HXML queryNode = xmlGetChild( iqNode , "query" );
+ if ( queryNode ) {
+ const TCHAR* xmlns = xmlGetAttrValue( queryNode, _T( "xmlns" ));
+ const TCHAR* node = xmlGetAttrValue( queryNode, _T( "node" ));
+ if ( xmlns && node
+ && !_tcscmp( xmlns, _T( JABBER_FEAT_DISCO_ITEMS ))
+ && !_tcscmp( node, _T( JABBER_FEAT_COMMANDS )) )
+ validResponse = TRUE;
+ }
+ if ( queryNode && xmlGetChild( queryNode ,0) && validResponse ) {
+ dat->CommandsNode = xi.copyNode( queryNode );
+
+ nodeIdx = 1;
+ int ypos = 20;
+ for (nodeIdx = 1; ; nodeIdx++ ) {
+ HXML itemNode = xmlGetNthChild( queryNode, _T("item"), nodeIdx );
+ if ( itemNode ) {
+ const TCHAR *name = xmlGetAttrValue( itemNode, _T("name"));
+ if (!name) name = xmlGetAttrValue( itemNode, _T("node"));
+ ypos = AdHoc_AddCommandRadio( GetDlgItem( hwndDlg,IDC_FRAME ), TranslateTS(name), nodeIdx, ypos, (nodeIdx==1) ? 1 : 0);
+ dat->CurrentHeight = ypos;
+ }
+ else break;
+ } }
+
+ if (nodeIdx>1) {
+ JabberFormSetInstruction( hwndDlg, TranslateT("Select Command"));
+ ShowDlgItem( hwndDlg, IDC_FRAME, SW_SHOW);
+ ShowDlgItem( hwndDlg, IDC_VSCROLL, SW_SHOW);
+ EnableDlgItem( hwndDlg, IDC_SUBMIT, TRUE);
+ } else {
+ JabberFormSetInstruction(hwndDlg, TranslateT("Not supported"));
+ } }
+
+ JabberAdHoc_RefreshFrameScroll( hwndDlg, dat );
+ return (TRUE);
+}
+
+int CJabberProto::AdHoc_OnJAHMProcessResult(HWND hwndDlg, HXML workNode, JabberAdHocData* dat)
+{
+ EnumChildWindows( GetDlgItem( hwndDlg, IDC_FRAME ), sttDeleteChildWindowsProc, 0 );
+ dat->CurrentHeight = 0;
+ dat->curPos = 0;
+ SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, 0, FALSE );
+
+ if ( workNode == NULL )
+ return TRUE;
+
+ dat->AdHocNode = xi.copyNode( workNode );
+
+ const TCHAR *type;
+ if (( type = xmlGetAttrValue( workNode, _T("type"))) == NULL ) return TRUE;
+ if ( !lstrcmp( type, _T("result")) ) {
+ // wParam = <iq/> node from responder as a result of command execution
+ HXML commandNode, xNode, n;
+ if (( commandNode = xmlGetChild( dat->AdHocNode, _T("command"))) == NULL )
+ return TRUE;
+
+ const TCHAR * status = xmlGetAttrValue( commandNode, _T("status"));
+ if (!status) status = _T("completed");
+
+ if (( xNode = xmlGetChild( commandNode , "x" ))) {
+ // use jabber:x:data form
+ HWND hFrame = GetDlgItem( hwndDlg, IDC_FRAME );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_FRAME_TEXT ), SW_HIDE );
+ if (( n = xmlGetChild( xNode , "instructions" )) != NULL && xmlGetText( n )!=NULL )
+ JabberFormSetInstruction( hwndDlg, xmlGetText( n ));
+ else if (( n = xmlGetChild( xNode , "title" )) != NULL && xmlGetText( n )!=NULL )
+ JabberFormSetInstruction( hwndDlg, xmlGetText( n ));
+ else
+ JabberFormSetInstruction(hwndDlg, TranslateTS(status));
+ JabberFormCreateUI( hFrame, xNode, &dat->CurrentHeight );
+ ShowDlgItem( hwndDlg, IDC_FRAME , SW_SHOW);
+ }
+ else {
+ //NO X FORM
+ int toHide[]={ IDC_FRAME_TEXT, IDC_FRAME, IDC_VSCROLL, 0};
+ sttShowControls(hwndDlg, FALSE, toHide );
+
+ const TCHAR * noteText=NULL;
+ HXML noteNode = xmlGetChild( commandNode , "note");
+ if (noteNode)
+ noteText = xmlGetText( noteNode );
+
+ JabberFormSetInstruction(hwndDlg, noteText ? noteText : TranslateTS(status));
+ }
+
+ //check actions
+ HXML actionsNode = xmlGetChild( commandNode , "actions");
+ if ( actionsNode != NULL ) {
+ ShowDlgItem( hwndDlg, IDC_PREV, ( xmlGetChild( actionsNode , "prev")!=NULL) ? SW_SHOW : SW_HIDE);
+ ShowDlgItem( hwndDlg, IDC_NEXT, ( xmlGetChild( actionsNode , "next")!=NULL) ? SW_SHOW : SW_HIDE);
+ ShowDlgItem( hwndDlg, IDC_COMPLETE, ( xmlGetChild( actionsNode , "complete")!=NULL) ? SW_SHOW : SW_HIDE);
+ ShowDlgItem( hwndDlg, IDC_SUBMIT, SW_HIDE);
+
+ int toEnable[]={ IDC_PREV, IDC_NEXT, IDC_COMPLETE, 0};
+ sttEnableControls( hwndDlg, TRUE, toEnable );
+ } else {
+ int toHide[]={ IDC_PREV, IDC_NEXT, IDC_COMPLETE, 0};
+ sttShowControls(hwndDlg, FALSE, toHide );
+
+ ShowDlgItem(hwndDlg,IDC_SUBMIT, SW_SHOW);
+ EnableDlgItem(hwndDlg,IDC_SUBMIT, TRUE);
+ }
+
+ if (!status || _tcscmp(status,_T("executing"))) {
+ ShowDlgItem( hwndDlg, IDC_SUBMIT, SW_HIDE);
+ SetWindowText(GetDlgItem(hwndDlg,IDCANCEL), TranslateT("Done"));
+ } }
+ else if ( !lstrcmp( type, _T("error"))) {
+ // error occurred here
+ int toHide[]={ IDC_FRAME, IDC_FRAME_TEXT, IDC_VSCROLL,
+ IDC_PREV, IDC_NEXT, IDC_COMPLETE, IDC_SUBMIT, 0};
+
+ sttShowControls(hwndDlg, FALSE, toHide );
+
+ const TCHAR* code=NULL;
+ const TCHAR* description=NULL;
+ TCHAR buff[255];
+ HXML errorNode = xmlGetChild( workNode , "error");
+ if ( errorNode ) {
+ code = xmlGetAttrValue( errorNode, _T("code"));
+ description = xmlGetText( errorNode );
+ }
+ _sntprintf(buff,SIZEOF(buff),TranslateT("Error %s %s"),code ? code : _T(""),description?description:_T(""));
+ JabberFormSetInstruction(hwndDlg,buff);
+ }
+ JabberAdHoc_RefreshFrameScroll( hwndDlg, dat );
+ return TRUE;
+}
+
+int CJabberProto::AdHoc_SubmitCommandForm(HWND hwndDlg, JabberAdHocData* dat, TCHAR* action)
+{
+ HXML commandNode = xmlGetChild( dat->AdHocNode, "command" );
+ HXML xNode = xmlGetChild( commandNode , "x" );
+ HXML dataNode = JabberFormGetData( GetDlgItem( hwndDlg, IDC_FRAME ), xNode);
+
+ int iqId = (int)hwndDlg;
+ XmlNodeIq iq( _T("set"), iqId, xmlGetAttrValue( dat->AdHocNode, _T("from")));
+ HXML command = iq << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS));
+
+ const TCHAR* sessionId = xmlGetAttrValue( commandNode, _T("sessionid"));
+ if ( sessionId )
+ command << XATTR( _T("sessionid"), sessionId );
+
+ const TCHAR* node = xmlGetAttrValue( commandNode, _T("node"));
+ if ( node )
+ command << XATTR( _T("node"), node );
+
+ if ( action )
+ command << XATTR( _T("action"), action );
+
+ xmlAddChild( command, dataNode );
+ IqAdd( iqId, IQ_PROC_EXECCOMMANDS, &CJabberProto::OnIqResult_CommandExecution );
+ m_ThreadInfo->send( iq );
+
+ xi.destroyNode( dataNode );
+
+ JabberFormSetInstruction(hwndDlg,TranslateT("In progress. Please Wait..."));
+
+ static const int toDisable[]={IDC_SUBMIT, IDC_PREV, IDC_NEXT, IDC_COMPLETE, 0};
+ sttEnableControls( hwndDlg, FALSE, toDisable);
+
+ return TRUE;
+}
+
+
+int CJabberProto::AdHoc_AddCommandRadio(HWND hFrame, TCHAR * labelStr, int id, int ypos, int value)
+{
+ int labelHeight;
+ RECT strRect={0};
+
+ int verticalStep=4;
+ int ctrlMinHeight=18;
+ HWND hCtrl=NULL;
+
+ RECT rcFrame;
+ GetClientRect(hFrame,&rcFrame);
+
+ int ctrlOffset=20;
+ int ctrlWidth=rcFrame.right-ctrlOffset;
+
+ HDC hdc = GetDC( hFrame );
+ labelHeight = max(ctrlMinHeight, DrawText( hdc , labelStr, -1, &strRect, DT_CALCRECT ));
+ ctrlWidth=min( ctrlWidth, strRect.right-strRect.left+20 );
+ ReleaseDC( hFrame, hdc );
+
+ hCtrl = CreateWindowEx( 0, _T("button"), labelStr, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_AUTORADIOBUTTON, ctrlOffset, ypos, ctrlWidth, labelHeight, hFrame, ( HMENU ) id, hInst, NULL );
+ SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) SendMessage( GetParent(hFrame), WM_GETFONT, 0, 0 ), 0 );
+ SendMessage( hCtrl, BM_SETCHECK, value, 0 );
+ return (ypos + labelHeight + verticalStep);
+
+}
+
+static INT_PTR CALLBACK JabberAdHoc_CommandDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ JabberAdHocData* dat = ( JabberAdHocData* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ CJabberAdhocStartupParams* pStartupParams = (CJabberAdhocStartupParams *)lParam;
+ dat=(JabberAdHocData *)mir_alloc(sizeof(JabberAdHocData));
+ memset(dat,0,sizeof(JabberAdHocData));
+
+ //hmmm, useless code? if (dat->ResponderJID) mir_free(dat->ResponderJID);
+ dat->ResponderJID = mir_tstrdup(pStartupParams->m_szJid);
+ dat->proto = pStartupParams->m_pProto;
+
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)dat);
+ WindowSetIcon( hwndDlg, dat->proto, "adhoc" );
+ dat->proto->m_hwndCommandWindow = hwndDlg;
+ TranslateDialogDefault( hwndDlg );
+
+ //Firstly hide frame
+ LONG frameExStyle = GetWindowLongPtr( GetDlgItem( hwndDlg, IDC_FRAME ), GWL_EXSTYLE );
+ frameExStyle |= WS_EX_CONTROLPARENT;
+
+ SetWindowLongPtr( GetDlgItem( hwndDlg, IDC_FRAME ), GWL_EXSTYLE, frameExStyle );
+
+ int toHide[]={ IDC_FRAME, IDC_VSCROLL, IDC_PREV, IDC_NEXT, IDC_COMPLETE, IDC_FRAME_TEXT, 0};
+ sttShowControls(hwndDlg, FALSE, toHide );
+
+ int toShow[]={ IDC_INSTRUCTION, IDC_SUBMIT, IDCANCEL, 0};
+ sttShowControls(hwndDlg, TRUE, toShow );
+
+ EnableDlgItem(hwndDlg,IDC_VSCROLL,TRUE);
+
+ SetWindowPos(GetDlgItem(hwndDlg,IDC_VSCROLL),HWND_BOTTOM,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
+
+ SetDlgItemText(hwndDlg,IDC_SUBMIT, TranslateT("Execute"));
+ JabberFormSetInstruction(hwndDlg,TranslateT("Requesting command list. Please wait..."));
+
+ if ( !pStartupParams->m_szNode ) {
+ dat->proto->AdHoc_RequestListOfCommands(pStartupParams->m_szJid, hwndDlg);
+
+ TCHAR Caption[ 512 ];
+ _sntprintf(Caption, SIZEOF(Caption), _T("%s %s"), TranslateT("Jabber Ad-Hoc commands at"), dat->ResponderJID );
+ SetWindowText(hwndDlg, Caption);
+ }
+ else
+ {
+ int iqId = (int)hwndDlg;
+ dat->proto->IqAdd( iqId, IQ_PROC_EXECCOMMANDS, &CJabberProto::OnIqResult_CommandExecution );
+ dat->proto->m_ThreadInfo->send(
+ XmlNodeIq( _T("set"), iqId, pStartupParams->m_szJid )
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS))
+ << XATTR( _T("node"), pStartupParams->m_szNode ) << XATTR( _T("action"), _T("execute")));
+
+ EnableDlgItem( hwndDlg, IDC_SUBMIT, FALSE );
+ SetDlgItemText( hwndDlg, IDC_SUBMIT, TranslateT( "OK" ));
+
+ TCHAR Caption[ 512 ];
+ _sntprintf(Caption, SIZEOF(Caption), _T("%s %s"), TranslateT("Sending Ad-Hoc command to"), dat->ResponderJID );
+ SetWindowText(hwndDlg, Caption);
+ }
+
+ delete pStartupParams;
+
+ return TRUE;
+ }
+ case WM_CTLCOLORSTATIC:
+ if ((GetWindowLongPtr((HWND)lParam, GWL_ID) == IDC_WHITERECT) ||
+ (GetWindowLongPtr((HWND)lParam, GWL_ID) == IDC_INSTRUCTION) ||
+ (GetWindowLongPtr((HWND)lParam, GWL_ID) == IDC_TITLE))
+ {
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+ } else
+ {
+ return NULL;
+ }
+ case WM_COMMAND:
+ {
+ switch ( LOWORD( wParam ))
+ {
+
+ case IDC_PREV:
+ return dat->proto->AdHoc_SubmitCommandForm(hwndDlg,dat,_T("prev"));
+ case IDC_NEXT:
+ return dat->proto->AdHoc_SubmitCommandForm(hwndDlg,dat,_T("next"));
+ case IDC_COMPLETE:
+ return dat->proto->AdHoc_SubmitCommandForm(hwndDlg,dat,_T("complete"));
+ case IDC_SUBMIT:
+ if (!dat->AdHocNode && dat->CommandsNode && LOWORD( wParam )==IDC_SUBMIT)
+ return dat->proto->AdHoc_ExecuteCommand(hwndDlg,dat->ResponderJID, dat);
+ else
+ return dat->proto->AdHoc_SubmitCommandForm(hwndDlg,dat, NULL);
+ case IDCLOSE:
+ case IDCANCEL:
+ xi.destroyNode( dat->AdHocNode ); dat->AdHocNode = NULL;
+ DestroyWindow( hwndDlg );
+ return TRUE;
+ }
+ break;
+ }
+ case JAHM_COMMANDLISTRESULT:
+ return dat->proto->AdHoc_OnJAHMCommandListResult(hwndDlg,(HXML)lParam,dat);
+ case JAHM_PROCESSRESULT:
+ return dat->proto->AdHoc_OnJAHMProcessResult(hwndDlg, (HXML)wParam,dat);
+
+ case WM_MOUSEWHEEL:
+ {
+ int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
+ if ( zDelta ) {
+ int nScrollLines=0;
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,(void*)&nScrollLines,0);
+ for (int i=0; i<(nScrollLines+1)/2; i++)
+ SendMessage(hwndDlg,WM_VSCROLL, (zDelta<0)?SB_LINEDOWN:SB_LINEUP,0);
+ } }
+ return TRUE;
+
+ case WM_VSCROLL:
+ {
+ int pos;
+ if ( dat != NULL ) {
+ pos = dat->curPos;
+ switch ( LOWORD( wParam ))
+ {
+ case SB_LINEDOWN:
+ pos += 10;
+ break;
+ case SB_LINEUP:
+ pos -= 10;
+ break;
+ case SB_PAGEDOWN:
+ pos += ( dat->CurrentHeight - 10 );
+ break;
+ case SB_PAGEUP:
+ pos -= ( dat->CurrentHeight - 10 );
+ break;
+ case SB_THUMBTRACK:
+ pos = HIWORD( wParam );
+ break;
+ }
+ if ( pos > ( dat->CurrentHeight - dat->frameHeight ))
+ pos = dat->CurrentHeight - dat->frameHeight;
+ if ( pos < 0 )
+ pos = 0;
+ if ( dat->curPos != pos ) {
+ ScrollWindow( GetDlgItem( hwndDlg, IDC_FRAME ), 0, dat->curPos - pos, NULL , &( dat->frameRect ));
+ SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, pos, TRUE );
+ RECT Invalid=dat->frameRect;
+ if (dat->curPos - pos >0)
+ Invalid.bottom=Invalid.top+(dat->curPos - pos);
+ else
+ Invalid.top=Invalid.bottom+(dat->curPos - pos);
+
+ RedrawWindow(GetDlgItem( hwndDlg, IDC_FRAME ), NULL, NULL, RDW_UPDATENOW |RDW_ALLCHILDREN);
+ dat->curPos = pos;
+ } }
+ break;
+ }
+ case WM_DESTROY:
+ {
+ JabberFormDestroyUI(GetDlgItem(hwndDlg, IDC_FRAME));
+ WindowFreeIcon(hwndDlg);
+
+ dat->proto->m_hwndCommandWindow = NULL;
+ mir_free( dat->ResponderJID );
+ xi.destroyNode( dat->CommandsNode );
+ xi.destroyNode( dat->AdHocNode );
+ mir_free(dat);
+ dat=NULL;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+int __cdecl CJabberProto::ContactMenuRunCommands(WPARAM wParam, LPARAM lParam )
+{
+ HANDLE hContact;
+ DBVARIANT dbv;
+ int res = -1;
+ JABBER_LIST_ITEM * item=NULL;
+
+ if ((( hContact=( HANDLE ) wParam )!=NULL || (lParam!=0)) && m_bJabberOnline ) {
+ if ( wParam && !JGetStringT( hContact, "jid", &dbv )) {
+ TCHAR jid[ JABBER_MAX_JID_LEN ];
+ int selected = 0;
+ _tcsncpy(jid, dbv.ptszVal, SIZEOF(jid));
+
+ ListLock();
+ {
+ item = ListGetItemPtr( LIST_ROSTER, jid);
+ if (item)
+ {
+ if (item->resourceCount>1)
+ {
+ HMENU hMenu=CreatePopupMenu();
+ for (int i=0; i<item->resourceCount; i++)
+ AppendMenu(hMenu,MF_STRING,i+1, item->resource[i].resourceName);
+ HWND hwndTemp=CreateWindowEx(WS_EX_TOOLWINDOW,_T("button"),_T("PopupMenuHost"),0,0,0,10,10,NULL,NULL,hInst,NULL);
+ SetForegroundWindow(hwndTemp);
+ POINT pt;
+ GetCursorPos(&pt);
+ RECT rc;
+ selected=TrackPopupMenu(hMenu,TPM_RETURNCMD,pt.x,pt.y,0,hwndTemp,&rc);
+ DestroyMenu(hMenu);
+ DestroyWindow(hwndTemp);
+ }
+ else selected=1;
+
+ if (selected>0)
+ {
+ selected--;
+ if (item->resource)
+ {
+ _tcsncat(jid,_T("/"),SIZEOF(jid));
+ _tcsncat(jid,item->resource[selected].resourceName,SIZEOF(jid));
+ }
+ selected=1;
+ }
+ }
+ }
+ ListUnlock();
+
+ if (!item || selected) {
+ CJabberAdhocStartupParams* pStartupParams = new CJabberAdhocStartupParams( this, jid, NULL );
+ CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_FORM ), NULL, JabberAdHoc_CommandDlgProc, ( LPARAM )(pStartupParams));
+ }
+ JFreeVariant( &dbv );
+
+ }
+ else if (lParam!=0)
+ CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_FORM ), NULL, JabberAdHoc_CommandDlgProc, lParam );
+ }
+ return res;
+}
+
+void CJabberProto::ContactMenuAdhocCommands( CJabberAdhocStartupParams* param )
+{
+ if ( param )
+ CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_FORM ), NULL, JabberAdHoc_CommandDlgProc, (LPARAM)param );
+}
diff --git a/protocols/JabberG/src/jabber_agent.cpp b/protocols/JabberG/src/jabber_agent.cpp new file mode 100644 index 0000000000..b0ad17e8a0 --- /dev/null +++ b/protocols/JabberG/src/jabber_agent.cpp @@ -0,0 +1,283 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Agent registration progress dialog
+
+class CAgentRegProgressDlg : public CJabberDlgBase
+{
+ CCtrlButton m_ok;
+
+public:
+ CAgentRegProgressDlg( CJabberProto* _ppro, HWND _owner ) :
+ CJabberDlgBase( _ppro, IDD_OPT_REGISTER, _owner, false ),
+ m_ok( this, IDOK )
+ {
+ m_ok.OnClick = Callback( this, &CAgentRegProgressDlg::OnOk );
+ }
+
+ virtual void OnInitDialog()
+ {
+ m_proto->m_hwndRegProgress = m_hwnd;
+ SetWindowTextA( m_hwnd, "Jabber Agent Registration" );
+ TranslateDialogDefault( m_hwnd );
+ }
+
+ virtual INT_PTR DlgProc( UINT msg, WPARAM wParam, LPARAM lParam )
+ {
+ if ( msg == WM_JABBER_REGDLG_UPDATE ) {
+ if (( TCHAR* )lParam == NULL )
+ SetDlgItemText( m_hwnd, IDC_REG_STATUS, TranslateT( "No message" ));
+ else
+ SetDlgItemText( m_hwnd, IDC_REG_STATUS, ( TCHAR* )lParam );
+ if ( wParam >= 0 )
+ SendMessage( GetDlgItem( m_hwnd, IDC_PROGRESS_REG ), PBM_SETPOS, wParam, 0 );
+ if ( wParam >= 100 )
+ m_ok.SetText( TranslateT( "OK" ));
+ }
+
+ return CJabberDlgBase::DlgProc( msg, wParam, lParam );
+ }
+
+ void OnOk( CCtrlButton* )
+ {
+ m_proto->m_hwndRegProgress = NULL;
+ EndDialog( m_hwnd, 0 );
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Transport registration form
+
+class CAgentRegDlg : public CJabberDlgBase
+{
+ int m_curPos;
+ int m_formHeight, m_frameHeight;
+ RECT m_frameRect;
+ HXML m_agentRegIqNode;
+ TCHAR* m_jid;
+
+ CCtrlButton m_submit;
+
+public:
+ CAgentRegDlg( CJabberProto* _ppro, TCHAR* _jid ) :
+ CJabberDlgBase( _ppro, IDD_FORM, NULL, false ),
+ m_submit( this, IDC_SUBMIT ),
+ m_jid( _jid ),
+ m_agentRegIqNode( NULL )
+ {
+ m_submit.OnClick = Callback( this, &CAgentRegDlg::OnSubmit );
+ }
+
+ virtual void OnInitDialog()
+ {
+ EnableWindow( GetParent( m_hwnd ), FALSE );
+ m_proto->m_hwndAgentRegInput = m_hwnd;
+ SetWindowText( m_hwnd, TranslateT( "Jabber Agent Registration" ));
+ SetDlgItemText( m_hwnd, IDC_SUBMIT, TranslateT( "Register" ));
+ SetDlgItemText( m_hwnd, IDC_FRAME_TEXT, TranslateT( "Please wait..." ));
+
+ int iqId = m_proto->SerialNext();
+ m_proto->IqAdd( iqId, IQ_PROC_GETREGISTER, &CJabberProto::OnIqResultGetRegister );
+ m_proto->m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId, m_jid ) << XQUERY( _T(JABBER_FEAT_REGISTER)));
+
+ // Enable WS_EX_CONTROLPARENT on IDC_FRAME ( so tab stop goes through all its children )
+ LONG frameExStyle = GetWindowLongPtr( GetDlgItem( m_hwnd, IDC_FRAME ), GWL_EXSTYLE );
+ frameExStyle |= WS_EX_CONTROLPARENT;
+ SetWindowLongPtr( GetDlgItem( m_hwnd, IDC_FRAME ), GWL_EXSTYLE, frameExStyle );
+ }
+
+ virtual void OnDestroy()
+ {
+ xi.destroyNode( m_agentRegIqNode );
+ JabberFormDestroyUI(GetDlgItem(m_hwnd, IDC_FRAME));
+ m_proto->m_hwndAgentRegInput = NULL;
+ EnableWindow( GetParent( m_hwnd ), TRUE );
+ SetActiveWindow( GetParent( m_hwnd ));
+ }
+
+ virtual INT_PTR DlgProc( UINT msg, WPARAM wParam, LPARAM lParam )
+ {
+ switch( msg ) {
+ case WM_CTLCOLORSTATIC:
+ switch( GetDlgCtrlID(( HWND )lParam )) {
+ case IDC_WHITERECT: case IDC_INSTRUCTION: case IDC_TITLE:
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+ default:
+ return NULL;
+ }
+
+ case WM_JABBER_REGINPUT_ACTIVATE:
+ if ( wParam == 1 ) { // success
+ // lParam = <iq/> node from agent JID as a result of "get jabber:iq:register"
+ HWND hFrame = GetDlgItem( m_hwnd, IDC_FRAME );
+ ShowWindow( GetDlgItem( m_hwnd, IDC_FRAME_TEXT ), SW_HIDE );
+
+ HXML queryNode, xNode;
+ if (( m_agentRegIqNode = ( HXML )lParam ) == NULL ) return TRUE;
+ if (( queryNode = xmlGetChild( m_agentRegIqNode , "query" )) == NULL ) return TRUE;
+
+ RECT rect;
+
+ m_curPos = 0;
+ GetClientRect( GetDlgItem( m_hwnd, IDC_FRAME ), &( m_frameRect ));
+ GetClientRect( GetDlgItem( m_hwnd, IDC_VSCROLL ), &rect );
+ m_frameRect.right -= ( rect.right - rect.left );
+ GetClientRect( GetDlgItem( m_hwnd, IDC_FRAME ), &rect );
+ m_frameHeight = rect.bottom - rect.top;
+
+ if (( xNode=xmlGetChild( queryNode , "x" )) != NULL ) {
+ // use new jabber:x:data form
+ HXML n = xmlGetChild( xNode , "instructions" );
+ if ( n != NULL && xmlGetText( n )!=NULL )
+ JabberFormSetInstruction( m_hwnd, xmlGetText( n ));
+
+ JabberFormCreateUI( hFrame, xNode, &m_formHeight /*dummy*/ );
+ }
+ else {
+ // use old registration information form
+ HJFORMLAYOUT layout_info = JabberFormCreateLayout(hFrame);
+ for ( int i=0; ; i++ ) {
+ HXML n = xmlGetChild( queryNode ,i);
+ if ( !n )
+ break;
+
+ if ( xmlGetName( n )) {
+ if ( !lstrcmp( xmlGetName( n ), _T("instructions"))) {
+ JabberFormSetInstruction( m_hwnd, xmlGetText( n ));
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("key")) || !lstrcmp( xmlGetName( n ), _T("registered"))) {
+ // do nothing
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("password")))
+ JabberFormAppendControl(hFrame, layout_info, JFORM_CTYPE_TEXT_PRIVATE, xmlGetName( n ), xmlGetText( n ));
+ else // everything else is a normal text field
+ JabberFormAppendControl(hFrame, layout_info, JFORM_CTYPE_TEXT_SINGLE, xmlGetName( n ), xmlGetText( n ));
+ } }
+ JabberFormLayoutControls(hFrame, layout_info, &m_formHeight);
+ mir_free(layout_info);
+ }
+
+ if ( m_formHeight > m_frameHeight ) {
+ HWND hwndScroll;
+
+ hwndScroll = GetDlgItem( m_hwnd, IDC_VSCROLL );
+ EnableWindow( hwndScroll, TRUE );
+ SetScrollRange( hwndScroll, SB_CTL, 0, m_formHeight - m_frameHeight, FALSE );
+ m_curPos = 0;
+ }
+
+ EnableWindow( GetDlgItem( m_hwnd, IDC_SUBMIT ), TRUE );
+ }
+ else if ( wParam == 0 ) {
+ // lParam = error message
+ SetDlgItemText( m_hwnd, IDC_FRAME_TEXT, ( LPCTSTR ) lParam );
+ }
+ return TRUE;
+
+ case WM_VSCROLL:
+ int pos = m_curPos;
+ switch ( LOWORD( wParam )) {
+ case SB_LINEDOWN: pos += 10; break;
+ case SB_LINEUP: pos -= 10; break;
+ case SB_PAGEDOWN: pos += ( m_frameHeight - 10 ); break;
+ case SB_PAGEUP: pos -= ( m_frameHeight - 10 ); break;
+ case SB_THUMBTRACK: pos = HIWORD( wParam ); break;
+ }
+ if ( pos > ( m_formHeight - m_frameHeight ))
+ pos = m_formHeight - m_frameHeight;
+ if ( pos < 0 )
+ pos = 0;
+ if ( m_curPos != pos ) {
+ ScrollWindow( GetDlgItem( m_hwnd, IDC_FRAME ), 0, m_curPos - pos, NULL, &( m_frameRect ));
+ SetScrollPos( GetDlgItem( m_hwnd, IDC_VSCROLL ), SB_CTL, pos, TRUE );
+ m_curPos = pos;
+ } }
+
+ return CJabberDlgBase::DlgProc( msg, wParam, lParam );
+ }
+
+ void OnSubmit( CCtrlButton* )
+ {
+ HXML queryNode, xNode;
+ const TCHAR *from;
+
+ if ( m_agentRegIqNode == NULL ) return;
+ if (( from = xmlGetAttrValue( m_agentRegIqNode, _T("from"))) == NULL ) return;
+ if (( queryNode = xmlGetChild( m_agentRegIqNode , "query" )) == NULL ) return;
+ HWND hFrame = GetDlgItem( m_hwnd, IDC_FRAME );
+
+ TCHAR *str2 = ( TCHAR* )alloca( sizeof(TCHAR) * 128 );
+ int id = 0;
+
+ int iqId = m_proto->SerialNext();
+ m_proto->IqAdd( iqId, IQ_PROC_SETREGISTER, &CJabberProto::OnIqResultSetRegister );
+
+ XmlNodeIq iq( _T("set"), iqId, from );
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_REGISTER));
+
+ if (( xNode = xmlGetChild( queryNode , "x" )) != NULL ) {
+ // use new jabber:x:data form
+ HXML n = JabberFormGetData( hFrame, xNode );
+ xmlAddChild( query, n );
+ xi.destroyNode( n );
+ }
+ else {
+ // use old registration information form
+ for ( int i=0; ; i++ ) {
+ HXML n = xmlGetChild( queryNode ,i);
+ if ( !n )
+ break;
+
+ if ( xmlGetName( n )) {
+ if ( !lstrcmp( xmlGetName( n ), _T("key"))) {
+ // field that must be passed along with the registration
+ if ( xmlGetText( n ))
+ xmlAddChild( query, xmlGetName( n ), xmlGetText( n ));
+ else
+ xmlAddChild( query, xmlGetName( n ));
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("registered")) || !lstrcmp( xmlGetName( n ), _T("instructions"))) {
+ // do nothing, we will skip these
+ }
+ else {
+ GetDlgItemText( hFrame, id, str2, 128 );
+ xmlAddChild( query, xmlGetName( n ), str2 );
+ id++;
+ } } } }
+
+ m_proto->m_ThreadInfo->send( iq );
+
+ CAgentRegProgressDlg( m_proto, m_hwnd ).DoModal();
+
+ Close();
+ }
+};
+
+void CJabberProto::RegisterAgent( HWND /*hwndDlg*/, TCHAR* jid )
+{
+ ( new CAgentRegDlg( this, jid ))->Show();
+}
diff --git a/protocols/JabberG/src/jabber_bookmarks.cpp b/protocols/JabberG/src/jabber_bookmarks.cpp new file mode 100644 index 0000000000..158f83e669 --- /dev/null +++ b/protocols/JabberG/src/jabber_bookmarks.cpp @@ -0,0 +1,483 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2007 Michael Stepura, George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Bookmarks editor window
+
+struct JabberAddBookmarkDlgParam {
+ CJabberProto* ppro;
+ JABBER_LIST_ITEM* m_item;
+};
+
+static INT_PTR CALLBACK JabberAddBookmarkDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ JabberAddBookmarkDlgParam* param = (JabberAddBookmarkDlgParam*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ TCHAR text[512];
+ JABBER_LIST_ITEM *item;
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ param = (JabberAddBookmarkDlgParam*)lParam;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+
+ param->ppro->m_hwndJabberAddBookmark = hwndDlg;
+ TranslateDialogDefault( hwndDlg );
+ if ( item = param->m_item ) {
+ if ( !lstrcmp( item->type, _T("conference"))) {
+ if (!_tcschr( item->jid, _T( '@' ))) { //no room name - consider it is transport
+ SendDlgItemMessage(hwndDlg, IDC_AGENT_RADIO, BM_SETCHECK, BST_CHECKED, 0);
+ EnableWindow( GetDlgItem( hwndDlg, IDC_NICK ), FALSE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_PASSWORD ), FALSE );
+ }
+ else SendDlgItemMessage(hwndDlg, IDC_ROOM_RADIO, BM_SETCHECK, BST_CHECKED, 0);
+ }
+ else {
+ SendDlgItemMessage(hwndDlg, IDC_URL_RADIO, BM_SETCHECK, BST_CHECKED, 0);
+ EnableWindow( GetDlgItem( hwndDlg, IDC_NICK ), FALSE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_PASSWORD ), FALSE );
+ SendDlgItemMessage(hwndDlg, IDC_CHECK_BM_AUTOJOIN, BM_SETCHECK, BST_UNCHECKED, 0);
+ EnableWindow( GetDlgItem( hwndDlg, IDC_CHECK_BM_AUTOJOIN), FALSE );
+ }
+
+ EnableWindow( GetDlgItem( hwndDlg, IDC_ROOM_RADIO), FALSE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_URL_RADIO), FALSE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_RADIO), FALSE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_CHECK_BM_AUTOJOIN), FALSE );
+
+ if ( item->jid ) SetDlgItemText( hwndDlg, IDC_ROOM_JID, item->jid );
+ if ( item->name ) SetDlgItemText( hwndDlg, IDC_NAME, item->name );
+ if ( item->nick ) SetDlgItemText( hwndDlg, IDC_NICK, item->nick );
+ if ( item->password ) SetDlgItemText( hwndDlg, IDC_PASSWORD, item->password );
+ if ( item->bAutoJoin ) SendDlgItemMessage( hwndDlg, IDC_CHECK_BM_AUTOJOIN, BM_SETCHECK, BST_CHECKED, 0 );
+ if ( SendDlgItemMessage(hwndDlg, IDC_ROOM_RADIO, BM_GETCHECK,0, 0) == BST_CHECKED )
+ EnableWindow( GetDlgItem( hwndDlg, IDC_CHECK_BM_AUTOJOIN), TRUE );
+ }
+ else {
+ EnableWindow( GetDlgItem( hwndDlg, IDOK ), FALSE );
+ SendDlgItemMessage(hwndDlg, IDC_ROOM_RADIO, BM_SETCHECK, BST_CHECKED, 0);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch ( HIWORD(wParam)) {
+ case BN_CLICKED:
+ switch (LOWORD (wParam)) {
+ case IDC_ROOM_RADIO:
+ EnableWindow( GetDlgItem( hwndDlg, IDC_NICK ), TRUE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_PASSWORD ), TRUE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_CHECK_BM_AUTOJOIN), TRUE );
+ break;
+ case IDC_AGENT_RADIO:
+ case IDC_URL_RADIO:
+ EnableWindow( GetDlgItem( hwndDlg, IDC_NICK ), FALSE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_PASSWORD ), FALSE );
+ SendDlgItemMessage(hwndDlg, IDC_CHECK_BM_AUTOJOIN, BM_SETCHECK, BST_UNCHECKED, 0);
+ EnableWindow( GetDlgItem( hwndDlg, IDC_CHECK_BM_AUTOJOIN), FALSE );
+ break;
+ }
+ }
+
+ switch ( LOWORD( wParam )) {
+ case IDC_ROOM_JID:
+ if (( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE )
+ EnableWindow( GetDlgItem( hwndDlg, IDOK ), GetDlgItemText( hwndDlg, IDC_ROOM_JID, text, SIZEOF( text )));
+ break;
+
+ case IDOK:
+ {
+ GetDlgItemText( hwndDlg, IDC_ROOM_JID, text, SIZEOF( text ));
+ TCHAR* roomJID = NEWTSTR_ALLOCA( text );
+
+ if ( param->m_item )
+ param->ppro->ListRemove( LIST_BOOKMARK, param->m_item->jid );
+
+ item = param->ppro->ListAdd( LIST_BOOKMARK, roomJID );
+ item->bUseResource = TRUE;
+
+ if ( SendDlgItemMessage(hwndDlg, IDC_URL_RADIO, BM_GETCHECK,0, 0) == BST_CHECKED )
+ replaceStrT( item->type, _T( "url" ));
+ else
+ replaceStrT( item->type, _T( "conference" ));
+
+ GetDlgItemText( hwndDlg, IDC_NICK, text, SIZEOF( text ));
+ replaceStrT( item->nick, text );
+
+ GetDlgItemText( hwndDlg, IDC_PASSWORD, text, SIZEOF( text ));
+ replaceStrT( item->password, text );
+
+ GetDlgItemText( hwndDlg, IDC_NAME, text, SIZEOF( text ));
+ replaceStrT( item->name, ( text[0] == 0 ) ? roomJID : text );
+
+ item->bAutoJoin = (SendDlgItemMessage(hwndDlg, IDC_CHECK_BM_AUTOJOIN, BM_GETCHECK,0, 0) == BST_CHECKED );
+ {
+ int iqId = param->ppro->SerialNext();
+ param->ppro->IqAdd( iqId, IQ_PROC_SETBOOKMARKS, &CJabberProto::OnIqResultSetBookmarks);
+
+ XmlNodeIq iq( _T("set"), iqId);
+ param->ppro->SetBookmarkRequest(iq);
+ param->ppro->m_ThreadInfo->send( iq );
+ }
+ }
+ // fall through
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ break;
+
+ case WM_JABBER_CHECK_ONLINE:
+ if ( !param->ppro->m_bJabberOnline )
+ EndDialog( hwndDlg, 0 );
+ break;
+
+ case WM_DESTROY:
+ param->ppro->m_hwndJabberAddBookmark = NULL;
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Bookmarks manager window
+
+class CJabberDlgBookmarks : public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+public:
+ CJabberDlgBookmarks(CJabberProto *proto);
+
+ void UpdateData();
+
+protected:
+ void OnInitDialog();
+ void OnClose();
+ void OnDestroy();
+ int Resizer(UTILRESIZECONTROL *urc);
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
+ void OnProtoCheckOnline(WPARAM wParam, LPARAM lParam);
+ void OnProtoRefresh(WPARAM wParam, LPARAM lParam);
+ void OpenBookmark();
+
+private:
+ CCtrlMButton m_btnAdd;
+ CCtrlMButton m_btnEdit;
+ CCtrlMButton m_btnRemove;
+ CCtrlFilterListView m_lvBookmarks;
+
+ void lvBookmarks_OnDoubleClick(CCtrlFilterListView *)
+ {
+ OpenBookmark();
+ }
+
+ void btnAdd_OnClick(CCtrlFilterListView *)
+ {
+ if (!m_proto->m_bJabberOnline) return;
+
+ JabberAddBookmarkDlgParam param;
+ param.ppro = m_proto;
+ param.m_item = NULL;
+ DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_BOOKMARK_ADD ), m_hwnd, JabberAddBookmarkDlgProc, (LPARAM)¶m);
+ }
+
+ void btnEdit_OnClick(CCtrlFilterListView *)
+ {
+ if (!m_proto->m_bJabberOnline) return;
+
+ int iItem = m_lvBookmarks.GetNextItem(-1, LVNI_SELECTED);
+ if (iItem < 0) return;
+
+ TCHAR *address = (TCHAR *)m_lvBookmarks.GetItemData(iItem);
+ if (!address) return;
+
+ JABBER_LIST_ITEM *item = m_proto->ListGetItemPtr(LIST_BOOKMARK, address);
+ if (!item) return;
+
+ JabberAddBookmarkDlgParam param;
+ param.ppro = m_proto;
+ param.m_item = item;
+ DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_BOOKMARK_ADD ), m_hwnd, JabberAddBookmarkDlgProc, (LPARAM)¶m);
+ }
+
+ void btnRemove_OnClick(CCtrlFilterListView *)
+ {
+ if (!m_proto->m_bJabberOnline) return;
+
+ int iItem = m_lvBookmarks.GetNextItem(-1, LVNI_SELECTED);
+ if (iItem < 0) return;
+
+ TCHAR *address = (TCHAR *)m_lvBookmarks.GetItemData(iItem);
+ if (!address) return;
+
+ JABBER_LIST_ITEM *item = m_proto->ListGetItemPtr(LIST_BOOKMARK, address);
+ if (!item) return;
+
+ m_btnAdd.Disable();
+ m_btnEdit.Disable();
+ m_btnRemove.Disable();
+
+ m_proto->ListRemove(LIST_BOOKMARK, address);
+
+ m_lvBookmarks.SetItemState(iItem, 0, LVIS_SELECTED); // Unselect the item
+
+ int iqId = m_proto->SerialNext();
+ m_proto->IqAdd(iqId, IQ_PROC_SETBOOKMARKS, &CJabberProto::OnIqResultSetBookmarks);
+
+ XmlNodeIq iq( _T("set"), iqId);
+ m_proto->SetBookmarkRequest(iq);
+ m_proto->m_ThreadInfo->send(iq);
+ }
+
+};
+
+CJabberDlgBookmarks::CJabberDlgBookmarks(CJabberProto *proto) :
+ CSuper(proto, IDD_BOOKMARKS, NULL),
+ m_btnAdd(this, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add")),
+ m_btnEdit(this, IDC_EDIT, SKINICON_OTHER_RENAME, LPGEN("Edit")),
+ m_btnRemove(this, IDC_REMOVE, SKINICON_OTHER_DELETE, LPGEN("Remove")),
+ m_lvBookmarks(this, IDC_BM_LIST, true, true)
+{
+ m_lvBookmarks.OnItemActivate = Callback(this, &CJabberDlgBookmarks::lvBookmarks_OnDoubleClick);
+ m_btnAdd.OnClick = Callback(this, &CJabberDlgBookmarks::btnAdd_OnClick);
+ m_btnEdit.OnClick = Callback(this, &CJabberDlgBookmarks::btnEdit_OnClick);
+ m_btnRemove.OnClick = Callback(this, &CJabberDlgBookmarks::btnRemove_OnClick);
+}
+
+void CJabberDlgBookmarks::UpdateData()
+{
+ if (!m_proto->m_bJabberOnline) return;
+
+ int iqId = m_proto->SerialNext();
+ m_proto->IqAdd( iqId, IQ_PROC_DISCOBOOKMARKS, &CJabberProto::OnIqResultDiscoBookmarks);
+ m_proto->m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId ) << XQUERY( _T(JABBER_FEAT_PRIVATE_STORAGE))
+ << XCHILDNS( _T("storage"), _T("storage:bookmarks")));
+}
+
+void CJabberDlgBookmarks::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+
+ WindowSetIcon( m_hwnd, m_proto, "bookmarks" );
+
+ m_btnAdd.Disable();
+ m_btnEdit.Disable();
+ m_btnRemove.Disable();
+
+ m_lvBookmarks.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_HEADERDRAGDROP | (IsWinVerXPPlus() ? LVS_EX_DOUBLEBUFFER : 0));
+
+ HIMAGELIST hIml = m_lvBookmarks.CreateImageList(LVSIL_SMALL);
+ ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("group"));
+ ImageList_AddIcon_Icolib(hIml, LoadSkinnedIcon(SKINICON_EVENT_URL));
+
+ m_lvBookmarks.AddColumn(0, TranslateT("Bookmark Name"), m_proto->JGetWord(NULL, "bookmarksWnd_cx0", 120));
+ m_lvBookmarks.AddColumn(1, TranslateT("Address (JID or URL)"), m_proto->JGetWord(NULL, "bookmarksWnd_cx1", 210));
+ m_lvBookmarks.AddColumn(2, TranslateT("Nickname"), m_proto->JGetWord(NULL, "bookmarksWnd_cx2", 90));
+
+ m_lvBookmarks.EnableGroupView(TRUE);
+ m_lvBookmarks.AddGroup(0, TranslateT("Conferences"));
+ m_lvBookmarks.AddGroup(1, TranslateT("Links"));
+
+ Utils_RestoreWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "bookmarksWnd_");
+}
+
+void CJabberDlgBookmarks::OnClose()
+{
+ LVCOLUMN lvc = {0};
+ lvc.mask = LVCF_WIDTH;
+ m_lvBookmarks.GetColumn(0, &lvc);
+ m_proto->JSetWord(NULL, "bookmarksWnd_cx0", lvc.cx);
+ m_lvBookmarks.GetColumn(1, &lvc);
+ m_proto->JSetWord(NULL, "bookmarksWnd_cx1", lvc.cx);
+ m_lvBookmarks.GetColumn(2, &lvc);
+ m_proto->JSetWord(NULL, "bookmarksWnd_cx2", lvc.cx);
+
+ Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "bookmarksWnd_");
+
+ CSuper::OnClose();
+}
+
+void CJabberDlgBookmarks::OnDestroy()
+{
+ m_proto->m_pDlgBookmarks = NULL;
+
+ CSuper::OnDestroy();
+}
+
+void CJabberDlgBookmarks::OpenBookmark()
+{
+ int iItem = m_lvBookmarks.GetNextItem(-1, LVNI_SELECTED);
+ if (iItem < 0) return;
+
+ TCHAR *address = (TCHAR *)m_lvBookmarks.GetItemData(iItem);
+ if (!address) return;
+
+ JABBER_LIST_ITEM *item = m_proto->ListGetItemPtr(LIST_BOOKMARK, address);
+ if (!item) return;
+
+ if (!lstrcmpi(item->type, _T("conference")))
+ {
+ if (!jabberChatDllPresent)
+ {
+ JabberChatDllError();
+ return;
+ }
+
+ m_lvBookmarks.SetItemState(iItem, 0, LVIS_SELECTED); // Unselect the item
+
+ /* some hack for using bookmark to transport not under XEP-0048 */
+ if (!_tcschr(item->jid, _T('@')))
+ { //the room name is not provided let consider that it is transport and send request to registration
+ m_proto->RegisterAgent(NULL, item->jid);
+ } else
+ {
+ TCHAR *room = NEWTSTR_ALLOCA(item->jid);
+ TCHAR *server = _tcschr(room, _T('@'));
+ *(server++) = 0;
+
+ if (item->nick && *item->nick)
+ {
+ m_proto->GroupchatJoinRoom(server, room, item->nick, item->password);
+ } else
+ {
+ TCHAR* nick = JabberNickFromJID(m_proto->m_szJabberJID);
+ m_proto->GroupchatJoinRoom(server, room, nick, item->password);
+ mir_free(nick);
+ }
+ }
+ } else
+ {
+ char *szUrl = mir_t2a(item->jid);
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+ mir_free(szUrl);
+ }
+}
+
+INT_PTR CJabberDlgBookmarks::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_GETMINMAXINFO:
+ {
+ LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
+ lpmmi->ptMinTrackSize.x = 451;
+ lpmmi->ptMinTrackSize.y = 320;
+ return 0;
+ }
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ OpenBookmark();
+ return TRUE;
+ }
+ }
+ break;
+ }
+
+ return CSuper::DlgProc(msg, wParam, lParam);
+}
+
+void CJabberDlgBookmarks::OnProtoCheckOnline(WPARAM, LPARAM)
+{
+ if (!m_proto->m_bJabberOnline)
+ {
+ m_btnAdd.Disable();
+ m_btnEdit.Disable();
+ m_btnRemove.Disable();
+ } else
+ {
+ UpdateData();
+ }
+
+}
+
+void CJabberDlgBookmarks::OnProtoRefresh(WPARAM, LPARAM)
+{
+ m_lvBookmarks.DeleteAllItems();
+
+ JABBER_LIST_ITEM *item = NULL;
+ LISTFOREACH(i, m_proto, LIST_BOOKMARK)
+ {
+ if (item = m_proto->ListGetItemPtrFromIndex(i))
+ {
+ int itemType = lstrcmpi(item->type, _T("conference")) ? 1 : 0;
+ int iItem = m_lvBookmarks.AddItem(item->name, itemType, (LPARAM)item->jid, itemType);
+ m_lvBookmarks.SetItem(iItem, 1, item->jid);
+ if (itemType == 0)
+ m_lvBookmarks.SetItem(iItem, 2, item->nick);
+ }
+ }
+
+ if (item)
+ {
+ m_btnEdit.Enable();
+ m_btnRemove.Enable();
+ }
+
+ m_btnAdd.Enable();
+}
+
+int CJabberDlgBookmarks::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch ( urc->wId ) {
+ case IDC_BM_LIST:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+
+ case IDCANCEL:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+
+ case IDC_ADD:
+ case IDC_EDIT:
+ case IDC_REMOVE:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
+ }
+ return CSuper::Resizer(urc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Launches the Bookmarks manager window
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleBookmarks( WPARAM, LPARAM)
+{
+ UI_SAFE_OPEN_EX(CJabberDlgBookmarks, m_pDlgBookmarks, pDlg);
+ pDlg->UpdateData();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Launches the Bookmark details window, lParam is JABBER_BOOKMARK_ITEM*
+int CJabberProto::AddEditBookmark( JABBER_LIST_ITEM* item )
+{
+ if ( m_bJabberOnline) {
+ JabberAddBookmarkDlgParam param;
+ param.ppro = this;
+ param.m_item = item;//(JABBER_LIST_ITEM*)lParam;
+ DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_BOOKMARK_ADD ), NULL, JabberAddBookmarkDlgProc, (LPARAM)¶m );
+ }
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber_byte.cpp b/protocols/JabberG/src/jabber_byte.cpp new file mode 100644 index 0000000000..9e86b51388 --- /dev/null +++ b/protocols/JabberG/src/jabber_byte.cpp @@ -0,0 +1,778 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_byte.h"
+#include "jabber_caps.h"
+
+#define JABBER_NETWORK_BUFFER_SIZE 4096
+
+///////////////// Bytestream sending /////////////////////////
+
+JABBER_BYTE_TRANSFER::~JABBER_BYTE_TRANSFER()
+{
+ filetransfer* pft = ft;
+ if ( pft )
+ pft->jbt = NULL;
+
+ mir_free( srcJID );
+ mir_free( dstJID );
+ mir_free( streamhostJID );
+ mir_free( iqId );
+ mir_free( sid );
+
+ xi.destroyNode( iqNode );
+
+ // XEP-0065 proxy support
+ mir_free( szProxyHost );
+ mir_free( szProxyPort );
+ mir_free( szProxyJid );
+ mir_free( szStreamhostUsed );
+}
+
+void CJabberProto::IqResultProxyDiscovery( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ JABBER_BYTE_TRANSFER *jbt = ( JABBER_BYTE_TRANSFER * )pInfo->GetUserData();
+
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) {
+ HXML queryNode = xmlGetChild( iqNode , "query" );
+ if ( queryNode ) {
+ const TCHAR *queryXmlns = xmlGetAttrValue( queryNode, _T( "xmlns" ));
+ if (queryXmlns && !_tcscmp( queryXmlns, _T(JABBER_FEAT_BYTESTREAMS))) {
+ HXML streamHostNode = xmlGetChild( queryNode , "streamhost" );
+ if ( streamHostNode ) {
+ const TCHAR *streamJid = xmlGetAttrValue( streamHostNode, _T( "jid" ));
+ const TCHAR *streamHost = xmlGetAttrValue( streamHostNode, _T( "host" ));
+ const TCHAR *streamPort = xmlGetAttrValue( streamHostNode, _T( "port" ));
+ if ( streamJid && streamHost && streamPort ) {
+ jbt->szProxyHost = mir_tstrdup( streamHost );
+ jbt->szProxyJid = mir_tstrdup( streamJid );
+ jbt->szProxyPort = mir_tstrdup( streamPort );
+ jbt->bProxyDiscovered = TRUE;
+ } } } } }
+ else if ( pInfo->GetIqType() == JABBER_IQ_TYPE_ERROR )
+ jbt->state = JBT_ERROR;
+
+ if ( jbt->hProxyEvent )
+ SetEvent( jbt->hProxyEvent );
+}
+
+void JabberByteSendConnection( HANDLE hConn, DWORD /*dwRemoteIP*/, void* extra )
+{
+ CJabberProto* ppro = ( CJabberProto* )extra;
+ TCHAR szPort[8];
+ JABBER_BYTE_TRANSFER *jbt;
+ int recvResult, bytesParsed;
+ HANDLE hListen;
+ JABBER_LIST_ITEM *item;
+ char* buffer;
+ int datalen;
+
+ NETLIBCONNINFO connInfo = { sizeof(connInfo) };
+ CallService(MS_NETLIB_GETCONNECTIONINFO, (WPARAM)hConn, (LPARAM)&connInfo);
+
+ mir_sntprintf( szPort, SIZEOF( szPort ), _T("%u"), connInfo.wPort );
+ ppro->Log( "bytestream_send_connection incoming connection accepted: %s", connInfo.szIpPort );
+
+ if (( item = ppro->ListGetItemPtr( LIST_BYTE, szPort )) == NULL ) {
+ ppro->Log( "No bytestream session is currently active, connection closed." );
+ Netlib_CloseHandle( hConn );
+ return;
+ }
+
+ jbt = item->jbt;
+
+ if (( buffer = ( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE )) == NULL ) {
+ ppro->Log( "bytestream_send cannot allocate network buffer, connection closed." );
+ jbt->state = JBT_ERROR;
+ Netlib_CloseHandle( hConn );
+ if ( jbt->hEvent != NULL ) SetEvent( jbt->hEvent );
+ return;
+ }
+
+ hListen = jbt->hConn;
+ jbt->hConn = hConn;
+ jbt->state = JBT_INIT;
+ datalen = 0;
+ while ( jbt->state!=JBT_DONE && jbt->state!=JBT_ERROR ) {
+ recvResult = Netlib_Recv( hConn, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 );
+ if ( recvResult <= 0 )
+ break;
+
+ datalen += recvResult;
+ bytesParsed = ppro->ByteSendParse( hConn, jbt, buffer, datalen );
+ if ( bytesParsed < datalen )
+ memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
+ datalen -= bytesParsed;
+ }
+
+ if ( jbt->hConn )
+ Netlib_CloseHandle( jbt->hConn );
+
+ ppro->Log( "bytestream_send_connection closing connection" );
+ jbt->hConn = hListen;
+ mir_free( buffer );
+
+ if ( jbt->hEvent != NULL )
+ SetEvent( jbt->hEvent );
+}
+
+void CJabberProto::ByteSendThread( JABBER_BYTE_TRANSFER *jbt )
+{
+ char* localAddr = NULL;
+ DBVARIANT dbv;
+ TCHAR szPort[8];
+ HANDLE hEvent = NULL;
+ TCHAR* proxyJid;
+ CJabberIqInfo* pInfo = NULL;
+ int nIqId = 0;
+
+ Log( "Thread started: type=bytestream_send" );
+
+ BOOL bDirect = m_options.BsDirect;
+
+ if ( m_options.BsProxyManual ) {
+ proxyJid = NULL;
+ if ( !DBGetContactSettingString( NULL, m_szModuleName, "BsProxyServer", &dbv )) {
+ proxyJid = mir_a2t( dbv.pszVal );
+ JFreeVariant( &dbv );
+ }
+
+ if ( proxyJid ) {
+ jbt->bProxyDiscovered = FALSE;
+ jbt->szProxyHost = NULL;
+ jbt->szProxyPort = NULL;
+ jbt->szProxyJid = NULL;
+ jbt->hProxyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+
+ pInfo = m_iqManager.AddHandler( &CJabberProto::IqResultProxyDiscovery, JABBER_IQ_TYPE_GET, proxyJid, 0, -1, jbt );
+ nIqId = pInfo->GetIqId();
+ XmlNodeIq iq( pInfo );
+ iq << XQUERY( _T(JABBER_FEAT_BYTESTREAMS));
+ m_ThreadInfo->send( iq );
+
+ WaitForSingleObject( jbt->hProxyEvent, INFINITE );
+ m_iqManager.ExpireIq ( nIqId );
+ CloseHandle( jbt->hProxyEvent );
+ jbt->hProxyEvent = NULL;
+
+ mir_free( proxyJid );
+
+ if ( jbt->state == JBT_ERROR && !bDirect ) {
+ Log( "Bytestream proxy failure" );
+ MsgPopup( pInfo->GetHContact(), TranslateT("Bytestream Proxy not available"), pInfo->GetReceiver());
+ jbt->ft->state = FT_DENIED;
+ (this->*jbt->pfnFinal)( FALSE, jbt->ft );
+ jbt->ft = NULL;
+ delete jbt;
+ return;
+ }
+ } }
+
+ pInfo = m_iqManager.AddHandler( &CJabberProto::ByteInitiateResult, JABBER_IQ_TYPE_SET, jbt->dstJID, 0, -1, jbt );
+ nIqId = pInfo->GetIqId();
+ {
+ XmlNodeIq iq( pInfo );
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_BYTESTREAMS)) << XATTR( _T("sid"), jbt->sid );
+
+ if ( bDirect ) {
+ if ( m_options.BsDirectManual ) {
+ if ( !DBGetContactSettingString( NULL, m_szModuleName, "BsDirectAddr", &dbv ))
+ localAddr = dbv.pszVal;
+ }
+
+ NETLIBBIND nlb = {0};
+ nlb.cbSize = sizeof( NETLIBBIND );
+ nlb.pfnNewConnectionV2 = JabberByteSendConnection;
+ nlb.pExtra = this;
+ nlb.wPort = 0; // Use user-specified incoming port ranges, if available
+ jbt->hConn = ( HANDLE ) CallService( MS_NETLIB_BINDPORT, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nlb );
+ if ( jbt->hConn == NULL ) {
+ Log( "Cannot allocate port for bytestream_send thread, thread ended." );
+ delete jbt;
+ return;
+ }
+
+ if ( localAddr == NULL )
+ localAddr = (char*)CallService( MS_NETLIB_ADDRESSTOSTRING, 1, nlb.dwExternalIP );
+
+ mir_sntprintf( szPort, SIZEOF( szPort ), _T("%d"), nlb.wPort );
+ JABBER_LIST_ITEM *item = ListAdd( LIST_BYTE, szPort );
+ item->jbt = jbt;
+ hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ jbt->hEvent = hEvent;
+ jbt->hSendEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ query << XCHILD( _T("streamhost")) << XATTR( _T("jid"), m_ThreadInfo->fullJID ) << XATTR( _T("host"), _A2T(localAddr)) << XATTRI( _T("port"), nlb.wPort );
+
+ NETLIBIPLIST* ihaddr = ( NETLIBIPLIST* )CallService( MS_NETLIB_GETMYIP, 1, 0 );
+ for ( unsigned i = 0; i < ihaddr->cbNum; ++i )
+ if ( strcmp( localAddr, ihaddr->szIp[i] ))
+ query << XCHILD( _T("streamhost")) << XATTR( _T("jid"), m_ThreadInfo->fullJID ) << XATTR( _T("host"), _A2T(ihaddr->szIp[i])) << XATTRI( _T("port"), nlb.wPort );
+
+ mir_free( ihaddr );
+ mir_free( localAddr );
+ }
+
+ if ( jbt->bProxyDiscovered )
+ query << XCHILD( _T("streamhost")) << XATTR( _T("jid"), jbt->szProxyJid ) << XATTR( _T("host"), jbt->szProxyHost ) << XATTR( _T("port"), jbt->szProxyPort );
+
+ jbt->hProxyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ jbt->szStreamhostUsed = NULL;
+
+ m_ThreadInfo->send( iq );
+ }
+
+ WaitForSingleObject( jbt->hProxyEvent, INFINITE );
+ m_iqManager.ExpireIq( nIqId );
+ CloseHandle( jbt->hProxyEvent );
+ jbt->hProxyEvent = NULL;
+
+ if ( !jbt->szStreamhostUsed ) {
+ if ( bDirect ) {
+ SetEvent( jbt->hSendEvent );
+ CloseHandle( jbt->hSendEvent );
+ CloseHandle( hEvent );
+ jbt->hEvent = NULL;
+ if ( jbt->hConn != NULL )
+ Netlib_CloseHandle( jbt->hConn );
+ jbt->hConn = NULL;
+ ListRemove( LIST_BYTE, szPort );
+ }
+ (this->*jbt->pfnFinal)(( jbt->state==JBT_DONE )?TRUE:FALSE, jbt->ft );
+ jbt->ft = NULL;
+ // stupid fix: wait for listening thread exit
+ Sleep( 100 );
+ delete jbt;
+ return;
+ }
+
+ if ( jbt->bProxyDiscovered && !_tcscmp( jbt->szProxyJid, jbt->szStreamhostUsed )) {
+ // jabber proxy used
+ if ( bDirect ) {
+ SetEvent( jbt->hSendEvent );
+ CloseHandle( jbt->hSendEvent );
+ CloseHandle( hEvent );
+ jbt->hEvent = NULL;
+ if ( jbt->hConn != NULL )
+ Netlib_CloseHandle( jbt->hConn );
+ jbt->hConn = NULL;
+ ListRemove( LIST_BYTE, szPort );
+ }
+ ByteSendViaProxy( jbt );
+ }
+ else {
+ SetEvent( jbt->hSendEvent );
+ WaitForSingleObject( hEvent, INFINITE );
+ CloseHandle( hEvent );
+ CloseHandle( jbt->hSendEvent );
+ jbt->hEvent = NULL;
+ (this->*jbt->pfnFinal)(( jbt->state == JBT_DONE ) ? TRUE : FALSE, jbt->ft );
+ jbt->ft = NULL;
+ if ( jbt->hConn != NULL )
+ Netlib_CloseHandle( jbt->hConn );
+ jbt->hConn = NULL;
+ ListRemove( LIST_BYTE, szPort );
+ }
+
+ // stupid fix: wait for listening connection thread exit
+ Sleep( 100 );
+ delete jbt;
+ Log( "Thread ended: type=bytestream_send" );
+}
+
+void CJabberProto::ByteInitiateResult( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ JABBER_BYTE_TRANSFER *jbt = ( JABBER_BYTE_TRANSFER * )pInfo->GetUserData();
+
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) {
+ HXML queryNode = xmlGetChild( iqNode , "query" );
+ if ( queryNode ) {
+ const TCHAR* queryXmlns = xmlGetAttrValue( queryNode, _T("xmlns"));
+ if ( queryXmlns && !_tcscmp( queryXmlns, _T( JABBER_FEAT_BYTESTREAMS ))) {
+ HXML streamHostNode = xmlGetChild( queryNode , "streamhost-used" );
+ if ( streamHostNode ) {
+ const TCHAR* streamJid = xmlGetAttrValue( streamHostNode, _T("jid"));
+ if ( streamJid )
+ jbt->szStreamhostUsed = mir_tstrdup( streamJid );
+ } } } }
+
+ if ( jbt->hProxyEvent )
+ SetEvent( jbt->hProxyEvent );
+}
+
+int CJabberProto::ByteSendParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen )
+{
+ int nMethods;
+ BYTE data[10];
+ int i;
+ char* str;
+
+ switch ( jbt->state ) {
+ case JBT_INIT:
+ // received:
+ // 00-00 ver ( 0x05 )
+ // 01-01 nmethods
+ // 02-xx list of methods ( nmethods bytes )
+ // send:
+ // 00-00 ver ( 0x05 )
+ // 01-01 select method ( 0=no auth required )
+ if ( datalen>=2 && buffer[0]==5 && buffer[1]+2==datalen ) {
+ nMethods = buffer[1];
+ for ( i=0; i<nMethods && buffer[2+i]!=0; i++ );
+ if ( i < nMethods ) {
+ data[1] = 0;
+ jbt->state = JBT_CONNECT;
+ }
+ else {
+ data[1] = 0xff;
+ jbt->state = JBT_ERROR;
+ }
+ data[0] = 5;
+ Netlib_Send( hConn, ( char* )data, 2, 0 );
+ }
+ else jbt->state = JBT_ERROR;
+ break;
+
+ case JBT_CONNECT:
+ // received:
+ // 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 )
+ // send:
+ // 00-00 ver ( 0x05 )
+ // 01-01 reply ( 0=success,2=not allowed )
+ // 02-02 reserved ( 0 )
+ // 03-03 address type ( 1=IPv4 address )
+ // 04-07 bnd.addr server bound address
+ // 08-09 bnd.port server bound port
+ if ( datalen == 47 && *(( DWORD* )buffer )==0x03000105 && buffer[4]==40 && *(( WORD* )( buffer+45 ))==0 ) {
+ 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 );
+ if (( str = JabberSha1( szAuthString )) != NULL ) {
+ for ( i=0; i<40 && buffer[i+5]==str[i]; i++ );
+ mir_free( str );
+
+ ZeroMemory( data, 10 );
+ data[1] = ( i>=20 )?0:2;
+ data[0] = 5;
+ data[3] = 1;
+ Netlib_Send( hConn, ( char* )data, 10, 0 );
+
+ // wait stream activation
+ WaitForSingleObject( jbt->hSendEvent, INFINITE );
+
+ if ( jbt->state == JBT_ERROR )
+ break;
+
+ if ( i>=20 && (this->*jbt->pfnSend)( hConn, jbt->ft )==TRUE )
+ jbt->state = JBT_DONE;
+ else
+ jbt->state = JBT_ERROR;
+ }
+ mir_free( szAuthString );
+ }
+ else
+ jbt->state = JBT_ERROR;
+ break;
+ }
+
+ return datalen;
+}
+
+///////////////// Bytestream receiving /////////////////////////
+
+void CJabberProto::IqResultStreamActivate( HXML iqNode )
+{
+ int id = JabberGetPacketID( iqNode );
+
+ TCHAR listJid[JABBER_MAX_JID_LEN];
+ mir_sntprintf(listJid, SIZEOF( listJid ), _T("ftproxy_%d"), id);
+
+ JABBER_LIST_ITEM *item = ListGetItemPtr( LIST_FTIQID, listJid );
+ if ( !item )
+ return;
+
+ if ( !lstrcmp( xmlGetAttrValue( iqNode, _T("type")), _T( "result" )))
+ item->jbt->bStreamActivated = TRUE;
+
+ if ( item->jbt->hProxyEvent )
+ SetEvent( item->jbt->hProxyEvent );
+}
+
+
+void CJabberProto::ByteSendViaProxy( JABBER_BYTE_TRANSFER *jbt )
+{
+ TCHAR *szHost, *szPort;
+ WORD port;
+ HANDLE hConn;
+ char data[3];
+ char* buffer;
+ int datalen, bytesParsed, recvResult;
+ BOOL validStreamhost;
+
+ if ( jbt == NULL ) return;
+ if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE )) == NULL ) {
+ m_ThreadInfo->send( XmlNodeIq( _T("error"), jbt->iqId, jbt->srcJID )
+ << XCHILD( _T("error")) << XATTRI( _T("code"), 406 ) << XATTR( _T("type"), _T("auth"))
+ << XCHILDNS( _T("not-acceptable"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+ return;
+ }
+
+ jbt->state = JBT_INIT;
+ validStreamhost = FALSE;
+ szPort = jbt->szProxyPort;
+ szHost = jbt->szProxyHost;
+
+ port = ( WORD )_ttoi( szPort );
+ if ( jbt->streamhostJID ) mir_free( jbt->streamhostJID );
+ jbt->streamhostJID = mir_tstrdup( jbt->szProxyJid );
+
+ NETLIBOPENCONNECTION nloc = { 0 };
+ nloc.cbSize = sizeof( nloc );
+ nloc.szHost = mir_t2a(szHost);
+ nloc.wPort = port;
+ hConn = ( HANDLE ) CallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nloc );
+ mir_free((void*)nloc.szHost);
+
+ if ( hConn != NULL ) {
+ jbt->hConn = hConn;
+
+ data[0] = 5;
+ data[1] = 1;
+ data[2] = 0;
+ Netlib_Send( hConn, data, 3, 0 );
+
+ jbt->state = JBT_INIT;
+ datalen = 0;
+ while ( jbt->state!=JBT_DONE && jbt->state!=JBT_ERROR && jbt->state!=JBT_SOCKSERR ) {
+ recvResult = Netlib_Recv( hConn, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 );
+ if ( recvResult <= 0 )
+ break;
+
+ datalen += recvResult;
+ bytesParsed = ByteSendProxyParse( hConn, jbt, buffer, datalen );
+ if ( bytesParsed < datalen )
+ memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
+ datalen -= bytesParsed;
+ if ( jbt->state == JBT_DONE ) validStreamhost = TRUE;
+ }
+ Netlib_CloseHandle( hConn );
+ }
+ mir_free( buffer );
+ (this->*jbt->pfnFinal)(( jbt->state == JBT_DONE ) ? TRUE : FALSE, jbt->ft );
+ jbt->ft = NULL;
+ if ( !validStreamhost )
+ m_ThreadInfo->send( XmlNodeIq( _T("error"), jbt->iqId, jbt->srcJID )
+ << XCHILD( _T("error")) << XATTRI( _T("code"), 404 ) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+}
+
+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;
+}
+
+
+void __cdecl CJabberProto::ByteReceiveThread( JABBER_BYTE_TRANSFER *jbt )
+{
+ HXML iqNode, queryNode = NULL, n;
+ const TCHAR *sid = NULL, *from = NULL, *to = NULL, *szId = NULL, *szHost, *szPort, *str;
+ int i;
+ WORD port;
+ HANDLE hConn;
+ char data[3];
+ char* buffer;
+ int datalen, bytesParsed, recvResult;
+ BOOL validStreamhost = FALSE;
+
+ if ( jbt == NULL ) return;
+
+ jbt->state = JBT_INIT;
+
+ if ( iqNode = jbt->iqNode ) {
+ from = xmlGetAttrValue( iqNode, _T("from"));
+ to = xmlGetAttrValue( iqNode, _T("to"));
+ szId = xmlGetAttrValue( iqNode, _T("id"));
+
+ queryNode = xmlGetChild( iqNode , "query" );
+ if ( queryNode )
+ sid = xmlGetAttrValue( queryNode, _T("sid"));
+ }
+
+ if ( szId && from && to && sid && ( n = xmlGetChild( queryNode , "streamhost" ))!=NULL ) {
+ jbt->iqId = mir_tstrdup( szId );
+ jbt->srcJID = mir_tstrdup( from );
+ jbt->dstJID = mir_tstrdup( to );
+ jbt->sid = mir_tstrdup( sid );
+
+ if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE ))) {
+ for ( i=1; ( n = xmlGetNthChild( queryNode, _T("streamhost"), i ))!=NULL; i++ ) {
+ if (( szHost = xmlGetAttrValue( n, _T("host"))) != NULL &&
+ ( szPort = xmlGetAttrValue( n, _T("port"))) != NULL &&
+ ( str = xmlGetAttrValue( n, _T("jid"))) != NULL ) {
+
+ port = ( WORD )_ttoi( szPort );
+ if ( jbt->streamhostJID ) mir_free( jbt->streamhostJID );
+ jbt->streamhostJID = mir_tstrdup( str );
+
+ Log( "bytestream_recv connecting to " TCHAR_STR_PARAM ":%d", szHost, port );
+ NETLIBOPENCONNECTION nloc = { 0 };
+ nloc.cbSize = sizeof( nloc );
+ nloc.szHost = mir_t2a(szHost);
+ nloc.wPort = port;
+ hConn = ( HANDLE ) CallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nloc );
+ mir_free((void*)nloc.szHost);
+
+ if ( hConn == NULL ) {
+ Log( "bytestream_recv_connection connection failed ( %d ), try next streamhost", WSAGetLastError());
+ continue;
+ }
+
+ jbt->hConn = hConn;
+
+ data[0] = 5;
+ data[1] = 1;
+ data[2] = 0;
+ Netlib_Send( hConn, data, 3, 0 );
+
+ jbt->state = JBT_INIT;
+ datalen = 0;
+ while ( jbt->state!=JBT_DONE && jbt->state!=JBT_ERROR && jbt->state!=JBT_SOCKSERR ) {
+ recvResult = Netlib_Recv( hConn, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 );
+ if ( recvResult <= 0 ) break;
+ datalen += recvResult;
+ bytesParsed = ByteReceiveParse( hConn, jbt, buffer, datalen );
+ if ( bytesParsed < datalen )
+ memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
+ datalen -= bytesParsed;
+ if ( jbt->state == JBT_RECVING ) validStreamhost = TRUE;
+ }
+ Netlib_CloseHandle( hConn );
+ Log( "bytestream_recv_connection closing connection" );
+ }
+ if ( jbt->state==JBT_ERROR || validStreamhost==TRUE )
+ break;
+ Log( "bytestream_recv_connection stream cannot be established, try next streamhost" );
+ }
+ mir_free( buffer );
+ }
+ }
+
+ (this->*jbt->pfnFinal)(( jbt->state==JBT_DONE )?TRUE:FALSE, jbt->ft );
+ jbt->ft = NULL;
+ if ( !validStreamhost && szId && from ) {
+ Log( "bytestream_recv_connection session not completed" );
+
+ m_ThreadInfo->send( XmlNodeIq( _T("error"), szId, from )
+ << XCHILD( _T("error")) << XATTRI( _T("code"), 404 ) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+ }
+
+ delete jbt;
+ Log( "Thread ended: type=bytestream_recv" );
+}
+
+int CJabberProto::ByteReceiveParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen )
+{
+ int bytesReceived, 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[JABBER_MAX_JID_LEN*2];
+ 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_RECVING;
+
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), jbt->iqId, jbt->srcJID ) << XQUERY( _T(JABBER_FEAT_BYTESTREAMS))
+ << XCHILD( _T("streamhost-used")) << XATTR( _T("jid"), jbt->streamhostJID ));
+ }
+ else jbt->state = JBT_SOCKSERR;
+ break;
+
+ case JBT_RECVING:
+ bytesReceived = (this->*jbt->pfnRecv)( hConn, jbt->ft, buffer, datalen );
+ if ( bytesReceived < 0 )
+ jbt->state = JBT_ERROR;
+ else if ( bytesReceived == 0 )
+ jbt->state = JBT_DONE;
+ break;
+ }
+
+ return num;
+}
diff --git a/protocols/JabberG/src/jabber_byte.h b/protocols/JabberG/src/jabber_byte.h new file mode 100644 index 0000000000..657d797fce --- /dev/null +++ b/protocols/JabberG/src/jabber_byte.h @@ -0,0 +1,61 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_BYTE_H_
+#define _JABBER_BYTE_H_
+
+typedef enum { JBT_INIT, JBT_AUTH, JBT_CONNECT, JBT_SOCKSERR, JBT_SENDING, JBT_RECVING, JBT_DONE, JBT_ERROR } JABBER_BYTE_STATE;
+
+struct CJabberProto;
+struct filetransfer;
+
+struct JABBER_BYTE_TRANSFER
+{
+ ~JABBER_BYTE_TRANSFER();
+
+ TCHAR* sid;
+ TCHAR* srcJID;
+ TCHAR* dstJID;
+ TCHAR* streamhostJID;
+ TCHAR* iqId;
+ JABBER_BYTE_STATE state;
+ HANDLE hConn;
+ HANDLE hEvent;
+ HXML iqNode;
+ BOOL ( CJabberProto::*pfnSend )( HANDLE hConn, filetransfer* ft );
+ int ( CJabberProto::*pfnRecv )( HANDLE hConn, filetransfer* ft, char* buffer, int datalen );
+ void ( CJabberProto::*pfnFinal )( BOOL success, filetransfer* ft );
+ filetransfer* ft;
+
+ // XEP-0065 proxy support
+ BOOL bProxyDiscovered;
+ HANDLE hProxyEvent;
+ TCHAR* szProxyHost;
+ TCHAR* szProxyPort;
+ TCHAR* szProxyJid;
+ TCHAR* szStreamhostUsed;
+ BOOL bStreamActivated;
+ HANDLE hSendEvent;
+};
+
+#endif
diff --git a/protocols/JabberG/src/jabber_caps.cpp b/protocols/JabberG/src/jabber_caps.cpp new file mode 100644 index 0000000000..380cf03087 --- /dev/null +++ b/protocols/JabberG/src/jabber_caps.cpp @@ -0,0 +1,697 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+#include "version.h"
+
+const JabberFeatCapPair g_JabberFeatCapPairs[] = {
+ { _T(JABBER_FEAT_DISCO_INFO), JABBER_CAPS_DISCO_INFO, _T("Supports Service Discovery info"), },
+ { _T(JABBER_FEAT_DISCO_ITEMS), JABBER_CAPS_DISCO_ITEMS, _T("Supports Service Discovery items list"), },
+ { _T(JABBER_FEAT_ENTITY_CAPS), JABBER_CAPS_ENTITY_CAPS, _T("Can inform about its Jabber capabilities"), },
+ { _T(JABBER_FEAT_SI), JABBER_CAPS_SI, _T("Supports stream initiation (for filetransfers for ex.)"), },
+ { _T(JABBER_FEAT_SI_FT), JABBER_CAPS_SI_FT, _T("Supports stream initiation for file transfers"), },
+ { _T(JABBER_FEAT_BYTESTREAMS), JABBER_CAPS_BYTESTREAMS, _T("Supports file transfers via SOCKS5 Bytestreams"), },
+ { _T(JABBER_FEAT_IBB), JABBER_CAPS_IBB, _T("Supports file transfers via In-Band Bytestreams"), },
+ { _T(JABBER_FEAT_OOB), JABBER_CAPS_OOB, _T("Supports file transfers via Out-of-Band Bytestreams"), },
+ { _T(JABBER_FEAT_OOB2), JABBER_CAPS_OOB, _T("Supports file transfers via Out-of-Band Bytestreams"), },
+ { _T(JABBER_FEAT_COMMANDS), JABBER_CAPS_COMMANDS, _T("Supports execution of Ad-Hoc commands"), },
+ { _T(JABBER_FEAT_REGISTER), JABBER_CAPS_REGISTER, _T("Supports in-band registration"), },
+ { _T(JABBER_FEAT_MUC), JABBER_CAPS_MUC, _T("Supports multi-user chat"), },
+ { _T(JABBER_FEAT_CHATSTATES), JABBER_CAPS_CHATSTATES, _T("Can report chat state in a chat session"), },
+ { _T(JABBER_FEAT_LAST_ACTIVITY), JABBER_CAPS_LAST_ACTIVITY, _T("Can report information about the last activity of the user"), },
+ { _T(JABBER_FEAT_VERSION), JABBER_CAPS_VERSION, _T("Can report own version information"), },
+ { _T(JABBER_FEAT_ENTITY_TIME), JABBER_CAPS_ENTITY_TIME, _T("Can report local time of the user"), },
+ { _T(JABBER_FEAT_PING), JABBER_CAPS_PING, _T("Can send and receive ping requests"), },
+ { _T(JABBER_FEAT_DATA_FORMS), JABBER_CAPS_DATA_FORMS, _T("Supports data forms"), },
+ { _T(JABBER_FEAT_MESSAGE_EVENTS), JABBER_CAPS_MESSAGE_EVENTS, _T("Can request and respond to events relating to the delivery, display, and composition of messages"), },
+ { _T(JABBER_FEAT_VCARD_TEMP), JABBER_CAPS_VCARD_TEMP, _T("Supports vCard"), },
+ { _T(JABBER_FEAT_AVATAR), JABBER_CAPS_AVATAR, _T("Supports iq-based avatars"), },
+ { _T(JABBER_FEAT_XHTML), JABBER_CAPS_XHTML, _T("Supports XHTML formatting of chat messages"), },
+ { _T(JABBER_FEAT_AGENTS), JABBER_CAPS_AGENTS, _T("Supports Jabber Browsing"), },
+ { _T(JABBER_FEAT_BROWSE), JABBER_CAPS_BROWSE, _T("Supports Jabber Browsing"), },
+ { _T(JABBER_FEAT_FEATURE_NEG), JABBER_CAPS_FEATURE_NEG, _T("Can negotiate options for specific features"), },
+ { _T(JABBER_FEAT_AMP), JABBER_CAPS_AMP, _T("Can request advanced processing of message stanzas"), },
+ { _T(JABBER_FEAT_USER_MOOD), JABBER_CAPS_USER_MOOD, _T("Can report information about user moods"), },
+ { _T(JABBER_FEAT_USER_MOOD_NOTIFY), JABBER_CAPS_USER_MOOD_NOTIFY, _T("Receives information about user moods"), },
+ { _T(JABBER_FEAT_PUBSUB), JABBER_CAPS_PUBSUB, _T("Supports generic publish-subscribe functionality"), },
+ { _T(JABBER_FEAT_SECUREIM), JABBER_CAPS_SECUREIM, _T("Supports SecureIM plugin for Miranda NG"), },
+ { _T(JABBER_FEAT_PRIVACY_LISTS), JABBER_CAPS_PRIVACY_LISTS, _T("Can block communications from particular other users using Privacy lists"), },
+ { _T(JABBER_FEAT_MESSAGE_RECEIPTS), JABBER_CAPS_MESSAGE_RECEIPTS, _T("Supports Message Receipts"), },
+ { _T(JABBER_FEAT_USER_TUNE), JABBER_CAPS_USER_TUNE, _T("Can report information about the music to which a user is listening"), },
+ { _T(JABBER_FEAT_USER_TUNE_NOTIFY), JABBER_CAPS_USER_TUNE_NOTIFY, _T("Receives information about the music to which a user is listening"), },
+ { _T(JABBER_FEAT_PRIVATE_STORAGE), JABBER_CAPS_PRIVATE_STORAGE, _T("Supports private XML Storage (for bookmakrs and other)"), },
+ { _T(JABBER_FEAT_ATTENTION), JABBER_CAPS_ATTENTION, _T("Supports attention requests ('nudge')"), },
+ { _T(JABBER_FEAT_ATTENTION_0), JABBER_CAPS_ATTENTION_0, _T("Supports attention requests ('nudge')"), },
+ { _T(JABBER_FEAT_USER_ACTIVITY), JABBER_CAPS_USER_ACTIVITY, _T("Can report information about user activity"), },
+ { _T(JABBER_FEAT_USER_ACTIVITY_NOTIFY), JABBER_CAPS_USER_ACTIVITY_NOTIFY, _T("Receives information about user activity"), },
+ { _T(JABBER_FEAT_MIRANDA_NOTES), JABBER_CAPS_MIRANDA_NOTES, _T("Supports Miranda NG notes extension"), },
+ { _T(JABBER_FEAT_JINGLE), JABBER_CAPS_JINGLE, _T("Supports Jingle"), },
+ { _T(JABBER_FEAT_ROSTER_EXCHANGE), JABBER_CAPS_ROSTER_EXCHANGE, _T("Supports Roster Exchange"), },
+ { _T(JABBER_FEAT_GTALK_PMUC), JABBER_CAPS_GTALK_PMUC, _T("Supports GTalk private multi-user chat"), },
+ { NULL, 0, NULL}
+};
+
+const JabberFeatCapPair g_JabberFeatCapPairsExt[] = {
+ { _T(JABBER_EXT_SECUREIM), JABBER_CAPS_SECUREIM },
+ { _T(JABBER_EXT_COMMANDS), JABBER_CAPS_COMMANDS },
+ { _T(JABBER_EXT_USER_MOOD), JABBER_CAPS_USER_MOOD_NOTIFY },
+ { _T(JABBER_EXT_USER_TUNE), JABBER_CAPS_USER_TUNE_NOTIFY },
+ { _T(JABBER_EXT_USER_ACTIVITY), JABBER_CAPS_USER_ACTIVITY_NOTIFY },
+ { _T(JABBER_EXT_GTALK_PMUC), JABBER_CAPS_GTALK_PMUC },
+ { _T(JABBER_EXT_MIR_NOTES), JABBER_CAPS_MIRANDA_NOTES, },
+ { szCoreVersion, JABBER_CAPS_MIRANDA_PARTIAL },
+ { NULL, 0 }
+};
+
+void CJabberProto::OnIqResultCapsDiscoInfoSI( HXML, CJabberIqInfo* pInfo )
+{
+ JABBER_RESOURCE_STATUS *r = ResourceInfoFromJID( pInfo->GetFrom());
+ if ( !r )
+ return;
+
+ if ( r->szCapsNode == NULL )
+ OnIqResultCapsDiscoInfo( NULL, pInfo );
+
+ HXML query = pInfo->GetChildNode();
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT && query ) {
+ // XEP-0232 support
+ HXML xform;
+ for ( int i = 1; ( xform = xmlGetNthChild( query, _T("x"), i)) != NULL; i++ ) {
+ TCHAR *szFormTypeValue = XPath( xform, _T("field[@var='FORM_TYPE']/value"));
+ if ( szFormTypeValue && !_tcscmp( szFormTypeValue, _T("urn:xmpp:dataforms:softwareinfo"))) {
+ if ( r->pSoftwareInfo )
+ delete r->pSoftwareInfo;
+ r->pSoftwareInfo = new JABBER_XEP0232_SOFTWARE_INFO;
+ if ( r->pSoftwareInfo ) {
+ TCHAR *szTmp = XPath( xform, _T("field[@var='os']/value"));
+ if ( szTmp )
+ r->pSoftwareInfo->szOs = mir_tstrdup( szTmp );
+ szTmp = XPath( xform, _T("field[@var='os_version']/value"));
+ if ( szTmp )
+ r->pSoftwareInfo->szOsVersion = mir_tstrdup( szTmp );
+ szTmp = XPath( xform, _T("field[@var='software']/value"));
+ if ( szTmp )
+ r->pSoftwareInfo->szSoftware = mir_tstrdup( szTmp );
+ szTmp = XPath( xform, _T("field[@var='software_version']/value"));
+ if ( szTmp )
+ r->pSoftwareInfo->szSoftwareVersion = mir_tstrdup( szTmp );
+ szTmp = XPath( xform, _T("field[@var='x-miranda-core-version']/value"));
+ if ( szTmp )
+ r->pSoftwareInfo->szXMirandaCoreVersion = mir_tstrdup( szTmp );
+ szTmp = XPath( xform, _T("field[@var='x-miranda-core-is-unicode']/value"));
+ if ( !szTmp ) // old deprecated format
+ szTmp = XPath( xform, _T("field[@var='x-miranda-is-unicode']/value"));
+ if ( szTmp && _ttoi( szTmp ))
+ r->pSoftwareInfo->bXMirandaIsUnicode = TRUE;
+ szTmp = XPath( xform, _T("field[@var='x-miranda-core-is-alpha']/value"));
+ if ( !szTmp ) // old deprecated format
+ szTmp = XPath( xform, _T("field[@var='x-miranda-is-alpha']/value"));
+ if ( szTmp && _ttoi( szTmp ))
+ r->pSoftwareInfo->bXMirandaIsAlpha = TRUE;
+ szTmp = XPath( xform, _T("field[@var='x-miranda-jabber-is-debug']/value"));
+ if ( szTmp && _ttoi( szTmp ))
+ r->pSoftwareInfo->bXMirandaIsDebug = TRUE;
+ }
+ JabberUserInfoUpdate( pInfo->GetHContact());
+ }
+ }
+ }
+}
+
+void CJabberProto::OnIqResultCapsDiscoInfo( HXML, CJabberIqInfo* pInfo )
+{
+ JABBER_RESOURCE_STATUS *r = ResourceInfoFromJID( pInfo->GetFrom());
+
+ HXML query = pInfo->GetChildNode();
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT && query ) {
+ JabberCapsBits jcbCaps = 0;
+ HXML feature;
+ for ( int i = 1; ( feature = xmlGetNthChild( query, _T("feature"), i )) != NULL; i++ ) {
+ const TCHAR *featureName = xmlGetAttrValue( feature, _T("var"));
+ if ( featureName ) {
+ for ( int j = 0; g_JabberFeatCapPairs[j].szFeature; j++ ) {
+ if ( !_tcscmp( g_JabberFeatCapPairs[j].szFeature, featureName )) {
+ jcbCaps |= g_JabberFeatCapPairs[j].jcbCap;
+ break;
+ } } } }
+
+ // no version info support and no XEP-0115 support?
+ if ( r && r->dwVersionRequestTime == -1 && !r->version && !r->software && !r->szCapsNode ) {
+ r->jcbCachedCaps = jcbCaps;
+ r->dwDiscoInfoRequestTime = -1;
+ return;
+ }
+
+ if (!m_clientCapsManager.SetClientCaps( pInfo->GetIqId(), jcbCaps ))
+ if ( r )
+ r->jcbCachedCaps = jcbCaps;
+ JabberUserInfoUpdate( pInfo->GetHContact());
+ }
+ else {
+ // no version info support and no XEP-0115 support?
+ if ( r && r->dwVersionRequestTime == -1 && !r->version && !r->software && !r->szCapsNode ) {
+ r->jcbCachedCaps = JABBER_RESOURCE_CAPS_NONE;
+ r->dwDiscoInfoRequestTime = -1;
+ return;
+ }
+ m_clientCapsManager.SetClientCaps( pInfo->GetIqId(), JABBER_RESOURCE_CAPS_ERROR );
+ }
+}
+
+JabberCapsBits CJabberProto::GetTotalJidCapabilites( const TCHAR *jid )
+{
+ if ( !jid )
+ return JABBER_RESOURCE_CAPS_NONE;
+
+ TCHAR szBareJid[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( jid, szBareJid, SIZEOF( szBareJid ));
+
+ JABBER_LIST_ITEM *item = ListGetItemPtr( LIST_ROSTER, szBareJid );
+ if ( !item )
+ item = ListGetItemPtr( LIST_VCARD_TEMP, szBareJid );
+
+ JabberCapsBits jcbToReturn = JABBER_RESOURCE_CAPS_NONE;
+
+ // get bare jid info only if where is no resources
+ if ( !item || ( item && !item->resourceCount )) {
+ jcbToReturn = GetResourceCapabilites( szBareJid, FALSE );
+ if ( jcbToReturn & JABBER_RESOURCE_CAPS_ERROR)
+ jcbToReturn = JABBER_RESOURCE_CAPS_NONE;
+ }
+
+ if ( item ) {
+ for ( int i = 0; i < item->resourceCount; i++ ) {
+ TCHAR szFullJid[ JABBER_MAX_JID_LEN ];
+ mir_sntprintf( szFullJid, JABBER_MAX_JID_LEN, _T("%s/%s"), szBareJid, item->resource[i].resourceName );
+ JabberCapsBits jcb = GetResourceCapabilites( szFullJid, FALSE );
+ if ( !( jcb & JABBER_RESOURCE_CAPS_ERROR ))
+ jcbToReturn |= jcb;
+ }
+ }
+ return jcbToReturn;
+}
+
+JabberCapsBits CJabberProto::GetResourceCapabilites( const TCHAR *jid, BOOL appendBestResource )
+{
+ TCHAR fullJid[ JABBER_MAX_JID_LEN ];
+ if ( appendBestResource )
+ GetClientJID( jid, fullJid, SIZEOF( fullJid ));
+ else
+ _tcsncpy( fullJid, jid, SIZEOF( fullJid ));
+
+ JABBER_RESOURCE_STATUS *r = ResourceInfoFromJID( fullJid );
+ if ( r == NULL )
+ return JABBER_RESOURCE_CAPS_ERROR;
+
+ // XEP-0115 mode
+ if ( r->szCapsNode && r->szCapsVer ) {
+ JabberCapsBits jcbCaps = 0, jcbExtCaps = 0;
+ BOOL bRequestSent = FALSE;
+ JabberCapsBits jcbMainCaps = m_clientCapsManager.GetClientCaps( r->szCapsNode, r->szCapsVer );
+
+ if ( jcbMainCaps == JABBER_RESOURCE_CAPS_TIMEOUT && !r->dwDiscoInfoRequestTime )
+ jcbMainCaps = JABBER_RESOURCE_CAPS_ERROR;
+
+ if ( jcbMainCaps == JABBER_RESOURCE_CAPS_UNINIT ) {
+ // send disco#info query
+
+ CJabberIqInfo *pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultCapsDiscoInfo, JABBER_IQ_TYPE_GET, fullJid, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_CHILD_TAG_NODE );
+ pInfo->SetTimeout( JABBER_RESOURCE_CAPS_QUERY_TIMEOUT );
+ m_clientCapsManager.SetClientCaps( r->szCapsNode, r->szCapsVer, JABBER_RESOURCE_CAPS_IN_PROGRESS, pInfo->GetIqId());
+ r->dwDiscoInfoRequestTime = pInfo->GetRequestTime();
+
+ TCHAR queryNode[512];
+ mir_sntprintf( queryNode, SIZEOF(queryNode), _T("%s#%s"), r->szCapsNode, r->szCapsVer );
+ m_ThreadInfo->send( XmlNodeIq( pInfo ) << XQUERY( _T(JABBER_FEAT_DISCO_INFO)) << XATTR( _T("node"), queryNode ));
+
+ bRequestSent = TRUE;
+ }
+ else if ( jcbMainCaps == JABBER_RESOURCE_CAPS_IN_PROGRESS )
+ bRequestSent = TRUE;
+ else if ( jcbMainCaps != JABBER_RESOURCE_CAPS_TIMEOUT )
+ jcbCaps |= jcbMainCaps;
+
+ if ( jcbMainCaps != JABBER_RESOURCE_CAPS_TIMEOUT && r->szCapsExt ) {
+ TCHAR *caps = mir_tstrdup( r->szCapsExt );
+
+ TCHAR *token = _tcstok( caps, _T(" "));
+ while ( token ) {
+ switch ( jcbExtCaps = m_clientCapsManager.GetClientCaps( r->szCapsNode, token )) {
+ case JABBER_RESOURCE_CAPS_ERROR:
+ break;
+
+ case JABBER_RESOURCE_CAPS_UNINIT:
+ {
+ // send disco#info query
+
+ CJabberIqInfo* pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultCapsDiscoInfo, JABBER_IQ_TYPE_GET, fullJid, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_CHILD_TAG_NODE );
+ pInfo->SetTimeout( JABBER_RESOURCE_CAPS_QUERY_TIMEOUT );
+ m_clientCapsManager.SetClientCaps( r->szCapsNode, token, JABBER_RESOURCE_CAPS_IN_PROGRESS, pInfo->GetIqId());
+
+ TCHAR queryNode[512];
+ mir_sntprintf( queryNode, SIZEOF(queryNode), _T("%s#%s"), r->szCapsNode, token );
+ m_ThreadInfo->send(
+ XmlNodeIq( pInfo ) << XQUERY( _T(JABBER_FEAT_DISCO_INFO)) << XATTR( _T("node"), queryNode ));
+
+ bRequestSent = TRUE;
+ break;
+ }
+ case JABBER_RESOURCE_CAPS_IN_PROGRESS:
+ bRequestSent = TRUE;
+ break;
+
+ default:
+ jcbCaps |= jcbExtCaps;
+ }
+
+ token = _tcstok( NULL, _T(" "));
+ }
+
+ mir_free(caps);
+ }
+
+ if ( bRequestSent )
+ return JABBER_RESOURCE_CAPS_IN_PROGRESS;
+
+ return jcbCaps | r->jcbManualDiscoveredCaps;
+ }
+
+ // capability mode (version request + service discovery)
+
+ // no version info:
+ if ( !r->version && !r->software ) {
+ // version request not sent:
+ if ( !r->dwVersionRequestTime ) {
+ // send version query
+
+ CJabberIqInfo *pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultVersion, JABBER_IQ_TYPE_GET, fullJid, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_HCONTACT | JABBER_IQ_PARSE_CHILD_TAG_NODE );
+ pInfo->SetTimeout( JABBER_RESOURCE_CAPS_QUERY_TIMEOUT );
+ r->dwVersionRequestTime = pInfo->GetRequestTime();
+
+ XmlNodeIq iq( pInfo );
+ iq << XQUERY( _T(JABBER_FEAT_VERSION));
+ m_ThreadInfo->send( iq );
+ return JABBER_RESOURCE_CAPS_IN_PROGRESS;
+ }
+ // version not received:
+ else if ( r->dwVersionRequestTime != -1 ) {
+ // no timeout?
+ if ( GetTickCount() - r->dwVersionRequestTime < JABBER_RESOURCE_CAPS_QUERY_TIMEOUT )
+ return JABBER_RESOURCE_CAPS_IN_PROGRESS;
+
+ // timeout
+ r->dwVersionRequestTime = -1;
+ }
+ // no version information, try direct service discovery
+ if ( !r->dwDiscoInfoRequestTime ) {
+ // send disco#info query
+
+ CJabberIqInfo *pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultCapsDiscoInfo, JABBER_IQ_TYPE_GET, fullJid, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_CHILD_TAG_NODE );
+ pInfo->SetTimeout( JABBER_RESOURCE_CAPS_QUERY_TIMEOUT );
+ r->dwDiscoInfoRequestTime = pInfo->GetRequestTime();
+
+ XmlNodeIq iq( pInfo );
+ iq << XQUERY( _T(JABBER_FEAT_DISCO_INFO));
+ m_ThreadInfo->send( iq );
+
+ return JABBER_RESOURCE_CAPS_IN_PROGRESS;
+ }
+ else if ( r->dwDiscoInfoRequestTime == -1 )
+ return r->jcbCachedCaps | r->jcbManualDiscoveredCaps;
+ else if ( GetTickCount() - r->dwDiscoInfoRequestTime < JABBER_RESOURCE_CAPS_QUERY_TIMEOUT )
+ return JABBER_RESOURCE_CAPS_IN_PROGRESS;
+ else
+ r->dwDiscoInfoRequestTime = -1;
+ // version request timeout:
+ return JABBER_RESOURCE_CAPS_NONE;
+ }
+
+ // version info available:
+ if ( r->software && r->version ) {
+ JabberCapsBits jcbMainCaps = m_clientCapsManager.GetClientCaps( r->software, r->version );
+ if ( jcbMainCaps == JABBER_RESOURCE_CAPS_ERROR ) {
+ // Bombus hack:
+ if ( !_tcscmp( r->software, _T( "Bombus" )) || !_tcscmp( r->software, _T( "BombusMod" ))) {
+ jcbMainCaps = JABBER_CAPS_SI|JABBER_CAPS_SI_FT|JABBER_CAPS_IBB|JABBER_CAPS_MESSAGE_EVENTS|JABBER_CAPS_MESSAGE_EVENTS_NO_DELIVERY|JABBER_CAPS_DATA_FORMS|JABBER_CAPS_LAST_ACTIVITY|JABBER_CAPS_VERSION|JABBER_CAPS_COMMANDS|JABBER_CAPS_VCARD_TEMP;
+ m_clientCapsManager.SetClientCaps( r->software, r->version, jcbMainCaps );
+ }
+ // Neos hack:
+ else if ( !_tcscmp( r->software, _T( "neos" ))) {
+ jcbMainCaps = JABBER_CAPS_OOB|JABBER_CAPS_MESSAGE_EVENTS|JABBER_CAPS_MESSAGE_EVENTS_NO_DELIVERY|JABBER_CAPS_LAST_ACTIVITY|JABBER_CAPS_VERSION;
+ m_clientCapsManager.SetClientCaps( r->software, r->version, jcbMainCaps );
+ }
+ // sim hack:
+ else if ( !_tcscmp( r->software, _T( "sim" ))) {
+ jcbMainCaps = JABBER_CAPS_OOB|JABBER_CAPS_VERSION|JABBER_CAPS_MESSAGE_EVENTS|JABBER_CAPS_MESSAGE_EVENTS_NO_DELIVERY;
+ m_clientCapsManager.SetClientCaps( r->software, r->version, jcbMainCaps );
+ } }
+
+ else if ( jcbMainCaps == JABBER_RESOURCE_CAPS_UNINIT ) {
+ // send disco#info query
+
+ CJabberIqInfo *pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultCapsDiscoInfo, JABBER_IQ_TYPE_GET, fullJid, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_CHILD_TAG_NODE );
+ pInfo->SetTimeout( JABBER_RESOURCE_CAPS_QUERY_TIMEOUT );
+ m_clientCapsManager.SetClientCaps( r->software, r->version, JABBER_RESOURCE_CAPS_IN_PROGRESS, pInfo->GetIqId());
+ r->dwDiscoInfoRequestTime = pInfo->GetRequestTime();
+
+ XmlNodeIq iq( pInfo );
+ iq << XQUERY( _T(JABBER_FEAT_DISCO_INFO));
+ m_ThreadInfo->send( iq );
+
+ jcbMainCaps = JABBER_RESOURCE_CAPS_IN_PROGRESS;
+ }
+ return jcbMainCaps | r->jcbManualDiscoveredCaps;
+ }
+
+ return JABBER_RESOURCE_CAPS_NONE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CJabberClientPartialCaps class members
+
+CJabberClientPartialCaps::CJabberClientPartialCaps( const TCHAR *szVer )
+{
+ m_szVer = mir_tstrdup( szVer );
+ m_jcbCaps = JABBER_RESOURCE_CAPS_UNINIT;
+ m_pNext = NULL;
+ m_nIqId = -1;
+ m_dwRequestTime = 0;
+}
+
+CJabberClientPartialCaps::~CJabberClientPartialCaps()
+{
+ mir_free( m_szVer );
+ if ( m_pNext )
+ delete m_pNext;
+}
+
+CJabberClientPartialCaps* CJabberClientPartialCaps::SetNext( CJabberClientPartialCaps *pCaps )
+{
+ CJabberClientPartialCaps *pRetVal = m_pNext;
+ m_pNext = pCaps;
+ return pRetVal;
+}
+
+void CJabberClientPartialCaps::SetCaps( JabberCapsBits jcbCaps, int nIqId /*= -1*/ )
+{
+ if ( jcbCaps == JABBER_RESOURCE_CAPS_IN_PROGRESS )
+ m_dwRequestTime = GetTickCount();
+ else
+ m_dwRequestTime = 0;
+ m_jcbCaps = jcbCaps;
+ m_nIqId = nIqId;
+}
+
+JabberCapsBits CJabberClientPartialCaps::GetCaps()
+{
+ if ( m_jcbCaps == JABBER_RESOURCE_CAPS_IN_PROGRESS && GetTickCount() - m_dwRequestTime > JABBER_RESOURCE_CAPS_QUERY_TIMEOUT ) {
+ m_jcbCaps = JABBER_RESOURCE_CAPS_TIMEOUT;
+ m_dwRequestTime = 0;
+ }
+ return m_jcbCaps;
+}
+
+CJabberClientPartialCaps* CJabberClientCaps::FindByVersion( const TCHAR *szVer )
+{
+ if ( !m_pCaps || !szVer )
+ return NULL;
+
+ CJabberClientPartialCaps *pCaps = m_pCaps;
+ while ( pCaps ) {
+ if ( !_tcscmp( szVer, pCaps->GetVersion()))
+ break;
+ pCaps = pCaps->GetNext();
+ }
+ return pCaps;
+}
+
+CJabberClientPartialCaps* CJabberClientCaps::FindById( int nIqId )
+{
+ if ( !m_pCaps || nIqId == -1 )
+ return NULL;
+
+ CJabberClientPartialCaps *pCaps = m_pCaps;
+ while ( pCaps ) {
+ if ( pCaps->GetIqId() == nIqId )
+ break;
+ pCaps = pCaps->GetNext();
+ }
+ return pCaps;
+}
+
+CJabberClientCaps::CJabberClientCaps( const TCHAR *szNode )
+{
+ m_szNode = mir_tstrdup( szNode );
+ m_pCaps = NULL;
+ m_pNext= NULL;
+}
+
+CJabberClientCaps::~CJabberClientCaps() {
+ mir_free( m_szNode );
+ if ( m_pCaps )
+ delete m_pCaps;
+ if ( m_pNext )
+ delete m_pNext;
+}
+
+CJabberClientCaps* CJabberClientCaps::SetNext( CJabberClientCaps *pClient )
+{
+ CJabberClientCaps *pRetVal = m_pNext;
+ m_pNext = pClient;
+ return pRetVal;
+}
+
+JabberCapsBits CJabberClientCaps::GetPartialCaps( TCHAR *szVer ) {
+ CJabberClientPartialCaps *pCaps = FindByVersion( szVer );
+ if ( !pCaps )
+ return JABBER_RESOURCE_CAPS_UNINIT;
+ return pCaps->GetCaps();
+}
+
+BOOL CJabberClientCaps::SetPartialCaps( const TCHAR *szVer, JabberCapsBits jcbCaps, int nIqId /*= -1*/ ) {
+ CJabberClientPartialCaps *pCaps = FindByVersion( szVer );
+ if ( !pCaps ) {
+ pCaps = new CJabberClientPartialCaps( szVer );
+ if ( !pCaps )
+ return FALSE;
+ pCaps->SetNext( m_pCaps );
+ m_pCaps = pCaps;
+ }
+ if ( !(jcbCaps & JABBER_RESOURCE_CAPS_ERROR) && m_szNode && szVer ) {
+ if ( !_tcscmp( m_szNode, _T( "http://miranda-im.org/caps" )) && !_tcscmp( szVer, _T( "0.7.0.13" )))
+ jcbCaps = jcbCaps & ( ~JABBER_CAPS_MESSAGE_RECEIPTS );
+ }
+ pCaps->SetCaps( jcbCaps, nIqId );
+ return TRUE;
+}
+
+BOOL CJabberClientCaps::SetPartialCaps( int nIqId, JabberCapsBits jcbCaps ) {
+ CJabberClientPartialCaps *pCaps = FindById( nIqId );
+ if ( !pCaps )
+ return FALSE;
+ if ( !(jcbCaps & JABBER_RESOURCE_CAPS_ERROR) && m_szNode && pCaps->GetVersion()) {
+ if ( !_tcscmp( m_szNode, _T( "http://miranda-im.org/caps" )) && !_tcscmp( pCaps->GetVersion(), _T( "0.7.0.13" )))
+ jcbCaps = jcbCaps & ( ~JABBER_CAPS_MESSAGE_RECEIPTS );
+ }
+ pCaps->SetCaps( jcbCaps, -1 );
+ return TRUE;
+}
+
+CJabberClientCapsManager::CJabberClientCapsManager( CJabberProto* proto )
+{
+ ppro = proto;
+ InitializeCriticalSection( &m_cs );
+ m_pClients = NULL;
+}
+
+CJabberClientCapsManager::~CJabberClientCapsManager()
+{
+ if ( m_pClients )
+ delete m_pClients;
+ DeleteCriticalSection( &m_cs );
+}
+
+CJabberClientCaps * CJabberClientCapsManager::FindClient( const TCHAR *szNode )
+{
+ if ( !m_pClients || !szNode )
+ return NULL;
+
+ CJabberClientCaps *pClient = m_pClients;
+ while ( pClient ) {
+ if ( !_tcscmp( szNode, pClient->GetNode()))
+ break;
+ pClient = pClient->GetNext();
+ }
+ return pClient;
+}
+
+void CJabberClientCapsManager::AddDefaultCaps() {
+ SetClientCaps( _T(JABBER_CAPS_MIRANDA_NODE), szCoreVersion, JABBER_CAPS_MIRANDA_ALL );
+
+ for ( int i = 0; g_JabberFeatCapPairsExt[i].szFeature; i++ )
+ SetClientCaps( _T(JABBER_CAPS_MIRANDA_NODE), g_JabberFeatCapPairsExt[i].szFeature, g_JabberFeatCapPairsExt[i].jcbCap );
+}
+
+JabberCapsBits CJabberClientCapsManager::GetClientCaps( TCHAR *szNode, TCHAR *szVer )
+{
+ Lock();
+ CJabberClientCaps *pClient = FindClient( szNode );
+ if ( !pClient ) {
+ Unlock();
+ ppro->Log( "CAPS: get no caps for: " TCHAR_STR_PARAM ", " TCHAR_STR_PARAM, szNode, szVer );
+ return JABBER_RESOURCE_CAPS_UNINIT;
+ }
+ JabberCapsBits jcbCaps = pClient->GetPartialCaps( szVer );
+ Unlock();
+ ppro->Log( "CAPS: get caps %I64x for: " TCHAR_STR_PARAM ", " TCHAR_STR_PARAM, jcbCaps, szNode, szVer );
+ return jcbCaps;
+}
+
+BOOL CJabberClientCapsManager::SetClientCaps( const TCHAR *szNode, const TCHAR *szVer, JabberCapsBits jcbCaps, int nIqId /*= -1*/ )
+{
+ Lock();
+ CJabberClientCaps *pClient = FindClient( szNode );
+ if (!pClient) {
+ pClient = new CJabberClientCaps( szNode );
+ if ( !pClient ) {
+ Unlock();
+ return FALSE;
+ }
+ pClient->SetNext( m_pClients );
+ m_pClients = pClient;
+ }
+ BOOL bOk = pClient->SetPartialCaps( szVer, jcbCaps, nIqId );
+ Unlock();
+ ppro->Log( "CAPS: set caps %I64x for: " TCHAR_STR_PARAM ", " TCHAR_STR_PARAM, jcbCaps, szNode, szVer );
+ return bOk;
+}
+
+BOOL CJabberClientCapsManager::SetClientCaps( int nIqId, JabberCapsBits jcbCaps )
+{
+ Lock();
+ if ( !m_pClients ) {
+ Unlock();
+ return FALSE;
+ }
+ BOOL bOk = FALSE;
+ CJabberClientCaps *pClient = m_pClients;
+ while ( pClient ) {
+ if ( pClient->SetPartialCaps( nIqId, jcbCaps )) {
+ ppro->Log( "CAPS: set caps %I64x for iq %d", jcbCaps, nIqId );
+ bOk = TRUE;
+ break;
+ }
+ pClient = pClient->GetNext();
+ }
+ Unlock();
+ return bOk;
+}
+
+BOOL CJabberClientCapsManager::HandleInfoRequest( HXML, CJabberIqInfo* pInfo, const TCHAR* szNode )
+{
+ int i;
+ JabberCapsBits jcb = 0;
+
+ if ( szNode ) {
+ for ( i = 0; g_JabberFeatCapPairsExt[i].szFeature; i++ ) {
+ TCHAR szExtCap[ 512 ];
+ mir_sntprintf( szExtCap, SIZEOF(szExtCap), _T("%s#%s"), _T(JABBER_CAPS_MIRANDA_NODE), g_JabberFeatCapPairsExt[i].szFeature );
+ if ( !_tcscmp( szNode, szExtCap )) {
+ jcb = g_JabberFeatCapPairsExt[i].jcbCap;
+ break;
+ }
+ }
+
+ // check features registered through IJabberNetInterface::RegisterFeature() and IJabberNetInterface::AddFeatures()
+ for ( i = 0; i < ppro->m_lstJabberFeatCapPairsDynamic.getCount(); i++ ) {
+ TCHAR szExtCap[ 512 ];
+ mir_sntprintf( szExtCap, SIZEOF(szExtCap), _T("%s#%s"), _T(JABBER_CAPS_MIRANDA_NODE), ppro->m_lstJabberFeatCapPairsDynamic[i]->szExt );
+ if ( !_tcscmp( szNode, szExtCap )) {
+ jcb = ppro->m_lstJabberFeatCapPairsDynamic[i]->jcbCap;
+ break;
+ }
+ }
+
+ // unknown node, not XEP-0115 request
+ if ( !jcb )
+ return FALSE;
+ }
+ else {
+ jcb = JABBER_CAPS_MIRANDA_ALL;
+ for ( i = 0; i < ppro->m_lstJabberFeatCapPairsDynamic.getCount(); i++ )
+ jcb |= ppro->m_lstJabberFeatCapPairsDynamic[i]->jcbCap;
+ }
+
+ if (!ppro->m_options.AllowVersionRequests)
+ jcb &= ~JABBER_CAPS_VERSION;
+
+ XmlNodeIq iq( _T("result"), pInfo );
+
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_DISCO_INFO));
+ if ( szNode )
+ query << XATTR( _T("node"), szNode );
+
+ query << XCHILD( _T("identity")) << XATTR( _T("category"), _T("client"))
+ << XATTR( _T("type"), _T("pc")) << XATTR( _T("name"), _T("Miranda"));
+
+ for ( i = 0; g_JabberFeatCapPairs[i].szFeature; i++ )
+ if ( jcb & g_JabberFeatCapPairs[i].jcbCap )
+ query << XCHILD( _T("feature")) << XATTR( _T("var"), g_JabberFeatCapPairs[i].szFeature );
+
+ for ( i = 0; i < ppro->m_lstJabberFeatCapPairsDynamic.getCount(); i++ )
+ if ( jcb & ppro->m_lstJabberFeatCapPairsDynamic[i]->jcbCap )
+ query << XCHILD( _T("feature")) << XATTR( _T("var"), ppro->m_lstJabberFeatCapPairsDynamic[i]->szFeature );
+
+ if ( ppro->m_options.AllowVersionRequests && !szNode ) {
+ TCHAR szOsBuffer[256] = {0};
+ TCHAR *os = szOsBuffer;
+
+ if ( ppro->m_options.ShowOSVersion ) {
+ if (!GetOSDisplayString(szOsBuffer, SIZEOF(szOsBuffer)))
+ lstrcpyn(szOsBuffer, _T(""), SIZEOF(szOsBuffer));
+ else {
+ TCHAR *szOsWindows = _T("Microsoft Windows");
+ size_t nOsWindowsLength = _tcslen( szOsWindows );
+ if (!_tcsnicmp(szOsBuffer, szOsWindows, nOsWindowsLength))
+ os += nOsWindowsLength + 1;
+ }
+ }
+
+ HXML form = query << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("result"));
+ form << XCHILD( _T("field")) << XATTR( _T("var"), _T("FORM_TYPE")) << XATTR( _T("type"), _T("hidden"))
+ << XCHILD( _T("value"), _T("urn:xmpp:dataforms:softwareinfo"));
+
+ if ( ppro->m_options.ShowOSVersion ) {
+ form << XCHILD( _T("field")) << XATTR( _T("var"), _T("os")) << XCHILD( _T("value"), _T("Microsoft Windows"));
+ form << XCHILD( _T("field")) << XATTR( _T("var"), _T("os_version")) << XCHILD( _T("value"), os );
+ }
+ form << XCHILD( _T("field")) << XATTR( _T("var"), _T("software")) << XCHILD( _T("value"), _T("Miranda NG Jabber Protocol"));
+ form << XCHILD( _T("field")) << XATTR( _T("var"), _T("software_version")) << XCHILD( _T("value"), szCoreVersion);
+ }
+
+ ppro->m_ThreadInfo->send( iq );
+
+ return TRUE;
+}
diff --git a/protocols/JabberG/src/jabber_caps.h b/protocols/JabberG/src/jabber_caps.h new file mode 100644 index 0000000000..e0525f2a16 --- /dev/null +++ b/protocols/JabberG/src/jabber_caps.h @@ -0,0 +1,287 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_CAPS_H_
+#define _JABBER_CAPS_H_
+
+#include "jabber_iq.h"
+
+typedef unsigned __int64 JabberCapsBits;
+
+#define JABBER_RESOURCE_CAPS_QUERY_TIMEOUT 10000
+
+#ifdef __GNUC__
+#define JABBER_RESOURCE_CAPS_ERROR 0x8000000000000000ULL
+#define JABBER_RESOURCE_CAPS_IN_PROGRESS 0x8000000000000001ULL
+#define JABBER_RESOURCE_CAPS_TIMEOUT 0x8000000000000002ULL
+#define JABBER_RESOURCE_CAPS_UNINIT 0x8000000000000003ULL
+#define JABBER_RESOURCE_CAPS_NONE 0x0000000000000000ULL
+#else
+#define JABBER_RESOURCE_CAPS_ERROR 0x8000000000000000
+#define JABBER_RESOURCE_CAPS_IN_PROGRESS 0x8000000000000001
+#define JABBER_RESOURCE_CAPS_TIMEOUT 0x8000000000000002
+#define JABBER_RESOURCE_CAPS_UNINIT 0x8000000000000003
+#define JABBER_RESOURCE_CAPS_NONE 0x0000000000000000
+#endif
+
+#define JABBER_FEAT_DISCO_INFO "http://jabber.org/protocol/disco#info"
+#define JABBER_CAPS_DISCO_INFO ((JabberCapsBits)1)
+#define JABBER_FEAT_DISCO_ITEMS "http://jabber.org/protocol/disco#items"
+#define JABBER_CAPS_DISCO_ITEMS ((JabberCapsBits)1<<1)
+#define JABBER_FEAT_ENTITY_CAPS "http://jabber.org/protocol/caps"
+#define JABBER_CAPS_ENTITY_CAPS ((JabberCapsBits)1<<2)
+#define JABBER_FEAT_SI "http://jabber.org/protocol/si"
+#define JABBER_CAPS_SI ((JabberCapsBits)1<<3)
+#define JABBER_FEAT_SI_FT "http://jabber.org/protocol/si/profile/file-transfer"
+#define JABBER_CAPS_SI_FT ((JabberCapsBits)1<<4)
+#define JABBER_FEAT_BYTESTREAMS "http://jabber.org/protocol/bytestreams"
+#define JABBER_CAPS_BYTESTREAMS ((JabberCapsBits)1<<5)
+#define JABBER_FEAT_IBB "http://jabber.org/protocol/ibb"
+#define JABBER_CAPS_IBB ((JabberCapsBits)1<<6)
+#define JABBER_FEAT_OOB "jabber:iq:oob"
+#define JABBER_FEAT_OOB2 "jabber:x:oob"
+#define JABBER_CAPS_OOB ((JabberCapsBits)1<<7)
+#define JABBER_FEAT_COMMANDS "http://jabber.org/protocol/commands"
+#define JABBER_CAPS_COMMANDS ((JabberCapsBits)1<<8)
+#define JABBER_FEAT_REGISTER "jabber:iq:register"
+#define JABBER_CAPS_REGISTER ((JabberCapsBits)1<<9)
+#define JABBER_FEAT_MUC "http://jabber.org/protocol/muc"
+#define JABBER_CAPS_MUC ((JabberCapsBits)1<<10)
+#define JABBER_FEAT_CHATSTATES "http://jabber.org/protocol/chatstates"
+#define JABBER_CAPS_CHATSTATES ((JabberCapsBits)1<<11)
+#define JABBER_FEAT_LAST_ACTIVITY "jabber:iq:last"
+#define JABBER_CAPS_LAST_ACTIVITY ((JabberCapsBits)1<<12)
+#define JABBER_FEAT_VERSION "jabber:iq:version"
+#define JABBER_CAPS_VERSION ((JabberCapsBits)1<<13)
+#define JABBER_FEAT_ENTITY_TIME "urn:xmpp:time"
+#define JABBER_CAPS_ENTITY_TIME ((JabberCapsBits)1<<14)
+#define JABBER_FEAT_PING "urn:xmpp:ping"
+#define JABBER_CAPS_PING ((JabberCapsBits)1<<15)
+#define JABBER_FEAT_DATA_FORMS "jabber:x:data"
+#define JABBER_CAPS_DATA_FORMS ((JabberCapsBits)1<<16)
+#define JABBER_FEAT_MESSAGE_EVENTS "jabber:x:event"
+#define JABBER_CAPS_MESSAGE_EVENTS ((JabberCapsBits)1<<17)
+#define JABBER_FEAT_VCARD_TEMP "vcard-temp"
+#define JABBER_CAPS_VCARD_TEMP ((JabberCapsBits)1<<18)
+#define JABBER_FEAT_AVATAR "jabber:iq:avatar"
+#define JABBER_FEAT_SERVER_AVATAR "storage:client:avatar"
+#define JABBER_CAPS_AVATAR ((JabberCapsBits)1<<19)
+#define JABBER_FEAT_XHTML "http://jabber.org/protocol/xhtml-im"
+#define JABBER_CAPS_XHTML ((JabberCapsBits)1<<20)
+#define JABBER_FEAT_AGENTS "jabber:iq:agents"
+#define JABBER_CAPS_AGENTS ((JabberCapsBits)1<<21)
+#define JABBER_FEAT_BROWSE "jabber:iq:browse"
+#define JABBER_CAPS_BROWSE ((JabberCapsBits)1<<22)
+#define JABBER_FEAT_FEATURE_NEG "http://jabber.org/protocol/feature-neg"
+#define JABBER_CAPS_FEATURE_NEG ((JabberCapsBits)1<<23)
+#define JABBER_FEAT_AMP "http://jabber.org/protocol/amp"
+#define JABBER_CAPS_AMP ((JabberCapsBits)1<<24)
+#define JABBER_FEAT_USER_MOOD "http://jabber.org/protocol/mood"
+#define JABBER_CAPS_USER_MOOD ((JabberCapsBits)1<<25)
+#define JABBER_FEAT_USER_MOOD_NOTIFY "http://jabber.org/protocol/mood+notify"
+#define JABBER_CAPS_USER_MOOD_NOTIFY ((JabberCapsBits)1<<26)
+#define JABBER_FEAT_PUBSUB "http://jabber.org/protocol/pubsub"
+#define JABBER_CAPS_PUBSUB ((JabberCapsBits)1<<27)
+#define JABBER_FEAT_SECUREIM "http://miranda-ng.org/caps/secureim"
+#define JABBER_CAPS_SECUREIM ((JabberCapsBits)1<<28)
+#define JABBER_FEAT_PRIVACY_LISTS "jabber:iq:privacy"
+#define JABBER_CAPS_PRIVACY_LISTS ((JabberCapsBits)1<<29)
+#define JABBER_FEAT_MESSAGE_RECEIPTS "urn:xmpp:receipts"
+#define JABBER_CAPS_MESSAGE_RECEIPTS ((JabberCapsBits)1<<30)
+#define JABBER_FEAT_USER_TUNE "http://jabber.org/protocol/tune"
+#define JABBER_CAPS_USER_TUNE ((JabberCapsBits)1<<31)
+#define JABBER_FEAT_USER_TUNE_NOTIFY "http://jabber.org/protocol/tune+notify"
+#define JABBER_CAPS_USER_TUNE_NOTIFY ((JabberCapsBits)1<<32)
+#define JABBER_FEAT_PRIVATE_STORAGE "jabber:iq:private"
+#define JABBER_CAPS_PRIVATE_STORAGE ((JabberCapsBits)1<<33)
+#define JABBER_FEAT_CAPTCHA "urn:xmpp:captcha"
+// deferred
+#define JABBER_FEAT_ATTENTION "http://www.xmpp.org/extensions/xep-0224.html#ns"
+#define JABBER_CAPS_ATTENTION ((JabberCapsBits)1<<34)
+#define JABBER_FEAT_USER_ACTIVITY "http://jabber.org/protocol/activity"
+#define JABBER_CAPS_USER_ACTIVITY ((JabberCapsBits)1<<35)
+#define JABBER_FEAT_USER_ACTIVITY_NOTIFY "http://jabber.org/protocol/activity+notify"
+#define JABBER_CAPS_USER_ACTIVITY_NOTIFY ((JabberCapsBits)1<<36)
+#define JABBER_FEAT_ATTENTION_0 "urn:xmpp:attention:0"
+#define JABBER_CAPS_ATTENTION_0 ((JabberCapsBits)1<<37)
+#define JABBER_FEAT_MIRANDA_NOTES "http://miranda-ng.org/storage#notes"
+#define JABBER_CAPS_MIRANDA_NOTES ((JabberCapsBits)1<<38)
+#define JABBER_FEAT_JINGLE "urn:xmpp:jingle:1"
+#define JABBER_CAPS_JINGLE ((JabberCapsBits)1<<39)
+#define JABBER_FEAT_ROSTER_EXCHANGE "http://jabber.org/protocol/rosterx"
+#define JABBER_CAPS_ROSTER_EXCHANGE ((JabberCapsBits)1<<40)
+#define JABBER_FEAT_GTALK_PMUC "http://www.google.com/xmpp/protocol/pmuc/v1"
+#define JABBER_CAPS_GTALK_PMUC ((JabberCapsBits)1<<41)
+
+#define JABBER_FEAT_PUBSUB_EVENT "http://jabber.org/protocol/pubsub#event"
+#define JABBER_FEAT_PUBSUB_NODE_CONFIG "http://jabber.org/protocol/pubsub#node_config"
+
+#define JABBER_CAPS_MESSAGE_EVENTS_NO_DELIVERY ((JabberCapsBits)1<<62)
+#define JABBER_CAPS_OTHER_SPECIAL (JABBER_CAPS_MESSAGE_EVENTS_NO_DELIVERY|JABBER_RESOURCE_CAPS_ERROR) // must contain all the caps not listed in g_JabberFeatCapPairs, to prevent using these bits for features registered through IJabberNetInterface::RegisterFeature()
+
+#define JABBER_CAPS_MIRANDA_NODE "http://miranda-ng.org/caps"
+#define JABBER_CAPS_MIRANDA_ALL (JABBER_CAPS_DISCO_INFO|JABBER_CAPS_DISCO_ITEMS|JABBER_CAPS_MUC|JABBER_CAPS_ENTITY_CAPS|JABBER_CAPS_SI|JABBER_CAPS_SI_FT|JABBER_CAPS_BYTESTREAMS|JABBER_CAPS_IBB|JABBER_CAPS_OOB|JABBER_CAPS_CHATSTATES|JABBER_CAPS_AGENTS|JABBER_CAPS_BROWSE|JABBER_CAPS_VERSION|JABBER_CAPS_LAST_ACTIVITY|JABBER_CAPS_DATA_FORMS|JABBER_CAPS_MESSAGE_EVENTS|JABBER_CAPS_VCARD_TEMP|JABBER_CAPS_ENTITY_TIME|JABBER_CAPS_PING|JABBER_CAPS_PRIVACY_LISTS|JABBER_CAPS_MESSAGE_RECEIPTS|JABBER_CAPS_PRIVATE_STORAGE|JABBER_CAPS_ATTENTION_0|JABBER_CAPS_JINGLE|JABBER_CAPS_ROSTER_EXCHANGE|JABBER_CAPS_SECUREIM|JABBER_CAPS_COMMANDS|JABBER_CAPS_USER_MOOD_NOTIFY|JABBER_CAPS_USER_TUNE_NOTIFY|JABBER_CAPS_USER_ACTIVITY_NOTIFY)
+
+#define JABBER_CAPS_MIRANDA_PARTIAL (JABBER_CAPS_DISCO_INFO|JABBER_CAPS_DISCO_ITEMS|JABBER_CAPS_MUC|JABBER_CAPS_ENTITY_CAPS|JABBER_CAPS_SI|JABBER_CAPS_SI_FT|JABBER_CAPS_BYTESTREAMS|JABBER_CAPS_IBB|JABBER_CAPS_OOB|JABBER_CAPS_CHATSTATES|JABBER_CAPS_AGENTS|JABBER_CAPS_BROWSE|JABBER_CAPS_VERSION|JABBER_CAPS_LAST_ACTIVITY|JABBER_CAPS_DATA_FORMS|JABBER_CAPS_MESSAGE_EVENTS|JABBER_CAPS_VCARD_TEMP|JABBER_CAPS_ENTITY_TIME|JABBER_CAPS_PING|JABBER_CAPS_PRIVACY_LISTS|JABBER_CAPS_MESSAGE_RECEIPTS|JABBER_CAPS_PRIVATE_STORAGE|JABBER_CAPS_ATTENTION_0|JABBER_CAPS_JINGLE|JABBER_CAPS_ROSTER_EXCHANGE)
+
+#define JABBER_EXT_SECUREIM "secureim"
+#define JABBER_EXT_COMMANDS "cmds"
+#define JABBER_EXT_USER_MOOD "mood"
+#define JABBER_EXT_USER_TUNE "tune"
+#define JABBER_EXT_USER_ACTIVITY "activity"
+#define JABBER_EXT_GTALK_PMUC "pmuc-v1"
+#define JABBER_EXT_MIR_NOTES "mir_notes"
+
+#define JABBER_FEAT_EXT_ADDRESSING "http://jabber.org/protocol/address"
+#define JABBER_FEAT_NESTED_ROSTER_GROUPS "roster:delimiter"
+
+#define JABBER_FEAT_RC "http://jabber.org/protocol/rc"
+#define JABBER_FEAT_RC_SET_STATUS "http://jabber.org/protocol/rc#set-status"
+#define JABBER_FEAT_RC_SET_OPTIONS "http://jabber.org/protocol/rc#set-options"
+#define JABBER_FEAT_RC_FORWARD "http://jabber.org/protocol/rc#forward"
+#define JABBER_FEAT_RC_LEAVE_GROUPCHATS "http://jabber.org/protocol/rc#leave-groupchats"
+#define JABBER_FEAT_RC_WS_LOCK "http://miranda-ng.org/rc#lock_workstation"
+#define JABBER_FEAT_RC_QUIT_MIRANDA "http://miranda-ng.org/rc#quit"
+
+#define JABBER_FEAT_IQ_ROSTER "jabber:iq:roster"
+#define JABBER_FEAT_DELAY "jabber:x:delay"
+#define JABBER_FEAT_ENTITY_TIME_OLD "jabber:iq:time"
+#define JABBER_FEAT_GTALK_SHARED_STATUS "google:shared-status"
+
+#define JABBER_FEAT_MUC_USER "http://jabber.org/protocol/muc#user"
+#define JABBER_FEAT_NICK "http://jabber.org/protocol/nick"
+
+#define JABBER_FEAT_HTTP_AUTH "http://jabber.org/protocol/http-auth"
+
+
+class CJabberClientPartialCaps
+{
+
+protected:
+ TCHAR *m_szVer;
+ JabberCapsBits m_jcbCaps;
+ CJabberClientPartialCaps *m_pNext;
+ int m_nIqId;
+ DWORD m_dwRequestTime;
+
+public:
+ CJabberClientPartialCaps( const TCHAR *szVer );
+ ~CJabberClientPartialCaps();
+
+ CJabberClientPartialCaps* SetNext( CJabberClientPartialCaps *pCaps );
+ __inline CJabberClientPartialCaps* GetNext()
+ { return m_pNext;
+ }
+
+ void SetCaps( JabberCapsBits jcbCaps, int nIqId = -1 );
+ JabberCapsBits GetCaps();
+
+ __inline TCHAR* GetVersion()
+ { return m_szVer;
+ }
+
+ __inline int GetIqId()
+ { return m_nIqId;
+ }
+};
+
+class CJabberClientCaps
+{
+
+protected:
+ TCHAR *m_szNode;
+ CJabberClientPartialCaps *m_pCaps;
+ CJabberClientCaps *m_pNext;
+
+protected:
+ CJabberClientPartialCaps* FindByVersion( const TCHAR *szVer );
+ CJabberClientPartialCaps* FindById( int nIqId );
+
+public:
+ CJabberClientCaps( const TCHAR *szNode );
+ ~CJabberClientCaps();
+
+ CJabberClientCaps* SetNext( CJabberClientCaps *pClient );
+ __inline CJabberClientCaps* GetNext()
+ { return m_pNext;
+ }
+
+ JabberCapsBits GetPartialCaps( TCHAR *szVer );
+ BOOL SetPartialCaps( const TCHAR *szVer, JabberCapsBits jcbCaps, int nIqId = -1 );
+ BOOL SetPartialCaps( int nIqId, JabberCapsBits jcbCaps );
+
+ __inline TCHAR* GetNode()
+ { return m_szNode;
+ }
+};
+
+class CJabberClientCapsManager
+{
+
+protected:
+ CRITICAL_SECTION m_cs;
+ CJabberClientCaps *m_pClients;
+ CJabberProto* ppro;
+
+protected:
+ CJabberClientCaps *FindClient( const TCHAR *szNode );
+
+public:
+ CJabberClientCapsManager( CJabberProto* proto );
+ ~CJabberClientCapsManager();
+
+ __inline void Lock()
+ { EnterCriticalSection( &m_cs );
+ }
+ __inline void Unlock()
+ { LeaveCriticalSection( &m_cs );
+ }
+
+ void AddDefaultCaps();
+
+ JabberCapsBits GetClientCaps( TCHAR *szNode, TCHAR *szVer );
+ BOOL SetClientCaps( const TCHAR *szNode, const TCHAR *szVer, JabberCapsBits jcbCaps, int nIqId = -1 );
+ BOOL SetClientCaps( int nIqId, JabberCapsBits jcbCaps );
+
+ BOOL HandleInfoRequest( HXML iqNode, CJabberIqInfo* pInfo, const TCHAR* szNode );
+};
+
+struct JabberFeatCapPair
+{
+ const TCHAR *szFeature;
+ JabberCapsBits jcbCap;
+ const TCHAR *szDescription;
+};
+
+struct JabberFeatCapPairDynamic
+{
+ TCHAR *szExt;
+ TCHAR *szFeature;
+ JabberCapsBits jcbCap;
+ TCHAR *szDescription;
+};
+
+extern const JabberFeatCapPair g_JabberFeatCapPairs[];
+extern const JabberFeatCapPair g_JabberFeatCapPairsExt[];
+
+#endif
diff --git a/protocols/JabberG/src/jabber_captcha.cpp b/protocols/JabberG/src/jabber_captcha.cpp new file mode 100644 index 0000000000..88d3038ab7 --- /dev/null +++ b/protocols/JabberG/src/jabber_captcha.cpp @@ -0,0 +1,232 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+struct CAPTCHA_FORM_PARAMS
+{
+ LPCTSTR from;
+ LPCTSTR challenge;
+ LPCTSTR fromjid;
+ LPCTSTR sid;
+ LPCTSTR to;
+ LPCTSTR hint;
+ HBITMAP bmp;
+ int w,h;
+ TCHAR Result[MAX_PATH];
+};
+
+INT_PTR CALLBACK JabberCaptchaFormDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ CAPTCHA_FORM_PARAMS *params = (CAPTCHA_FORM_PARAMS*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+ switch (msg) {
+ case WM_INITDIALOG: {
+ TranslateDialogDefault( hwndDlg );
+ SendMessage( hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIconBig(IDI_KEYS));
+ SendMessage( hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinnedIcon(IDI_KEYS));
+ params = (CAPTCHA_FORM_PARAMS*)lParam;
+
+ LPCTSTR hint = params->hint;
+ if ( hint == NULL )
+ hint = TranslateT("Enter the text you see");
+ SetDlgItemText( hwndDlg, IDC_INSTRUCTION, TranslateTS( hint ));
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, ( LONG )params );
+
+ return TRUE;
+ }
+ case WM_CTLCOLORSTATIC:
+ switch( GetWindowLongPtr((HWND)lParam, GWL_ID)) {
+ case IDC_WHITERECT:
+ case IDC_INSTRUCTION:
+ case IDC_TITLE:
+ return (BOOL)GetStockObject(WHITE_BRUSH);
+ }
+ return NULL;
+
+ case WM_PAINT:
+ if ( params ) {
+ PAINTSTRUCT ps;
+ HDC hdc, hdcMem;
+ RECT rc;
+
+ GetClientRect( hwndDlg, &rc );
+ hdc = BeginPaint( hwndDlg, &ps );
+ hdcMem = CreateCompatibleDC( hdc );
+ HGDIOBJ hOld = SelectObject( hdcMem, params->bmp );
+
+ int y = ( rc.bottom + rc.top - params->h ) / 2;
+ int x = ( rc.right + rc.left - params->w ) / 2;
+ BitBlt( hdc, x, y, params->w, params->h, hdcMem, 0,0, SRCCOPY );
+ SelectObject( hdcMem, hOld );
+ DeleteDC( hdcMem );
+
+ EndPaint( hwndDlg, &ps );
+ }
+ break;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ return TRUE;
+
+ case IDC_SUBMIT:
+ GetDlgItemText( hwndDlg, IDC_VALUE, params->Result, SIZEOF(params->Result));
+ EndDialog( hwndDlg, 1 );
+ return TRUE;
+ }
+ break;
+
+ case WM_CLOSE:
+ EndDialog( hwndDlg, 0 );
+ break;
+
+ case WM_DESTROY:
+ WindowFreeIcon( hwndDlg );
+ break;
+ }
+ return FALSE;
+}
+
+bool CJabberProto::ProcessCaptcha (HXML node, HXML parentNode, ThreadData* info ) {
+ CAPTCHA_FORM_PARAMS param;
+ char *ImageBuf = 0;
+ const TCHAR *PicType = 0;
+ TCHAR *CaptchaPath = 0;
+
+ HXML x = xmlGetChildByTag( node, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS));
+ if ( x == NULL )
+ return false;
+
+ HXML y = xmlGetChildByTag(x, _T("field"), _T("var"), _T("from"));
+ if ( y == NULL )
+ return false;
+ if (( y = xmlGetChild( y, "value" )) == NULL )
+ return false;
+ param.fromjid = xmlGetText( y );
+
+ if (( y = xmlGetChildByTag(x, _T("field"), _T("var"), _T("sid"))) == NULL )
+ return false;
+ if (( y = xmlGetChild( y, "value" )) == NULL )
+ return false;
+ param.sid = xmlGetText( y );
+
+ if (( y = xmlGetChildByTag(x, _T("field"), _T("var"), _T("ocr"))) == NULL )
+ return false;
+ param.hint = xmlGetAttrValue (y, _T("label"));
+
+ param.from = xmlGetAttrValue( parentNode, _T("from"));
+ param.to = xmlGetAttrValue( parentNode, _T("to"));
+ param.challenge = xmlGetAttrValue( parentNode, _T("id"));
+ HXML o = xmlGetChild( parentNode, "data" );
+ if ( o == NULL || xmlGetText( o ) == NULL )
+ return false;
+
+ GetCaptchaImage(parentNode, ImageBuf, PicType, CaptchaPath);
+ char* p = mir_t2a( CaptchaPath );
+ param.bmp = ( HBITMAP ) CallService( MS_UTILS_LOADBITMAP, 0, ( LPARAM )p );
+ DeleteFile(CaptchaPath);
+ mir_free(CaptchaPath);
+ mir_free(p);
+
+ BITMAP bmp = {0};
+ GetObject( param.bmp, sizeof(bmp), &bmp );
+ param.w = bmp.bmWidth;
+ param.h = bmp.bmHeight;
+ int res = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_CAPTCHAFORM), NULL, JabberCaptchaFormDlgProc, (LPARAM)¶m);
+ if (lstrcmp(param.Result, _T("")) == 0 || !res)
+ sendCaptchaError(info, param.from, param.to, param.challenge);
+ else
+ sendCaptchaResult (param.Result, info, param.from, param.challenge, param.fromjid, param.sid);
+ return true;
+}
+
+void CJabberProto::GetCaptchaImage ( HXML node, char *ImageBuf, const TCHAR *PicType, TCHAR*& CaptchaPath ) {
+ HXML o = xmlGetChild( node , "data" );
+ int bufferLen;
+ char* buffer = JabberBase64DecodeT(xmlGetText( o ), &bufferLen );
+ if ( buffer == NULL )
+ return;
+
+ const TCHAR* szPicType;
+ HXML m = xmlGetChild( node , "TYPE" );
+ if ( m == NULL || xmlGetText( m ) == NULL ) {
+ LBL_NoTypeSpecified:
+ switch( JabberGetPictureType( buffer )) {
+ case PA_FORMAT_GIF: szPicType = _T("image/gif"); break;
+ case PA_FORMAT_BMP: szPicType = _T("image/bmp"); break;
+ case PA_FORMAT_PNG: szPicType = _T("image/png"); break;
+ case PA_FORMAT_JPEG: szPicType = _T("image/jpeg"); break;
+ default:
+ goto LBL_Ret;
+ }
+ }
+ else {
+ const TCHAR* tszType = xmlGetText( m );
+ if ( !_tcscmp( tszType, _T("image/jpeg")) ||
+ !_tcscmp( tszType, _T("image/png")) ||
+ !_tcscmp( tszType, _T("image/gif")) ||
+ !_tcscmp( tszType, _T("image/bmp")))
+ szPicType = tszType;
+ else
+ goto LBL_NoTypeSpecified;
+ }
+
+ DWORD nWritten;
+
+LBL_Ret:
+ TCHAR* ext = _tcsstr((TCHAR*)szPicType, _T("/"))+1;
+ TCHAR filename[MAX_PATH];
+ mir_sntprintf(filename, SIZEOF(filename), _T("%%TEMP%%\\captcha.%s"), ext);
+ CaptchaPath = Utils_ReplaceVarsT(filename);
+ HANDLE hFile = CreateFile( CaptchaPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+ if ( hFile == INVALID_HANDLE_VALUE )
+ goto LBL_Ret;
+
+ if ( !WriteFile( hFile, buffer, bufferLen, &nWritten, NULL ))
+ goto LBL_Ret;
+
+ CloseHandle( hFile );
+
+ ImageBuf = buffer;
+ PicType = szPicType;
+}
+
+void CJabberProto::sendCaptchaResult(TCHAR* buf, ThreadData* info, LPCTSTR from, LPCTSTR challenge, LPCTSTR fromjid, LPCTSTR sid){
+ XmlNodeIq iq( _T("set"), SerialNext());
+ HXML query= iq <<XATTR(_T("to"), from) << XCHILD(_T("captcha")) << XATTR( _T("xmlns"), _T("urn:xmpp:captcha")) << XCHILD (_T("x")) << XATTR(_T("xmlns"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR(_T("type"), _T("submit"));
+ query << XCHILD(_T("field")) << XATTR (_T("var"), _T("FORM_TYPE")) << XCHILD(_T("value"), _T("urn:xmpp:captcha"));
+ query << XCHILD(_T("field")) << XATTR (_T("var"), _T("from")) << XCHILD(_T("value"), fromjid);
+ query << XCHILD(_T("field")) << XATTR (_T("var"), _T("challenge")) << XCHILD(_T("value"), challenge);
+ query << XCHILD(_T("field")) << XATTR (_T("var"), _T("sid")) << XCHILD(_T("value"), sid);
+ query << XCHILD(_T("field")) << XATTR (_T("var"), _T("ocr")) << XCHILD(_T("value"), buf);
+ info -> send (iq);
+}
+
+void CJabberProto::sendCaptchaError(ThreadData* info, LPCTSTR from, LPCTSTR to, LPCTSTR challenge ) {
+ XmlNode message( _T("message"));
+ HXML query= message << XATTR(_T("type"), _T("error")) << XATTR(_T("to"), from) << XATTR(_T("id"), challenge) << XATTR(_T("from"), to)
+ << XCHILD(_T("error")) << XATTR(_T("type"), _T("modify"))
+ << XCHILD(_T("not-acceptable")) << XATTR(_T("xmlns"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"));
+ info -> send (message);
+}
diff --git a/protocols/JabberG/src/jabber_chat.cpp b/protocols/JabberG/src/jabber_chat.cpp new file mode 100644 index 0000000000..8dacc24619 --- /dev/null +++ b/protocols/JabberG/src/jabber_chat.cpp @@ -0,0 +1,1628 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+#include <m_addcontact.h>
+
+const TCHAR xmlnsAdmin[] = _T("http://jabber.org/protocol/muc#admin");
+const TCHAR xmlnsOwner[] = _T("http://jabber.org/protocol/muc#owner");
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Global definitions
+
+enum {
+ IDM_CANCEL,
+
+ IDM_ROLE, IDM_AFFLTN,
+
+ IDM_CONFIG, IDM_NICK, IDM_DESTROY, IDM_INVITE, IDM_BOOKMARKS, IDM_LEAVE, IDM_TOPIC,
+ IDM_LST_PARTICIPANT, IDM_LST_MODERATOR,
+ IDM_LST_MEMBER, IDM_LST_ADMIN, IDM_LST_OWNER, IDM_LST_BAN,
+
+ IDM_MESSAGE, IDM_SLAP, IDM_VCARD, IDM_INFO, IDM_KICK,
+ IDM_RJID, IDM_RJID_ADD, IDM_RJID_VCARD, IDM_RJID_COPY,
+ IDM_SET_VISITOR, IDM_SET_PARTICIPANT, IDM_SET_MODERATOR,
+ IDM_SET_NONE, IDM_SET_MEMBER, IDM_SET_ADMIN, IDM_SET_OWNER, IDM_SET_BAN,
+ IDM_CPY_NICK, IDM_CPY_TOPIC, IDM_CPY_RJID, IDM_CPY_INROOMJID,
+
+ IDM_LINK0, IDM_LINK1, IDM_LINK2, IDM_LINK3, IDM_LINK4, IDM_LINK5, IDM_LINK6, IDM_LINK7, IDM_LINK8, IDM_LINK9,
+
+ IDM_PRESENCE_ONLINE = ID_STATUS_ONLINE,
+ IDM_PRESENCE_AWAY = ID_STATUS_AWAY,
+ IDM_PRESENCE_NA = ID_STATUS_NA,
+ IDM_PRESENCE_DND = ID_STATUS_DND,
+ IDM_PRESENCE_FREE4CHAT = ID_STATUS_FREECHAT,
+};
+
+struct TRoleOrAffiliationInfo
+{
+ int value;
+ int id;
+ TCHAR *title_en;
+ int min_role;
+ int min_affiliation;
+
+ TCHAR *title;
+
+ BOOL check(JABBER_RESOURCE_STATUS *me, JABBER_RESOURCE_STATUS *him)
+ {
+ if (me->affiliation == AFFILIATION_OWNER) return TRUE;
+ if (me == him) return FALSE;
+ if (me->affiliation <= him->affiliation) return FALSE;
+ if (me->role < this->min_role) return FALSE;
+ if (me->affiliation < this->min_affiliation) return FALSE;
+ return TRUE;
+ }
+ void translate()
+ {
+ this->title = TranslateTS(this->title_en);
+ }
+};
+
+static TRoleOrAffiliationInfo sttAffiliationItems[] =
+{
+ { AFFILIATION_NONE, IDM_SET_NONE, LPGENT("None"), ROLE_NONE, AFFILIATION_ADMIN },
+ { AFFILIATION_MEMBER, IDM_SET_MEMBER, LPGENT("Member"), ROLE_NONE, AFFILIATION_ADMIN },
+ { AFFILIATION_ADMIN, IDM_SET_ADMIN, LPGENT("Admin"), ROLE_NONE, AFFILIATION_OWNER },
+ { AFFILIATION_OWNER, IDM_SET_OWNER, LPGENT("Owner"), ROLE_NONE, AFFILIATION_OWNER },
+};
+
+static TRoleOrAffiliationInfo sttRoleItems[] =
+{
+ { ROLE_VISITOR, IDM_SET_VISITOR, LPGENT("Visitor"), ROLE_MODERATOR, AFFILIATION_NONE },
+ { ROLE_PARTICIPANT, IDM_SET_PARTICIPANT, LPGENT("Participant"), ROLE_MODERATOR, AFFILIATION_NONE },
+ { ROLE_MODERATOR, IDM_SET_MODERATOR, LPGENT("Moderator"), ROLE_MODERATOR, AFFILIATION_ADMIN },
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGcInit - initializes the new chat
+
+static const TCHAR* sttStatuses[] = { _T("Visitors"), _T("Participants"), _T("Moderators"), _T("Owners") };
+
+int JabberGcGetStatus(JABBER_GC_AFFILIATION a, JABBER_GC_ROLE r)
+{
+ switch (a) {
+ case AFFILIATION_OWNER: return 3;
+
+ default:
+ switch (r) {
+ case ROLE_MODERATOR: return 2;
+ case ROLE_PARTICIPANT: return 1;
+ } }
+
+ return 0;
+}
+
+int JabberGcGetStatus(JABBER_RESOURCE_STATUS *r)
+{
+ return JabberGcGetStatus(r->affiliation, r->role);
+}
+
+int CJabberProto::JabberGcInit( WPARAM wParam, LPARAM )
+{
+ int i;
+ JABBER_LIST_ITEM* item = ( JABBER_LIST_ITEM* )wParam;
+ GCSESSION gcw = {0};
+ GCEVENT gce = {0};
+
+ // translate string for menus (this can't be done in initializer)
+ for (i = 0; i < SIZEOF(sttAffiliationItems); ++i) sttAffiliationItems[i].translate();
+ for (i = 0; i < SIZEOF(sttRoleItems); ++i) sttRoleItems[i].translate();
+
+ TCHAR* szNick = JabberNickFromJID( item->jid );
+ gcw.cbSize = sizeof(GCSESSION);
+ gcw.iType = GCW_CHATROOM;
+ gcw.pszModule = m_szModuleName;
+ gcw.ptszName = szNick;
+ gcw.ptszID = item->jid;
+ gcw.dwFlags = GC_TCHAR;
+ CallServiceSync( MS_GC_NEWSESSION, NULL, (LPARAM)&gcw );
+
+ HANDLE hContact = HContactFromJID( item->jid );
+ if ( hContact != NULL ) {
+ DBVARIANT dbv;
+ if ( JABBER_LIST_ITEM* bookmark = ListGetItemPtr( LIST_BOOKMARK, item->jid ))
+ if ( bookmark->name ) {
+ if ( !DBGetContactSettingTString( hContact, "CList", "MyHandle", &dbv ))
+ JFreeVariant( &dbv );
+ else
+ DBWriteContactSettingTString( hContact, "CList", "MyHandle", bookmark->name );
+ }
+
+ if ( !JGetStringT( hContact, "MyNick", &dbv )) {
+ if ( !lstrcmp( dbv.ptszVal, szNick ))
+ JDeleteSetting( hContact, "MyNick" );
+ else
+ JSetStringT( hContact, "MyNick", item->nick );
+ JFreeVariant( &dbv );
+ }
+ else JSetStringT( hContact, "MyNick", item->nick );
+
+ TCHAR *passw = JGetStringCrypt( hContact, "LoginPassword" );
+ if ( lstrcmp_null( passw, item->password )) {
+ if ( !item->password || !item->password[0] )
+ JDeleteSetting( hContact, "LoginPassword" );
+ else
+ JSetStringCrypt( hContact, "LoginPassword", item->password );
+ }
+ mir_free(passw);
+ }
+ mir_free( szNick );
+
+ item->bChatActive = TRUE;
+
+ GCDEST gcd = { m_szModuleName, NULL, GC_EVENT_ADDGROUP };
+ gcd.ptszID = item->jid;
+ gce.cbSize = sizeof(GCEVENT);
+ gce.pDest = &gcd;
+ gce.dwFlags = GC_TCHAR;
+ for (i = SIZEOF(sttStatuses)-1; i >= 0; i-- ) {
+ gce.ptszStatus = TranslateTS( sttStatuses[i] );
+ CallServiceSync( MS_GC_EVENT, NULL, ( LPARAM )&gce );
+ }
+
+ gce.cbSize = sizeof(GCEVENT);
+ gce.pDest = &gcd;
+ gcd.iType = GC_EVENT_CONTROL;
+ CallServiceSync( MS_GC_EVENT, (item->bAutoJoin && m_options.AutoJoinHidden) ? WINDOW_HIDDEN : SESSION_INITDONE, (LPARAM)&gce );
+ CallServiceSync( MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gce );
+ return 0;
+}
+
+void CJabberProto::GcLogCreate( JABBER_LIST_ITEM* item )
+{
+ if ( item->bChatActive )
+ return;
+
+ NotifyEventHooks( m_hInitChat, (WPARAM)item, 0 );
+}
+
+void CJabberProto::GcLogShowInformation( JABBER_LIST_ITEM *item, JABBER_RESOURCE_STATUS *user, TJabberGcLogInfoType type )
+{
+ if (!item || !user || (item->bChatActive != 2)) return;
+
+ TCHAR buf[512] = _T("");
+
+ switch (type)
+ {
+ case INFO_BAN:
+ if (m_options.GcLogBans)
+ {
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("User %s in now banned."), user->resourceName);
+ }
+ break;
+ case INFO_STATUS:
+ if (m_options.GcLogStatuses)
+ {
+ if (user->statusMessage)
+ {
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("User %s changed status to %s with message: %s"),
+ user->resourceName,
+ CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, user->status, GSMDF_TCHAR),
+ user->statusMessage);
+ } else
+ {
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("User %s changed status to %s"),
+ user->resourceName,
+ CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, user->status, GSMDF_TCHAR));
+ }
+ }
+ break;
+ case INFO_CONFIG:
+ if (m_options.GcLogConfig)
+ {
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("Room configuration was changed."));
+ }
+ break;
+ case INFO_AFFILIATION:
+ if (m_options.GcLogAffiliations)
+ {
+ TCHAR *name = NULL;
+ switch (user->affiliation)
+ {
+ case AFFILIATION_NONE: name = TranslateT("None"); break;
+ case AFFILIATION_MEMBER: name = TranslateT("Member"); break;
+ case AFFILIATION_ADMIN: name = TranslateT("Admin"); break;
+ case AFFILIATION_OWNER: name = TranslateT("Owner"); break;
+ case AFFILIATION_OUTCAST: name = TranslateT("Outcast"); break;
+ }
+ if (name) mir_sntprintf(buf, SIZEOF(buf), TranslateT("Affiliation of %s was changed to '%s'."), user->resourceName, name);
+ }
+ break;
+ case INFO_ROLE:
+ if (m_options.GcLogRoles)
+ {
+ TCHAR *name = NULL;
+ switch (user->role)
+ {
+ case ROLE_NONE: name = TranslateT("None"); break;
+ case ROLE_VISITOR: name = TranslateT("Visitor"); break;
+ case ROLE_PARTICIPANT: name = TranslateT("Participant"); break;
+ case ROLE_MODERATOR: name = TranslateT("Moderator"); break;
+ }
+ if (name) mir_sntprintf(buf, SIZEOF(buf), TranslateT("Role of %s was changed to '%s'."), user->resourceName, name);
+ }
+ break;
+ }
+
+ if (*buf)
+ {
+ GCDEST gcd = { m_szModuleName, 0, 0 };
+ gcd.ptszID = item->jid;
+ GCEVENT gce = {0};
+ gce.cbSize = sizeof(GCEVENT);
+ gce.ptszNick = user->resourceName;
+ gce.ptszUID = user->resourceName;
+ gce.ptszText = EscapeChatTags( buf );
+ gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG;
+ gce.pDest = &gcd;
+ gce.time = time(0);
+ gcd.iType = GC_EVENT_INFORMATION;
+ CallServiceSync( MS_GC_EVENT, NULL, ( LPARAM )&gce );
+
+ mir_free( (void*)gce.ptszText ); // Since we processed msgText and created a new string
+ }
+}
+
+void CJabberProto::GcLogUpdateMemberStatus( JABBER_LIST_ITEM* item, const TCHAR* resource, const TCHAR* nick, const TCHAR* jid, int action, HXML reason, int nStatusCode )
+{
+ int statusToSet = 0;
+ const TCHAR* szReason = NULL;
+ if ( reason != NULL && xmlGetText( reason ) != NULL )
+ szReason = xmlGetText( reason );
+
+ if ( !szReason ) {
+ if ( nStatusCode == 322 )
+ szReason = TranslateT( "because room is now members-only" );
+ else if ( nStatusCode == 301 )
+ szReason = TranslateT( "user banned" );
+ }
+
+ TCHAR* myNick = (item->nick == NULL) ? NULL : mir_tstrdup( item->nick );
+ if ( myNick == NULL )
+ myNick = JabberNickFromJID( m_szJabberJID );
+
+ GCDEST gcd = { m_szModuleName, 0, 0 };
+ gcd.ptszID = item->jid;
+ GCEVENT gce = {0};
+ gce.cbSize = sizeof(GCEVENT);
+ gce.ptszNick = nick;
+ gce.ptszUID = resource;
+ if (jid != NULL)
+ gce.ptszUserInfo = jid;
+ gce.ptszText = szReason;
+ gce.dwFlags = GC_TCHAR;
+ gce.pDest = &gcd;
+ if ( item->bChatActive == 2 ) {
+ gce.dwFlags |= GCEF_ADDTOLOG;
+ gce.time = time(0);
+ }
+
+ switch( gcd.iType = action ) {
+ case GC_EVENT_PART: break;
+ case GC_EVENT_KICK:
+ gce.ptszStatus = TranslateT( "Moderator" );
+ break;
+ default:
+ for ( int i=0; i < item->resourceCount; i++ ) {
+ JABBER_RESOURCE_STATUS& JS = item->resource[i];
+ if ( !lstrcmp( resource, JS.resourceName )) {
+ if ( action != GC_EVENT_JOIN ) {
+ switch( action ) {
+ case 0:
+ gcd.iType = GC_EVENT_ADDSTATUS;
+ case GC_EVENT_REMOVESTATUS:
+ gce.dwFlags &= ~GCEF_ADDTOLOG;
+ }
+ gce.ptszText = TranslateT( "Moderator" );
+ }
+ gce.ptszStatus = TranslateTS( sttStatuses[JabberGcGetStatus(&JS)] );
+ gce.bIsMe = ( lstrcmp( nick, myNick ) == 0 );
+ statusToSet = JS.status;
+ break;
+ } } }
+
+ CallServiceSync( MS_GC_EVENT, NULL, ( LPARAM )&gce );
+
+ if ( statusToSet != 0 ) {
+ gce.ptszText = nick;
+ if ( statusToSet == ID_STATUS_AWAY || statusToSet == ID_STATUS_NA || statusToSet == ID_STATUS_DND )
+ gce.dwItemData = 3;
+ else
+ gce.dwItemData = 1;
+ gcd.iType = GC_EVENT_SETSTATUSEX;
+ CallServiceSync( MS_GC_EVENT, NULL, ( LPARAM )&gce );
+
+ gce.ptszUID = resource;
+ gce.dwItemData = statusToSet;
+ gcd.iType = GC_EVENT_SETCONTACTSTATUS;
+ CallServiceSync( MS_GC_EVENT, NULL, ( LPARAM )&gce );
+ }
+
+ mir_free( myNick );
+}
+
+void CJabberProto::GcQuit( JABBER_LIST_ITEM* item, int code, HXML reason )
+{
+ TCHAR *szMessage = NULL;
+
+ const TCHAR* szReason = NULL;
+ if ( reason != NULL && xmlGetText( reason ) != NULL )
+ szReason = xmlGetText( reason );
+
+ GCDEST gcd = { m_szModuleName, NULL, GC_EVENT_CONTROL };
+ gcd.ptszID = item->jid;
+ GCEVENT gce = {0};
+ gce.cbSize = sizeof(GCEVENT);
+ gce.ptszUID = item->jid;
+ gce.ptszText = szReason;
+ gce.dwFlags = GC_TCHAR;
+ gce.pDest = &gcd;
+
+ if ( code != 307 && code != 301 ) {
+ CallServiceSync( MS_GC_EVENT, SESSION_TERMINATE, ( LPARAM )&gce );
+ CallServiceSync( MS_GC_EVENT, WINDOW_CLEARLOG, ( LPARAM )&gce );
+
+ DBVARIANT dbvMessage;
+ if (!DBGetContactSettingTString( NULL, m_szModuleName, "GcMsgQuit", &dbvMessage)) {
+ szMessage = NEWTSTR_ALLOCA(dbvMessage.ptszVal);
+ DBFreeVariant(&dbvMessage);
+ }
+ else szMessage = TranslateTS(JABBER_GC_MSG_QUIT);
+ }
+ else {
+ TCHAR* myNick = JabberNickFromJID( m_szJabberJID );
+ GcLogUpdateMemberStatus( item, myNick, myNick, NULL, GC_EVENT_KICK, reason );
+ mir_free( myNick );
+ CallServiceSync( MS_GC_EVENT, SESSION_OFFLINE, ( LPARAM )&gce );
+ }
+
+ DBDeleteContactSetting( HContactFromJID( item->jid ), "CList", "Hidden" );
+ item->bChatActive = FALSE;
+
+ if ( m_bJabberOnline ) {
+ TCHAR szPresenceTo[ JABBER_MAX_JID_LEN ];
+ mir_sntprintf( szPresenceTo, SIZEOF( szPresenceTo ), _T("%s/%s"), item->jid, item->nick );
+
+ m_ThreadInfo->send(
+ XmlNode( _T("presence")) << XATTR( _T("to"), szPresenceTo ) << XATTR( _T("type"), _T("unavailable"))
+ << XCHILD( _T("status"), szMessage));
+
+ ListRemove( LIST_CHATROOM, item->jid );
+} }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Context menu hooks
+
+static struct gc_item *sttFindGcMenuItem(GCMENUITEMS *items, DWORD id)
+{
+ for (int i = 0; i < items->nItems; ++i)
+ if (items->Item[i].dwID == id)
+ return items->Item + i;
+ return NULL;
+}
+
+static void sttSetupGcMenuItem(GCMENUITEMS *items, DWORD id, bool disabled)
+{
+ for (int i = 0; i < items->nItems; ++i)
+ if (!id || (items->Item[i].dwID == id))
+ items->Item[i].bDisabled = disabled;
+}
+
+static void sttShowGcMenuItem(GCMENUITEMS *items, DWORD id, int type)
+{
+ for (int i = 0; i < items->nItems; ++i)
+ if (!id || (items->Item[i].dwID == id))
+ items->Item[i].uType = type;
+}
+
+static void sttSetupGcMenuItems(GCMENUITEMS *items, DWORD *ids, bool disabled)
+{
+ for ( ; *ids; ++ids)
+ sttSetupGcMenuItem(items, *ids, disabled);
+}
+
+static void sttShowGcMenuItems(GCMENUITEMS *items, DWORD *ids, int type)
+{
+ for ( ; *ids; ++ids)
+ sttShowGcMenuItem(items, *ids, type);
+}
+
+static gc_item sttLogListItems[] =
+{
+ { LPGENT("Change &nickname"), IDM_NICK, MENU_ITEM },
+ { LPGENT("&Invite a user"), IDM_INVITE, MENU_ITEM },
+ { NULL, 0, MENU_SEPARATOR },
+
+ { LPGENT("&Roles"), IDM_ROLE, MENU_NEWPOPUP },
+ { LPGENT("&Participant list"), IDM_LST_PARTICIPANT, MENU_POPUPITEM },
+ { LPGENT("&Moderator list"), IDM_LST_MODERATOR, MENU_POPUPITEM },
+
+ { LPGENT("&Affiliations"), IDM_AFFLTN, MENU_NEWPOPUP },
+ { LPGENT("&Member list"), IDM_LST_MEMBER, MENU_POPUPITEM },
+ { LPGENT("&Admin list"), IDM_LST_ADMIN, MENU_POPUPITEM },
+ { LPGENT("&Owner list"), IDM_LST_OWNER, MENU_POPUPITEM },
+ { NULL, 0, MENU_POPUPSEPARATOR },
+ { LPGENT("Outcast list (&ban)"), IDM_LST_BAN, MENU_POPUPITEM },
+
+ { LPGENT("&Room options"), 0, MENU_NEWPOPUP },
+ { LPGENT("View/change &topic"), IDM_TOPIC, MENU_POPUPITEM },
+ { LPGENT("Add to &bookmarks"), IDM_BOOKMARKS, MENU_POPUPITEM },
+ { LPGENT("&Configure..."), IDM_CONFIG, MENU_POPUPITEM },
+ { LPGENT("&Destroy room"), IDM_DESTROY, MENU_POPUPITEM },
+
+ { NULL, 0, MENU_SEPARATOR },
+
+ { LPGENT("Lin&ks"), 0, MENU_NEWPOPUP },
+ { NULL, IDM_LINK0, 0 },
+ { NULL, IDM_LINK1, 0 },
+ { NULL, IDM_LINK2, 0 },
+ { NULL, IDM_LINK3, 0 },
+ { NULL, IDM_LINK4, 0 },
+ { NULL, IDM_LINK5, 0 },
+ { NULL, IDM_LINK6, 0 },
+ { NULL, IDM_LINK7, 0 },
+ { NULL, IDM_LINK8, 0 },
+ { NULL, IDM_LINK9, 0 },
+
+ { LPGENT("Copy room &JID"), IDM_CPY_RJID, MENU_ITEM },
+ { LPGENT("Copy room topic"), IDM_CPY_TOPIC, MENU_ITEM },
+ { NULL, 0, MENU_SEPARATOR },
+
+ { LPGENT("&Send presence"), 0, MENU_NEWPOPUP},
+ { LPGENT("Online"), IDM_PRESENCE_ONLINE, MENU_POPUPITEM },
+ { LPGENT("Away"), IDM_PRESENCE_AWAY, MENU_POPUPITEM },
+ { LPGENT("NA"), IDM_PRESENCE_NA, MENU_POPUPITEM },
+ { LPGENT("DND"), IDM_PRESENCE_DND, MENU_POPUPITEM },
+ { LPGENT("Free for chat"), IDM_PRESENCE_FREE4CHAT, MENU_POPUPITEM },
+
+ { LPGENT("&Leave chat session"), IDM_LEAVE, MENU_ITEM }
+};
+
+static TCHAR sttRJidBuf[JABBER_MAX_JID_LEN] = {0};
+static struct gc_item sttListItems[] =
+{
+ { LPGENT("&Slap"), IDM_SLAP, MENU_ITEM }, // 0
+ { LPGENT("&User details"), IDM_VCARD, MENU_ITEM }, // 1
+ { LPGENT("Member &info"), IDM_INFO, MENU_ITEM }, // 2
+
+ { sttRJidBuf, 0, MENU_NEWPOPUP }, // 3 -> accessed explicitly by index!!!
+ { LPGENT("User &details"), IDM_RJID_VCARD, MENU_POPUPITEM },
+ { LPGENT("&Add to roster"), IDM_RJID_ADD, MENU_POPUPITEM },
+ { LPGENT("&Copy to clipboard"), IDM_RJID_COPY, MENU_POPUPITEM },
+
+ { LPGENT("Invite to room"), 0, MENU_NEWPOPUP },
+ { NULL, IDM_LINK0, 0 },
+ { NULL, IDM_LINK1, 0 },
+ { NULL, IDM_LINK2, 0 },
+ { NULL, IDM_LINK3, 0 },
+ { NULL, IDM_LINK4, 0 },
+ { NULL, IDM_LINK5, 0 },
+ { NULL, IDM_LINK6, 0 },
+ { NULL, IDM_LINK7, 0 },
+ { NULL, IDM_LINK8, 0 },
+ { NULL, IDM_LINK9, 0 },
+
+ { NULL, 0, MENU_SEPARATOR },
+
+ { LPGENT("Set &role"), IDM_ROLE, MENU_NEWPOPUP },
+ { LPGENT("&Visitor"), IDM_SET_VISITOR, MENU_POPUPITEM },
+ { LPGENT("&Participant"), IDM_SET_PARTICIPANT, MENU_POPUPITEM },
+ { LPGENT("&Moderator"), IDM_SET_MODERATOR, MENU_POPUPITEM },
+
+ { LPGENT("Set &affiliation"), IDM_AFFLTN, MENU_NEWPOPUP },
+ { LPGENT("&None"), IDM_SET_NONE, MENU_POPUPITEM },
+ { LPGENT("&Member"), IDM_SET_MEMBER, MENU_POPUPITEM },
+ { LPGENT("&Admin"), IDM_SET_ADMIN, MENU_POPUPITEM },
+ { LPGENT("&Owner"), IDM_SET_OWNER, MENU_POPUPITEM },
+ { NULL, 0, MENU_POPUPSEPARATOR },
+ { LPGENT("Outcast (&ban)"), IDM_SET_BAN, MENU_POPUPITEM },
+
+ { LPGENT("&Kick"), IDM_KICK, MENU_ITEM },
+ { NULL, 0, MENU_SEPARATOR },
+ { LPGENT("Copy &nickname"), IDM_CPY_NICK, MENU_ITEM },
+ { LPGENT("Copy real &JID"), IDM_CPY_RJID, MENU_ITEM },
+ { LPGENT("Copy in-room JID"), IDM_CPY_INROOMJID, MENU_ITEM }
+};
+
+int CJabberProto::JabberGcMenuHook( WPARAM, LPARAM lParam )
+{
+ GCMENUITEMS* gcmi = ( GCMENUITEMS* )lParam;
+ if ( gcmi == NULL )
+ return 0;
+
+ if ( lstrcmpiA( gcmi->pszModule, m_szModuleName ))
+ return 0;
+
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_CHATROOM, gcmi->pszID );
+ if ( item == NULL )
+ return 0;
+
+ JABBER_RESOURCE_STATUS *me = NULL, *him = NULL;
+ for ( int i=0; i < item->resourceCount; i++ ) {
+ JABBER_RESOURCE_STATUS& p = item->resource[i];
+ if ( !lstrcmp( p.resourceName, item->nick )) me = &p;
+ if ( !lstrcmp( p.resourceName, gcmi->pszUID )) him = &p;
+ }
+
+ if ( gcmi->Type == MENU_ON_LOG ) {
+ static TCHAR url_buf[1024] = {0};
+
+ gcmi->nItems = SIZEOF( sttLogListItems );
+ gcmi->Item = sttLogListItems;
+
+ static DWORD sttModeratorItems[] = { IDM_LST_PARTICIPANT, 0 };
+ static DWORD sttAdminItems[] = { IDM_LST_MODERATOR, IDM_LST_MEMBER, IDM_LST_ADMIN, IDM_LST_OWNER, IDM_LST_BAN, 0 };
+ static DWORD sttOwnerItems[] = { IDM_CONFIG, IDM_DESTROY, 0 };
+
+ sttSetupGcMenuItem(gcmi, 0, FALSE);
+
+ int idx = IDM_LINK0;
+ if (item->itemResource.statusMessage && *item->itemResource.statusMessage) {
+ TCHAR *bufPtr = url_buf;
+ for (TCHAR *p = _tcsstr(item->itemResource.statusMessage, _T("http://")); p && *p; p = _tcsstr(p+1, _T("http://"))) {
+ lstrcpyn(bufPtr, p, SIZEOF(url_buf) - (bufPtr - url_buf));
+ gc_item *pItem = sttFindGcMenuItem(gcmi, idx);
+ pItem->pszDesc = bufPtr;
+ pItem->uType = MENU_POPUPITEM;
+ for ( ; *bufPtr && !_istspace(*bufPtr); ++bufPtr) ;
+ *bufPtr++ = 0;
+
+ if (++idx > IDM_LINK9) break;
+ }
+ }
+ for ( ; idx <= IDM_LINK9; ++idx)
+ sttFindGcMenuItem(gcmi, idx)->uType = 0;
+
+ if ( !GetAsyncKeyState(VK_CONTROL)) {
+ if (me) {
+ sttSetupGcMenuItems(gcmi, sttModeratorItems, (me->role < ROLE_MODERATOR));
+ sttSetupGcMenuItems(gcmi, sttAdminItems, (me->affiliation < AFFILIATION_ADMIN));
+ sttSetupGcMenuItems(gcmi, sttOwnerItems, (me->affiliation < AFFILIATION_OWNER));
+ }
+ if (m_ThreadInfo->jabberServerCaps & JABBER_CAPS_PRIVATE_STORAGE)
+ sttSetupGcMenuItem(gcmi, IDM_BOOKMARKS, FALSE);
+ }
+ }
+ else if ( gcmi->Type == MENU_ON_NICKLIST ) {
+ gcmi->nItems = SIZEOF(sttListItems);
+ gcmi->Item = sttListItems;
+
+ static DWORD sttRJidItems[] = { IDM_RJID_VCARD, IDM_RJID_ADD, IDM_RJID_COPY, 0 };
+
+ if (me && him) {
+ int i, idx;
+ BOOL force = GetAsyncKeyState(VK_CONTROL);
+ sttSetupGcMenuItem(gcmi, 0, FALSE);
+
+ idx = IDM_LINK0;
+ LISTFOREACH_NODEF(i, this, LIST_CHATROOM)
+ if (item = ListGetItemPtrFromIndex(i)) {
+ gc_item *pItem = sttFindGcMenuItem(gcmi, idx);
+ pItem->pszDesc = item->jid;
+ pItem->uType = MENU_POPUPITEM;
+ if (++idx > IDM_LINK9) break;
+ }
+
+ for ( ; idx <= IDM_LINK9; ++idx)
+ sttFindGcMenuItem(gcmi, idx)->uType = 0;
+
+ for (i = 0; i < SIZEOF(sttAffiliationItems); ++i) {
+ struct gc_item *item = sttFindGcMenuItem(gcmi, sttAffiliationItems[i].id);
+ item->uType = (him->affiliation == sttAffiliationItems[i].value) ? MENU_POPUPCHECK : MENU_POPUPITEM;
+ item->bDisabled = !(force || sttAffiliationItems[i].check(me, him));
+ }
+
+ for (i = 0; i < SIZEOF(sttRoleItems); ++i) {
+ struct gc_item *item = sttFindGcMenuItem(gcmi, sttRoleItems[i].id);
+ item->uType = (him->role == sttRoleItems[i].value) ? MENU_POPUPCHECK : MENU_POPUPITEM;
+ item->bDisabled = !(force || sttRoleItems[i].check(me, him));
+ }
+
+ if (him->szRealJid && *him->szRealJid) {
+ mir_sntprintf(sttRJidBuf, SIZEOF(sttRJidBuf), TranslateT("Real &JID: %s"), him->szRealJid);
+ if (TCHAR *tmp = _tcschr(sttRJidBuf, _T('/'))) *tmp = 0;
+
+ if (HANDLE hContact = HContactFromJID(him->szRealJid)) {
+ gcmi->Item[3].uType = MENU_HMENU;
+ gcmi->Item[3].dwID = CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)hContact, 0);
+ sttShowGcMenuItems(gcmi, sttRJidItems, 0);
+ }
+ else {
+ gcmi->Item[3].uType = MENU_NEWPOPUP;
+ sttShowGcMenuItems(gcmi, sttRJidItems, MENU_POPUPITEM);
+ }
+
+ sttSetupGcMenuItem(gcmi, IDM_CPY_RJID, FALSE);
+ }
+ else {
+ gcmi->Item[3].uType = 0;
+ sttShowGcMenuItems(gcmi, sttRJidItems, 0);
+
+ sttSetupGcMenuItem(gcmi, IDM_CPY_RJID, TRUE);
+ }
+
+ if (!force) {
+ if (me->role < ROLE_MODERATOR || (me->affiliation <= him->affiliation))
+ sttSetupGcMenuItem(gcmi, IDM_KICK, TRUE);
+
+ if ((me->affiliation < AFFILIATION_ADMIN) ||
+ (me->affiliation == AFFILIATION_ADMIN) && (me->affiliation <= him->affiliation))
+ sttSetupGcMenuItem(gcmi, IDM_SET_BAN, TRUE);
+ }
+ }
+ else {
+ sttSetupGcMenuItem(gcmi, 0, TRUE);
+ gcmi->Item[2].uType = 0;
+ sttShowGcMenuItems(gcmi, sttRJidItems, 0);
+ }
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Conference invitation dialog
+
+class CGroupchatInviteDlg : public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+ struct JabberGcLogInviteDlgJidData
+ {
+ int hItem;
+ TCHAR jid[JABBER_MAX_JID_LEN];
+ };
+
+ LIST<JabberGcLogInviteDlgJidData> m_newJids;
+ TCHAR *m_room;
+
+ CCtrlButton m_btnInvite;
+ CCtrlEdit m_txtNewJid;
+ CCtrlMButton m_btnAddJid;
+ CCtrlEdit m_txtReason;
+ CCtrlClc m_clc;
+
+ void FilterList(CCtrlClc *)
+ {
+ for (HANDLE hContact = db_find_first();
+ hContact;
+ hContact = db_find_next(hContact))
+ {
+ char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (lstrcmpA(proto, m_proto->m_szModuleName) || DBGetContactSettingByte(hContact, proto, "ChatRoom", 0))
+ if (HANDLE hItem = m_clc.FindContact(hContact))
+ m_clc.DeleteItem(hItem);
+ } }
+
+ void ResetListOptions(CCtrlClc *)
+ {
+ m_clc.SetBkBitmap(0, NULL);
+ m_clc.SetBkColor(GetSysColor(COLOR_WINDOW));
+ m_clc.SetGreyoutFlags(0);
+ m_clc.SetLeftMargin(4);
+ m_clc.SetIndent(10);
+ m_clc.SetHideEmptyGroups(1);
+ m_clc.SetHideOfflineRoot(1);
+ for (int i=0; i <= FONTID_MAX; i++)
+ m_clc.SetTextColor(i, GetSysColor(COLOR_WINDOWTEXT));
+ }
+
+ void InviteUser(TCHAR *pUser, TCHAR *text)
+ {
+ XmlNode msg( _T("message"));
+ HXML invite = msg << XATTR( _T("to"), m_room ) << XATTRID( m_proto->SerialNext())
+ << XCHILDNS( _T("x"), _T(JABBER_FEAT_MUC_USER))
+ << XCHILD( _T("invite")) << XATTR( _T("to"), pUser );
+ if ( text )
+ invite << XCHILD( _T("reason"), text );
+
+ m_proto->m_ThreadInfo->send( msg );
+ }
+
+public:
+ CGroupchatInviteDlg(CJabberProto* ppro, TCHAR *room) :
+ CSuper(ppro, IDD_GROUPCHAT_INVITE, NULL),
+ m_newJids(1),
+ m_btnInvite(this, IDC_INVITE),
+ m_txtNewJid(this, IDC_NEWJID),
+ m_btnAddJid(this, IDC_ADDJID, ppro->LoadIconEx("addroster"), "Add"),
+ m_txtReason(this, IDC_REASON),
+ m_clc(this, IDC_CLIST)
+ {
+ m_room = mir_tstrdup(room);
+ m_btnAddJid.OnClick = Callback( this, &CGroupchatInviteDlg::OnCommand_AddJid );
+ m_btnInvite.OnClick = Callback( this, &CGroupchatInviteDlg::OnCommand_Invite );
+ m_clc.OnNewContact =
+ m_clc.OnListRebuilt = Callback( this, &CGroupchatInviteDlg::FilterList );
+ m_clc.OnOptionsChanged = Callback( this, &CGroupchatInviteDlg::ResetListOptions );
+ }
+
+ ~CGroupchatInviteDlg()
+ {
+ for (int i = 0; i < m_newJids.getCount(); ++i)
+ mir_free(m_newJids[i]);
+ mir_free(m_room);
+ }
+
+ void OnInitDialog()
+ {
+ CSuper::OnInitDialog();
+
+ TCHAR buf[256];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s\n%s"), m_room, TranslateT("Send groupchat invitation."));
+ SetDlgItemText(m_hwnd, IDC_HEADERBAR, buf);
+ WindowSetIcon(m_hwnd, m_proto, "group");
+
+ SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_CLIST), GWL_STYLE,
+ GetWindowLongPtr(GetDlgItem(m_hwnd, IDC_CLIST), GWL_STYLE)|CLS_HIDEOFFLINE|CLS_CHECKBOXES|CLS_HIDEEMPTYGROUPS|CLS_USEGROUPS|CLS_GREYALTERNATE|CLS_GROUPCHECKBOXES);
+ SendMessage(GetDlgItem(m_hwnd, IDC_CLIST), CLM_SETEXSTYLE, CLS_EX_DISABLEDRAGDROP|CLS_EX_TRACKSELECT, 0);
+ ResetListOptions(&m_clc);
+ FilterList(&m_clc);
+ }
+
+ void OnCommand_AddJid( CCtrlButton* )
+ {
+ TCHAR buf[JABBER_MAX_JID_LEN];
+ m_txtNewJid.GetText(buf, SIZEOF(buf));
+ m_txtNewJid.SetTextA("");
+
+ HANDLE hContact = m_proto->HContactFromJID(buf);
+ if ( hContact )
+ {
+ int hItem = SendDlgItemMessage( m_hwnd, IDC_CLIST, CLM_FINDCONTACT, (WPARAM)hContact, 0 );
+ if ( hItem )
+ SendDlgItemMessage( m_hwnd, IDC_CLIST, CLM_SETCHECKMARK, hItem, 1 );
+ return;
+ }
+
+ int i;
+ for (i = 0; i < m_newJids.getCount(); ++i)
+ if (!lstrcmp(m_newJids[i]->jid, buf))
+ break;
+ if (i != m_newJids.getCount())
+ return;
+
+ JabberGcLogInviteDlgJidData *jidData = (JabberGcLogInviteDlgJidData *)mir_alloc(sizeof(JabberGcLogInviteDlgJidData));
+ lstrcpy(jidData->jid, buf);
+ CLCINFOITEM cii = {0};
+ cii.cbSize = sizeof(cii);
+ cii.flags = CLCIIF_CHECKBOX;
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s (%s)"), jidData->jid, TranslateT("not on roster"));
+ cii.pszText = buf;
+ jidData->hItem = SendDlgItemMessage(m_hwnd,IDC_CLIST,CLM_ADDINFOITEM,0,(LPARAM)&cii);
+ SendDlgItemMessage(m_hwnd, IDC_CLIST, CLM_SETCHECKMARK, jidData->hItem, 1);
+ m_newJids.insert(jidData);
+ }
+
+ void OnCommand_Invite( CCtrlButton* )
+ {
+ if (!m_room) return;
+
+ TCHAR *text = m_txtReason.GetText();
+ HWND hwndList = GetDlgItem(m_hwnd, IDC_CLIST);
+
+ // invite users from roster
+ for (HANDLE hContact = db_find_first();
+ hContact;
+ hContact = db_find_next(hContact))
+ {
+ char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (!lstrcmpA(proto, m_proto->m_szModuleName) && !DBGetContactSettingByte(hContact, proto, "ChatRoom", 0))
+ {
+ if (int hItem = SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0))
+ {
+ if (SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0))
+ {
+ DBVARIANT dbv={0};
+ m_proto->JGetStringT(hContact, "jid", &dbv);
+ if (dbv.ptszVal && ( dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR ))
+ InviteUser(dbv.ptszVal, text);
+ JFreeVariant(&dbv);
+ }
+ }
+ }
+ }
+
+ // invite others
+ for (int i = 0; i < m_newJids.getCount(); ++i)
+ if (SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)m_newJids[i]->hItem, 0))
+ InviteUser(m_newJids[i]->jid, text);
+
+ mir_free(text);
+ Close();
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Context menu processing
+
+void CJabberProto::AdminSet( const TCHAR* to, const TCHAR* ns, const TCHAR* szItem, const TCHAR* itemVal, const TCHAR* var, const TCHAR* varVal )
+{
+ m_ThreadInfo->send( XmlNodeIq( _T("set"), SerialNext(), to ) << XQUERY( ns ) << XCHILD( _T("item")) << XATTR( szItem, itemVal ) << XATTR( var, varVal ));
+}
+
+void CJabberProto::AdminSetReason( const TCHAR* to, const TCHAR* ns, const TCHAR* szItem, const TCHAR* itemVal, const TCHAR* var, const TCHAR* varVal , const TCHAR* rsn)
+{ m_ThreadInfo->send( XmlNodeIq( _T("set"), SerialNext(), to ) << XQUERY( ns ) << XCHILD( _T("item")) << XATTR( szItem, itemVal ) << XATTR( var, varVal ) << XCHILD( _T("reason"), rsn));
+}
+
+void CJabberProto::AdminGet( const TCHAR* to, const TCHAR* ns, const TCHAR* var, const TCHAR* varVal, JABBER_IQ_PFUNC foo )
+{
+ int id = SerialNext();
+ IqAdd( id, IQ_PROC_NONE, foo );
+ m_ThreadInfo->send( XmlNodeIq( _T("get"), id, to ) << XQUERY( ns ) << XCHILD( _T("item")) << XATTR( var, varVal ));
+}
+
+// Member info dialog
+struct TUserInfoData
+{
+ CJabberProto* ppro;
+ JABBER_LIST_ITEM *item;
+ JABBER_RESOURCE_STATUS *me, *him;
+};
+
+static INT_PTR CALLBACK sttUserInfoDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ TUserInfoData *dat = (TUserInfoData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ int i, idx;
+ TCHAR buf[256];
+
+ TranslateDialogDefault(hwndDlg);
+
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ dat = (TUserInfoData *)lParam;
+
+ WindowSetIcon( hwndDlg, dat->ppro, "group" );
+
+ LOGFONT lf;
+ GetObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_TXT_NICK, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ HFONT hfnt = CreateFontIndirect(&lf);
+ SendDlgItemMessage(hwndDlg, IDC_TXT_NICK, WM_SETFONT, (WPARAM)hfnt, TRUE);
+
+ SendDlgItemMessage(hwndDlg, IDC_BTN_AFFILIATION, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadSkinnedIcon(SKINICON_EVENT_FILE));
+ SendDlgItemMessage(hwndDlg, IDC_BTN_AFFILIATION, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_BTN_AFFILIATION, BUTTONADDTOOLTIP, (WPARAM)"Apply", 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_BTN_ROLE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadSkinnedIcon(SKINICON_EVENT_FILE));
+ SendDlgItemMessage(hwndDlg, IDC_BTN_ROLE, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_BTN_ROLE, BUTTONADDTOOLTIP, (WPARAM)"Apply", 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_ICO_STATUS, STM_SETICON, (WPARAM)LoadSkinnedProtoIcon(dat->ppro->m_szModuleName, dat->him->status), 0);
+
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s %s"), TranslateT("Member Info:"), dat->him->resourceName);
+ SetWindowText(hwndDlg, buf);
+
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s\n%s %s %s"), TranslateT("Member Information"), dat->him->resourceName, TranslateT("from"), dat->item->jid);
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, buf);
+
+ SetDlgItemText(hwndDlg, IDC_TXT_NICK, dat->him->resourceName);
+ SetDlgItemText(hwndDlg, IDC_TXT_JID, dat->him->szRealJid ? dat->him->szRealJid : TranslateT("Real JID not available"));
+ SetDlgItemText(hwndDlg, IDC_TXT_STATUS, dat->him->statusMessage);
+
+ for (i = 0; i < SIZEOF(sttRoleItems); ++i)
+ {
+ if ((sttRoleItems[i].value == dat->him->role) || sttRoleItems[i].check(dat->me, dat->him))
+ {
+ SendDlgItemMessage(hwndDlg, IDC_TXT_ROLE, CB_SETITEMDATA,
+ idx = SendDlgItemMessage(hwndDlg, IDC_TXT_ROLE, CB_ADDSTRING, 0, (LPARAM)sttRoleItems[i].title),
+ sttRoleItems[i].value);
+ if (sttRoleItems[i].value == dat->him->role)
+ SendDlgItemMessage(hwndDlg, IDC_TXT_ROLE, CB_SETCURSEL, idx, 0);
+ }
+ }
+ for (i = 0; i < SIZEOF(sttAffiliationItems); ++i)
+ {
+ if ((sttAffiliationItems[i].value == dat->him->affiliation) || sttAffiliationItems[i].check(dat->me, dat->him))
+ {
+ SendDlgItemMessage(hwndDlg, IDC_TXT_AFFILIATION, CB_SETITEMDATA,
+ idx = SendDlgItemMessage(hwndDlg, IDC_TXT_AFFILIATION, CB_ADDSTRING, 0, (LPARAM)sttAffiliationItems[i].title),
+ sttAffiliationItems[i].value);
+ if (sttAffiliationItems[i].value == dat->him->affiliation)
+ SendDlgItemMessage(hwndDlg, IDC_TXT_AFFILIATION, CB_SETCURSEL, idx, 0);
+ }
+ }
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_ROLE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_AFFILIATION), FALSE);
+
+ break;
+ }
+
+ case WM_COMMAND:
+ if (!dat)break;
+
+ switch ( LOWORD( wParam )) {
+ case IDCANCEL:
+ PostMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+
+ case IDC_TXT_AFFILIATION:
+ if (HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ int value = SendDlgItemMessage(hwndDlg, IDC_TXT_AFFILIATION, CB_GETITEMDATA,
+ SendDlgItemMessage(hwndDlg, IDC_TXT_AFFILIATION, CB_GETCURSEL, 0, 0), 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_AFFILIATION), dat->him->affiliation != value);
+ }
+ break;
+
+ case IDC_BTN_AFFILIATION:
+ {
+ int value = SendDlgItemMessage(hwndDlg, IDC_TXT_AFFILIATION, CB_GETITEMDATA,
+ SendDlgItemMessage(hwndDlg, IDC_TXT_AFFILIATION, CB_GETCURSEL, 0, 0), 0);
+ if (dat->him->affiliation == value) break;
+
+ switch (value)
+ {
+ TCHAR szBareJid[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( dat->him->szRealJid, szBareJid, SIZEOF(szBareJid));
+ case AFFILIATION_NONE:
+ if (dat->him->szRealJid)
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("jid"), szBareJid, _T("affiliation"), _T("none"));
+ else
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("nick"), dat->him->resourceName, _T("affiliation"), _T("none"));
+ break;
+ case AFFILIATION_MEMBER:
+ if (dat->him->szRealJid)
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("jid"), szBareJid, _T("affiliation"), _T("member"));
+ else
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("nick"), dat->him->resourceName, _T("affiliation"), _T("member"));
+ break;
+ case AFFILIATION_ADMIN:
+ if (dat->him->szRealJid)
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("jid"), szBareJid, _T("affiliation"), _T("admin"));
+ else
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("nick"), dat->him->resourceName, _T("affiliation"), _T("admin"));
+ break;
+ case AFFILIATION_OWNER:
+ if (dat->him->szRealJid)
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("jid"), szBareJid, _T("affiliation"), _T("owner"));
+ else
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("nick"), dat->him->resourceName, _T("affiliation"), _T("owner"));
+ break;
+ }
+ }
+ break;
+
+ case IDC_TXT_ROLE:
+ if (HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ int value = SendDlgItemMessage(hwndDlg, IDC_TXT_ROLE, CB_GETITEMDATA,
+ SendDlgItemMessage(hwndDlg, IDC_TXT_ROLE, CB_GETCURSEL, 0, 0), 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_ROLE), dat->him->role != value);
+ }
+ break;
+
+ case IDC_BTN_ROLE:
+ {
+ int value = SendDlgItemMessage(hwndDlg, IDC_TXT_ROLE, CB_GETITEMDATA,
+ SendDlgItemMessage(hwndDlg, IDC_TXT_ROLE, CB_GETCURSEL, 0, 0), 0);
+ if (dat->him->role == value) break;
+
+ switch (value) {
+ case ROLE_VISITOR:
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("nick"), dat->him->resourceName, _T("role"), _T("visitor"));
+ break;
+ case ROLE_PARTICIPANT:
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("nick"), dat->him->resourceName, _T("role"), _T("participant"));
+ break;
+ case ROLE_MODERATOR:
+ dat->ppro->AdminSet(dat->item->jid, xmlnsAdmin, _T("nick"), dat->him->resourceName, _T("role"), _T("moderator"));
+ break;
+ }
+ }
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ {
+ WindowFreeIcon( hwndDlg );
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( hwndDlg, IDC_BTN_AFFILIATION, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( hwndDlg, IDC_BTN_ROLE, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ TUserInfoData *dat = (TUserInfoData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (!dat)break;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ mir_free(dat);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static void sttNickListHook( CJabberProto* ppro, JABBER_LIST_ITEM* item, GCHOOK* gch )
+{
+ JABBER_RESOURCE_STATUS *me = NULL, *him = NULL;
+ for ( int i=0; i < item->resourceCount; i++ ) {
+ JABBER_RESOURCE_STATUS& p = item->resource[i];
+ if ( !lstrcmp( p.resourceName, item->nick )) me = &p;
+ if ( !lstrcmp( p.resourceName, gch->ptszUID )) him = &p;
+ }
+
+ if ( him == NULL || me == NULL )
+ return;
+
+ // 1 kick per second, prevents crashes...
+ enum { BAN_KICK_INTERVAL = 1000 };
+ static DWORD dwLastBanKickTime = 0;
+
+ TCHAR szBuffer[1024];
+ TCHAR szTitle[256];
+
+ if ((gch->dwData >= CLISTMENUIDMIN) && (gch->dwData <= CLISTMENUIDMAX))
+ {
+ if (him->szRealJid && *him->szRealJid)
+ if (HANDLE hContact = ppro->HContactFromJID(him->szRealJid))
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(gch->dwData, MPCF_CONTACTMENU), (LPARAM)hContact);
+ return;
+ }
+
+ switch( gch->dwData ) {
+ case IDM_SLAP:
+ {
+ if ( ppro->m_bJabberOnline ) {
+ DBVARIANT dbv = {0};
+ TCHAR *szMessage = DBGetContactSettingTString( NULL, ppro->m_szModuleName, "GcMsgSlap", &dbv) ?
+ NEWTSTR_ALLOCA(TranslateTS(JABBER_GC_MSG_SLAP)) : dbv.ptszVal;
+
+ TCHAR buf[256];
+ // do not use snprintf to avoid possible problems with % symbol
+ if (TCHAR *p = _tcsstr(szMessage, _T("%s"))) {
+ *p = 0;
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s%s%s"), szMessage, him->resourceName, p+2);
+ }
+ else lstrcpyn(buf, szMessage, SIZEOF(buf));
+ UnEscapeChatTags( buf );
+
+ ppro->m_ThreadInfo->send(
+ XmlNode( _T("message")) << XATTR( _T("to"), item->jid ) << XATTR( _T("type"), _T("groupchat"))
+ << XCHILD( _T("body"), buf ));
+
+ if (szMessage == dbv.ptszVal)
+ DBFreeVariant(&dbv);
+ }
+ break;
+ }
+ case IDM_VCARD:
+ {
+ HANDLE hContact;
+ JABBER_SEARCH_RESULT jsr = {0};
+ mir_sntprintf(jsr.jid, SIZEOF(jsr.jid), _T("%s/%s"), item->jid, him->resourceName );
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+
+ JABBER_LIST_ITEM* item = ppro->ListAdd( LIST_VCARD_TEMP, jsr.jid );
+ item->bUseResource = TRUE;
+ ppro->ListAddResource( LIST_VCARD_TEMP, jsr.jid, him->status, him->statusMessage, him->priority );
+
+ hContact = ( HANDLE )CallProtoService( ppro->m_szModuleName, PS_ADDTOLIST, PALF_TEMPORARY, ( LPARAM )&jsr );
+ CallService( MS_USERINFO_SHOWDIALOG, ( WPARAM )hContact, 0 );
+ break;
+ }
+ case IDM_INFO:
+ {
+ TUserInfoData *dat = (TUserInfoData *)mir_alloc(sizeof(TUserInfoData));
+ dat->me = me;
+ dat->him = him;
+ dat->item = item;
+ dat->ppro = ppro;
+ HWND hwndInfo = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_GROUPCHAT_INFO), NULL, sttUserInfoDlgProc, (LPARAM)dat);
+ ShowWindow(hwndInfo, SW_SHOW);
+ break;
+ }
+ case IDM_KICK:
+ {
+ if ((GetTickCount() - dwLastBanKickTime) > BAN_KICK_INTERVAL)
+ {
+ dwLastBanKickTime = GetTickCount();
+ mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s: "), me->resourceName );
+ mir_sntprintf( szTitle, SIZEOF(szTitle), _T("%s %s"), TranslateT( "Reason to kick" ), him->resourceName );
+ TCHAR *resourceName_copy = mir_tstrdup(him->resourceName); // copy resource name to prevent possible crash if user list rebuilds
+ if ( ppro->EnterString(szBuffer, SIZEOF(szBuffer), szTitle, JES_MULTINE, "gcReason_" ))
+ ppro->m_ThreadInfo->send(
+ XmlNodeIq( _T("set"), ppro->SerialNext(), item->jid ) << XQUERY( xmlnsAdmin )
+ << XCHILD( _T("item")) << XATTR( _T("nick"), resourceName_copy ) << XATTR( _T("role"), _T("none"))
+ << XCHILD( _T("reason"), szBuffer ));
+
+ mir_free(resourceName_copy);
+ }
+ dwLastBanKickTime = GetTickCount();
+ break;
+ }
+
+ case IDM_SET_VISITOR:
+ if (him->role != ROLE_VISITOR)
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("nick"), him->resourceName, _T("role"), _T("visitor"));
+ break;
+ case IDM_SET_PARTICIPANT:
+ if (him->role != ROLE_PARTICIPANT)
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("nick"), him->resourceName, _T("role"), _T("participant"));
+ break;
+ case IDM_SET_MODERATOR:
+ if (him->role != ROLE_MODERATOR)
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("nick"), him->resourceName, _T("role"), _T("moderator"));
+ break;
+
+ case IDM_SET_NONE:
+ if (him->affiliation != AFFILIATION_NONE)
+ {
+ if (him->szRealJid)
+ {
+ TCHAR szBareJid[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( him->szRealJid, szBareJid, SIZEOF(szBareJid));
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("jid"), szBareJid, _T("affiliation"), _T("none"));
+ }
+ else
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("nick"), him->resourceName, _T("affiliation"), _T("none"));
+ }
+ break;
+ case IDM_SET_MEMBER:
+ if (him->affiliation != AFFILIATION_MEMBER)
+ {
+ if (him->szRealJid)
+ {
+ TCHAR szBareJid[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( him->szRealJid, szBareJid, SIZEOF(szBareJid));
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("jid"), szBareJid, _T("affiliation"), _T("member"));
+ }
+ else
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("nick"), him->resourceName, _T("affiliation"), _T("member"));
+ }
+ break;
+ case IDM_SET_ADMIN:
+ if (him->affiliation != AFFILIATION_ADMIN)
+ {
+ if (him->szRealJid)
+ {
+ TCHAR szBareJid[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( him->szRealJid, szBareJid, SIZEOF(szBareJid));
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("jid"), szBareJid, _T("affiliation"), _T("admin"));
+ }
+ else
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("nick"), him->resourceName, _T("affiliation"), _T("admin"));
+ }
+ break;
+ case IDM_SET_OWNER:
+ if (him->affiliation != AFFILIATION_OWNER)
+ {
+ if (him->szRealJid)
+ {
+ TCHAR szBareJid[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( him->szRealJid, szBareJid, SIZEOF(szBareJid));
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("jid"), szBareJid, _T("affiliation"), _T("owner"));
+ }
+ else
+ ppro->AdminSet(item->jid, xmlnsAdmin, _T("nick"), him->resourceName, _T("affiliation"), _T("owner"));
+ }
+ break;
+
+ case IDM_SET_BAN:
+ if ((GetTickCount() - dwLastBanKickTime) > BAN_KICK_INTERVAL) {
+ if ( him->szRealJid && *him->szRealJid ) {
+ TCHAR szVictimBareJid[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( him->szRealJid, szVictimBareJid, SIZEOF(szVictimBareJid));
+
+ mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s: "), me->resourceName );
+ mir_sntprintf( szTitle, SIZEOF(szTitle), _T("%s %s"), TranslateT( "Reason to ban" ), him->resourceName );
+
+ if ( ppro->EnterString(szBuffer, SIZEOF(szBuffer), szTitle, JES_MULTINE, "gcReason_" )) {
+ ppro->m_ThreadInfo->send(
+ XmlNodeIq( _T("set"), ppro->SerialNext(), item->jid ) << XQUERY( xmlnsAdmin )
+ << XCHILD( _T("item")) << XATTR( _T("jid"), szVictimBareJid ) << XATTR( _T("affiliation"), _T("outcast"))
+ << XCHILD( _T("reason"), szBuffer ));
+ }
+ }
+ }
+ dwLastBanKickTime = GetTickCount();
+ break;
+
+ case IDM_LINK0: case IDM_LINK1: case IDM_LINK2: case IDM_LINK3: case IDM_LINK4:
+ case IDM_LINK5: case IDM_LINK6: case IDM_LINK7: case IDM_LINK8: case IDM_LINK9:
+ {
+ if ((GetTickCount() - dwLastBanKickTime) > BAN_KICK_INTERVAL)
+ {
+ TCHAR *resourceName_copy = NEWTSTR_ALLOCA(him->resourceName); // copy resource name to prevent possible crash if user list rebuilds
+
+ TCHAR *szInviteTo = 0;
+ int idx = gch->dwData - IDM_LINK0;
+ LISTFOREACH(i, ppro, LIST_CHATROOM)
+ if (JABBER_LIST_ITEM *item = ppro->ListGetItemPtrFromIndex(i))
+ if (!idx--)
+ {
+ szInviteTo = item->jid;
+ break;
+ }
+
+ if (!szInviteTo) break;
+
+ mir_sntprintf( szTitle, SIZEOF(szTitle), TranslateT("Invite %s to %s"), him->resourceName, szInviteTo );
+ *szBuffer = 0;
+ if (!ppro->EnterString(szBuffer, SIZEOF(szBuffer), szTitle, JES_MULTINE))
+ break;
+
+ mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%s/%s"), item->jid, resourceName_copy);
+
+ XmlNode msg( _T("message"));
+ HXML invite = msg << XATTR( _T("to"), szTitle ) << XATTRID(ppro->SerialNext())
+ << XCHILD(_T("x"), szBuffer)
+ << XATTR(_T("xmlns"), _T("jabber:x:conference"))
+ << XATTR( _T("jid"), szInviteTo )
+ << XCHILD(_T("invite")) << XATTR(_T("from"), item->nick);
+ ppro->m_ThreadInfo->send( msg );
+ }
+ dwLastBanKickTime = GetTickCount();
+ break;
+ }
+
+ case IDM_CPY_NICK:
+ JabberCopyText((HWND)CallService(MS_CLUI_GETHWND, 0, 0), him->resourceName);
+ break;
+ case IDM_RJID_COPY:
+ case IDM_CPY_RJID:
+ JabberCopyText((HWND)CallService(MS_CLUI_GETHWND, 0, 0), him->szRealJid);
+ break;
+ case IDM_CPY_INROOMJID:
+ mir_sntprintf(szBuffer, SIZEOF(szBuffer), _T("%s/%s"), item->jid, him->resourceName);
+ JabberCopyText((HWND)CallService(MS_CLUI_GETHWND, 0, 0), szBuffer);
+ break;
+
+ case IDM_RJID_VCARD:
+ if (him->szRealJid && *him->szRealJid)
+ {
+ HANDLE hContact;
+ JABBER_SEARCH_RESULT jsr ={0};
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+ mir_sntprintf(jsr.jid, SIZEOF(jsr.jid), _T("%s"), him->szRealJid);
+ if (TCHAR *tmp = _tcschr(jsr.jid, _T('/'))) *tmp = 0;
+
+ JABBER_LIST_ITEM* item = ppro->ListAdd( LIST_VCARD_TEMP, jsr.jid );
+ item->bUseResource = TRUE;
+ ppro->ListAddResource( LIST_VCARD_TEMP, jsr.jid, him->status, him->statusMessage, him->priority );
+
+ hContact = ( HANDLE )CallProtoService( ppro->m_szModuleName, PS_ADDTOLIST, PALF_TEMPORARY, ( LPARAM )&jsr );
+ CallService( MS_USERINFO_SHOWDIALOG, ( WPARAM )hContact, 0 );
+ break;
+ }
+
+ case IDM_RJID_ADD:
+ if (him->szRealJid && *him->szRealJid)
+ {
+ JABBER_SEARCH_RESULT jsr={0};
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+ jsr.hdr.flags = PSR_TCHAR;
+ mir_sntprintf(jsr.jid, SIZEOF(jsr.jid), _T("%s"), him->szRealJid);
+ if (TCHAR *tmp = _tcschr(jsr.jid, _T('/'))) *tmp = 0;
+ jsr.hdr.nick = jsr.jid;
+
+ ADDCONTACTSTRUCT acs={0};
+ acs.handleType = HANDLE_SEARCHRESULT;
+ acs.szProto = ppro->m_szModuleName;
+ acs.psr = (PROTOSEARCHRESULT *)&jsr;
+ CallService(MS_ADDCONTACT_SHOW, (WPARAM)CallService(MS_CLUI_GETHWND, 0, 0), (LPARAM)&acs);
+ break;
+ }
+ }
+}
+
+static void sttLogListHook( CJabberProto* ppro, JABBER_LIST_ITEM* item, GCHOOK* gch )
+{
+ TCHAR szBuffer[ 1024 ];
+ TCHAR szCaption[ 1024 ];
+ szBuffer[ 0 ] = _T('\0');
+
+ switch( gch->dwData ) {
+ case IDM_LST_PARTICIPANT:
+ ppro->AdminGet(gch->pDest->ptszID, xmlnsAdmin, _T("role"), _T("participant"), &CJabberProto::OnIqResultMucGetVoiceList );
+ break;
+
+ case IDM_LST_MEMBER:
+ ppro->AdminGet(gch->pDest->ptszID, xmlnsAdmin, _T("affiliation"), _T("member"), &CJabberProto::OnIqResultMucGetMemberList );
+ break;
+
+ case IDM_LST_MODERATOR:
+ ppro->AdminGet(gch->pDest->ptszID, xmlnsAdmin, _T("role"), _T("moderator"), &CJabberProto::OnIqResultMucGetModeratorList );
+ break;
+
+ case IDM_LST_BAN:
+ ppro->AdminGet(gch->pDest->ptszID, xmlnsAdmin, _T("affiliation"), _T("outcast"), &CJabberProto::OnIqResultMucGetBanList );
+ break;
+
+ case IDM_LST_ADMIN:
+ ppro->AdminGet(gch->pDest->ptszID, xmlnsAdmin, _T("affiliation"), _T("admin"), &CJabberProto::OnIqResultMucGetAdminList );
+ break;
+
+ case IDM_LST_OWNER:
+ ppro->AdminGet(gch->pDest->ptszID, xmlnsAdmin, _T("affiliation"), _T("owner"), &CJabberProto::OnIqResultMucGetOwnerList );
+ break;
+
+ case IDM_TOPIC:
+ mir_sntprintf( szCaption, SIZEOF(szCaption), _T("%s %s"), TranslateT( "Set topic for" ), gch->pDest->ptszID );
+ TCHAR szTmpBuff[ SIZEOF(szBuffer) * 2 ];
+ if ( item->itemResource.statusMessage ) {
+ int j = 0;
+ for ( int i = 0; i < SIZEOF(szTmpBuff); i++ ) {
+ if ( item->itemResource.statusMessage[ i ] != _T('\n') || ( i && item->itemResource.statusMessage[ i - 1 ] == _T('\r')))
+ szTmpBuff[ j++ ] = item->itemResource.statusMessage[ i ];
+ else {
+ szTmpBuff[ j++ ] = _T('\r');
+ szTmpBuff[ j++ ] = _T('\n');
+ }
+ if ( !item->itemResource.statusMessage[ i ] )
+ break;
+ }
+ }
+ else szTmpBuff[ 0 ] = _T('\0');
+
+ if ( ppro->EnterString( szTmpBuff, SIZEOF(szTmpBuff), szCaption, JES_RICHEDIT, "gcTopic_" ))
+ ppro->m_ThreadInfo->send(
+ XmlNode( _T("message")) << XATTR( _T("to"), gch->pDest->ptszID ) << XATTR( _T("type"), _T("groupchat"))
+ << XCHILD( _T("subject"), szTmpBuff ));
+
+ break;
+
+ case IDM_NICK:
+ mir_sntprintf( szCaption, SIZEOF(szCaption), _T("%s %s"), TranslateT( "Change nickname in" ), gch->pDest->ptszID );
+ if ( item->nick )
+ mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s"), item->nick );
+ if ( ppro->EnterString(szBuffer, SIZEOF(szBuffer), szCaption, JES_COMBO, "gcNick_" )) {
+ JABBER_LIST_ITEM* item = ppro->ListGetItemPtr( LIST_CHATROOM, gch->pDest->ptszID );
+ if ( item != NULL ) {
+ TCHAR text[ 1024 ];
+ mir_sntprintf( text, SIZEOF( text ), _T("%s/%s"), gch->pDest->ptszID, szBuffer );
+ ppro->SendPresenceTo( ppro->m_iStatus == ID_STATUS_INVISIBLE ? ID_STATUS_ONLINE : ppro->m_iStatus, text, NULL );
+ } }
+ break;
+
+ case IDM_INVITE:
+ {
+ CGroupchatInviteDlg *dlg = new CGroupchatInviteDlg( ppro, gch->pDest->ptszID );
+ dlg->Show();
+ break;
+ }
+
+ case IDM_CONFIG:
+ {
+ int iqId = ppro->SerialNext();
+ ppro->IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetMuc );
+
+ XmlNodeIq iq( _T("get"), iqId, gch->pDest->ptszID );
+ iq << XQUERY( xmlnsOwner );
+ ppro->m_ThreadInfo->send( iq );
+ break;
+ }
+ case IDM_BOOKMARKS:
+ {
+ JABBER_LIST_ITEM* item = ppro->ListGetItemPtr( LIST_BOOKMARK, gch->pDest->ptszID );
+ if ( item == NULL ) {
+ item = ppro->ListGetItemPtr( LIST_CHATROOM, gch->pDest->ptszID );
+ if (item != NULL) {
+ item->type = _T("conference");
+ HANDLE hContact = ppro->HContactFromJID( item->jid );
+ item->name = ( TCHAR* )CallService( MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR );
+ ppro->AddEditBookmark( item );
+ }
+ }
+ break;
+ }
+ case IDM_DESTROY:
+ mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s %s"), TranslateT( "Reason to destroy" ), gch->pDest->ptszID );
+ if ( !ppro->EnterString(szBuffer, SIZEOF(szBuffer), NULL, JES_MULTINE, "gcReason_" ))
+ break;
+
+ ppro->m_ThreadInfo->send(
+ XmlNodeIq( _T("set"), ppro->SerialNext(), gch->pDest->ptszID ) << XQUERY( xmlnsOwner )
+ << XCHILD( _T("destroy")) << XCHILD( _T("reason"), szBuffer ));
+
+ case IDM_LEAVE:
+ ppro->GcQuit( item, 0, NULL );
+ break;
+
+ case IDM_PRESENCE_ONLINE:
+ case IDM_PRESENCE_AWAY:
+ case IDM_PRESENCE_NA:
+ case IDM_PRESENCE_DND:
+ case IDM_PRESENCE_FREE4CHAT:
+ {
+ if ( HANDLE h = ppro->HContactFromJID( item->jid ))
+ ppro->OnMenuHandleDirectPresence( (WPARAM)h, 0, gch->dwData );
+ break;
+ }
+
+
+ case IDM_LINK0: case IDM_LINK1: case IDM_LINK2: case IDM_LINK3: case IDM_LINK4:
+ case IDM_LINK5: case IDM_LINK6: case IDM_LINK7: case IDM_LINK8: case IDM_LINK9:
+ {
+ unsigned idx = IDM_LINK0;
+ for (TCHAR *p = _tcsstr(item->itemResource.statusMessage, _T("http://")); p && *p; p = _tcsstr(p+1, _T("http://")))
+ {
+ if (idx == gch->dwData)
+ {
+ char *bufPtr, *url = mir_t2a(p);
+ for (bufPtr = url; *bufPtr && !isspace(*bufPtr); ++bufPtr) ;
+ *bufPtr++ = 0;
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)url);
+ mir_free(url);
+ break;
+ }
+
+ if (++idx > IDM_LINK9) break;
+ }
+
+ break;
+ }
+
+ case IDM_CPY_RJID:
+ JabberCopyText((HWND)CallService(MS_CLUI_GETHWND, 0, 0), item->jid);
+ break;
+ case IDM_CPY_TOPIC:
+ JabberCopyText((HWND)CallService(MS_CLUI_GETHWND, 0, 0), item->itemResource.statusMessage);
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Sends a private message to a chat user
+
+static void sttSendPrivateMessage( CJabberProto* ppro, JABBER_LIST_ITEM* item, const TCHAR* nick )
+{
+ TCHAR szFullJid[ JABBER_MAX_JID_LEN ];
+ mir_sntprintf( szFullJid, SIZEOF(szFullJid), _T("%s/%s"), item->jid, nick );
+ HANDLE hContact = ppro->DBCreateContact( szFullJid, NULL, TRUE, FALSE );
+ if ( hContact != NULL ) {
+ for ( int i=0; i < item->resourceCount; i++ ) {
+ if ( _tcsicmp( item->resource[i].resourceName, nick ) == 0 ) {
+ ppro->JSetWord( hContact, "Status", item->resource[i].status );
+ break;
+ } }
+
+ DBWriteContactSettingByte( hContact, "CList", "Hidden", 1 );
+ ppro->JSetStringT( hContact, "Nick", nick );
+ DBWriteContactSettingDword( hContact, "Ignore", "Mask1", 0 );
+ CallService( MS_MSG_SENDMESSAGE, ( WPARAM )hContact, 0 );
+} }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// General chat event processing hook
+
+int CJabberProto::JabberGcEventHook(WPARAM, LPARAM lParam)
+{
+ GCHOOK* gch = ( GCHOOK* )lParam;
+ if ( gch == NULL )
+ return 0;
+
+ if ( lstrcmpiA( gch->pDest->pszModule, m_szModuleName ))
+ return 0;
+
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_CHATROOM, gch->pDest->ptszID );
+ if ( item == NULL )
+ return 0;
+
+ switch ( gch->pDest->iType ) {
+ case GC_USER_MESSAGE:
+ if ( gch->pszText && lstrlen( gch->ptszText) > 0 ) {
+ trtrim( gch->ptszText );
+
+ if ( m_bJabberOnline ) {
+ TCHAR* buf = NEWTSTR_ALLOCA(gch->ptszText);
+ UnEscapeChatTags( buf );
+ m_ThreadInfo->send(
+ XmlNode( _T("message")) << XATTR( _T("to"), item->jid ) << XATTR( _T("type"), _T("groupchat"))
+ << XCHILD( _T("body"), buf ));
+ } }
+ break;
+
+ case GC_USER_PRIVMESS:
+ sttSendPrivateMessage( this, item, gch->ptszUID );
+ break;
+
+ case GC_USER_LOGMENU:
+ sttLogListHook( this, item, gch );
+ break;
+
+ case GC_USER_NICKLISTMENU:
+ sttNickListHook( this, item, gch );
+ break;
+
+ case GC_USER_CHANMGR:
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetMuc );
+ m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId, item->jid ) << XQUERY( xmlnsOwner ));
+ break;
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+void CJabberProto::AddMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* str , TCHAR* rsn)
+{
+ const TCHAR* field = ( jidListInfo->type == MUC_BANLIST || _tcschr(str,'@')) ? _T("jid") : _T("nick");
+ TCHAR* roomJid = jidListInfo->roomJid;
+ if ( jidListInfo->type == MUC_BANLIST ) {
+ AdminSetReason( roomJid, xmlnsAdmin, field, str, _T("affiliation"), _T("outcast"), rsn);
+ AdminGet( roomJid, xmlnsAdmin, _T("affiliation"), _T("outcast"), &CJabberProto::OnIqResultMucGetBanList);
+} }
+
+void CJabberProto::AddMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* str )
+{
+ const TCHAR* field = ( jidListInfo->type == MUC_BANLIST || _tcschr(str,'@')) ? _T("jid") : _T("nick");
+ TCHAR* roomJid = jidListInfo->roomJid;
+
+ switch (jidListInfo->type) {
+ case MUC_VOICELIST:
+ AdminSet( roomJid, xmlnsAdmin, field, str, _T("role"), _T("participant"));
+ AdminGet( roomJid, xmlnsAdmin, _T("role"), _T("participant"), &CJabberProto::OnIqResultMucGetVoiceList);
+ break;
+ case MUC_MEMBERLIST:
+ AdminSet( roomJid, xmlnsAdmin, field, str, _T("affiliation"), _T("member"));
+ AdminGet( roomJid, xmlnsAdmin, _T("affiliation"), _T("member"), &CJabberProto::OnIqResultMucGetMemberList);
+ break;
+ case MUC_MODERATORLIST:
+ AdminSet( roomJid, xmlnsAdmin, field, str, _T("role"), _T("moderator"));
+ AdminGet( roomJid, xmlnsAdmin, _T("role"), _T("moderator"), &CJabberProto::OnIqResultMucGetModeratorList);
+ break;
+ case MUC_BANLIST:
+ AdminSet( roomJid, xmlnsAdmin, field, str, _T("affiliation"), _T("outcast"));
+ AdminGet( roomJid, xmlnsAdmin, _T("affiliation"), _T("outcast"), &CJabberProto::OnIqResultMucGetBanList);
+ break;
+ case MUC_ADMINLIST:
+ AdminSet( roomJid, xmlnsAdmin, field, str, _T("affiliation"), _T("admin"));
+ AdminGet( roomJid, xmlnsAdmin, _T("affiliation"), _T("admin"), &CJabberProto::OnIqResultMucGetAdminList);
+ break;
+ case MUC_OWNERLIST:
+ AdminSet( roomJid, xmlnsAdmin, field, str, _T("affiliation"), _T("owner"));
+ AdminGet( roomJid, xmlnsAdmin, _T("affiliation"), _T("owner"), &CJabberProto::OnIqResultMucGetOwnerList);
+ break;
+} }
+
+void CJabberProto::DeleteMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* jid )
+{
+ TCHAR* roomJid = jidListInfo->roomJid;
+
+ switch ( jidListInfo->type ) {
+ case MUC_VOICELIST: // change role to visitor ( from participant )
+ AdminSet( roomJid, xmlnsAdmin, _T("jid"), jid, _T("role"), _T("visitor"));
+ break;
+ case MUC_BANLIST: // change affiliation to none ( from outcast )
+ case MUC_MEMBERLIST: // change affiliation to none ( from member )
+ AdminSet( roomJid, xmlnsAdmin, _T("jid"), jid, _T("affiliation"), _T("none"));
+ break;
+ case MUC_MODERATORLIST: // change role to participant ( from moderator )
+ AdminSet( roomJid, xmlnsAdmin, _T("jid"), jid, _T("role"), _T("participant"));
+ break;
+ case MUC_ADMINLIST: // change affiliation to member ( from admin )
+ AdminSet( roomJid, xmlnsAdmin, _T("jid"), jid, _T("affiliation"), _T("member"));
+ break;
+ case MUC_OWNERLIST: // change affiliation to admin ( from owner )
+ AdminSet( roomJid, xmlnsAdmin, _T("jid"), jid, _T("affiliation"), _T("admin"));
+ break;
+} }
diff --git a/protocols/JabberG/src/jabber_console.cpp b/protocols/JabberG/src/jabber_console.cpp new file mode 100644 index 0000000000..ca2c82b3e4 --- /dev/null +++ b/protocols/JabberG/src/jabber_console.cpp @@ -0,0 +1,716 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2007 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include <richedit.h>
+
+#define JCPF_IN 0x01UL
+#define JCPF_OUT 0x02UL
+#define JCPF_ERROR 0x04UL
+
+#define JCPF_TCHAR 0x00UL
+
+#define WM_CREATECONSOLE WM_USER+1000
+
+#ifndef SES_EXTENDBACKCOLOR
+#define SES_EXTENDBACKCOLOR 4
+#endif
+
+/* increment buffer with 1K steps */
+#define STRINGBUF_INCREMENT 1024
+
+struct StringBuf
+{
+ char *buf;
+ int size;
+ int offset;
+ int streamOffset;
+};
+
+static void sttAppendBufRaw(StringBuf *buf, const char *str);
+static void sttAppendBufW(StringBuf *buf, const WCHAR *str);
+#define sttAppendBufT(a,b) (sttAppendBufW((a),(b)))
+static void sttEmptyBuf(StringBuf *buf);
+
+#define RTF_HEADER \
+ "{\\rtf1\\ansi{\\colortbl;" \
+ "\\red128\\green0\\blue0;" \
+ "\\red0\\green0\\blue128;" \
+ "\\red245\\green255\\blue245;" \
+ "\\red245\\green245\\blue255;" \
+ "\\red128\\green128\\blue128;" \
+ "\\red255\\green235\\blue235;" \
+ "}"
+#define RTF_FOOTER "}"
+#define RTF_BEGINTAG "\\pard "
+#define RTF_INDENT_FMT "\\fi-100\\li%d "
+#define RTF_ENDTAG "\\par"
+#define RTF_BEGINTAGNAME "\\cf1\\b "
+#define RTF_ENDTAGNAME "\\cf0\\b0 "
+#define RTF_BEGINATTRNAME "\\cf2\\b "
+#define RTF_ENDATTRNAME "\\cf0\\b0 "
+#define RTF_BEGINATTRVAL "\\b0 "
+#define RTF_ENDATTRVAL ""
+#define RTF_BEGINTEXT "\\pard "
+#define RTF_TEXTINDENT_FMT "\\fi0\\li%d "
+#define RTF_ENDTEXT "\\par"
+#define RTF_BEGINPLAINXML "\\pard\\fi0\\li100\\highlight6\\cf0 "
+#define RTF_ENDPLAINXML "\\par"
+#define RTF_SEPARATOR "\\sl-1\\slmult0\\highlight5\\cf5\\-\\par\\sl0"
+
+static void sttRtfAppendXml(StringBuf *buf, HXML node, DWORD flags, int indent);
+
+void CJabberProto::OnConsoleProcessXml(HXML node, DWORD flags)
+{
+ if ( node && m_pDlgConsole ) {
+ if ( xmlGetName( node )) {
+ if ( FilterXml( node, flags )) {
+ StringBuf buf = {0};
+ sttAppendBufRaw(&buf, RTF_HEADER);
+ sttRtfAppendXml(&buf, node, flags, 1);
+ sttAppendBufRaw(&buf, RTF_SEPARATOR);
+ sttAppendBufRaw(&buf, RTF_FOOTER);
+ SendMessage(m_pDlgConsole->GetHwnd(), WM_JABBER_REFRESH, 0, (LPARAM)&buf);
+ sttEmptyBuf(&buf);
+ }
+ }
+ else {
+ for ( int i = 0; i < xmlGetChildCount( node ); i++ )
+ OnConsoleProcessXml( xmlGetChild( node, i), flags );
+ }
+ }
+}
+
+bool CJabberProto::RecursiveCheckFilter(HXML node, DWORD flags)
+{
+ int i;
+
+ for (i = 0; i < xmlGetAttrCount(node); ++i)
+ {
+ if ( JabberStrIStr( xmlGetAttr( node,i ), m_filterInfo.pattern ))
+ return true;
+ }
+
+ for (i = 0; i < xmlGetChildCount( node ); ++i) {
+ if (RecursiveCheckFilter( xmlGetChild( node, i ), flags))
+ return true;
+ }
+
+ return false;
+}
+
+bool CJabberProto::FilterXml(HXML node, DWORD flags)
+{
+ if (!m_filterInfo.msg && !lstrcmp(xmlGetName( node ), _T("message"))) return false;
+ if (!m_filterInfo.presence && !lstrcmp(xmlGetName( node ), _T("presence"))) return false;
+ if (!m_filterInfo.iq && !lstrcmp(xmlGetName( node ), _T("iq"))) return false;
+ if (m_filterInfo.type == TFilterInfo::T_OFF) return true;
+
+ bool result = false;
+ EnterCriticalSection(&m_filterInfo.csPatternLock);
+
+ switch (m_filterInfo.type)
+ {
+ case TFilterInfo::T_JID:
+ {
+ const TCHAR *attrValue = xmlGetAttrValue( node,(flags&JCPF_OUT)?_T("to"):_T("from"));
+ if (!attrValue) break;
+
+ result = JabberStrIStr(attrValue, m_filterInfo.pattern) ? true : false;
+ break;
+ }
+ case TFilterInfo::T_XMLNS:
+ {
+ if ( !xmlGetChildCount( node )) break;
+
+ const TCHAR *attrValue = xmlGetAttrValue( xmlGetChild( node, 0 ), _T("xmlns"));
+ if ( !attrValue )
+ break;
+
+ result = JabberStrIStr(attrValue, m_filterInfo.pattern) ? true : false;
+ break;
+ }
+
+ case TFilterInfo::T_ANY:
+ {
+ result = RecursiveCheckFilter(node, flags);
+ break;
+ }
+ }
+
+ LeaveCriticalSection(&m_filterInfo.csPatternLock);
+ return result;
+}
+
+static void sttAppendBufRaw(StringBuf *buf, const char *str)
+{
+ if (!str) return;
+
+ int length = lstrlenA(str);
+ if (buf->size - buf->offset < length+1)
+ {
+ buf->size += (length + STRINGBUF_INCREMENT);
+ buf->buf = (char *)mir_realloc(buf->buf, buf->size);
+ }
+ lstrcpyA(buf->buf + buf->offset, str);
+ buf->offset += length;
+}
+
+static void sttAppendBufW(StringBuf *buf, const WCHAR *str)
+{
+ char tmp[32];
+
+ if (!str) return;
+
+ sttAppendBufRaw(buf, "{\\uc1 ");
+ for (const WCHAR *p = str; *p; ++p)
+ {
+ if ((*p == '\\') || (*p == '{') || (*p == '}'))
+ {
+ tmp[0] = '\\';
+ tmp[1] = (char)*p;
+ tmp[2] = 0;
+ } else
+ if (*p < 128)
+ {
+ tmp[0] = (char)*p;
+ tmp[1] = 0;
+ } else
+ {
+ mir_snprintf(tmp, sizeof(tmp), "\\u%d ?", (int)*p);
+ }
+ sttAppendBufRaw(buf, tmp);
+ }
+ sttAppendBufRaw(buf, "}");
+}
+
+static void sttEmptyBuf(StringBuf *buf)
+{
+ if (buf->buf) mir_free(buf->buf);
+ buf->buf = 0;
+ buf->size = 0;
+ buf->offset = 0;
+}
+
+static void sttRtfAppendXml(StringBuf *buf, HXML node, DWORD flags, int indent)
+{
+ int i;
+ char *indentLevel = (char *)mir_alloc(128);
+ mir_snprintf(indentLevel, 128, RTF_INDENT_FMT,
+ (int)(indent*200)
+ );
+
+ sttAppendBufRaw(buf, RTF_BEGINTAG);
+ sttAppendBufRaw(buf, indentLevel);
+ if (flags&JCPF_IN) sttAppendBufRaw(buf, "\\highlight3 ");
+ if (flags&JCPF_OUT) sttAppendBufRaw(buf, "\\highlight4 ");
+ sttAppendBufRaw(buf, "<");
+ sttAppendBufRaw(buf, RTF_BEGINTAGNAME);
+ sttAppendBufW(buf, (TCHAR*)xmlGetName( node ));
+ sttAppendBufRaw(buf, RTF_ENDTAGNAME);
+
+ for (i = 0; i < xmlGetAttrCount( node); i++)
+ {
+ TCHAR* attr = ( TCHAR* )xmlGetAttrName( node, i );
+ sttAppendBufRaw(buf, " ");
+ sttAppendBufRaw(buf, RTF_BEGINATTRNAME);
+ sttAppendBufW(buf, attr);
+ sttAppendBufRaw(buf, RTF_ENDATTRNAME);
+ sttAppendBufRaw(buf, "=\"");
+ sttAppendBufRaw(buf, RTF_BEGINATTRVAL);
+ sttAppendBufT(buf, ( TCHAR* )xmlGetAttr( node, i));
+ sttAppendBufRaw(buf, "\"");
+ sttAppendBufRaw(buf, RTF_ENDATTRVAL);
+ }
+
+ if ( xmlGetChild( node ) || xmlGetText( node ))
+ {
+ sttAppendBufRaw(buf, ">");
+ if ( xmlGetChild( node ))
+ sttAppendBufRaw(buf, RTF_ENDTAG);
+ }
+
+ if (xmlGetText( node ))
+ {
+ if ( xmlGetChildCount( node ))
+ {
+ sttAppendBufRaw(buf, RTF_BEGINTEXT);
+ char *indentTextLevel = (char *)mir_alloc(128);
+ mir_snprintf( indentTextLevel, 128, RTF_TEXTINDENT_FMT, (int)(( indent + 1) * 200 ));
+ sttAppendBufRaw(buf, indentTextLevel);
+ mir_free(indentTextLevel);
+ }
+
+ sttAppendBufT(buf, xmlGetText( node ));
+ if ( xmlGetChild( node ))
+ sttAppendBufRaw(buf, RTF_ENDTEXT);
+ }
+
+ for (i = 0; i < xmlGetChildCount( node ) ; ++i)
+ sttRtfAppendXml(buf, xmlGetChild( node ,i), flags & ~(JCPF_IN|JCPF_OUT), indent+1);
+
+ if (xmlGetChildCount( node ) || xmlGetText( node ))
+ {
+ sttAppendBufRaw(buf, RTF_BEGINTAG);
+ sttAppendBufRaw(buf, indentLevel);
+ sttAppendBufRaw(buf, "</");
+ sttAppendBufRaw(buf, RTF_BEGINTAGNAME);
+ sttAppendBufT(buf, xmlGetName( node ));
+ sttAppendBufRaw(buf, RTF_ENDTAGNAME);
+ sttAppendBufRaw(buf, ">");
+ } else
+ {
+ sttAppendBufRaw(buf, " />");
+ }
+
+ sttAppendBufRaw(buf, RTF_ENDTAG);
+ mir_free(indentLevel);
+}
+
+DWORD CALLBACK sttStreamInCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+ StringBuf *buf = (StringBuf *)dwCookie;
+ *pcb = 0;
+
+ if (buf->streamOffset < buf->offset)
+ {
+ *pcb = min(cb, buf->offset - buf->streamOffset);
+ memcpy(pbBuff, buf->buf + buf->streamOffset, *pcb);
+ buf->streamOffset += *pcb;
+ }
+
+ return 0;
+}
+
+static void sttJabberConsoleRebuildStrings(CJabberProto* ppro, HWND hwndCombo)
+{
+ int i;
+ JABBER_LIST_ITEM *item = NULL;
+
+ int len = GetWindowTextLength(hwndCombo) + 1;
+ TCHAR *buf = (TCHAR *)_alloca(len * sizeof(TCHAR));
+ GetWindowText(hwndCombo, buf, len);
+
+ SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
+
+ for (i = 0; g_JabberFeatCapPairs[i].szFeature; ++i)
+ SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)g_JabberFeatCapPairs[i].szFeature);
+ for (i = 0; g_JabberFeatCapPairsExt[i].szFeature; ++i)
+ SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)g_JabberFeatCapPairsExt[i].szFeature);
+
+ LISTFOREACH_NODEF(i, ppro, LIST_ROSTER)
+ if (item = ppro->ListGetItemPtrFromIndex(i))
+ SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)item->jid);
+ LISTFOREACH_NODEF(i, ppro, LIST_CHATROOM)
+ if (item = ppro->ListGetItemPtrFromIndex(i))
+ SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)item->jid);
+
+ SetWindowText(hwndCombo, buf);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CJabberDlgConsole class
+static struct
+{
+ int type;
+ TCHAR *title;
+ char *icon;
+} filter_modes[] =
+{
+ { TFilterInfo::T_JID, _T("JID"), "main" },
+ { TFilterInfo::T_XMLNS, _T("xmlns"), "xmlconsole" },
+ { TFilterInfo::T_ANY, _T("all attributes"), "sd_filter_apply" },
+ { TFilterInfo::T_OFF, _T("disabled"), "sd_filter_reset" },
+};
+
+class CJabberDlgConsole: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+public:
+ CJabberDlgConsole(CJabberProto *proto);
+
+protected:
+ void OnInitDialog();
+ void OnClose();
+ void OnDestroy();
+ void OnProtoRefresh(WPARAM wParam, LPARAM lParam);
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
+ int Resizer(UTILRESIZECONTROL *urc);
+};
+
+CJabberDlgConsole::CJabberDlgConsole(CJabberProto *proto):
+ CJabberDlgBase(proto, IDD_CONSOLE, NULL)
+{
+}
+
+void CJabberDlgConsole::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+
+ int i;
+
+ WindowSetIcon( m_hwnd, m_proto, "xmlconsole" );
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR);
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, EM_EXLIMITTEXT, 0, 0x80000000);
+
+ m_proto->m_filterInfo.msg = DBGetContactSettingByte(NULL, m_proto->m_szModuleName, "consoleWnd_msg", TRUE);
+ m_proto->m_filterInfo.presence = DBGetContactSettingByte(NULL, m_proto->m_szModuleName, "consoleWnd_presence", TRUE);
+ m_proto->m_filterInfo.iq = DBGetContactSettingByte(NULL, m_proto->m_szModuleName, "consoleWnd_iq", TRUE);
+ m_proto->m_filterInfo.type = (TFilterInfo::Type)DBGetContactSettingByte(NULL, m_proto->m_szModuleName, "consoleWnd_ftype", TFilterInfo::T_OFF);
+
+ DBVARIANT dbv;
+ *m_proto->m_filterInfo.pattern = 0;
+ if ( !m_proto->JGetStringT(NULL, "consoleWnd_fpattern", &dbv)) {
+ lstrcpyn(m_proto->m_filterInfo.pattern, dbv.ptszVal, SIZEOF(m_proto->m_filterInfo.pattern));
+ JFreeVariant(&dbv);
+ }
+
+ sttJabberConsoleRebuildStrings(m_proto, GetDlgItem(m_hwnd, IDC_CB_FILTER));
+ SetWindowText(GetDlgItem(m_hwnd, IDC_CB_FILTER), m_proto->m_filterInfo.pattern);
+
+ static struct
+ {
+ int idc;
+ char *title;
+ char *icon;
+ bool push;
+ BOOL pushed;
+ } buttons[] =
+ {
+ {IDC_BTN_MSG, "Messages", "pl_msg_allow", true, m_proto->m_filterInfo.msg},
+ {IDC_BTN_PRESENCE, "Presences", "pl_prin_allow", true, m_proto->m_filterInfo.presence},
+ {IDC_BTN_IQ, "Queries", "pl_iq_allow", true, m_proto->m_filterInfo.iq},
+ {IDC_BTN_FILTER, "Filter mode", "sd_filter_apply", true, FALSE},
+ {IDC_BTN_FILTER_REFRESH, "Refresh list", "sd_nav_refresh", false, FALSE},
+ };
+ for (i = 0; i < SIZEOF(buttons); ++i)
+ {
+ SendDlgItemMessage(m_hwnd, buttons[i].idc, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_proto->LoadIconEx(buttons[i].icon));
+ SendDlgItemMessage(m_hwnd, buttons[i].idc, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(m_hwnd, buttons[i].idc, BUTTONADDTOOLTIP, (WPARAM)buttons[i].title, 0);
+ if (buttons[i].push) SendDlgItemMessage(m_hwnd, buttons[i].idc, BUTTONSETASPUSHBTN, TRUE, 0);
+ if (buttons[i].pushed) CheckDlgButton(m_hwnd, buttons[i].idc, TRUE);
+ }
+
+ for (i = 0; i < SIZEOF(filter_modes); ++i)
+ if (filter_modes[i].type == m_proto->m_filterInfo.type)
+ {
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( m_hwnd, IDC_BTN_FILTER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_proto->LoadIconEx(filter_modes[i].icon)));
+ SendDlgItemMessage(m_hwnd, IDC_BTN_FILTER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_proto->LoadIconEx(filter_modes[i].icon));
+ break;
+ }
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CB_FILTER), (m_proto->m_filterInfo.type == TFilterInfo::T_OFF) ? FALSE : TRUE);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BTN_FILTER_REFRESH), (m_proto->m_filterInfo.type == TFilterInfo::T_OFF) ? FALSE : TRUE);
+
+ Utils_RestoreWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "consoleWnd_");
+}
+
+void CJabberDlgConsole::OnClose()
+{
+ DBWriteContactSettingByte(NULL, m_proto->m_szModuleName, "consoleWnd_msg", m_proto->m_filterInfo.msg);
+ DBWriteContactSettingByte(NULL, m_proto->m_szModuleName, "consoleWnd_presence", m_proto->m_filterInfo.presence);
+ DBWriteContactSettingByte(NULL, m_proto->m_szModuleName, "consoleWnd_iq", m_proto->m_filterInfo.iq);
+ DBWriteContactSettingByte(NULL, m_proto->m_szModuleName, "consoleWnd_ftype", m_proto->m_filterInfo.type);
+ m_proto->JSetStringT(NULL, "consoleWnd_fpattern", m_proto->m_filterInfo.pattern);
+
+ Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "consoleWnd_");
+ DestroyWindow(m_hwnd);
+ CSuper::OnClose();
+}
+
+void CJabberDlgConsole::OnDestroy()
+{
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( m_hwnd, IDC_BTN_MSG, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( m_hwnd, IDC_BTN_PRESENCE, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( m_hwnd, IDC_BTN_IQ, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( m_hwnd, IDC_BTN_FILTER, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( m_hwnd, IDC_BTN_FILTER_REFRESH, BM_SETIMAGE, IMAGE_ICON, 0 ));
+
+ m_proto->m_pDlgConsole = NULL;
+ CSuper::OnDestroy();
+}
+
+void CJabberDlgConsole::OnProtoRefresh(WPARAM, LPARAM lParam)
+{
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, WM_SETREDRAW, FALSE, 0);
+
+ StringBuf *buf = (StringBuf *)lParam;
+ buf->streamOffset = 0;
+
+ EDITSTREAM es = {0};
+ es.dwCookie = (DWORD_PTR)buf;
+ es.pfnCallback = sttStreamInCallback;
+
+ SCROLLINFO si = {0};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ GetScrollInfo(GetDlgItem(m_hwnd, IDC_CONSOLE), SB_VERT, &si);
+
+ CHARRANGE oldSel, sel;
+ POINT ptScroll;
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, EM_GETSCROLLPOS, 0, (LPARAM)&ptScroll);
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, EM_EXGETSEL, 0, (LPARAM)&oldSel);
+ sel.cpMin = sel.cpMax = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_CONSOLE));
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, EM_EXSETSEL, 0, (LPARAM)&sel);
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, EM_STREAMIN, SF_RTF|SFF_SELECTION, (LPARAM)&es);
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, EM_EXSETSEL, 0, (LPARAM)&oldSel);
+
+ // magic expression from tabsrmm :)
+ if ((UINT)si.nPos >= (UINT)si.nMax-si.nPage-5 || si.nMax-si.nMin-si.nPage < 50)
+ {
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0);
+ sel.cpMin = sel.cpMax = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_CONSOLE));
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, EM_EXSETSEL, 0, (LPARAM)&sel);
+ } else
+ {
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, EM_SETSCROLLPOS, 0, (LPARAM)&ptScroll);
+ }
+
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLE, WM_SETREDRAW, TRUE, 0);
+ InvalidateRect(GetDlgItem(m_hwnd, IDC_CONSOLE), NULL, FALSE);
+}
+
+int CJabberDlgConsole::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch ( urc->wId )
+ {
+ case IDC_CONSOLE:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ case IDC_CONSOLEIN:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM;
+
+ case IDC_BTN_MSG:
+ case IDC_BTN_PRESENCE:
+ case IDC_BTN_IQ:
+ case IDC_BTN_FILTER:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
+
+ case IDC_CB_FILTER:
+ {
+ RECT rc;
+ GetWindowRect(GetDlgItem(m_hwnd, urc->wId), &rc);
+ urc->rcItem.right += (urc->dlgNewSize.cx - urc->dlgOriginalSize.cx);
+ urc->rcItem.top += (urc->dlgNewSize.cy - urc->dlgOriginalSize.cy);
+ urc->rcItem.bottom = urc->rcItem.top + rc.bottom - rc.top;
+ return 0;
+ }
+
+ case IDC_RESET:
+ case IDOK:
+ case IDC_BTN_FILTER_REFRESH:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ }
+ return CSuper::Resizer(urc);
+}
+
+INT_PTR CJabberDlgConsole::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch ( msg )
+ {
+ case WM_GETMINMAXINFO:
+ {
+ LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
+ lpmmi->ptMinTrackSize.x = 300;
+ lpmmi->ptMinTrackSize.y = 400;
+ return 0;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ if (!m_proto->m_bJabberOnline)
+ {
+ MessageBox(m_hwnd, TranslateT("Can't send data while you are offline."), TranslateT("Jabber Error"), MB_ICONSTOP|MB_OK);
+ return TRUE;
+ }
+
+ int length = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_CONSOLEIN)) + 1;
+ TCHAR *textToSend = (TCHAR *)mir_alloc(length * sizeof(TCHAR));
+ GetWindowText(GetDlgItem(m_hwnd, IDC_CONSOLEIN), textToSend, length);
+
+ int bytesProcessed = 0;
+ XmlNode xmlTmp( textToSend, &bytesProcessed, NULL);
+ if (xmlTmp)
+ {
+ m_proto->m_ThreadInfo->send( xmlTmp );
+ }
+ else
+ {
+ StringBuf buf = {0};
+ sttAppendBufRaw(&buf, RTF_HEADER);
+ sttAppendBufRaw(&buf, RTF_BEGINPLAINXML);
+ sttAppendBufT(&buf, TranslateT("Outgoing XML parsing error"));
+ sttAppendBufRaw(&buf, RTF_ENDPLAINXML);
+ sttAppendBufRaw(&buf, RTF_SEPARATOR);
+ sttAppendBufRaw(&buf, RTF_FOOTER);
+ SendMessage(m_hwnd, WM_JABBER_REFRESH, 0, (LPARAM)&buf);
+ sttEmptyBuf(&buf);
+ }
+
+ mir_free(textToSend);
+
+ SendDlgItemMessage(m_hwnd, IDC_CONSOLEIN, WM_SETTEXT, 0, (LPARAM)_T(""));
+ return TRUE;
+ }
+
+ case IDC_RESET:
+ {
+ SetDlgItemText(m_hwnd, IDC_CONSOLE, _T(""));
+ break;
+ }
+
+ case IDC_BTN_MSG:
+ case IDC_BTN_PRESENCE:
+ case IDC_BTN_IQ:
+ {
+ m_proto->m_filterInfo.msg = IsDlgButtonChecked(m_hwnd, IDC_BTN_MSG);
+ m_proto->m_filterInfo.presence = IsDlgButtonChecked(m_hwnd, IDC_BTN_PRESENCE);
+ m_proto->m_filterInfo.iq = IsDlgButtonChecked(m_hwnd, IDC_BTN_IQ);
+ break;
+ }
+
+ case IDC_BTN_FILTER_REFRESH:
+ {
+ sttJabberConsoleRebuildStrings(m_proto, GetDlgItem(m_hwnd, IDC_CB_FILTER));
+ break;
+ }
+
+ case IDC_BTN_FILTER:
+ {
+ int i;
+ HMENU hMenu = CreatePopupMenu();
+ for (i = 0; i < SIZEOF(filter_modes); ++i)
+ {
+ AppendMenu(hMenu,
+ MF_STRING | ((filter_modes[i].type == m_proto->m_filterInfo.type) ? MF_CHECKED : 0),
+ filter_modes[i].type+1, TranslateTS(filter_modes[i].title));
+ }
+ RECT rc; GetWindowRect(GetDlgItem(m_hwnd, IDC_BTN_FILTER), &rc);
+ CheckDlgButton(m_hwnd, IDC_BTN_FILTER, TRUE);
+ int res = TrackPopupMenu(hMenu, TPM_RETURNCMD|TPM_BOTTOMALIGN, rc.left, rc.top, 0, m_hwnd, NULL);
+ CheckDlgButton(m_hwnd, IDC_BTN_FILTER, FALSE);
+ DestroyMenu(hMenu);
+
+ if (res)
+ {
+ m_proto->m_filterInfo.type = (TFilterInfo::Type)(res - 1);
+ for (i = 0; i < SIZEOF(filter_modes); ++i)
+ if (filter_modes[i].type == m_proto->m_filterInfo.type)
+ {
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( m_hwnd, IDC_BTN_FILTER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_proto->LoadIconEx(filter_modes[i].icon)));
+ break;
+ }
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CB_FILTER), (m_proto->m_filterInfo.type == TFilterInfo::T_OFF) ? FALSE : TRUE);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BTN_FILTER_REFRESH), (m_proto->m_filterInfo.type == TFilterInfo::T_OFF) ? FALSE : TRUE);
+ }
+
+ break;
+ }
+
+ case IDC_CB_FILTER:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ int idx = SendDlgItemMessage(m_hwnd, IDC_CB_FILTER, CB_GETCURSEL, 0, 0);
+ int len = SendDlgItemMessage(m_hwnd, IDC_CB_FILTER, CB_GETLBTEXTLEN, idx, 0) + 1;
+
+ EnterCriticalSection(&m_proto->m_filterInfo.csPatternLock);
+ if (len > SIZEOF(m_proto->m_filterInfo.pattern))
+ {
+ TCHAR *buf = (TCHAR *)_alloca(len * sizeof(TCHAR));
+ SendDlgItemMessage(m_hwnd, IDC_CB_FILTER, CB_GETLBTEXT, idx, (LPARAM)buf);
+ lstrcpyn(m_proto->m_filterInfo.pattern, buf, SIZEOF(m_proto->m_filterInfo.pattern));
+ } else
+ {
+ SendDlgItemMessage(m_hwnd, IDC_CB_FILTER, CB_GETLBTEXT, idx, (LPARAM)m_proto->m_filterInfo.pattern);
+ }
+ LeaveCriticalSection(&m_proto->m_filterInfo.csPatternLock);
+ } else
+ if (HIWORD(wParam) == CBN_EDITCHANGE)
+ {
+ EnterCriticalSection(&m_proto->m_filterInfo.csPatternLock);
+ GetWindowText(GetDlgItem(m_hwnd, IDC_CB_FILTER), m_proto->m_filterInfo.pattern, SIZEOF(m_proto->m_filterInfo.pattern));
+ LeaveCriticalSection(&m_proto->m_filterInfo.csPatternLock);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return CSuper::DlgProc(msg, wParam, lParam);
+}
+
+void __cdecl CJabberProto::ConsoleThread( void* )
+{
+ MSG msg;
+ while ( GetMessage(&msg, NULL, 0, 0 )) {
+ if ( msg.message == WM_CREATECONSOLE ) {
+ m_pDlgConsole = new CJabberDlgConsole( this );
+ m_pDlgConsole->Show();
+ continue;
+ }
+
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ m_dwConsoleThreadId = 0;
+}
+
+void CJabberProto::ConsoleInit()
+{
+ LoadLibraryA("riched20.dll");
+ InitializeCriticalSection(&m_filterInfo.csPatternLock);
+ m_hThreadConsole = JForkThreadEx( &CJabberProto::ConsoleThread, 0, &m_dwConsoleThreadId );
+}
+
+void CJabberProto::ConsoleUninit()
+{
+ if ( m_hThreadConsole ) {
+ PostThreadMessage(m_dwConsoleThreadId, WM_QUIT, 0, 0);
+ if ( WaitForSingleObject( m_hThreadConsole, 5000 ) == WAIT_TIMEOUT) {
+ TerminateThread( m_hThreadConsole, 0 );
+ }
+ CloseHandle( m_hThreadConsole );
+ m_hThreadConsole = NULL;
+ }
+
+ m_filterInfo.iq = m_filterInfo.msg = m_filterInfo.presence = FALSE;
+ m_filterInfo.type = TFilterInfo::T_OFF;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleConsole(WPARAM, LPARAM)
+{
+ if ( m_pDlgConsole )
+ SetForegroundWindow( m_pDlgConsole->GetHwnd());
+ else
+ if ( m_hThreadConsole )
+ PostThreadMessage( m_dwConsoleThreadId, WM_CREATECONSOLE, 0, 0 );
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber_db_utils.h b/protocols/JabberG/src/jabber_db_utils.h new file mode 100644 index 0000000000..ae9a6c9781 --- /dev/null +++ b/protocols/JabberG/src/jabber_db_utils.h @@ -0,0 +1,279 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007-09 Maxim Mluhov
+Copyright ( C ) 2007-09 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jabber_db_utils_h__
+#define __jabber_db_utils_h__
+
+template<typename Int> struct CMIntTraits { static __forceinline bool IsSigned() { return false; } };
+template<> struct CMIntTraits<signed char> { static __forceinline bool IsSigned() { return true; } };
+template<> struct CMIntTraits<signed short> { static __forceinline bool IsSigned() { return true; } };
+template<> struct CMIntTraits<signed long> { static __forceinline bool IsSigned() { return true; } };
+
+template<int Size>
+struct CMDBTraits
+{
+};
+
+template<>
+struct CMDBTraits<1>
+{
+ typedef BYTE DBType;
+ enum { DBTypeId = DBVT_BYTE };
+ static __forceinline DBType Get(char *szModule, char *szSetting, DBType value)
+ {
+ return DBGetContactSettingByte(NULL, szModule, szSetting, value);
+ }
+ static __forceinline void Set(char *szModule, char *szSetting, DBType value)
+ {
+ DBWriteContactSettingByte(NULL, szModule, szSetting, value);
+ }
+};
+
+template<>
+struct CMDBTraits<2>
+{
+ typedef WORD DBType;
+ enum { DBTypeId = DBVT_WORD };
+ static __forceinline DBType Get(char *szModule, char *szSetting, DBType value)
+ {
+ return DBGetContactSettingWord(NULL, szModule, szSetting, value);
+ }
+ static __forceinline void Set(char *szModule, char *szSetting, DBType value)
+ {
+ DBWriteContactSettingWord(NULL, szModule, szSetting, value);
+ }
+};
+
+template<>
+struct CMDBTraits<4>
+{
+ typedef DWORD DBType;
+ enum { DBTypeId = DBVT_DWORD };
+ static __forceinline BYTE GetDBType()
+ {
+ return DBVT_DWORD;
+ }
+ static __forceinline DBType Get(char *szModule, char *szSetting, DBType value)
+ {
+ return DBGetContactSettingDword(NULL, szModule, szSetting, value);
+ }
+ static __forceinline void Set(char *szModule, char *szSetting, DBType value)
+ {
+ DBWriteContactSettingDword(NULL, szModule, szSetting, value);
+ }
+};
+
+class CMOptionBase
+{
+public:
+ BYTE GetDBType() { return m_dbType; }
+ char *GetDBModuleName() { return m_proto->m_szModuleName; }
+ char *GetDBSettingName() { return m_szSetting; }
+
+protected:
+ CMOptionBase(PROTO_INTERFACE *proto, char *szSetting, BYTE dbType): m_proto(proto), m_szSetting(szSetting), m_dbType(dbType) {}
+
+ PROTO_INTERFACE *m_proto;
+ char *m_szSetting;
+ BYTE m_dbType;
+
+private:
+ CMOptionBase(const CMOptionBase &) {}
+ void operator= (const CMOptionBase &) {}
+};
+
+template<class T>
+class CMOption: public CMOptionBase
+{
+public:
+ typedef T Type;
+
+ __forceinline CMOption(PROTO_INTERFACE *proto, char *szSetting, Type defValue):
+ CMOptionBase(proto, szSetting, CMDBTraits<sizeof(T)>::DBTypeId), m_default(defValue) {}
+
+ __forceinline operator Type()
+ {
+ return (Type)CMDBTraits<sizeof(Type)>::Get(m_proto->m_szModuleName, m_szSetting, m_default);
+ }
+ __forceinline Type operator= (Type value)
+ {
+ CMDBTraits<sizeof(Type)>::Set(m_proto->m_szModuleName, m_szSetting, (CMDBTraits<sizeof(Type)>::DBType)value);
+ return value;
+ }
+
+private:
+ Type m_default;
+
+ CMOption(const CMOption &): CMOptionBase(NULL, NULL, DBVT_DELETED) {}
+ void operator= (const CMOption &) {}
+};
+
+template<>
+class CMOption<CMString>: public CMOptionBase
+{
+public:
+ typedef const TCHAR *Type;
+
+ __forceinline CMOption(PROTO_INTERFACE *proto, char *szSetting, Type defValue, bool crypt=false):
+ CMOptionBase(proto, szSetting, DBVT_TCHAR), m_default(defValue), m_crypt(crypt) {}
+
+ __forceinline operator CMString()
+ {
+ CMString result;
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(NULL, m_proto->m_szModuleName, m_szSetting, &dbv))
+ {
+ result = dbv.ptszVal;
+ DBFreeVariant(&dbv);
+ }
+ return result;
+ }
+ __forceinline Type operator= (Type value)
+ {
+ DBWriteContactSettingTString(NULL, m_proto->m_szModuleName, m_szSetting, value);
+ return value;
+ }
+
+private:
+ Type m_default;
+ bool m_crypt;
+
+ CMOption(const CMOption &): CMOptionBase(NULL, NULL, DBVT_DELETED) {}
+ void operator= (const CMOption &) {}
+};
+
+struct CJabberOptions
+{
+ CMOption<BYTE> AllowVersionRequests;
+ CMOption<BYTE> AcceptHttpAuth;
+ CMOption<BYTE> AddRoster2Bookmarks;
+ CMOption<BYTE> AutoAcceptAuthorization;
+ CMOption<BYTE> AutoAcceptMUC;
+ CMOption<BYTE> AutoAdd;
+ CMOption<BYTE> AutoJoinBookmarks;
+ CMOption<BYTE> AutoJoinConferences;
+ CMOption<BYTE> AutoJoinHidden;
+ CMOption<BYTE> AvatarType;
+ CMOption<BYTE> BsDirect;
+ CMOption<BYTE> BsDirectManual;
+ CMOption<BYTE> BsOnlyIBB;
+ CMOption<BYTE> BsProxyManual;
+ CMOption<BYTE> Disable3920auth;
+ CMOption<BYTE> DisableFrame;
+ CMOption<BYTE> EnableAvatars;
+ CMOption<BYTE> EnableRemoteControl;
+ CMOption<BYTE> EnableUserActivity;
+ CMOption<BYTE> EnableUserMood;
+ CMOption<BYTE> EnableUserTune;
+ CMOption<BYTE> EnableZlib;
+ CMOption<BYTE> ExtendedSearch;
+ CMOption<BYTE> FixIncorrectTimestamps;
+ CMOption<BYTE> GcLogAffiliations;
+ CMOption<BYTE> GcLogBans;
+ CMOption<BYTE> GcLogConfig;
+ CMOption<BYTE> GcLogRoles;
+ CMOption<BYTE> GcLogStatuses;
+ CMOption<BYTE> GcLogChatHistory;
+ CMOption<BYTE> HostNameAsResource;
+ CMOption<BYTE> IgnoreMUCInvites;
+ CMOption<BYTE> KeepAlive;
+ CMOption<BYTE> LogChatstates;
+ CMOption<BYTE> LogPresence;
+ CMOption<BYTE> LogPresenceErrors;
+ CMOption<BYTE> ManualConnect;
+ CMOption<BYTE> MsgAck;
+ CMOption<BYTE> RosterSync;
+ CMOption<BYTE> SavePassword;
+ CMOption<BYTE> UseDomainLogin;
+ CMOption<BYTE> ShowForeignResourceInMirVer;
+ CMOption<BYTE> ShowOSVersion;
+ CMOption<BYTE> ShowTransport;
+ CMOption<BYTE> UseSSL;
+ CMOption<BYTE> UseTLS;
+ CMOption<BYTE> AcceptNotes;
+ CMOption<BYTE> AutosaveNotes;
+ CMOption<BYTE> RcMarkMessagesAsRead;
+ CMOption<DWORD> ConnectionKeepAliveInterval;
+ CMOption<DWORD> ConnectionKeepAliveTimeout;
+ CMOption<BYTE> ProcessXMPPLinks;
+ CMOption<BYTE> IgnoreRosterGroups;
+
+ CJabberOptions(PROTO_INTERFACE *proto):
+ BsDirect(proto, "BsDirect", TRUE),
+ AllowVersionRequests(proto, "AllowVersionRequests", TRUE),
+ AcceptHttpAuth(proto, "AcceptHttpAuth", TRUE),
+ AddRoster2Bookmarks(proto, "AddRoster2Bookmarks", TRUE),
+ AutoAcceptAuthorization(proto, "AutoAcceptAuthorization", FALSE),
+ AutoAcceptMUC(proto, "AutoAcceptMUC", FALSE),
+ AutoAdd(proto, "AutoAdd", TRUE),
+ AutoJoinBookmarks(proto, "AutoJoinBookmarks", TRUE),
+ AutoJoinConferences(proto, "AutoJoinConferences", 0),
+ AutoJoinHidden(proto, "AutoJoinHidden", TRUE),
+ AvatarType(proto, "AvatarType", PA_FORMAT_UNKNOWN),
+ BsDirectManual(proto, "BsDirectManual", FALSE),
+ BsOnlyIBB(proto, "BsOnlyIBB", FALSE),
+ BsProxyManual(proto, "BsProxyManual", FALSE),
+ Disable3920auth(proto, "Disable3920auth", FALSE),
+ DisableFrame(proto, "DisableFrame", TRUE),
+ EnableAvatars(proto, "EnableAvatars", TRUE),
+ EnableRemoteControl(proto, "EnableRemoteControl", FALSE),
+ EnableUserActivity(proto, "EnableUserActivity", TRUE),
+ EnableUserMood(proto, "EnableUserMood", TRUE),
+ EnableUserTune(proto, "EnableUserTune", FALSE),
+ EnableZlib(proto, "EnableZlib", TRUE),
+ ExtendedSearch(proto, "ExtendedSearch", TRUE),
+ FixIncorrectTimestamps(proto, "FixIncorrectTimestamps", TRUE),
+ GcLogAffiliations(proto, "GcLogAffiliations", FALSE),
+ GcLogBans(proto, "GcLogBans", TRUE),
+ GcLogConfig(proto, "GcLogConfig", FALSE),
+ GcLogRoles(proto, "GcLogRoles", FALSE),
+ GcLogStatuses(proto, "GcLogStatuses", FALSE),
+ GcLogChatHistory(proto, "GcLogChatHistory", FALSE),
+ HostNameAsResource(proto, "HostNameAsResource", FALSE),
+ IgnoreMUCInvites(proto, "IgnoreMUCInvites", FALSE),
+ KeepAlive(proto, "KeepAlive", TRUE),
+ LogChatstates(proto, "LogChatstates", FALSE),
+ LogPresence(proto, "LogPresence", TRUE),
+ LogPresenceErrors(proto, "LogPresenceErrors", FALSE),
+ ManualConnect(proto, "ManualConnect", FALSE),
+ MsgAck(proto, "MsgAck", FALSE),
+ RosterSync(proto, "RosterSync", FALSE),
+ SavePassword(proto, "SavePassword", TRUE),
+ ShowForeignResourceInMirVer(proto, "ShowForeignResourceInMirVer", FALSE),
+ ShowOSVersion(proto, "ShowOSVersion", TRUE),
+ ShowTransport(proto, "ShowTransport", TRUE),
+ UseSSL(proto, "UseSSL", FALSE),
+ UseTLS(proto, "UseTLS", TRUE),
+ UseDomainLogin(proto, "UseDomainLogin", FALSE),
+ AcceptNotes(proto, "AcceptNotes", TRUE),
+ AutosaveNotes(proto, "AutosaveNotes", FALSE),
+ RcMarkMessagesAsRead(proto, "RcMarkMessagesAsRead", 1),
+ ConnectionKeepAliveInterval(proto, "ConnectionKeepAliveInterval", 60000),
+ ConnectionKeepAliveTimeout(proto, "ConnectionKeepAliveTimeout", 50000),
+ ProcessXMPPLinks(proto, "ProcessXMPPLinks", FALSE),
+ IgnoreRosterGroups(proto, "IgnoreRosterGroups", FALSE)
+ {}
+};
+
+#endif // __jabber_db_utils_h__
diff --git a/protocols/JabberG/src/jabber_disco.cpp b/protocols/JabberG/src/jabber_disco.cpp new file mode 100644 index 0000000000..eadfd2d5d1 --- /dev/null +++ b/protocols/JabberG/src/jabber_disco.cpp @@ -0,0 +1,1529 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_disco.h"
+
+#define SD_FAKEJID_CONFERENCES "@@conferences"
+#define SD_FAKEJID_MYAGENTS "@@my-transports"
+#define SD_FAKEJID_AGENTS "@@transports"
+#define SD_FAKEJID_FAVORITES "@@favorites"
+
+enum {
+ SD_BROWSE_NORMAL,
+ SD_BROWSE_MYAGENTS,
+ SD_BROWSE_AGENTS,
+ SD_BROWSE_CONFERENCES,
+ SD_BROWSE_FAVORITES
+};
+static int sttBrowseMode = SD_BROWSE_NORMAL;
+
+#define REFRESH_TIMEOUT 500
+#define REFRESH_TIMER 1607
+static DWORD sttLastRefresh = 0;
+
+#define AUTODISCO_TIMEOUT 500
+#define AUTODISCO_TIMER 1608
+static DWORD sttLastAutoDisco = 0;
+
+enum { SD_OVERLAY_NONE, SD_OVERLAY_FAIL, SD_OVERLAY_PROGRESS, SD_OVERLAY_REGISTERED };
+
+static struct
+{
+ TCHAR *feature;
+ TCHAR *category;
+ TCHAR *type;
+ char *iconName;
+ int iconIndex;
+ int listIndex;
+} sttNodeIcons[] =
+{
+// standard identities: http://www.xmpp.org/registrar/disco-categories.html#directory
+// {NULL, _T("account"), _T("admin"), NULL, 0},
+// {NULL, _T("account"), _T("anonymous"), NULL, 0},
+// {NULL, _T("account"), _T("registered"), NULL, 0},
+ {NULL, _T("account"), NULL, NULL, SKINICON_STATUS_ONLINE},
+
+// {NULL, _T("auth"), _T("cert"), NULL, 0},
+// {NULL, _T("auth"), _T("generic"), NULL, 0},
+// {NULL, _T("auth"), _T("ldap"), NULL, 0},
+// {NULL, _T("auth"), _T("ntlm"), NULL, 0},
+// {NULL, _T("auth"), _T("pam"), NULL, 0},
+// {NULL, _T("auth"), _T("radius"), NULL, 0},
+ {NULL, _T("auth"), NULL, "key", 0},
+
+/// {NULL, _T("automation"), _T("command-list"), NULL, 0},
+/// {NULL, _T("automation"), _T("command-node"), NULL, 0},
+// {NULL, _T("automation"), _T("rpc"), NULL, 0},
+// {NULL, _T("automation"), _T("soap"), NULL, 0},
+ {NULL, _T("automation"), NULL, "adhoc", 0},
+
+// {NULL, _T("client"), _T("bot"), NULL, 0},
+// {NULL, _T("client"), _T("console"), NULL, 0},
+// {NULL, _T("client"), _T("handheld"), NULL, 0},
+// {NULL, _T("client"), _T("pc"), NULL, 0},
+// {NULL, _T("client"), _T("phone"), NULL, 0},
+// {NULL, _T("client"), _T("web"), NULL, 0},
+ {NULL, _T("client"), NULL, NULL, SKINICON_STATUS_ONLINE},
+
+// {NULL, _T("collaboration"), _T("whiteboard"), NULL, 0},
+ {NULL, _T("collaboration"), NULL, "group", 0},
+
+// {NULL, _T("component"), _T("archive"), NULL, 0},
+// {NULL, _T("component"), _T("c2s"), NULL, 0},
+// {NULL, _T("component"), _T("generic"), NULL, 0},
+// {NULL, _T("component"), _T("load"), NULL, 0},
+// {NULL, _T("component"), _T("log"), NULL, 0},
+// {NULL, _T("component"), _T("presence"), NULL, 0},
+// {NULL, _T("component"), _T("router"), NULL, 0},
+// {NULL, _T("component"), _T("s2s"), NULL, 0},
+// {NULL, _T("component"), _T("sm"), NULL, 0},
+// {NULL, _T("component"), _T("stats"), NULL, 0},
+
+// {NULL, _T("conference"), _T("irc"), NULL, 0},
+// {NULL, _T("conference"), _T("text"), NULL, 0},
+ {NULL, _T("conference"), NULL, "group", 0},
+
+ {NULL, _T("directory"), _T("chatroom"), "group", 0},
+ {NULL, _T("directory"), _T("group"), "group", 0},
+ {NULL, _T("directory"), _T("user"), NULL, SKINICON_OTHER_FINDUSER},
+// {NULL, _T("directory"), _T("waitinglist"), NULL, 0},
+ {NULL, _T("directory"), NULL, NULL, SKINICON_OTHER_SEARCHALL},
+
+ {NULL, _T("gateway"), _T("aim"), "AIM", SKINICON_STATUS_ONLINE},
+ {NULL, _T("gateway"), _T("gadu-gadu"), "GG", SKINICON_STATUS_ONLINE},
+// {NULL, _T("gateway"), _T("http-ws"), NUL, 0},
+ {NULL, _T("gateway"), _T("icq"), "ICQ", SKINICON_STATUS_ONLINE},
+ {NULL, _T("gateway"), _T("msn"), "MSN", SKINICON_STATUS_ONLINE},
+ {NULL, _T("gateway"), _T("qq"), "QQ", SKINICON_STATUS_ONLINE},
+// {NULL, _T("gateway"), _T("sms"), NULL, 0},
+// {NULL, _T("gateway"), _T("smtp"), NULL, 0},
+ {NULL, _T("gateway"), _T("tlen"), "TLEN", SKINICON_STATUS_ONLINE},
+ {NULL, _T("gateway"), _T("yahoo"), "YAHOO", SKINICON_STATUS_ONLINE},
+ {NULL, _T("gateway"), NULL, "Agents", 0},
+
+// {NULL, _T("headline"), _T("newmail"), NULL, 0},
+ {NULL, _T("headline"), _T("rss"), "node_rss", 0},
+ {NULL, _T("headline"), _T("weather"), "node_weather", 0},
+
+// {NULL, _T("hierarchy"), _T("branch"), NULL, 0},
+// {NULL, _T("hierarchy"), _T("leaf"), NULL, 0},
+
+// {NULL, _T("proxy"), _T("bytestreams"), NULL, 0},
+ {NULL, _T("proxy"), NULL, NULL, SKINICON_EVENT_FILE},
+
+// {NULL, _T("pubsub"), _T("collection"), NULL, 0},
+// {NULL, _T("pubsub"), _T("leaf"), NULL, 0},
+// {NULL, _T("pubsub"), _T("pep"), NULL, 0},
+// {NULL, _T("pubsub"), _T("service"), NULL, 0},
+
+// {NULL, _T("server"), _T("im"), NULL, 0},
+ {NULL, _T("server"), NULL, "node_server", 0},
+
+// {NULL, _T("store"), _T("berkeley"), NULL, 0},
+/// {NULL, _T("store"), _T("file"), NULL, 0},
+// {NULL, _T("store"), _T("generic"), NULL, 0},
+// {NULL, _T("store"), _T("ldap"), NULL, 0},
+// {NULL, _T("store"), _T("mysql"), NULL, 0},
+// {NULL, _T("store"), _T("oracle"), NULL, 0},
+// {NULL, _T("store"), _T("postgres"), NULL, 0},
+ {NULL, _T("store"), NULL, "node_store", 0},
+
+// icons for non-standard identities
+ {NULL, _T("x-service"), _T("x-rss"), "node_rss", 0},
+ {NULL, _T("application"), _T("x-weather"), "node_weather", 0},
+ {NULL, _T("user"), NULL, NULL, SKINICON_STATUS_ONLINE},
+
+// icon suggestions based on supported features
+ {_T("jabber:iq:gateway"), NULL,NULL, "Agents", 0},
+ {_T("jabber:iq:search"), NULL,NULL, NULL, SKINICON_OTHER_FINDUSER},
+ {_T(JABBER_FEAT_COMMANDS), NULL,NULL, "adhoc", 0},
+ {_T(JABBER_FEAT_REGISTER), NULL,NULL, "key", 0},
+};
+
+static void sttApplyNodeIcon(HTREELISTITEM hItem, CJabberSDNode *pNode);
+
+void CJabberProto::OnIqResultServiceDiscoveryInfo( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ m_SDManager.Lock();
+ CJabberSDNode* pNode = m_SDManager.FindByIqId( pInfo->GetIqId(), TRUE );
+ if ( !pNode ) {
+ m_SDManager.Unlock();
+ return;
+ }
+
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) {
+ HXML query = xmlGetChild( iqNode , "query" );
+ if ( !query )
+ pNode->SetInfoRequestId( JABBER_DISCO_RESULT_ERROR );
+ else {
+ HXML feature;
+ int i;
+ for ( i = 1; ( feature = xmlGetNthChild( query, _T("feature"), i )) != NULL; i++ )
+ pNode->AddFeature( xmlGetAttrValue( feature, _T("var")));
+ HXML identity;
+ for ( i = 1; ( identity = xmlGetNthChild( query, _T("identity"), i )) != NULL; i++ )
+ pNode->AddIdentity( xmlGetAttrValue( identity, _T("category")), xmlGetAttrValue( identity, _T("type")), xmlGetAttrValue( identity, _T("name")));
+
+ pNode->SetInfoRequestId( JABBER_DISCO_RESULT_OK );
+ pNode->SetInfoRequestErrorText( NULL );
+ }
+ }
+ else {
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_ERROR ) {
+ HXML errorNode = xmlGetChild( iqNode , "error" );
+ TCHAR* str = JabberErrorMsg( errorNode );
+ pNode->SetInfoRequestErrorText( str );
+ mir_free( str );
+ }
+ else pNode->SetInfoRequestErrorText( TranslateT("request timeout."));
+
+ pNode->SetInfoRequestId( JABBER_DISCO_RESULT_ERROR );
+ }
+
+ m_SDManager.Unlock();
+
+ if ( m_pDlgServiceDiscovery ) {
+ ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode);
+ PostMessage( m_pDlgServiceDiscovery->GetHwnd(), WM_JABBER_REFRESH, 0, 0 );
+ }
+}
+
+void CJabberProto::OnIqResultServiceDiscoveryItems( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ m_SDManager.Lock();
+ CJabberSDNode* pNode = m_SDManager.FindByIqId( pInfo->GetIqId(), FALSE );
+ if ( !pNode ) {
+ m_SDManager.Unlock();
+ return;
+ }
+
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) {
+ HXML query = xmlGetChild( iqNode , "query" );
+ if ( !query )
+ pNode->SetItemsRequestId( JABBER_DISCO_RESULT_ERROR );
+ else {
+ HXML item;
+ for ( int i = 1; ( item = xmlGetNthChild( query, _T("item"), i )) != NULL; i++ ) {
+ pNode->AddChildNode( xmlGetAttrValue( item, _T("jid")), xmlGetAttrValue( item, _T("node")), xmlGetAttrValue( item, _T("name")));
+ }
+
+ pNode->SetItemsRequestId( JABBER_DISCO_RESULT_OK );
+ pNode->SetItemsRequestErrorText( NULL );
+ }
+ }
+ else {
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_ERROR ) {
+ HXML errorNode = xmlGetChild( iqNode , "error" );
+ TCHAR* str = JabberErrorMsg( errorNode );
+ pNode->SetItemsRequestErrorText( str );
+ mir_free( str );
+ }
+ else {
+ pNode->SetItemsRequestErrorText( _T("request timeout."));
+ }
+ pNode->SetItemsRequestId( JABBER_DISCO_RESULT_ERROR );
+ }
+
+ m_SDManager.Unlock();
+
+ if ( m_pDlgServiceDiscovery ) {
+ ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode);
+ PostMessage( m_pDlgServiceDiscovery->GetHwnd(), WM_JABBER_REFRESH, 0, 0 );
+ }
+}
+
+void CJabberProto::OnIqResultServiceDiscoveryRootInfo( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if (!pInfo->m_pUserData) return;
+ m_SDManager.Lock();
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) {
+ HXML query = xmlGetChild( iqNode , "query" );
+ if ( query ) {
+ HXML feature;
+ int i;
+ for ( i = 1; ( feature = xmlGetNthChild( query, _T("feature"), i )) != NULL; i++ ) {
+ if ( !lstrcmp( xmlGetAttrValue( feature, _T("var")), (TCHAR *)pInfo->m_pUserData)) {
+ CJabberSDNode *pNode = m_SDManager.AddPrimaryNode( pInfo->GetReceiver(), xmlGetAttrValue( iqNode, _T("node")), NULL);
+ SendBothRequests( pNode, NULL );
+ break;
+ } } } }
+ m_SDManager.Unlock();
+
+ UI_SAFE_NOTIFY(m_pDlgServiceDiscovery, WM_JABBER_REFRESH);
+}
+
+void CJabberProto::OnIqResultServiceDiscoveryRootItems( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if (!pInfo->m_pUserData)
+ return;
+
+ XmlNode packet( NULL );
+ m_SDManager.Lock();
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) {
+ HXML query = xmlGetChild( iqNode , "query" );
+ if ( query ) {
+ HXML item;
+ for ( int i = 1; ( item = xmlGetNthChild( query, _T("item"), i )) != NULL; i++ ) {
+ const TCHAR *szJid = xmlGetAttrValue( item, _T("jid"));
+ const TCHAR *szNode = xmlGetAttrValue( item, _T("node"));
+ CJabberIqInfo* pNewInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultServiceDiscoveryRootInfo, JABBER_IQ_TYPE_GET, szJid );
+ pNewInfo->m_pUserData = pInfo->m_pUserData;
+ pNewInfo->SetTimeout( 30000 );
+
+ XmlNodeIq iq( pNewInfo );
+ iq << XQUERY( _T(JABBER_FEAT_DISCO_INFO)) << XATTR( _T("node"), szNode );
+ xmlAddChild( packet, iq );
+ } } }
+ m_SDManager.Unlock();
+
+ if ( xmlGetChild( packet ,0))
+ m_ThreadInfo->send( packet );
+}
+
+BOOL CJabberProto::SendInfoRequest(CJabberSDNode* pNode, HXML parent)
+{
+ if ( !pNode || !m_bJabberOnline )
+ return FALSE;
+
+ // disco#info
+ if ( !pNode->GetInfoRequestId()) {
+ CJabberIqInfo* pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultServiceDiscoveryInfo, JABBER_IQ_TYPE_GET, pNode->GetJid());
+ pInfo->SetTimeout( 30000 );
+ pNode->SetInfoRequestId( pInfo->GetIqId());
+
+ XmlNodeIq iq( pInfo );
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_DISCO_INFO));
+ if ( pNode->GetNode())
+ xmlAddAttr( query, _T("node"), pNode->GetNode());
+
+ if ( parent )
+ xmlAddChild( parent, iq );
+ else
+ m_ThreadInfo->send( iq );
+ }
+
+ if ( m_pDlgServiceDiscovery ) {
+ ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode);
+ PostMessage( m_pDlgServiceDiscovery->GetHwnd(), WM_JABBER_REFRESH, 0, 0 );
+ }
+
+ return TRUE;
+}
+
+BOOL CJabberProto::SendBothRequests(CJabberSDNode* pNode, HXML parent)
+{
+ if ( !pNode || !m_bJabberOnline )
+ return FALSE;
+
+ // disco#info
+ if ( !pNode->GetInfoRequestId()) {
+ CJabberIqInfo* pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultServiceDiscoveryInfo, JABBER_IQ_TYPE_GET, pNode->GetJid());
+ pInfo->SetTimeout( 30000 );
+ pNode->SetInfoRequestId( pInfo->GetIqId());
+
+ XmlNodeIq iq( pInfo );
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_DISCO_INFO));
+ if ( pNode->GetNode())
+ xmlAddAttr( query, _T("node"), pNode->GetNode());
+
+ if ( parent )
+ xmlAddChild( parent, iq );
+ else
+ m_ThreadInfo->send( iq );
+ }
+
+ // disco#items
+ if ( !pNode->GetItemsRequestId()) {
+ CJabberIqInfo* pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultServiceDiscoveryItems, JABBER_IQ_TYPE_GET, pNode->GetJid());
+ pInfo->SetTimeout( 30000 );
+ pNode->SetItemsRequestId( pInfo->GetIqId());
+
+ XmlNodeIq iq( pInfo );
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_DISCO_ITEMS));
+ if ( pNode->GetNode())
+ xmlAddAttr( query, _T("node"), pNode->GetNode());
+
+ if ( parent )
+ xmlAddChild( parent, iq );
+ else
+ m_ThreadInfo->send( iq );
+ }
+
+ if ( m_pDlgServiceDiscovery ) {
+ ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode);
+ PostMessage( m_pDlgServiceDiscovery->GetHwnd(), WM_JABBER_REFRESH, 0, 0 );
+ }
+
+ return TRUE;
+}
+
+void CJabberProto::PerformBrowse(HWND hwndDlg)
+{
+ TCHAR szJid[ JABBER_MAX_JID_LEN ];
+ TCHAR szNode[ 512 ];
+ if ( !GetDlgItemText( hwndDlg, IDC_COMBO_JID, szJid, SIZEOF( szJid )))
+ szJid[ 0 ] = 0;
+ if ( !GetDlgItemText( hwndDlg, IDC_COMBO_NODE, szNode, SIZEOF( szNode )))
+ szNode[ 0 ] = 0;
+
+ ComboAddRecentString(hwndDlg, IDC_COMBO_JID, "discoWnd_rcJid", szJid);
+ ComboAddRecentString(hwndDlg, IDC_COMBO_NODE, "discoWnd_rcNode", szNode);
+
+ if ( _tcslen( szJid )) {
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_TREE_DISCO);
+ TreeList_Reset(hwndList);
+
+ m_SDManager.Lock();
+ m_SDManager.RemoveAll();
+ if (!lstrcmp(szJid, _T(SD_FAKEJID_MYAGENTS))) {
+ sttBrowseMode = SD_BROWSE_MYAGENTS;
+ JABBER_LIST_ITEM *item = NULL;
+ LISTFOREACH(i, this, LIST_ROSTER)
+ {
+ if (( item=ListGetItemPtrFromIndex( i )) != NULL ) {
+ if ( _tcschr( item->jid, '@' )==NULL && _tcschr( item->jid, '/' )==NULL && item->subscription!=SUB_NONE ) {
+ HANDLE hContact = HContactFromJID( item->jid );
+ if ( hContact != NULL )
+ JSetByte( hContact, "IsTransport", TRUE );
+
+ if ( m_lstTransports.getIndex( item->jid ) == -1 )
+ m_lstTransports.insert( mir_tstrdup( item->jid ));
+
+ CJabberSDNode* pNode = m_SDManager.AddPrimaryNode(item->jid, NULL, NULL);
+ SendBothRequests( pNode, NULL );
+ } }
+ } }
+ else if (!lstrcmp(szJid, _T(SD_FAKEJID_CONFERENCES))) {
+ sttBrowseMode = SD_BROWSE_CONFERENCES;
+ TCHAR *szServerJid = mir_a2t(m_ThreadInfo->server);
+ CJabberIqInfo* pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultServiceDiscoveryRootItems, JABBER_IQ_TYPE_GET, szServerJid );
+ pInfo->m_pUserData = (void *)_T(JABBER_FEAT_MUC);
+ pInfo->SetTimeout( 30000 );
+ XmlNodeIq iq( pInfo );
+ iq << XQUERY( _T(JABBER_FEAT_DISCO_ITEMS));
+ m_ThreadInfo->send( iq );
+ mir_free(szServerJid);
+ }
+ else if (!lstrcmp(szJid, _T(SD_FAKEJID_AGENTS))) {
+ sttBrowseMode = SD_BROWSE_AGENTS;
+ TCHAR *szServerJid = mir_a2t(m_ThreadInfo->server);
+ CJabberIqInfo* pInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultServiceDiscoveryRootItems, JABBER_IQ_TYPE_GET, szServerJid );
+ pInfo->m_pUserData = (void *)_T("jabber:iq:gateway");
+ pInfo->SetTimeout( 30000 );
+ XmlNodeIq iq( pInfo );
+ iq << XQUERY( _T(JABBER_FEAT_DISCO_ITEMS));
+ m_ThreadInfo->send( iq );
+ mir_free(szServerJid);
+ }
+ else if (!lstrcmp(szJid, _T(SD_FAKEJID_FAVORITES))) {
+ sttBrowseMode = SD_BROWSE_FAVORITES;
+ int count = JGetDword(NULL, "discoWnd_favCount", 0);
+ for (int i = 0; i < count; ++i)
+ {
+ DBVARIANT dbv;
+ char setting[MAXMODULELABELLENGTH];
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", i);
+ if (!JGetStringT(NULL, setting, &dbv)) {
+ DBVARIANT dbvJid, dbvNode;
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", i);
+ JGetStringT(NULL, setting, &dbvJid);
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", i);
+ JGetStringT(NULL, setting, &dbvNode);
+ CJabberSDNode* pNode = m_SDManager.AddPrimaryNode(dbvJid.ptszVal, dbvNode.ptszVal, dbv.ptszVal);
+ SendBothRequests( pNode, NULL );
+ JFreeVariant(&dbv);
+ JFreeVariant(&dbvJid);
+ JFreeVariant(&dbvNode);
+ } } }
+ else {
+ sttBrowseMode = SD_BROWSE_NORMAL;
+ CJabberSDNode* pNode = m_SDManager.AddPrimaryNode(szJid, _tcslen( szNode ) ? szNode : NULL, NULL);
+ SendBothRequests( pNode, NULL );
+ }
+ m_SDManager.Unlock();
+
+ PostMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 );
+ }
+}
+
+BOOL CJabberProto::IsNodeRegistered(CJabberSDNode *pNode)
+{
+ if (pNode->GetNode())
+ return FALSE;
+
+ JABBER_LIST_ITEM *item;
+ if (item = ListGetItemPtr(LIST_ROSTER, pNode->GetJid()))
+ return (item->subscription != SUB_NONE) ? TRUE : FALSE;
+
+ if (item = ListGetItemPtr(LIST_BOOKMARK, pNode->GetJid()))
+ return TRUE;
+
+ return FALSE;
+}
+
+void CJabberProto::ApplyNodeIcon(HTREELISTITEM hItem, CJabberSDNode *pNode)
+{
+ if (!hItem || !pNode) return;
+
+ int iIcon = -1, iOverlay = -1;
+
+ if ((pNode->GetInfoRequestId() > 0) || (pNode->GetItemsRequestId() > 0))
+ iOverlay = SD_OVERLAY_PROGRESS;
+ else if (pNode->GetInfoRequestId() == JABBER_DISCO_RESULT_ERROR)
+ iOverlay = SD_OVERLAY_FAIL;
+ else if (pNode->GetInfoRequestId() == JABBER_DISCO_RESULT_NOT_REQUESTED) {
+ if (IsNodeRegistered(pNode))
+ iOverlay = SD_OVERLAY_REGISTERED;
+ else
+ iOverlay = SD_OVERLAY_NONE;
+ } else if (pNode->GetInfoRequestId() == JABBER_DISCO_RESULT_OK) {
+ if (IsNodeRegistered(pNode))
+ iOverlay = SD_OVERLAY_REGISTERED;
+ else if (pNode->GetInfoRequestId() == JABBER_DISCO_RESULT_ERROR)
+ iOverlay = SD_OVERLAY_FAIL;
+ else
+ iOverlay = SD_OVERLAY_NONE;
+ }
+
+ for (int i = 0; i < SIZEOF(sttNodeIcons); ++i)
+ {
+ if (!sttNodeIcons[i].iconIndex && !sttNodeIcons[i].iconName) continue;
+
+ if (sttNodeIcons[i].category)
+ {
+ CJabberSDIdentity *iIdentity;
+ for (iIdentity = pNode->GetFirstIdentity(); iIdentity; iIdentity = iIdentity->GetNext())
+ if (!lstrcmp(iIdentity->GetCategory(), sttNodeIcons[i].category) &&
+ (!sttNodeIcons[i].type || !lstrcmp(iIdentity->GetType(), sttNodeIcons[i].type)))
+ {
+ iIcon = sttNodeIcons[i].listIndex;
+ break;
+ }
+ if (iIdentity) break;
+ }
+
+ if (sttNodeIcons[i].feature)
+ {
+ CJabberSDFeature *iFeature;
+ for (iFeature = pNode->GetFirstFeature(); iFeature; iFeature = iFeature->GetNext())
+ if (!lstrcmp(iFeature->GetVar(), sttNodeIcons[i].feature))
+ {
+ iIcon = sttNodeIcons[i].listIndex;
+ break;
+ }
+ if (iFeature) break;
+ }
+ }
+
+ TreeList_SetIcon(pNode->GetTreeItemHandle(), iIcon, iOverlay);
+}
+
+BOOL CJabberProto::SyncTree(HTREELISTITEM hIndex, CJabberSDNode* pNode)
+{
+ if (!m_pDlgServiceDiscovery) return FALSE;
+
+ CJabberSDNode* pTmp = pNode;
+ while (pTmp) {
+ if ( !pTmp->GetTreeItemHandle()) {
+ HTREELISTITEM hNewItem = TreeList_AddItem(
+ GetDlgItem(m_pDlgServiceDiscovery->GetHwnd(), IDC_TREE_DISCO), hIndex,
+ pTmp->GetName() ? pTmp->GetName() : pTmp->GetJid(),
+ (LPARAM)pTmp);
+ TreeList_AppendColumn(hNewItem, pTmp->GetJid());
+ TreeList_AppendColumn(hNewItem, pTmp->GetNode());
+ if (!pTmp->GetInfoRequestId())
+ TreeList_MakeFakeParent(hNewItem, TRUE);
+ else
+ TreeList_MakeFakeParent(hNewItem, FALSE);
+ pTmp->SetTreeItemHandle( hNewItem );
+ }
+
+ ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode);
+
+ if ( pTmp->GetFirstChildNode())
+ SyncTree( pTmp->GetTreeItemHandle(), pTmp->GetFirstChildNode());
+
+ pTmp = pTmp->GetNext();
+ }
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CJabberDlgDiscovery
+class CJabberDlgDiscovery: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+public:
+ CJabberDlgDiscovery(CJabberProto *proto, TCHAR *jid);
+
+protected:
+ void OnInitDialog();
+ void OnClose();
+ void OnDestroy();
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
+ int Resizer(UTILRESIZECONTROL *urc);
+
+private:
+ TCHAR *m_jid;
+ bool m_focusEditAfterBrowse;
+
+ CCtrlMButton m_btnViewAsTree;
+ CCtrlMButton m_btnViewAsList;
+ CCtrlMButton m_btnGoHome;
+ CCtrlMButton m_btnBookmarks;
+ CCtrlMButton m_btnRefresh;
+ CCtrlMButton m_btnBrowse;
+ CCtrlFilterListView m_lstDiscoTree;
+
+ void btnViewAsTree_OnClick(CCtrlButton *);
+ void btnViewAsList_OnClick(CCtrlButton *);
+ void btnGoHome_OnClick(CCtrlButton *);
+ void btnBookmarks_OnClick(CCtrlButton *);
+ void btnRefresh_OnClick(CCtrlButton *);
+ void btnBrowse_OnClick(CCtrlButton *);
+ void lstDiscoTree_OnFilter(CCtrlFilterListView *);
+};
+
+CJabberDlgDiscovery::CJabberDlgDiscovery(CJabberProto *proto, TCHAR *jid) :
+ CJabberDlgBase(proto, IDD_SERVICE_DISCOVERY, NULL),
+ m_jid(jid),
+ m_btnViewAsTree(this, IDC_BTN_VIEWTREE, proto->LoadIconEx("sd_view_tree"), "View as tree"),
+ m_btnViewAsList(this, IDC_BTN_VIEWLIST, proto->LoadIconEx("sd_view_list"), "View as list"),
+ m_btnGoHome(this, IDC_BTN_NAVHOME, proto->LoadIconEx("sd_nav_home"), "Navigate home"),
+ m_btnBookmarks(this, IDC_BTN_FAVORITE, proto->LoadIconEx("bookmarks"), "Favorites"),
+ m_btnRefresh(this, IDC_BTN_REFRESH, proto->LoadIconEx("sd_nav_refresh"), "Refresh node"),
+ m_btnBrowse(this, IDC_BUTTON_BROWSE, proto->LoadIconEx("sd_browse"), "Browse"),
+ m_lstDiscoTree(this, IDC_TREE_DISCO, true, false)
+{
+ m_btnViewAsTree.OnClick = Callback(this, &CJabberDlgDiscovery::btnViewAsTree_OnClick);
+ m_btnViewAsList.OnClick = Callback(this, &CJabberDlgDiscovery::btnViewAsList_OnClick);
+ m_btnGoHome.OnClick = Callback(this, &CJabberDlgDiscovery::btnGoHome_OnClick);
+ m_btnBookmarks.OnClick = Callback(this, &CJabberDlgDiscovery::btnBookmarks_OnClick);
+ m_btnRefresh.OnClick = Callback(this, &CJabberDlgDiscovery::btnRefresh_OnClick);
+ m_btnBrowse.OnClick = Callback(this, &CJabberDlgDiscovery::btnBrowse_OnClick);
+ m_lstDiscoTree.OnFilterChanged = Callback(this, &CJabberDlgDiscovery::lstDiscoTree_OnFilter);
+}
+
+void CJabberDlgDiscovery::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+
+// TranslateDialogDefault( m_hwnd );
+ WindowSetIcon( m_hwnd, m_proto, "servicediscovery" );
+
+ int i;
+
+ if ( m_jid ) {
+ SetDlgItemText( m_hwnd, IDC_COMBO_JID, m_jid );
+ SetDlgItemText( m_hwnd, IDC_COMBO_NODE, _T(""));
+ m_focusEditAfterBrowse = false;
+ } else {
+ SetDlgItemTextA( m_hwnd, IDC_COMBO_JID, m_proto->m_ThreadInfo->server );
+ SetDlgItemText( m_hwnd, IDC_COMBO_NODE, _T(""));
+ m_focusEditAfterBrowse = true;
+ }
+
+ m_btnViewAsList.MakePush();
+ m_btnViewAsTree.MakePush();
+ m_btnBookmarks.MakePush();
+
+ CheckDlgButton(m_hwnd,
+ DBGetContactSettingByte(NULL, m_proto->m_szModuleName, "discoWnd_useTree", 1) ?
+ IDC_BTN_VIEWTREE : IDC_BTN_VIEWLIST,
+ TRUE);
+
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BTN_FILTERRESET), FALSE);
+
+ SendDlgItemMessage(m_hwnd, IDC_COMBO_JID, CB_ADDSTRING, 0, (LPARAM)_T(SD_FAKEJID_CONFERENCES));
+ SendDlgItemMessage(m_hwnd, IDC_COMBO_JID, CB_ADDSTRING, 0, (LPARAM)_T(SD_FAKEJID_MYAGENTS));
+ SendDlgItemMessage(m_hwnd, IDC_COMBO_JID, CB_ADDSTRING, 0, (LPARAM)_T(SD_FAKEJID_AGENTS));
+ SendDlgItemMessage(m_hwnd, IDC_COMBO_JID, CB_ADDSTRING, 0, (LPARAM)_T(SD_FAKEJID_FAVORITES));
+ m_proto->ComboLoadRecentStrings(m_hwnd, IDC_COMBO_JID, "discoWnd_rcJid");
+ m_proto->ComboLoadRecentStrings(m_hwnd, IDC_COMBO_NODE, "discoWnd_rcNode");
+
+ HWND hwndList = m_lstDiscoTree.GetHwnd();//GetDlgItem(m_hwnd, IDC_TREE_DISCO);
+ LVCOLUMN lvc = {0};
+ lvc.mask = LVCF_SUBITEM|LVCF_WIDTH|LVCF_TEXT;
+ lvc.cx = DBGetContactSettingWord(NULL, m_proto->m_szModuleName, "discoWnd_cx0", 200);
+ lvc.iSubItem = 0;
+ lvc.pszText = TranslateT("Node hierarchy");
+ ListView_InsertColumn(hwndList, 0, &lvc);
+ lvc.cx = DBGetContactSettingWord(NULL, m_proto->m_szModuleName, "discoWnd_cx1", 200);
+ lvc.iSubItem = 1;
+ lvc.pszText = _T("JID");
+ ListView_InsertColumn(hwndList, 1, &lvc);
+ lvc.cx = DBGetContactSettingWord(NULL, m_proto->m_szModuleName, "discoWnd_cx2", 200);
+ lvc.iSubItem = 2;
+ lvc.pszText = TranslateT("Node");
+ ListView_InsertColumn(hwndList, 2, &lvc);
+
+ TreeList_Create(hwndList);
+ TreeList_AddIcon(hwndList, m_proto->LoadIconEx("main"), 0);
+ for (i = 0; i < SIZEOF(sttNodeIcons); ++i)
+ {
+ bool needDestroy = false;
+ HICON hIcon;
+ if ((sttNodeIcons[i].iconIndex == SKINICON_STATUS_ONLINE) && sttNodeIcons[i].iconName) {
+ hIcon = (HICON)CallProtoService(sttNodeIcons[i].iconName, PS_LOADICON, PLI_PROTOCOL|PLIF_SMALL, 0);
+ needDestroy = true;
+ }
+ else if (sttNodeIcons[i].iconName)
+ hIcon = m_proto->LoadIconEx(sttNodeIcons[i].iconName);
+ else if (sttNodeIcons[i].iconIndex)
+ hIcon = LoadSkinnedIcon(sttNodeIcons[i].iconIndex);
+ else continue;
+ sttNodeIcons[i].listIndex = TreeList_AddIcon(hwndList, hIcon, 0);
+ if (needDestroy) DestroyIcon(hIcon);
+ }
+ TreeList_AddIcon(hwndList, m_proto->LoadIconEx("disco_fail"), SD_OVERLAY_FAIL);
+ TreeList_AddIcon(hwndList, m_proto->LoadIconEx("disco_progress"), SD_OVERLAY_PROGRESS);
+ TreeList_AddIcon(hwndList, m_proto->LoadIconEx("disco_ok"), SD_OVERLAY_REGISTERED);
+
+ TreeList_SetMode(hwndList, DBGetContactSettingByte(NULL, m_proto->m_szModuleName, "discoWnd_useTree", 1) ? TLM_TREE : TLM_REPORT);
+
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_BUTTON_BROWSE, 0 ), 0 );
+
+ Utils_RestoreWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "discoWnd_");
+}
+
+void CJabberDlgDiscovery::OnClose()
+{
+ DBWriteContactSettingByte(NULL, m_proto->m_szModuleName, "discoWnd_useTree", IsDlgButtonChecked(m_hwnd, IDC_BTN_VIEWTREE));
+
+ HWND hwndList = GetDlgItem(m_hwnd, IDC_TREE_DISCO);
+ LVCOLUMN lvc = {0};
+ lvc.mask = LVCF_WIDTH;
+ ListView_GetColumn(hwndList, 0, &lvc);
+ DBWriteContactSettingWord(NULL, m_proto->m_szModuleName, "discoWnd_cx0", lvc.cx);
+ ListView_GetColumn(hwndList, 1, &lvc);
+ DBWriteContactSettingWord(NULL, m_proto->m_szModuleName, "discoWnd_cx1", lvc.cx);
+ ListView_GetColumn(hwndList, 2, &lvc);
+ DBWriteContactSettingWord(NULL, m_proto->m_szModuleName, "discoWnd_cx2", lvc.cx);
+
+ Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "discoWnd_");
+ DestroyWindow( m_hwnd );
+
+ CSuper::OnClose();
+}
+
+void CJabberDlgDiscovery::OnDestroy()
+{
+ m_proto->m_pDlgServiceDiscovery = NULL;
+ m_proto->m_SDManager.Lock();
+ m_proto->m_SDManager.RemoveAll();
+ m_proto->m_SDManager.Unlock();
+ TreeList_Destroy(GetDlgItem(m_hwnd, IDC_TREE_DISCO));
+
+ CSuper::OnDestroy();
+}
+
+int CJabberDlgDiscovery::Resizer(UTILRESIZECONTROL *urc)
+{
+ RECT rc;
+
+ switch ( urc->wId ) {
+ case IDC_COMBO_JID:
+ {
+ GetWindowRect(GetDlgItem(m_hwnd, urc->wId), &rc);
+ urc->rcItem.right += (urc->dlgNewSize.cx - urc->dlgOriginalSize.cx) / 2;
+ urc->rcItem.bottom = urc->rcItem.top + rc.bottom - rc.top;
+ return 0;
+ }
+ case IDC_TXT_NODELABEL:
+ {
+ urc->rcItem.left += (urc->dlgNewSize.cx - urc->dlgOriginalSize.cx) / 2;
+ urc->rcItem.right += (urc->dlgNewSize.cx - urc->dlgOriginalSize.cx) / 2;
+ return 0;
+ }
+ case IDC_COMBO_NODE:
+ {
+ GetWindowRect(GetDlgItem(m_hwnd, urc->wId), &rc);
+ urc->rcItem.left += (urc->dlgNewSize.cx - urc->dlgOriginalSize.cx) / 2;
+ urc->rcItem.right += urc->dlgNewSize.cx - urc->dlgOriginalSize.cx;
+ urc->rcItem.bottom = urc->rcItem.top + rc.bottom - rc.top;
+ return 0;
+ }
+ case IDC_BUTTON_BROWSE:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_TOP;
+
+ case IDC_TREE_DISCO:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+
+ case IDC_TXT_FILTER:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
+ case IDC_TXT_FILTERTEXT:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM;
+ case IDC_BTN_FILTERAPPLY:
+ case IDC_BTN_FILTERRESET:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ }
+ return CSuper::Resizer(urc);
+}
+
+void CJabberDlgDiscovery::btnViewAsTree_OnClick(CCtrlButton *)
+{
+ CheckDlgButton(m_hwnd, IDC_BTN_VIEWLIST, FALSE);
+ CheckDlgButton(m_hwnd, IDC_BTN_VIEWTREE, TRUE);
+ TreeList_SetMode(GetDlgItem(m_hwnd, IDC_TREE_DISCO), TLM_TREE);
+}
+
+void CJabberDlgDiscovery::btnViewAsList_OnClick(CCtrlButton *)
+{
+ CheckDlgButton(m_hwnd, IDC_BTN_VIEWLIST, TRUE);
+ CheckDlgButton(m_hwnd, IDC_BTN_VIEWTREE, FALSE);
+ TreeList_SetMode(GetDlgItem(m_hwnd, IDC_TREE_DISCO), TLM_REPORT);
+}
+
+void CJabberDlgDiscovery::btnGoHome_OnClick(CCtrlButton *)
+{
+ SetDlgItemTextA( m_hwnd, IDC_COMBO_JID, m_proto->m_ThreadInfo->server );
+ SetDlgItemText( m_hwnd, IDC_COMBO_NODE, _T(""));
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_BUTTON_BROWSE, 0 ), 0 );
+}
+
+void CJabberDlgDiscovery::btnBookmarks_OnClick(CCtrlButton *)
+{
+ HMENU hMenu = CreatePopupMenu();
+ int count = m_proto->JGetDword(NULL, "discoWnd_favCount", 0);
+ for (int i = 0; i < count; ++i)
+ {
+ DBVARIANT dbv;
+ char setting[MAXMODULELABELLENGTH];
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", i);
+ if (!m_proto->JGetStringT(NULL, setting, &dbv))
+ {
+ HMENU hSubMenu = CreatePopupMenu();
+ AppendMenu(hSubMenu, MF_STRING, 100+i*10+0, TranslateT("Navigate"));
+ AppendMenu(hSubMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(hSubMenu, MF_STRING, 100+i*10+1, TranslateT("Remove"));
+ AppendMenu(hMenu, MF_POPUP|MF_STRING, (UINT_PTR)hSubMenu, dbv.ptszVal);
+ }
+ JFreeVariant(&dbv);
+ }
+ int res = 0;
+ if (GetMenuItemCount(hMenu)) {
+ AppendMenu(hMenu, MF_SEPARATOR, 1, NULL);
+ AppendMenu(hMenu, MF_STRING, 10+SD_BROWSE_FAVORITES, TranslateT("Browse all favorites"));
+ AppendMenu(hMenu, MF_STRING, 1, TranslateT("Remove all favorites"));
+ }
+ if (GetMenuItemCount(hMenu))
+ AppendMenu(hMenu, MF_SEPARATOR, 1, NULL);
+
+ AppendMenu(hMenu, MF_STRING, 10+SD_BROWSE_MYAGENTS, TranslateT("Registered transports"));
+ AppendMenu(hMenu, MF_STRING, 10+SD_BROWSE_AGENTS, TranslateT("Browse local transports"));
+ AppendMenu(hMenu, MF_STRING, 10+SD_BROWSE_CONFERENCES, TranslateT("Browse chatrooms"));
+
+ RECT rc; GetWindowRect(GetDlgItem(m_hwnd, IDC_BTN_FAVORITE), &rc);
+ CheckDlgButton(m_hwnd, IDC_BTN_FAVORITE, TRUE);
+ res = TrackPopupMenu(hMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, NULL);
+ CheckDlgButton(m_hwnd, IDC_BTN_FAVORITE, FALSE);
+ DestroyMenu(hMenu);
+
+ if (res >= 100)
+ {
+ res -= 100;
+ if (res % 10)
+ {
+ res /= 10;
+ char setting[MAXMODULELABELLENGTH];
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", res);
+ m_proto->JDeleteSetting(NULL, setting);
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", res);
+ m_proto->JDeleteSetting(NULL, setting);
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", res);
+ m_proto->JDeleteSetting(NULL, setting);
+ } else
+ {
+ res /= 10;
+
+ SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(""));
+ SetDlgItemText(m_hwnd, IDC_COMBO_NODE, _T(""));
+
+ DBVARIANT dbv;
+ char setting[MAXMODULELABELLENGTH];
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", res);
+ if (!m_proto->JGetStringT(NULL, setting, &dbv)) SetDlgItemText(m_hwnd, IDC_COMBO_JID, dbv.ptszVal);
+ JFreeVariant(&dbv);
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", res);
+ if (!m_proto->JGetStringT(NULL, setting, &dbv)) SetDlgItemText(m_hwnd, IDC_COMBO_NODE, dbv.ptszVal);
+ JFreeVariant(&dbv);
+
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_BUTTON_BROWSE, 0 ), 0 );
+ }
+ } else
+ if (res == 1)
+ {
+ int count = m_proto->JGetDword(NULL, "discoWnd_favCount", 0);
+ for (int i = 0; i < count; ++i)
+ {
+ char setting[MAXMODULELABELLENGTH];
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", i);
+ m_proto->JDeleteSetting(NULL, setting);
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", i);
+ m_proto->JDeleteSetting(NULL, setting);
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", i);
+ m_proto->JDeleteSetting(NULL, setting);
+ }
+ m_proto->JDeleteSetting(NULL, "discoWnd_favCount");
+ } else
+ if ((res >= 10) && (res <= 20))
+ {
+ switch (res-10) {
+ case SD_BROWSE_FAVORITES:
+ SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_FAVORITES));
+ break;
+ case SD_BROWSE_MYAGENTS:
+ SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_MYAGENTS));
+ break;
+ case SD_BROWSE_AGENTS:
+ SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_AGENTS));
+ break;
+ case SD_BROWSE_CONFERENCES:
+ SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_CONFERENCES));
+ break;
+ }
+ SetDlgItemText(m_hwnd, IDC_COMBO_NODE, _T(""));
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_BUTTON_BROWSE, 0 ), 0 );
+ }
+
+ CheckDlgButton(m_hwnd, IDC_BTN_FAVORITE, FALSE);
+}
+
+void CJabberDlgDiscovery::btnRefresh_OnClick(CCtrlButton *)
+{
+ HTREELISTITEM hItem = (HTREELISTITEM)TreeList_GetActiveItem(GetDlgItem(m_hwnd, IDC_TREE_DISCO));
+ if (!hItem) return;
+
+ m_proto->m_SDManager.Lock();
+ XmlNode packet( NULL );
+ CJabberSDNode* pNode = (CJabberSDNode* )TreeList_GetData(hItem);
+ if ( pNode ) {
+ TreeList_ResetItem(GetDlgItem(m_hwnd, IDC_TREE_DISCO), hItem);
+ pNode->ResetInfo();
+ m_proto->SendBothRequests( pNode, packet );
+ TreeList_MakeFakeParent(hItem, FALSE);
+ }
+ m_proto->m_SDManager.Unlock();
+
+ if ( xmlGetChild( packet ,0))
+ m_proto->m_ThreadInfo->send( packet );
+}
+
+void CJabberDlgDiscovery::btnBrowse_OnClick(CCtrlButton *)
+{
+ SetFocus(GetDlgItem(m_hwnd, m_focusEditAfterBrowse ? IDC_COMBO_JID : IDC_TREE_DISCO));
+ m_focusEditAfterBrowse = false;
+
+ m_proto->PerformBrowse(m_hwnd);
+}
+
+void CJabberDlgDiscovery::lstDiscoTree_OnFilter(CCtrlFilterListView *)
+{
+ TreeList_SetFilter(GetDlgItem(m_hwnd, IDC_TREE_DISCO), m_lstDiscoTree.GetFilterText());
+}
+
+INT_PTR CJabberDlgDiscovery::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL result;
+ if (TreeList_ProcessMessage(m_hwnd, msg, wParam, lParam, IDC_TREE_DISCO, &result))
+ return result;
+
+ switch ( msg ) {
+ case WM_GETMINMAXINFO:
+ {
+ LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
+ lpmmi->ptMinTrackSize.x = 538;
+ lpmmi->ptMinTrackSize.y = 374;
+ return 0;
+ }
+
+ case WM_JABBER_TRANSPORT_REFRESH:
+ if (m_proto->m_nSDBrowseMode == SD_BROWSE_MYAGENTS) {
+ SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_MYAGENTS));
+ SetDlgItemText(m_hwnd, IDC_COMBO_NODE, _T(""));
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_BUTTON_BROWSE, 0 ), 0 );
+ }
+ break;
+
+ case WM_JABBER_REFRESH:
+ KillTimer(m_hwnd, REFRESH_TIMER);
+ if (GetTickCount() - m_proto->m_dwSDLastRefresh < REFRESH_TIMEOUT) {
+ SetTimer(m_hwnd, REFRESH_TIMER, REFRESH_TIMEOUT, NULL);
+ return TRUE;
+ }
+
+ wParam = REFRESH_TIMER;
+ // fall through
+
+ case WM_TIMER:
+ if (wParam == REFRESH_TIMER) {
+ m_proto->m_SDManager.Lock();
+
+ CJabberSDNode* pNode = m_proto->m_SDManager.GetPrimaryNode();
+ while (pNode)
+ {
+ if ( pNode->GetJid()) {
+ if ( !pNode->GetTreeItemHandle()) {
+ HTREELISTITEM hNewItem = TreeList_AddItem(
+ GetDlgItem( m_hwnd, IDC_TREE_DISCO), NULL,
+ pNode->GetName() ? pNode->GetName() : pNode->GetJid(),
+ (LPARAM)pNode);
+ TreeList_AppendColumn(hNewItem, pNode->GetJid());
+ TreeList_AppendColumn(hNewItem, pNode->GetNode());
+ pNode->SetTreeItemHandle( hNewItem );
+ } }
+ m_proto->SyncTree( NULL, pNode );
+ pNode = pNode->GetNext();
+ }
+ m_proto->m_SDManager.Unlock();
+ TreeList_Update(GetDlgItem(m_hwnd, IDC_TREE_DISCO));
+ KillTimer(m_hwnd, REFRESH_TIMER);
+ m_proto->m_dwSDLastRefresh = GetTickCount();
+ return TRUE;
+ }
+ else if (wParam == AUTODISCO_TIMER) {
+ HWND hwndList = GetDlgItem(m_hwnd, IDC_TREE_DISCO);
+ RECT rcCtl; GetClientRect(hwndList, &rcCtl);
+ RECT rcHdr; GetClientRect(ListView_GetHeader(hwndList), &rcHdr);
+ LVHITTESTINFO lvhti = {0};
+ lvhti.pt.x = rcCtl.left + 5;
+ lvhti.pt.y = rcHdr.bottom + 5;
+ int iFirst = ListView_HitTest(hwndList, &lvhti);
+ ZeroMemory(&lvhti, sizeof(lvhti));
+ lvhti.pt.x = rcCtl.left + 5;
+ lvhti.pt.y = rcCtl.bottom - 5;
+ int iLast = ListView_HitTest(hwndList, &lvhti);
+ if (iFirst < 0) return FALSE;
+ if (iLast < 0) iLast = ListView_GetItemCount(hwndList) - 1;
+
+ m_proto->m_SDManager.Lock();
+ XmlNode packet( NULL );
+ for (int i = iFirst; i <= iLast; ++i)
+ {
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = i;
+ ListView_GetItem(hwndList, &lvi);
+ if (!lvi.lParam)
+ continue;
+
+ CJabberSDNode *pNode = (CJabberSDNode *)TreeList_GetData((HTREELISTITEM)lvi.lParam);
+ if (!pNode || pNode->GetInfoRequestId())
+ continue;
+
+ m_proto->SendInfoRequest(pNode, packet);
+ }
+ m_proto->m_SDManager.Unlock();
+ if ( xmlGetChild( packet, 0))
+ m_proto->m_ThreadInfo->send( packet );
+
+ KillTimer(m_hwnd, AUTODISCO_TIMER);
+ m_proto->m_dwSDLastRefresh = GetTickCount();
+ return TRUE;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_TREE_DISCO)
+ {
+ HWND hwndList = (HWND)wParam;
+ POINT pt = { (signed short)LOWORD( lParam ), (signed short)HIWORD( lParam ) };
+
+ if (( pt.x == -1 ) && ( pt.y == -1 )) {
+ LVITEM lvi = {0};
+ lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0) return FALSE;
+
+ RECT rc;
+ ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL);
+ pt.x = rc.left;
+ pt.y = rc.bottom;
+ ClientToScreen(hwndList, &pt);
+ }
+
+ HTREELISTITEM hItem = TreeList_GetActiveItem(hwndList);
+ if (!hItem) break;
+ CJabberSDNode *pNode = (CJabberSDNode *)TreeList_GetData(hItem);
+ if (!pNode) break;
+
+ m_proto->ServiceDiscoveryShowMenu(pNode, hItem, pt);
+ }
+ break;
+
+ case WM_NOTIFY:
+ if ( wParam == IDC_TREE_DISCO ) {
+ NMHDR* pHeader = (NMHDR* )lParam;
+ if ( pHeader->code == LVN_GETINFOTIP ) {
+ NMLVGETINFOTIP *pInfoTip = (NMLVGETINFOTIP *)lParam;
+ LVITEM lvi;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = pInfoTip->iItem;
+ ListView_GetItem(pHeader->hwndFrom, &lvi);
+ HTREELISTITEM hItem = (HTREELISTITEM)lvi.lParam;
+ m_proto->m_SDManager.Lock();
+ CJabberSDNode* pNode = (CJabberSDNode* )TreeList_GetData(hItem);
+ if ( pNode ) {
+ pNode->GetTooltipText( pInfoTip->pszText, pInfoTip->cchTextMax );
+ }
+ m_proto->m_SDManager.Unlock();
+ }
+ else if ( pHeader->code == TVN_ITEMEXPANDED ) {
+ NMTREEVIEW *pNmTreeView = (NMTREEVIEW *)lParam;
+ HTREELISTITEM hItem = (HTREELISTITEM)pNmTreeView->itemNew.hItem;
+
+ m_proto->m_SDManager.Lock();
+ XmlNode packet( NULL );
+ CJabberSDNode* pNode;
+ pNode = (CJabberSDNode* )TreeList_GetData(hItem);
+ if ( pNode )
+ {
+ m_proto->SendBothRequests( pNode, packet );
+ TreeList_MakeFakeParent(hItem, FALSE);
+ }
+ m_proto->m_SDManager.Unlock();
+
+ if ( xmlGetChild( packet ))
+ m_proto->m_ThreadInfo->send( packet );
+ }
+ else if ( pHeader->code == NM_CUSTOMDRAW ) {
+ LPNMLVCUSTOMDRAW lpnmlvcd = (LPNMLVCUSTOMDRAW)lParam;
+ if (lpnmlvcd->nmcd.dwDrawStage != CDDS_PREPAINT)
+ return CDRF_DODEFAULT;
+
+ KillTimer(m_hwnd, AUTODISCO_TIMER);
+ if (GetTickCount() - sttLastAutoDisco < AUTODISCO_TIMEOUT) {
+ SetTimer(m_hwnd, AUTODISCO_TIMER, AUTODISCO_TIMEOUT, NULL);
+ return CDRF_DODEFAULT;
+ }
+
+ SendMessage(m_hwnd, WM_TIMER, AUTODISCO_TIMER, 0);
+
+ return CDRF_DODEFAULT;
+ }
+ return TRUE;
+ }
+
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND hwndFocus = GetFocus();
+ if (!hwndFocus) return TRUE;
+ if (GetWindowLongPtr(hwndFocus, GWL_ID) == IDC_TXT_FILTERTEXT)
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_BTN_FILTERAPPLY, 0 ), 0 );
+ else if (m_hwnd == (hwndFocus = GetParent(hwndFocus)))
+ break;
+ else if ((GetWindowLongPtr(hwndFocus, GWL_ID) == IDC_COMBO_NODE) || (GetWindowLongPtr(hwndFocus, GWL_ID) == IDC_COMBO_JID))
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_BUTTON_BROWSE, 0 ), 0 );
+ return TRUE;
+ }
+ case IDCANCEL:
+ {
+ PostMessage(m_hwnd, WM_CLOSE, 0, 0);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam);
+ case WM_DRAWITEM:
+ return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam);
+
+ }
+
+ return CSuper::DlgProc(msg, wParam, lParam);
+}
+
+// extern references to used functions:
+void SearchAddToRecent( TCHAR* szAddr, HWND hwndDialog = NULL );
+
+void CJabberProto::ServiceDiscoveryShowMenu(CJabberSDNode *pNode, HTREELISTITEM hItem, POINT pt)
+{
+ //ClientToScreen(GetDlgItem(hwndServiceDiscovery, IDC_TREE_DISCO), &pt);
+
+ enum { // This values are below CLISTMENUIDMAX and won't overlap
+ SD_ACT_REFRESH = 1, SD_ACT_REFRESHCHILDREN, SD_ACT_FAVORITE,
+ SD_ACT_ROSTER, SD_ACT_COPYJID, SD_ACT_COPYNODE, SD_ACT_USERMENU,
+ SD_ACT_COPYINFO,
+
+ SD_ACT_LOGON = 100, SD_ACT_LOGOFF, SD_ACT_UNREGISTER,
+
+ SD_ACT_REGISTER = 200, SD_ACT_ADHOC, SD_ACT_ADDDIRECTORY,
+ SD_ACT_JOIN, SD_ACT_BOOKMARK, SD_ACT_PROXY, SD_ACT_VCARD
+ };
+
+ enum {
+ SD_FLG_NONODE = 0x001,
+ SD_FLG_NOTONROSTER = 0x002,
+ SD_FLG_ONROSTER = 0x004,
+ SD_FLG_SUBSCRIBED = 0x008,
+ SD_FLG_NOTSUBSCRIBED = 0x010,
+ SD_FLG_ONLINE = 0x020,
+ SD_FLG_NOTONLINE = 0x040,
+ SD_FLG_NORESOURCE = 0x080,
+ SD_FLG_HASUSER = 0x100
+ };
+
+ static struct
+ {
+ TCHAR *feature;
+ TCHAR *title;
+ int action;
+ DWORD flags;
+ } items[] =
+ {
+ {NULL, _T("Contact Menu..."), SD_ACT_USERMENU, SD_FLG_NONODE},
+ {NULL, _T("View vCard"), SD_ACT_VCARD, SD_FLG_NONODE},
+ {_T(JABBER_FEAT_MUC), _T("Join chatroom"), SD_ACT_JOIN, SD_FLG_NORESOURCE},
+ {0},
+ {NULL, _T("Refresh Info"), SD_ACT_REFRESH},
+ {NULL, _T("Refresh Children"), SD_ACT_REFRESHCHILDREN},
+ {0},
+ {NULL, _T("Add to favorites"), SD_ACT_FAVORITE},
+ {NULL, _T("Add to roster"), SD_ACT_ROSTER, SD_FLG_NONODE|SD_FLG_NOTONROSTER},
+ {_T(JABBER_FEAT_MUC), _T("Bookmark chatroom"), SD_ACT_BOOKMARK, SD_FLG_NORESOURCE|SD_FLG_HASUSER},
+ {_T("jabber:iq:search"), _T("Add search directory"), SD_ACT_ADDDIRECTORY},
+ {_T(JABBER_FEAT_BYTESTREAMS), _T("Use this proxy"), SD_ACT_PROXY},
+ {0},
+ {_T(JABBER_FEAT_REGISTER), _T("Register"), SD_ACT_REGISTER},
+ {_T("jabber:iq:gateway"), _T("Unregister"), SD_ACT_UNREGISTER, SD_FLG_ONROSTER|SD_FLG_SUBSCRIBED},
+ {_T(JABBER_FEAT_COMMANDS), _T("Commands..."), SD_ACT_ADHOC},
+ {0},
+ {_T("jabber:iq:gateway"), _T("Logon"), SD_ACT_LOGON, SD_FLG_ONROSTER|SD_FLG_SUBSCRIBED|SD_FLG_ONLINE},
+ {_T("jabber:iq:gateway"), _T("Logoff"), SD_ACT_LOGOFF, SD_FLG_ONROSTER|SD_FLG_SUBSCRIBED|SD_FLG_NOTONLINE},
+ {0},
+ {NULL, _T("Copy JID"), SD_ACT_COPYJID},
+ {NULL, _T("Copy node name"), SD_ACT_COPYNODE},
+ {NULL, _T("Copy node information"),SD_ACT_COPYINFO},
+ };
+
+ HMENU hMenu = CreatePopupMenu();
+ BOOL lastSeparator = TRUE;
+ bool bFilterItems = !GetAsyncKeyState(VK_CONTROL);
+ for (int i = 0; i < SIZEOF(items); ++i)
+ {
+ JABBER_LIST_ITEM *rosterItem = NULL;
+
+ if (bFilterItems)
+ {
+ if ((items[i].flags&SD_FLG_NONODE) && pNode->GetNode())
+ continue;
+ if ((items[i].flags&SD_FLG_NOTONROSTER) && (rosterItem = ListGetItemPtr(LIST_ROSTER, pNode->GetJid())))
+ continue;
+ if ((items[i].flags&SD_FLG_ONROSTER) && !(rosterItem = ListGetItemPtr(LIST_ROSTER, pNode->GetJid())))
+ continue;
+ if ((items[i].flags&SD_FLG_SUBSCRIBED) && (!rosterItem || (rosterItem->subscription == SUB_NONE)))
+ continue;
+ if ((items[i].flags&SD_FLG_NOTSUBSCRIBED) && (rosterItem && (rosterItem->subscription != SUB_NONE)))
+ continue;
+ if ((items[i].flags&SD_FLG_ONLINE) && rosterItem && (rosterItem->itemResource.status != ID_STATUS_OFFLINE))
+ continue;
+ if ((items[i].flags&SD_FLG_NOTONLINE) && rosterItem && (rosterItem->itemResource.status == ID_STATUS_OFFLINE))
+ continue;
+ if ((items[i].flags&SD_FLG_NORESOURCE) && _tcschr(pNode->GetJid(), _T('/')))
+ continue;
+ if ((items[i].flags&SD_FLG_HASUSER) && !_tcschr(pNode->GetJid(), _T('@')))
+ continue;
+ }
+
+ if (!items[i].feature)
+ {
+ if (items[i].title)
+ {
+ HANDLE hContact;
+ if ((items[i].action == SD_ACT_USERMENU) && (hContact = HContactFromJID(pNode->GetJid()))) {
+ HMENU hContactMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)hContact, 0);
+ AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT_PTR)hContactMenu, TranslateTS(items[i].title));
+ } else
+ AppendMenu(hMenu, MF_STRING, items[i].action, TranslateTS(items[i].title));
+ lastSeparator = FALSE;
+ } else
+ if (!lastSeparator)
+ {
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ lastSeparator = TRUE;
+ }
+ continue;
+ }
+
+ bool bFeatureOk = !bFilterItems;
+ if (bFilterItems)
+ for (CJabberSDFeature *iFeature = pNode->GetFirstFeature(); iFeature; iFeature = iFeature->GetNext())
+ if (!lstrcmp(iFeature->GetVar(), items[i].feature))
+ {
+ bFeatureOk = true;
+ break;
+ }
+
+ if (bFeatureOk)
+ {
+ if (items[i].title)
+ {
+ AppendMenu(hMenu, MF_STRING, items[i].action, TranslateTS(items[i].title));
+ lastSeparator = FALSE;
+ } else
+ if (!lastSeparator)
+ {
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ lastSeparator = TRUE;
+ }
+ }
+ }
+
+ if (!GetMenuItemCount(hMenu))
+ {
+ DestroyMenu(hMenu);
+ return;
+ }
+
+ int res = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlgServiceDiscovery->GetHwnd(), NULL);
+ DestroyMenu(hMenu);
+
+ switch (res)
+ {
+ case SD_ACT_REFRESH:
+ {
+ m_SDManager.Lock();
+ XmlNode packet( NULL );
+ if ( pNode )
+ {
+ TreeList_ResetItem(GetDlgItem(m_pDlgServiceDiscovery->GetHwnd(), IDC_TREE_DISCO), hItem);
+ pNode->ResetInfo();
+ SendBothRequests( pNode, packet );
+ TreeList_MakeFakeParent(hItem, FALSE);
+ }
+ m_SDManager.Unlock();
+
+ if ( xmlGetChild( packet ))
+ m_ThreadInfo->send( packet );
+ break;
+ }
+
+ case SD_ACT_REFRESHCHILDREN:
+ {
+ m_SDManager.Lock();
+ XmlNode packet( NULL );
+ for (int iChild = TreeList_GetChildrenCount(hItem); iChild--; ) {
+ HTREELISTITEM hNode = TreeList_GetChild(hItem, iChild);
+ CJabberSDNode *pNode = (CJabberSDNode *)TreeList_GetData(hNode);
+ if ( pNode )
+ {
+ TreeList_ResetItem(GetDlgItem(m_pDlgServiceDiscovery->GetHwnd(), IDC_TREE_DISCO), hNode);
+ pNode->ResetInfo();
+ SendBothRequests( pNode, packet );
+ TreeList_MakeFakeParent(hNode, FALSE);
+ }
+
+ if ( xmlGetChildCount( packet ) > 50 ) {
+ m_ThreadInfo->send( packet );
+ packet = XmlNode( NULL );
+ } }
+ m_SDManager.Unlock();
+
+ if ( xmlGetChildCount( packet ))
+ m_ThreadInfo->send( packet );
+ break;
+ }
+
+ case SD_ACT_COPYJID:
+ JabberCopyText(m_pDlgServiceDiscovery->GetHwnd(), pNode->GetJid());
+ break;
+
+ case SD_ACT_COPYNODE:
+ JabberCopyText(m_pDlgServiceDiscovery->GetHwnd(), pNode->GetNode());
+ break;
+
+ case SD_ACT_COPYINFO:
+ {
+ TCHAR buf[8192];
+ pNode->GetTooltipText(buf, SIZEOF(buf));
+ JabberCopyText(m_pDlgServiceDiscovery->GetHwnd(), buf);
+ break;
+ }
+
+ case SD_ACT_FAVORITE:
+ {
+ char setting[MAXMODULELABELLENGTH];
+ int count = JGetDword(NULL, "discoWnd_favCount", 0);
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", count);
+ JSetStringT(NULL, setting, pNode->GetName() ? pNode->GetName() : pNode->GetJid());
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", count);
+ JSetStringT(NULL, setting, pNode->GetJid());
+ mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", count);
+ JSetStringT(NULL, setting, pNode->GetNode() ? pNode->GetNode() : _T(""));
+ JSetDword(NULL, "discoWnd_favCount", ++count);
+ break;
+ }
+
+ case SD_ACT_REGISTER:
+ RegisterAgent(m_pDlgServiceDiscovery->GetHwnd(), pNode->GetJid());
+ break;
+
+ case SD_ACT_ADHOC:
+ ContactMenuAdhocCommands( new CJabberAdhocStartupParams( this, pNode->GetJid(), pNode->GetNode()));
+ break;
+
+ case SD_ACT_ADDDIRECTORY:
+ SearchAddToRecent(pNode->GetJid());
+ break;
+
+ case SD_ACT_PROXY:
+ m_options.BsDirect = FALSE;
+ m_options.BsProxyManual = TRUE;
+ JSetStringT( NULL, "BsProxyServer", pNode->GetJid());
+ break;
+
+ case SD_ACT_JOIN:
+ if ( jabberChatDllPresent )
+ GroupchatJoinRoomByJid(m_pDlgServiceDiscovery->GetHwnd(), pNode->GetJid());
+ else
+ JabberChatDllError();
+ break;
+
+ case SD_ACT_BOOKMARK:
+ {
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_BOOKMARK, pNode->GetJid());
+ if ( item == NULL ) {
+ item = ListGetItemPtr( LIST_BOOKMARK, pNode->GetJid());
+ if ( item == NULL ) {
+ item = ListAdd( LIST_ROOM, pNode->GetJid());
+ item->name = mir_tstrdup( pNode->GetName());
+ }
+ if ( item != NULL ) {
+ item->type = _T("conference");
+ AddEditBookmark( item );
+ } }
+ break;
+ }
+
+ case SD_ACT_USERMENU:
+ {
+ HANDLE hContact = HContactFromJID( pNode->GetJid());
+ if ( !hContact ) {
+ hContact = DBCreateContact( pNode->GetJid(), pNode->GetName(), TRUE, FALSE );
+ JABBER_LIST_ITEM* item = ListAdd( LIST_VCARD_TEMP, pNode->GetJid());
+ item->bUseResource = TRUE;
+ }
+ HMENU hContactMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)hContact, 0);
+ GetCursorPos(&pt);
+ int res = TrackPopupMenu(hContactMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlgServiceDiscovery->GetHwnd(), NULL);
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(res, MPCF_CONTACTMENU), (LPARAM)hContact);
+ break;
+ }
+
+ case SD_ACT_VCARD:
+ {
+ TCHAR * jid = pNode->GetJid();
+ HANDLE hContact = HContactFromJID(pNode->GetJid());
+ if ( !hContact ) {
+ JABBER_SEARCH_RESULT jsr={0};
+ mir_sntprintf( jsr.jid, SIZEOF(jsr.jid), _T("%s"), jid );
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+ hContact = ( HANDLE )CallProtoService( m_szModuleName, PS_ADDTOLIST, PALF_TEMPORARY, ( LPARAM )&jsr );
+ }
+ if ( ListGetItemPtr( LIST_VCARD_TEMP, pNode->GetJid()) == NULL ) {
+ JABBER_LIST_ITEM* item = ListAdd( LIST_VCARD_TEMP, pNode->GetJid());
+ item->bUseResource = TRUE;
+ if ( item->resource == NULL )
+ ListAddResource( LIST_VCARD_TEMP, jid, ID_STATUS_OFFLINE, NULL, 0);
+ }
+ CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)hContact, 0);
+ break;
+ }
+
+ case SD_ACT_ROSTER:
+ {
+ HANDLE hContact = DBCreateContact(pNode->GetJid(), pNode->GetName(), FALSE, FALSE);
+ DBDeleteContactSetting( hContact, "CList", "NotOnList" );
+ JABBER_LIST_ITEM* item = ListAdd( LIST_VCARD_TEMP, pNode->GetJid());
+ item->bUseResource = TRUE;
+ break;
+ }
+
+ case SD_ACT_LOGON:
+ case SD_ACT_LOGOFF:
+ m_ThreadInfo->send( XmlNode( _T("presence")) << XATTR( _T("to"), pNode->GetJid()) << XATTR( _T("type"), ( res != SD_ACT_LOGON ) ? _T("unavailable") : NULL ));
+ break;
+
+ case SD_ACT_UNREGISTER:
+ m_ThreadInfo->send( XmlNodeIq( _T("set"), SerialNext(), pNode->GetJid()) << XQUERY( _T(JABBER_FEAT_REGISTER)) << XCHILD( _T("remove")));
+
+ m_ThreadInfo->send( XmlNodeIq( _T("set"), SerialNext()) << XQUERY( _T(JABBER_FEAT_IQ_ROSTER))
+ << XCHILD( _T("item")) << XATTR( _T("jid"), pNode->GetJid()) << XATTR( _T("subscription"), _T("remove")));
+ break;
+
+ default:
+ if ((res >= CLISTMENUIDMIN) && (res <= CLISTMENUIDMAX)) {
+ HANDLE hContact = HContactFromJID(pNode->GetJid());
+ if (hContact)
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(res, MPCF_CONTACTMENU), (LPARAM)hContact);
+ }
+ break;
+ }
+}
+
+void CJabberProto::LaunchServiceDiscovery(TCHAR *jid)
+{
+ if ( m_pDlgServiceDiscovery ) {
+ SetForegroundWindow( m_pDlgServiceDiscovery->GetHwnd());
+ if (jid) {
+ SetDlgItemText( m_pDlgServiceDiscovery->GetHwnd(), IDC_COMBO_JID, jid);
+ SetDlgItemTextA( m_pDlgServiceDiscovery->GetHwnd(), IDC_COMBO_NODE, "");
+ PostMessage( m_pDlgServiceDiscovery->GetHwnd(), WM_COMMAND, MAKEWPARAM( IDC_BUTTON_BROWSE, 0 ), 0 );
+ }
+ } else {
+ m_pDlgServiceDiscovery = new CJabberDlgDiscovery(this, jid);
+ m_pDlgServiceDiscovery->Show();
+ }
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleServiceDiscovery( WPARAM, LPARAM )
+{
+ LaunchServiceDiscovery(NULL);
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleServiceDiscoveryMyTransports( WPARAM, LPARAM )
+{
+ LaunchServiceDiscovery(_T(SD_FAKEJID_MYAGENTS));
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleServiceDiscoveryTransports( WPARAM, LPARAM )
+{
+ LaunchServiceDiscovery(_T(SD_FAKEJID_AGENTS));
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleServiceDiscoveryConferences( WPARAM, LPARAM )
+{
+ LaunchServiceDiscovery(_T(SD_FAKEJID_CONFERENCES));
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber_disco.h b/protocols/JabberG/src/jabber_disco.h new file mode 100644 index 0000000000..79df12781e --- /dev/null +++ b/protocols/JabberG/src/jabber_disco.h @@ -0,0 +1,493 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2005-07 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_DISCO_H_
+#define _JABBER_DISCO_H_
+
+#define CHR_BULLET ((WCHAR)0x2022)
+// #define STR_BULLET L" \u2022 "
+
+#define JABBER_DISCO_RESULT_NOT_REQUESTED 0
+#define JABBER_DISCO_RESULT_ERROR -1
+#define JABBER_DISCO_RESULT_OK -2
+
+class CJabberSDIdentity;
+class CJabberSDIdentity
+{
+protected:
+ TCHAR *m_szCategory;
+ TCHAR *m_szType;
+ TCHAR *m_szName;
+ CJabberSDIdentity *m_pNext;
+public:
+ CJabberSDIdentity(const TCHAR *szCategory, const TCHAR *szType, const TCHAR *szName)
+ {
+ m_szCategory = mir_tstrdup(szCategory);
+ m_szType = mir_tstrdup(szType);
+ m_szName = mir_tstrdup(szName);
+ m_pNext = NULL;
+ }
+ ~CJabberSDIdentity()
+ {
+ mir_free(m_szCategory);
+ mir_free(m_szType);
+ mir_free(m_szName);
+ if (m_pNext)
+ delete m_pNext;
+ }
+ TCHAR* GetCategory()
+ {
+ return m_szCategory;
+ }
+ TCHAR* GetType()
+ {
+ return m_szType;
+ }
+ TCHAR* GetName()
+ {
+ return m_szName;
+ }
+ CJabberSDIdentity* GetNext()
+ {
+ return m_pNext;
+ }
+ CJabberSDIdentity* SetNext(CJabberSDIdentity *pNext)
+ {
+ CJabberSDIdentity *pRetVal = m_pNext;
+ m_pNext = pNext;
+ return pRetVal;
+ }
+};
+
+class CJabberSDFeature;
+class CJabberSDFeature
+{
+protected:
+ TCHAR *m_szVar;
+ CJabberSDFeature *m_pNext;
+public:
+ CJabberSDFeature(const TCHAR *szVar)
+ {
+ m_szVar = szVar ? mir_tstrdup(szVar) : NULL;
+ m_pNext = NULL;
+ }
+ ~CJabberSDFeature()
+ {
+ mir_free(m_szVar);
+ if (m_pNext)
+ delete m_pNext;
+ }
+ TCHAR* GetVar()
+ {
+ return m_szVar;
+ }
+ CJabberSDFeature* GetNext()
+ {
+ return m_pNext;
+ }
+ CJabberSDFeature* SetNext(CJabberSDFeature *pNext)
+ {
+ CJabberSDFeature *pRetVal = m_pNext;
+ m_pNext = pNext;
+ return pRetVal;
+ }
+};
+
+class CJabberSDNode;
+class CJabberSDNode
+{
+protected:
+ TCHAR *m_szJid;
+ TCHAR *m_szNode;
+ TCHAR *m_szName;
+ CJabberSDIdentity *m_pIdentities;
+ CJabberSDFeature *m_pFeatures;
+ CJabberSDNode *m_pNext;
+ CJabberSDNode *m_pChild;
+ DWORD m_dwInfoRequestTime;
+ DWORD m_dwItemsRequestTime;
+ int m_nInfoRequestId;
+ int m_nItemsRequestId;
+ HTREELISTITEM m_hTreeItem;
+ TCHAR *m_szInfoError;
+ TCHAR *m_szItemsError;
+public:
+ CJabberSDNode( const TCHAR *szJid = NULL, const TCHAR *szNode = NULL, const TCHAR *szName = NULL)
+ {
+ m_szJid = mir_tstrdup(szJid);
+ m_szNode = mir_tstrdup(szNode);
+ m_szName = mir_tstrdup(szName);
+ m_pIdentities = NULL;
+ m_pFeatures = NULL;
+ m_pNext = NULL;
+ m_pChild = NULL;
+ m_dwInfoRequestTime = 0;
+ m_dwItemsRequestTime = 0;
+ m_nInfoRequestId = 0;
+ m_nItemsRequestId = 0;
+ m_hTreeItem = NULL;
+ m_szInfoError = NULL;
+ m_szItemsError = NULL;
+ }
+ ~CJabberSDNode()
+ {
+ RemoveAll();
+ }
+ BOOL RemoveAll()
+ {
+ replaceStrT( m_szJid, NULL );
+ replaceStrT( m_szNode, NULL );
+ replaceStrT( m_szName, NULL );
+ replaceStrT( m_szInfoError, NULL );
+ replaceStrT( m_szItemsError, NULL );
+ if ( m_pIdentities )
+ delete m_pIdentities;
+ m_pIdentities = NULL;
+ if ( m_pFeatures )
+ delete m_pFeatures;
+ m_pFeatures = NULL;
+ if ( m_pNext )
+ delete m_pNext;
+ m_pNext = NULL;
+ if ( m_pChild )
+ delete m_pChild;
+ m_pChild = NULL;
+ m_nInfoRequestId = JABBER_DISCO_RESULT_NOT_REQUESTED;
+ m_nItemsRequestId = JABBER_DISCO_RESULT_NOT_REQUESTED;
+ m_dwInfoRequestTime = 0;
+ m_dwItemsRequestTime = 0;
+ m_hTreeItem = NULL;
+ return TRUE;
+ }
+ BOOL ResetInfo()
+ {
+ replaceStrT( m_szInfoError, NULL );
+ replaceStrT( m_szItemsError, NULL );
+ if ( m_pIdentities )
+ delete m_pIdentities;
+ m_pIdentities = NULL;
+ if ( m_pFeatures )
+ delete m_pFeatures;
+ m_pFeatures = NULL;
+ if ( m_pChild )
+ delete m_pChild;
+ m_pChild = NULL;
+ m_nInfoRequestId = JABBER_DISCO_RESULT_NOT_REQUESTED;
+ m_nItemsRequestId = JABBER_DISCO_RESULT_NOT_REQUESTED;
+ m_dwInfoRequestTime = 0;
+ m_dwItemsRequestTime = 0;
+ return TRUE;
+ }
+ BOOL SetTreeItemHandle(HTREELISTITEM hItem)
+ {
+ m_hTreeItem = hItem;
+ return TRUE;
+ }
+ HTREELISTITEM GetTreeItemHandle()
+ {
+ return m_hTreeItem;
+ }
+ BOOL SetInfoRequestId(int nId)
+ {
+ m_nInfoRequestId = nId;
+ m_dwInfoRequestTime = GetTickCount();
+ return TRUE;
+ }
+ int GetInfoRequestId()
+ {
+ return m_nInfoRequestId;
+ }
+ BOOL SetItemsRequestId(int nId)
+ {
+ m_nItemsRequestId = nId;
+ m_dwItemsRequestTime = GetTickCount();
+ return TRUE;
+ }
+ int GetItemsRequestId()
+ {
+ return m_nItemsRequestId;
+ }
+ BOOL SetJid(TCHAR *szJid)
+ {
+ replaceStrT(m_szJid, szJid);
+ return TRUE;
+ }
+ TCHAR* GetJid()
+ {
+ return m_szJid;
+ }
+ BOOL SetNode(TCHAR *szNode)
+ {
+ replaceStrT(m_szNode, szNode);
+ return TRUE;
+ }
+ TCHAR* GetNode()
+ {
+ return m_szNode;
+ }
+ TCHAR* GetName()
+ {
+ return m_szName;
+ }
+ CJabberSDIdentity* GetFirstIdentity()
+ {
+ return m_pIdentities;
+ }
+ CJabberSDFeature* GetFirstFeature()
+ {
+ return m_pFeatures;
+ }
+ CJabberSDNode* GetFirstChildNode()
+ {
+ return m_pChild;
+ }
+ CJabberSDNode* GetNext()
+ {
+ return m_pNext;
+ }
+ CJabberSDNode* SetNext(CJabberSDNode *pNext)
+ {
+ CJabberSDNode *pRetVal = m_pNext;
+ m_pNext = pNext;
+ return pRetVal;
+ }
+ CJabberSDNode* FindByIqId(int nIqId, BOOL bInfoId = TRUE)
+ {
+ if (( m_nInfoRequestId == nIqId && bInfoId ) || ( m_nItemsRequestId == nIqId && !bInfoId ))
+ return this;
+
+ CJabberSDNode *pNode = NULL;
+ if ( m_pChild && (pNode = m_pChild->FindByIqId( nIqId, bInfoId )))
+ return pNode;
+
+ CJabberSDNode *pTmpNode = NULL;
+ pNode = m_pNext;
+ while ( pNode ) {
+ if (( pNode->m_nInfoRequestId == nIqId && bInfoId ) || ( pNode->m_nItemsRequestId == nIqId && !bInfoId ))
+ return pNode;
+ if ( pNode->m_pChild && (pTmpNode = pNode->m_pChild->FindByIqId( nIqId, bInfoId )))
+ return pTmpNode;
+ pNode = pNode->GetNext();
+ }
+ return NULL;
+ }
+ BOOL AddFeature(const TCHAR *szFeature)
+ {
+ if ( !szFeature )
+ return FALSE;
+
+ CJabberSDFeature *pFeature = new CJabberSDFeature( szFeature );
+ if ( !pFeature )
+ return FALSE;
+
+ pFeature->SetNext( m_pFeatures );
+ m_pFeatures = pFeature;
+
+ return TRUE;
+ }
+ BOOL AddIdentity(const TCHAR *szCategory, const TCHAR *szType, const TCHAR *szName)
+ {
+ if ( !szCategory || !szType )
+ return FALSE;
+
+ CJabberSDIdentity *pIdentity = new CJabberSDIdentity( szCategory, szType, szName );
+ if ( !pIdentity )
+ return FALSE;
+
+ pIdentity->SetNext( m_pIdentities );
+ m_pIdentities = pIdentity;
+
+ return TRUE;
+ }
+ BOOL AddChildNode(const TCHAR *szJid, const TCHAR *szNode, const TCHAR *szName)
+ {
+ if ( !szJid )
+ return FALSE;
+
+ CJabberSDNode *pNode = new CJabberSDNode( szJid, szNode, szName );
+ if ( !pNode )
+ return FALSE;
+
+ pNode->SetNext( m_pChild );
+ m_pChild = pNode;
+
+ return TRUE;
+ }
+ BOOL AppendString(TCHAR **ppBuffer, TCHAR *szString)
+ {
+ if ( !*ppBuffer ) {
+ *ppBuffer = mir_tstrdup( szString );
+ return TRUE;
+ }
+
+ *ppBuffer = (TCHAR *)mir_realloc( *ppBuffer, (_tcslen( *ppBuffer) + _tcslen(szString) + 1 ) * sizeof( TCHAR ));
+ _tcscat(*ppBuffer, szString);
+
+ return TRUE;
+ }
+ BOOL SetItemsRequestErrorText(TCHAR *szError)
+ {
+ replaceStrT(m_szItemsError, szError);
+ return TRUE;
+ }
+ BOOL SetInfoRequestErrorText(TCHAR *szError)
+ {
+ replaceStrT(m_szInfoError, szError);
+ return TRUE;
+ }
+ BOOL GetTooltipText(TCHAR *szText, int nMaxLength)
+ {
+ TCHAR *szBuffer = NULL;
+
+ TCHAR szTmp[ 8192 ];
+
+ mir_sntprintf( szTmp, SIZEOF( szTmp ), _T("Jid: %s\r\n"), m_szJid );
+ AppendString( &szBuffer, szTmp );
+
+ if ( m_szNode ) {
+ mir_sntprintf( szTmp, SIZEOF( szTmp ), _T("%s: %s\r\n"), TranslateT("Node"), m_szNode );
+ AppendString( &szBuffer, szTmp );
+ }
+
+ if ( m_pIdentities ) {
+ mir_sntprintf( szTmp, SIZEOF( szTmp ), _T("\r\n%s:\r\n"), TranslateT("Identities"));
+ AppendString( &szBuffer, szTmp );
+
+ CJabberSDIdentity *pIdentity = m_pIdentities;
+ while ( pIdentity ) {
+ if ( pIdentity->GetName())
+ mir_sntprintf( szTmp, SIZEOF( szTmp ), _T(" %c %s (%s: %s, %s: %s)\r\n"),
+ CHR_BULLET, pIdentity->GetName(),
+ TranslateT("category"), pIdentity->GetCategory(),
+ TranslateT("type"), pIdentity->GetType());
+ else
+ mir_sntprintf( szTmp, SIZEOF( szTmp ), _T(" %c %s: %s, %s: %s\r\n"),
+ CHR_BULLET,
+ TranslateT("Category"), pIdentity->GetCategory(),
+ TranslateT("Type"), pIdentity->GetType());
+
+ AppendString( &szBuffer, szTmp );
+
+ pIdentity = pIdentity->GetNext();
+ }
+ }
+
+ if ( m_pFeatures ) {
+ mir_sntprintf( szTmp, SIZEOF( szTmp ), _T("\r\n%s:\r\n"), TranslateT("Supported features"));
+ AppendString( &szBuffer, szTmp );
+
+ CJabberSDFeature *pFeature = m_pFeatures;
+ while ( pFeature ) {
+ mir_sntprintf( szTmp, SIZEOF( szTmp ), _T(" %c %s\r\n"), CHR_BULLET, pFeature->GetVar());
+
+ AppendString( &szBuffer, szTmp );
+
+ pFeature = pFeature->GetNext();
+ }
+ }
+
+ if ( m_szInfoError ) {
+ mir_sntprintf( szTmp, SIZEOF( szTmp ), _T("\r\n%s: %s\r\n"), TranslateT("Info request error"), m_szInfoError );
+ AppendString( &szBuffer, szTmp );
+ }
+
+ if ( m_szItemsError ) {
+ mir_sntprintf( szTmp, SIZEOF( szTmp ), _T("\r\n%s: %s\r\n"), TranslateT("Items request error"), m_szItemsError );
+ AppendString( &szBuffer, szTmp );
+ }
+
+ szBuffer[lstrlen(szBuffer)-2] = 0; // remove CR/LF
+ mir_sntprintf( szText, nMaxLength, _T("%s"), szBuffer );
+
+ mir_free( szBuffer );
+
+ return TRUE;
+ }
+};
+
+class CJabberSDManager
+{
+protected:
+ CRITICAL_SECTION m_cs;
+ CJabberSDNode *m_pPrimaryNodes;
+public:
+ CJabberSDManager()
+ {
+ m_pPrimaryNodes = NULL;
+ InitializeCriticalSection(&m_cs);
+ }
+ ~CJabberSDManager()
+ {
+ DeleteCriticalSection(&m_cs);
+ RemoveAll();
+ }
+ void RemoveAll()
+ {
+ delete m_pPrimaryNodes;
+ m_pPrimaryNodes = NULL;
+ }
+ BOOL Lock()
+ {
+ EnterCriticalSection(&m_cs);
+ return TRUE;
+ }
+ BOOL Unlock()
+ {
+ LeaveCriticalSection(&m_cs);
+ return TRUE;
+ }
+ CJabberSDNode* GetPrimaryNode()
+ {
+ return m_pPrimaryNodes;
+ }
+ CJabberSDNode* AddPrimaryNode(const TCHAR *szJid, const TCHAR *szNode, const TCHAR *szName)
+ {
+ if ( !szJid )
+ return FALSE;
+
+ CJabberSDNode *pNode = new CJabberSDNode( szJid, szNode, szName );
+ if ( !pNode )
+ return NULL;
+
+ pNode->SetNext( m_pPrimaryNodes );
+ m_pPrimaryNodes = pNode;
+
+ return pNode;
+ }
+ CJabberSDNode* FindByIqId(int nIqId, BOOL bInfoId = TRUE)
+ {
+ CJabberSDNode *pNode = NULL;
+ CJabberSDNode *pTmpNode = NULL;
+ pNode = m_pPrimaryNodes;
+ while ( pNode ) {
+ if ( pTmpNode = pNode->FindByIqId( nIqId, bInfoId ))
+ return pTmpNode;
+ pNode = pNode->GetNext();
+ }
+ return NULL;
+ }
+};
+
+#undef STR_BULLET // used for formatting
+
+#endif // _JABBER_DISCO_H_
diff --git a/protocols/JabberG/src/jabber_events.cpp b/protocols/JabberG/src/jabber_events.cpp new file mode 100644 index 0000000000..3f416aa54b --- /dev/null +++ b/protocols/JabberG/src/jabber_events.cpp @@ -0,0 +1,234 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "jabber_list.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+#include "m_file.h"
+#include "m_addcontact.h"
+#include "jabber_disco.h"
+#include "m_proto_listeningto.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// OnContactDeleted - processes a contact deletion
+
+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;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberDbSettingChanged - process database changes
+
+static TCHAR* sttSettingToTchar( DBCONTACTWRITESETTING* cws )
+{
+ switch( cws->value.type ) {
+ case DBVT_ASCIIZ:
+ return mir_a2t( cws->value.pszVal );
+
+ case DBVT_UTF8:
+ return mir_utf8decodeT( cws->value.pszVal );
+
+ case DBVT_WCHAR:
+ return mir_u2t( cws->value.pwszVal );
+ }
+ return NULL;
+}
+
+void __cdecl CJabberProto::OnRenameGroup( DBCONTACTWRITESETTING* cws, HANDLE hContact )
+{
+ DBVARIANT jid, dbv;
+ if ( JGetStringT( hContact, "jid", &jid ))
+ return;
+
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_ROSTER, jid.ptszVal );
+ JFreeVariant( &jid );
+ if ( item == NULL )
+ return;
+
+ TCHAR* nick;
+ if ( !DBGetContactSettingTString( hContact, "CList", "MyHandle", &dbv )) {
+ nick = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else if ( !JGetStringT( hContact, "Nick", &dbv )) {
+ nick = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else nick = JabberNickFromJID( item->jid );
+ if ( nick == NULL )
+ return;
+
+ if ( cws->value.type == DBVT_DELETED ) {
+ if ( item->group != NULL ) {
+ Log( "Group set to nothing" );
+ AddContactToRoster( item->jid, nick, NULL );
+ }
+ }
+ else {
+ TCHAR* p = sttSettingToTchar( cws );
+ if ( cws->value.pszVal != NULL && lstrcmp( p, item->group )) {
+ Log( "Group set to " TCHAR_STR_PARAM, p );
+ if ( p )
+ AddContactToRoster( item->jid, nick, p );
+ }
+ mir_free( p );
+ }
+ mir_free( nick );
+}
+
+void __cdecl CJabberProto::OnRenameContact( DBCONTACTWRITESETTING* cws, HANDLE hContact )
+{
+ DBVARIANT jid;
+ if ( JGetStringT( hContact, "jid", &jid ))
+ return;
+
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_ROSTER, jid.ptszVal );
+ JFreeVariant( &jid );
+ if ( item == NULL )
+ return;
+
+ if ( cws->value.type == DBVT_DELETED ) {
+ TCHAR* nick = ( TCHAR* )CallService( MS_CLIST_GETCONTACTDISPLAYNAME, ( WPARAM )hContact, GCDNF_NOMYHANDLE | GCDNF_TCHAR );
+ AddContactToRoster( item->jid, nick, item->group );
+ mir_free(nick);
+ return;
+ }
+
+ TCHAR* newNick = sttSettingToTchar( cws );
+ if ( newNick ) {
+ if ( lstrcmp( item->nick, newNick )) {
+ Log( "Renaming contact " TCHAR_STR_PARAM ": " TCHAR_STR_PARAM " -> " TCHAR_STR_PARAM, item->jid, item->nick, newNick );
+ AddContactToRoster( item->jid, newNick, item->group );
+ }
+ mir_free( newNick );
+} }
+
+void __cdecl CJabberProto::OnAddContactForever( DBCONTACTWRITESETTING* cws, HANDLE hContact )
+{
+ if ( cws->value.type != DBVT_DELETED && !( cws->value.type==DBVT_BYTE && cws->value.bVal==0 ))
+ return;
+
+ DBVARIANT jid, dbv;
+ if ( JGetStringT( hContact, "jid", &jid ))
+ return;
+
+ TCHAR *nick;
+ Log( "Add " TCHAR_STR_PARAM " permanently to list", jid.pszVal );
+ if ( !DBGetContactSettingTString( hContact, "CList", "MyHandle", &dbv )) {
+ nick = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else if ( !JGetStringT( hContact, "Nick", &dbv )) {
+ nick = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else nick = JabberNickFromJID( jid.ptszVal );
+ if ( nick == NULL ) {
+ JFreeVariant( &jid );
+ return;
+ }
+
+ if ( !DBGetContactSettingTString( hContact, "CList", "Group", &dbv )) {
+ AddContactToRoster( jid.ptszVal, nick, dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else AddContactToRoster( jid.ptszVal, nick, NULL );
+
+ m_ThreadInfo->send( XmlNode( _T("presence")) << XATTR( _T("to"), jid.ptszVal ) << XATTR( _T("type"), _T("subscribe")));
+
+ SendGetVcard( jid.ptszVal );
+
+ mir_free( nick );
+ DBDeleteContactSetting( hContact, "CList", "Hidden" );
+ JFreeVariant( &jid );
+}
+
+int __cdecl CJabberProto::OnDbSettingChanged( WPARAM wParam, LPARAM lParam )
+{
+ HANDLE hContact = ( HANDLE ) wParam;
+ if ( hContact == NULL || !m_bJabberOnline )
+ return 0;
+
+ DBCONTACTWRITESETTING* cws = ( DBCONTACTWRITESETTING* )lParam;
+ if ( strcmp( cws->szModule, "CList" ))
+ return 0;
+
+ if ( !strcmp( cws->szSetting, "Group" ))
+ OnRenameGroup( cws, hContact );
+ else if ( !strcmp( cws->szSetting, "MyHandle" ))
+ OnRenameContact( cws, hContact );
+ else if ( !strcmp( cws->szSetting, "NotOnList" ))
+ OnAddContactForever( cws, hContact );
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// OnIdleChanged - tracks idle start time for XEP-0012 support
+
+int CJabberProto::OnIdleChanged( WPARAM, LPARAM lParam )
+{
+ // don't report idle time, if user disabled
+ if (lParam & IDF_PRIVACY) {
+ m_tmJabberIdleStartTime = 0;
+ return 0;
+ }
+
+ if ( lParam & IDF_ISIDLE ) {
+ MIRANDA_IDLE_INFO mii = { 0 };
+ mii.cbSize = sizeof( mii );
+ CallService( MS_IDLE_GETIDLEINFO, 0, (LPARAM)&mii );
+ m_tmJabberIdleStartTime = time( 0 ) - mii.idleTime * 60;
+ } else
+ m_tmJabberIdleStartTime = 0;
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber_file.cpp b/protocols/JabberG/src/jabber_file.cpp new file mode 100644 index 0000000000..5db215d024 --- /dev/null +++ b/protocols/JabberG/src/jabber_file.cpp @@ -0,0 +1,552 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include <io.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "jabber_caps.h"
+
+#define JABBER_NETWORK_BUFFER_SIZE 2048
+
+void __cdecl CJabberProto::FileReceiveThread( filetransfer* ft )
+{
+ char* buffer;
+ int datalen;
+ ThreadData info( this, JABBER_SESSION_NORMAL );
+
+ Log( "Thread started: type=file_receive server='%s' port='%d'", ft->httpHostName, ft->httpPort );
+
+ ft->type = FT_OOB;
+
+ if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE )) == NULL ) {
+ Log( "Cannot allocate network buffer, thread ended" );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
+ delete ft;
+ return;
+ }
+
+ NETLIBOPENCONNECTION nloc = { 0 };
+ nloc.cbSize = sizeof( nloc );
+ nloc.cbSize = sizeof( NETLIBOPENCONNECTION );
+ nloc.szHost = ft->httpHostName;
+ nloc.wPort = ft->httpPort;
+ info.s = ( HANDLE ) CallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nloc );
+ if ( info.s == NULL ) {
+ Log( "Connection failed ( %d ), thread ended", WSAGetLastError());
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
+ mir_free( buffer );
+ delete ft;
+ return;
+ }
+
+ ft->s = info.s;
+
+ info.send( "GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n", ft->httpPath, ft->httpHostName );
+ ft->state = FT_CONNECTING;
+
+ Log( "Entering file_receive recv loop" );
+ datalen = 0;
+
+ while ( ft->state != FT_DONE && ft->state != FT_ERROR ) {
+ int recvResult, bytesParsed;
+
+ Log( "Waiting for data..." );
+ recvResult = info.recv( buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen );
+ if ( recvResult <= 0 )
+ break;
+ datalen += recvResult;
+
+ bytesParsed = FileReceiveParse( ft, buffer, datalen );
+ if ( bytesParsed < datalen )
+ memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
+ datalen -= bytesParsed;
+ }
+
+ ft->s = NULL;
+
+ if ( ft->state==FT_DONE || ( ft->state==FT_RECEIVING && ft->std.currentFileSize < 0 ))
+ ft->complete();
+
+ Log( "Thread ended: type=file_receive server='%s'", ft->httpHostName );
+
+ mir_free( buffer );
+ delete ft;
+}
+
+int CJabberProto::FileReceiveParse( filetransfer* ft, char* buffer, int datalen )
+{
+ char* p, *q, *s, *eob;
+ char* str;
+ int num, code;
+
+ eob = buffer + datalen;
+ p = buffer;
+ num = 0;
+ while ( true ) {
+ if ( ft->state==FT_CONNECTING || ft->state==FT_INITIALIZING ) {
+ for ( q=p; q+1<eob && ( *q!='\r' || *( q+1 )!='\n' ); q++ );
+ if ( q+1 < eob ) {
+ if (( str=( char* )mir_alloc( q-p+1 )) != NULL ) {
+ strncpy( str, p, q-p );
+ str[q-p] = '\0';
+ Log( "FT Got: %s", str );
+ if ( ft->state == FT_CONNECTING ) {
+ // looking for "HTTP/1.1 200 OK"
+ if ( sscanf( str, "HTTP/%*d.%*d %d %*s", &code )==1 && code==200 ) {
+ ft->state = FT_INITIALIZING;
+ ft->std.currentFileSize = -1;
+ Log( "Change to FT_INITIALIZING" );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, ft, 0 );
+ }
+ }
+ else { // FT_INITIALIZING
+ if ( str[0] == '\0' ) {
+ TCHAR* s;
+ if (( s = _tcsrchr( ft->httpPath, '/' )) != NULL )
+ s++;
+ else
+ s = ft->httpPath;
+ ft->std.tszCurrentFile = mir_tstrdup( s );
+ JabberHttpUrlDecode( ft->std.tszCurrentFile );
+ if ( ft->create() == -1 ) {
+ ft->state = FT_ERROR;
+ break;
+ }
+ ft->state = FT_RECEIVING;
+ ft->std.currentFileProgress = 0;
+ Log( "Change to FT_RECEIVING" );
+ }
+ else if (( s=strchr( str, ':' )) != NULL ) {
+ *s = '\0';
+ if ( !strcmp( str, "Content-Length" ))
+ ft->std.totalBytes = ft->std.currentFileSize = _atoi64( s+1 );
+ } }
+
+ mir_free( str );
+ q += 2;
+ num += ( q-p );
+ p = q;
+ }
+ else {
+ ft->state = FT_ERROR;
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+ else if ( ft->state == FT_RECEIVING ) {
+ int bufferSize, writeSize;
+ __int64 remainingBytes;
+
+ if ( ft->std.currentFileSize < 0 || ft->std.currentFileProgress < ft->std.currentFileSize ) {
+ bufferSize = eob - p;
+ remainingBytes = ft->std.currentFileSize - ft->std.currentFileProgress;
+ if ( remainingBytes < bufferSize )
+ writeSize = remainingBytes;
+ else
+ writeSize = bufferSize;
+ if ( _write( ft->fileId, p, writeSize ) != writeSize ) {
+ Log( "_write() error" );
+ ft->state = FT_ERROR;
+ }
+ else {
+ ft->std.currentFileProgress += writeSize;
+ ft->std.totalProgress += writeSize;
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std );
+ if ( ft->std.currentFileProgress == ft->std.currentFileSize )
+ ft->state = FT_DONE;
+ }
+ }
+ num = datalen;
+ break;
+ }
+ else break;
+ }
+
+ return num;
+}
+
+void JabberFileServerConnection( JABBER_SOCKET hConnection, DWORD /*dwRemoteIP*/, void* extra )
+{
+ CJabberProto* ppro = ( CJabberProto* )extra;
+
+ NETLIBCONNINFO connInfo = { sizeof(connInfo) };
+ CallService(MS_NETLIB_GETCONNECTIONINFO, (WPARAM)hConnection, (LPARAM)&connInfo);
+
+ TCHAR szPort[10];
+ mir_sntprintf( szPort, SIZEOF( szPort ), _T("%d"), connInfo.wPort );
+ ppro->Log( "File server incoming connection accepted: %s", connInfo.szIpPort );
+
+ JABBER_LIST_ITEM *item = ppro->ListGetItemPtr( LIST_FILE, szPort );
+ if ( item == NULL ) {
+ ppro->Log( "No file is currently served, file server connection closed." );
+ Netlib_CloseHandle( hConnection );
+ return;
+ }
+
+ filetransfer* ft = item->ft;
+ JABBER_SOCKET slisten = ft->s;
+ ft->s = hConnection;
+ ppro->Log( "Set ft->s to %d ( saving %d )", hConnection, slisten );
+
+ char* buffer = ( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE+1 );
+ if ( buffer == NULL ) {
+ ppro->Log( "Cannot allocate network buffer, file server connection closed." );
+ Netlib_CloseHandle( hConnection );
+ ft->state = FT_ERROR;
+ if ( ft->hFileEvent != NULL )
+ SetEvent( ft->hFileEvent );
+ return;
+ }
+
+ ppro->Log( "Entering recv loop for this file connection... ( ft->s is hConnection )" );
+ int datalen = 0;
+ while ( ft->state!=FT_DONE && ft->state!=FT_ERROR ) {
+ int recvResult, bytesParsed;
+
+ recvResult = Netlib_Recv( hConnection, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 );
+ if ( recvResult <= 0 )
+ break;
+ datalen += recvResult;
+
+ buffer[datalen] = '\0';
+ ppro->Log( "RECV:%s", buffer );
+
+ bytesParsed = ppro->FileSendParse( hConnection, ft, buffer, datalen );
+ if ( bytesParsed < datalen )
+ memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
+ datalen -= bytesParsed;
+ }
+
+ ppro->Log( "Closing connection for this file transfer... ( ft->s is now hBind )" );
+ Netlib_CloseHandle( hConnection );
+ ft->s = slisten;
+ ppro->Log( "ft->s is restored to %d", ft->s );
+ if ( ft->hFileEvent != NULL )
+ SetEvent( ft->hFileEvent );
+ mir_free( buffer );
+}
+
+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 ) CallService( 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];
+
+ 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 = NULL;
+ DBVARIANT dbv;
+ if (m_options.BsDirect && m_options.BsDirectManual) {
+ if ( !DBGetContactSettingString( NULL, m_szModuleName, "BsDirectAddr", &dbv ))
+ myAddr = dbv.pszVal;
+ }
+
+ if ( myAddr == NULL )
+ myAddr = (char*)CallService( MS_NETLIB_ADDRESSTOSTRING, 1, nlb.dwExternalIP );
+
+ char szAddr[ 256 ];
+ mir_snprintf( szAddr, sizeof(szAddr), "http://%s:%d/%s", myAddr, nlb.wPort, pFileName );
+
+ mir_free( pFileName );
+ mir_free( myAddr );
+
+ 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 );
+ }
+ 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;
+}
+
+int CJabberProto::FileSendParse( JABBER_SOCKET s, filetransfer* ft, char* buffer, int datalen )
+{
+ char* p, *q, *t, *eob;
+ char* str;
+ int num;
+ int currentFile;
+ int fileId;
+ int numRead;
+
+ eob = buffer + datalen;
+ p = buffer;
+ num = 0;
+ while ( ft->state==FT_CONNECTING || ft->state==FT_INITIALIZING ) {
+ for ( q=p; q+1<eob && ( *q!='\r' || *( q+1 )!='\n' ); q++ );
+ if ( q+1 >= eob )
+ break;
+ if (( str=( char* )mir_alloc( q-p+1 )) == NULL ) {
+ ft->state = FT_ERROR;
+ break;
+ }
+ strncpy( str, p, q-p );
+ str[q-p] = '\0';
+ Log( "FT Got: %s", str );
+ if ( ft->state == FT_CONNECTING ) {
+ // looking for "GET filename.ext HTTP/1.1"
+ if ( !strncmp( str, "GET ", 4 )) {
+ for ( t=str+4; *t!='\0' && *t!=' '; t++ );
+ *t = '\0';
+ for ( t=str+4; *t!='\0' && *t=='/'; t++ );
+ ft->httpPath = mir_a2t( t );
+ JabberHttpUrlDecode( ft->httpPath );
+ ft->state = FT_INITIALIZING;
+ Log( "Change to FT_INITIALIZING" );
+ }
+ }
+ else { // FT_INITIALIZING
+ if ( str[0] == '\0' ) {
+ struct _stati64 statbuf;
+
+ mir_free( str );
+ num += 2;
+
+ currentFile = ft->std.currentFileNumber;
+ TCHAR* t = _tcsrchr( ft->std.ptszFiles[ currentFile ], '\\' );
+ if ( t != NULL )
+ t++;
+ else
+ t = ft->std.ptszFiles[currentFile];
+
+ if ( ft->httpPath==NULL || lstrcmp( ft->httpPath, t )) {
+ if ( ft->httpPath == NULL )
+ Log( "Requested file name does not matched ( httpPath==NULL )" );
+ else
+ Log( "Requested file name does not matched ( '%s' vs. '%s' )", ft->httpPath, t );
+ ft->state = FT_ERROR;
+ break;
+ }
+ Log( "Sending [%s]", ft->std.ptszFiles[ currentFile ] );
+ _tstati64( ft->std.ptszFiles[ currentFile ], &statbuf ); // file size in statbuf.st_size
+ if (( fileId = _topen( ft->std.ptszFiles[currentFile], _O_BINARY|_O_RDONLY )) < 0 ) {
+ Log( "File cannot be opened" );
+ ft->state = FT_ERROR;
+ mir_free( ft->httpPath );
+ ft->httpPath = NULL;
+ break;
+ }
+
+ char fileBuffer[ 2048 ];
+ int bytes = mir_snprintf( fileBuffer, sizeof(fileBuffer), "HTTP/1.1 200 OK\r\nContent-Length: %I64u\r\n\r\n", statbuf.st_size );
+ WsSend( s, fileBuffer, bytes, MSG_DUMPASTEXT );
+
+ ft->std.flags |= PFTS_SENDING;
+ ft->std.currentFileProgress = 0;
+ Log( "Sending file data..." );
+
+ while (( numRead = _read( fileId, fileBuffer, 2048 )) > 0 ) {
+ if ( Netlib_Send( s, fileBuffer, numRead, 0 ) != numRead ) {
+ ft->state = FT_ERROR;
+ break;
+ }
+ ft->std.currentFileProgress += numRead;
+ ft->std.totalProgress += numRead;
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std );
+ }
+ _close( fileId );
+ if ( ft->state != FT_ERROR )
+ ft->state = FT_DONE;
+ Log( "Finishing this file..." );
+ mir_free( ft->httpPath );
+ ft->httpPath = NULL;
+ break;
+ } }
+
+ mir_free( str );
+ q += 2;
+ num += ( q-p );
+ p = q;
+ }
+
+ return num;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// filetransfer class members
+
+filetransfer::filetransfer( CJabberProto* proto )
+{
+ memset( this, 0, sizeof( filetransfer ));
+ ppro = proto;
+ fileId = -1;
+ std.cbSize = sizeof( std );
+ std.flags = PFTS_TCHAR;
+}
+
+filetransfer::~filetransfer()
+{
+ ppro->Log( "Destroying file transfer session %08p", this );
+
+ if ( !bCompleted )
+ ppro->JSendBroadcast( std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, this, 0 );
+
+ close();
+
+ if ( hWaitEvent != INVALID_HANDLE_VALUE )
+ CloseHandle( hWaitEvent );
+
+ if ( jid ) mir_free( jid );
+ if ( sid ) mir_free( sid );
+ if ( iqId ) mir_free( iqId );
+ if ( fileSize ) mir_free( fileSize );
+ if ( httpHostName ) mir_free( httpHostName );
+ if ( httpPath ) mir_free( httpPath );
+ if ( szDescription ) mir_free( szDescription );
+
+ if ( std.tszWorkingDir ) mir_free( std.tszWorkingDir );
+ if ( std.tszCurrentFile ) mir_free( std.tszCurrentFile );
+
+ if ( std.ptszFiles ) {
+ for ( int i=0; i < std.totalFiles; i++ )
+ if ( std.ptszFiles[i] ) mir_free( std.ptszFiles[i] );
+
+ mir_free( std.ptszFiles );
+} }
+
+void filetransfer::close()
+{
+ if ( fileId != -1 ) {
+ _close( fileId );
+ fileId = -1;
+} }
+
+void filetransfer::complete()
+{
+ close();
+
+ bCompleted = true;
+ ppro->JSendBroadcast( std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, this, 0);
+}
+
+int filetransfer::create()
+{
+ if ( fileId != -1 )
+ return fileId;
+
+ TCHAR filefull[ MAX_PATH ];
+ mir_sntprintf( filefull, SIZEOF(filefull), _T("%s\\%s"), std.tszWorkingDir, std.tszCurrentFile );
+ replaceStrT( std.tszCurrentFile, filefull );
+
+ if ( hWaitEvent != INVALID_HANDLE_VALUE )
+ CloseHandle( hWaitEvent );
+ hWaitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+
+ if ( ppro->JSendBroadcast( std.hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, this, ( LPARAM )&std ))
+ WaitForSingleObject( hWaitEvent, INFINITE );
+
+ if ( fileId == -1 ) {
+ ppro->Log( "Saving to [%s]", std.tszCurrentFile );
+ fileId = _topen( std.tszCurrentFile, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE );
+ }
+
+ if ( fileId == -1 )
+ ppro->Log( "Cannot create file '%s' during a file transfer", filefull );
+ else if ( std.currentFileSize != 0 )
+ _chsize( fileId, std.currentFileSize );
+
+ return fileId;
+}
diff --git a/protocols/JabberG/src/jabber_form.cpp b/protocols/JabberG/src/jabber_form.cpp new file mode 100644 index 0000000000..a90dcf62fa --- /dev/null +++ b/protocols/JabberG/src/jabber_form.cpp @@ -0,0 +1,905 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_caps.h"
+
+
+static BOOL CALLBACK JabberFormMultiLineWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ switch ( msg ) {
+ //case WM_GETDLGCODE:
+ // return DLGC_WANTARROWS|DLGC_WANTCHARS|DLGC_HASSETSEL|DLGC_WANTALLKEYS;
+ case WM_KEYDOWN:
+ if ( wParam == VK_TAB ) {
+ SetFocus( GetNextDlgTabItem( GetParent( GetParent( hwnd )), hwnd, GetKeyState( VK_SHIFT )<0?TRUE:FALSE ));
+ return TRUE;
+ };
+ break;
+ }
+ return CallWindowProc(( WNDPROC ) GetWindowLongPtr( hwnd, GWLP_USERDATA ), hwnd, msg, wParam, lParam );
+}
+
+struct TJabberFormControlInfo
+{
+ TJabberFormControlType type;
+ SIZE szBlock;
+ POINT ptLabel, ptCtrl;
+ HWND hLabel, hCtrl;
+};
+typedef LIST<TJabberFormControlInfo> TJabberFormControlList;
+
+struct TJabberFormLayoutInfo
+{
+ int ctrlHeight;
+ int offset, width, maxLabelWidth;
+ int y_pos, y_spacing;
+ int id;
+ bool compact;
+};
+
+void JabberFormCenterContent(HWND hwndStatic)
+{
+ RECT rcWindow;
+ int minX;
+ GetWindowRect(hwndStatic,&rcWindow);
+ minX=rcWindow.right;
+ HWND oldChild=NULL;
+ HWND hWndChild=GetWindow(hwndStatic,GW_CHILD);
+ while (hWndChild!=oldChild && hWndChild!=NULL)
+ {
+ DWORD style=GetWindowLongPtr(hWndChild, GWL_STYLE);
+ RECT rc;
+ GetWindowRect(hWndChild,&rc);
+ if ((style&SS_RIGHT) && !(style&WS_TABSTOP))
+ {
+ TCHAR * text;
+ RECT calcRect=rc;
+ int len=GetWindowTextLength(hWndChild);
+ text=(TCHAR*)malloc(sizeof(TCHAR)*(len+1));
+ GetWindowText(hWndChild,text,len+1);
+ HDC hdc=GetDC(hWndChild);
+ HFONT hfntSave = (HFONT)SelectObject(hdc, (HFONT)SendMessage(hWndChild, WM_GETFONT, 0, 0));
+ DrawText(hdc,text,-1,&calcRect,DT_CALCRECT|DT_WORDBREAK);
+ minX=min(minX, rc.right-(calcRect.right-calcRect.left));
+ SelectObject(hdc, hfntSave);
+ ReleaseDC(hWndChild,hdc);
+ }
+ else
+ {
+ minX=min(minX,rc.left);
+ }
+ oldChild=hWndChild;
+ hWndChild=GetWindow(hWndChild,GW_HWNDNEXT);
+ }
+ if (minX>rcWindow.left+5)
+ {
+ int dx=(minX-rcWindow.left)/2;
+ oldChild=NULL;
+ hWndChild=GetWindow(hwndStatic,GW_CHILD);
+ while (hWndChild!=oldChild && hWndChild!=NULL )
+ {
+ DWORD style=GetWindowLongPtr(hWndChild, GWL_STYLE);
+ RECT rc;
+ GetWindowRect(hWndChild,&rc);
+ if ((style&SS_RIGHT) && !(style&WS_TABSTOP))
+ MoveWindow(hWndChild,rc.left-rcWindow.left,rc.top-rcWindow.top, rc.right-rc.left-dx, rc.bottom-rc.top, TRUE);
+ else
+ MoveWindow(hWndChild,rc.left-dx-rcWindow.left,rc.top-rcWindow.top, rc.right-rc.left, rc.bottom-rc.top, TRUE);
+ oldChild=hWndChild;
+ hWndChild=GetWindow(hWndChild,GW_HWNDNEXT);
+ }
+ }
+}
+
+void JabberFormSetInstruction( HWND hwndForm, const TCHAR *text )
+{
+ if (!text) text = _T("");
+
+ int len = lstrlen(text);
+ int fixedLen = len;
+ for (int i = 1; i < len; ++i)
+ if ((text[i - 1] == _T('\n')) && (text[i] != _T('\r')))
+ ++fixedLen;
+ TCHAR *fixedText = NULL;
+ if (fixedLen != len) {
+ fixedText = (TCHAR *)mir_alloc(sizeof(TCHAR) * (fixedLen+1));
+ TCHAR *p = fixedText;
+ for (int i = 0; i < len; ++i) {
+ *p = text[i];
+ if (i && (text[i] == _T('\n')) && (text[i] != _T('\r'))) {
+ *p++ = _T('\r');
+ *p = _T('\n');
+ }
+ ++p;
+ }
+ *p = 0;
+ text = fixedText;
+ }
+
+ SetDlgItemText( hwndForm, IDC_INSTRUCTION, text );
+
+ RECT rcText;
+ GetWindowRect(GetDlgItem(hwndForm, IDC_INSTRUCTION), &rcText);
+ int oldWidth = rcText.right-rcText.left;
+ int deltaHeight = -(rcText.bottom-rcText.top);
+
+ SetRect(&rcText, 0, 0, rcText.right-rcText.left, 0);
+ HDC hdcEdit = GetDC(GetDlgItem(hwndForm, IDC_INSTRUCTION));
+ HFONT hfntSave = (HFONT)SelectObject(hdcEdit, (HFONT)SendDlgItemMessage(hwndForm, IDC_INSTRUCTION, WM_GETFONT, 0, 0));
+ DrawTextEx(hdcEdit, (TCHAR *)text, lstrlen(text), &rcText,
+ DT_CALCRECT|DT_EDITCONTROL|DT_TOP|DT_WORDBREAK, NULL);
+ SelectObject(hdcEdit, hfntSave);
+ ReleaseDC(GetDlgItem(hwndForm, IDC_INSTRUCTION), hdcEdit);
+
+ RECT rcWindow; GetClientRect(hwndForm, &rcWindow);
+ if (rcText.bottom-rcText.top > (rcWindow.bottom-rcWindow.top)/5) {
+ HWND hwndEdit = GetDlgItem(hwndForm, IDC_INSTRUCTION);
+ SetWindowLongPtr(hwndEdit, GWL_STYLE, WS_VSCROLL | GetWindowLongPtr(hwndEdit, GWL_STYLE));
+ rcText.bottom = rcText.top + (rcWindow.bottom-rcWindow.top)/5;
+ } else {
+ HWND hwndEdit = GetDlgItem(hwndForm, IDC_INSTRUCTION);
+ SetWindowLongPtr(hwndEdit, GWL_STYLE, ~WS_VSCROLL & GetWindowLongPtr(hwndEdit, GWL_STYLE));
+ }
+ deltaHeight += rcText.bottom-rcText.top;
+
+ SetWindowPos(GetDlgItem(hwndForm, IDC_INSTRUCTION), 0, 0, 0,
+ oldWidth,
+ rcText.bottom-rcText.top,
+ SWP_NOMOVE|SWP_NOZORDER);
+
+ GetWindowRect(GetDlgItem(hwndForm, IDC_WHITERECT), &rcText);
+ MapWindowPoints(NULL, hwndForm, (LPPOINT)&rcText, 2);
+ rcText.bottom += deltaHeight;
+ SetWindowPos(GetDlgItem(hwndForm, IDC_WHITERECT), 0, 0, 0,
+ rcText.right-rcText.left,
+ rcText.bottom-rcText.top,
+ SWP_NOMOVE|SWP_NOZORDER);
+
+ GetWindowRect(GetDlgItem(hwndForm, IDC_FRAME1), &rcText);
+ MapWindowPoints(NULL, hwndForm, (LPPOINT)&rcText, 2);
+ rcText.top += deltaHeight;
+ SetWindowPos(GetDlgItem(hwndForm, IDC_FRAME1), 0,
+ rcText.left,
+ rcText.top,
+ 0, 0,
+ SWP_NOSIZE|SWP_NOZORDER);
+
+ GetWindowRect(GetDlgItem(hwndForm, IDC_FRAME), &rcText);
+ MapWindowPoints(NULL, hwndForm, (LPPOINT)&rcText, 2);
+ rcText.top += deltaHeight;
+ SetWindowPos(GetDlgItem(hwndForm, IDC_FRAME), 0,
+ rcText.left,
+ rcText.top,
+ rcText.right-rcText.left,
+ rcText.bottom-rcText.top,
+ SWP_NOZORDER);
+
+ GetWindowRect(GetDlgItem(hwndForm, IDC_VSCROLL), &rcText);
+ MapWindowPoints(NULL, hwndForm, (LPPOINT)&rcText, 2);
+ rcText.top += deltaHeight;
+ SetWindowPos(GetDlgItem(hwndForm, IDC_VSCROLL), 0,
+ rcText.left,
+ rcText.top,
+ rcText.right-rcText.left,
+ rcText.bottom-rcText.top,
+ SWP_NOZORDER);
+
+ if (fixedText) mir_free(fixedText);
+}
+
+static TJabberFormControlType JabberFormTypeNameToId( const TCHAR *type )
+{
+ if ( !_tcscmp( type, _T("text-private")))
+ return JFORM_CTYPE_TEXT_PRIVATE;
+ if ( !_tcscmp( type, _T("text-multi")) || !_tcscmp( type, _T("jid-multi")))
+ return JFORM_CTYPE_TEXT_MULTI;
+ if ( !_tcscmp( type, _T("boolean")))
+ return JFORM_CTYPE_BOOLEAN;
+ if ( !_tcscmp( type, _T("list-single")))
+ return JFORM_CTYPE_LIST_SINGLE;
+ if ( !_tcscmp( type, _T("list-multi")))
+ return JFORM_CTYPE_LIST_MULTI;
+ if ( !_tcscmp( type, _T("fixed")))
+ return JFORM_CTYPE_FIXED;
+ if ( !_tcscmp( type, _T("hidden")))
+ return JFORM_CTYPE_HIDDEN;
+ // else
+ return JFORM_CTYPE_TEXT_SINGLE;
+}
+
+void JabberFormLayoutSingleControl(TJabberFormControlInfo *item, TJabberFormLayoutInfo *layout_info, const TCHAR *labelStr, const TCHAR *valueStr)
+{
+ RECT rcLabel = {0}, rcCtrl = {0};
+ if (item->hLabel)
+ {
+ SetRect(&rcLabel, 0, 0, layout_info->width, 0);
+ HDC hdc = GetDC( item->hLabel );
+ HFONT hfntSave = (HFONT)SelectObject(hdc, (HFONT)SendMessage(item->hLabel, WM_GETFONT, 0, 0));
+ DrawText( hdc, labelStr, -1, &rcLabel, DT_CALCRECT|DT_WORDBREAK );
+ SelectObject(hdc, hfntSave);
+ ReleaseDC(item->hLabel, hdc);
+ }
+
+ int indent = layout_info->compact ? 10 : 20;
+
+ if ((layout_info->compact && (item->type != JFORM_CTYPE_BOOLEAN) && (item->type != JFORM_CTYPE_FIXED))||
+ (rcLabel.right >= layout_info->maxLabelWidth) ||
+ (rcLabel.bottom > layout_info->ctrlHeight) ||
+ (item->type == JFORM_CTYPE_LIST_MULTI) ||
+ (item->type == JFORM_CTYPE_TEXT_MULTI))
+ {
+ int height = layout_info->ctrlHeight;
+ if ((item->type == JFORM_CTYPE_LIST_MULTI) || (item->type == JFORM_CTYPE_TEXT_MULTI)) height *= 3;
+ SetRect(&rcCtrl, indent, rcLabel.bottom, layout_info->width, rcLabel.bottom + height);
+ } else
+ if (item->type == JFORM_CTYPE_BOOLEAN)
+ {
+ SetRect(&rcCtrl, 0, 0, layout_info->width-20, 0);
+ HDC hdc = GetDC( item->hCtrl );
+ HFONT hfntSave = (HFONT)SelectObject(hdc, (HFONT)SendMessage(item->hCtrl, WM_GETFONT, 0, 0));
+ DrawText( hdc, labelStr, -1, &rcCtrl, DT_CALCRECT|DT_RIGHT|DT_WORDBREAK );
+ SelectObject(hdc, hfntSave);
+ ReleaseDC(item->hCtrl, hdc);
+ rcCtrl.right += 20;
+ } else
+ if (item->type == JFORM_CTYPE_FIXED)
+ {
+ SetRect(&rcCtrl, 0, 0, layout_info->width, 0);
+ HDC hdc = GetDC( item->hCtrl );
+ HFONT hfntSave = (HFONT)SelectObject(hdc, (HFONT)SendMessage(item->hCtrl, WM_GETFONT, 0, 0));
+ DrawText( hdc, valueStr, -1, &rcCtrl, DT_CALCRECT|DT_EDITCONTROL );
+ rcCtrl.right += 20;
+ SelectObject(hdc, hfntSave);
+ ReleaseDC(item->hCtrl, hdc);
+ } else
+ {
+ SetRect(&rcCtrl, rcLabel.right+5, 0, layout_info->width, layout_info->ctrlHeight);
+ rcLabel.bottom = rcCtrl.bottom;
+ }
+
+ if (item->hLabel)
+ SetWindowPos(item->hLabel, 0,
+ 0, 0, rcLabel.right-rcLabel.left, rcLabel.bottom-rcLabel.top,
+ SWP_NOZORDER|SWP_NOMOVE);
+ if (item->hCtrl)
+ SetWindowPos(item->hCtrl, 0,
+ 0, 0, rcCtrl.right-rcCtrl.left, rcCtrl.bottom-rcCtrl.top,
+ SWP_NOZORDER|SWP_NOMOVE);
+
+ item->ptLabel.x = rcLabel.left;
+ item->ptLabel.y = rcLabel.top;
+ item->ptCtrl.x = rcCtrl.left;
+ item->ptCtrl.y = rcCtrl.top;
+ item->szBlock.cx = layout_info->width;
+ item->szBlock.cy = max(rcLabel.bottom, rcCtrl.bottom);
+}
+
+#define JabberFormCreateLabel() \
+ CreateWindow( _T("static"), labelStr, WS_CHILD|WS_VISIBLE|SS_CENTERIMAGE, \
+ 0, 0, 0, 0, hwndStatic, ( HMENU )-1, hInst, NULL )
+
+TJabberFormControlInfo *JabberFormAppendControl(HWND hwndStatic, TJabberFormLayoutInfo *layout_info, TJabberFormControlType type, const TCHAR *labelStr, const TCHAR *valueStr)
+{
+ TJabberFormControlList *controls = (TJabberFormControlList *)GetWindowLongPtr(hwndStatic, GWLP_USERDATA);
+ if (!controls)
+ {
+ controls = new TJabberFormControlList(5);
+ SetWindowLongPtr(hwndStatic, GWLP_USERDATA, (LONG_PTR)controls);
+ }
+
+ TJabberFormControlInfo *item = (TJabberFormControlInfo *)mir_alloc(sizeof(TJabberFormControlInfo));
+ item->type = type;
+ item->hLabel = item->hCtrl = NULL;
+
+ switch (type)
+ {
+ case JFORM_CTYPE_TEXT_PRIVATE:
+ {
+ item->hLabel = JabberFormCreateLabel();
+ item->hCtrl = CreateWindowEx( WS_EX_CLIENTEDGE, _T("edit"), valueStr,
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL|ES_PASSWORD,
+ 0, 0, 0, 0,
+ hwndStatic, ( HMENU ) layout_info->id, hInst, NULL );
+ ++layout_info->id;
+ break;
+ }
+ case JFORM_CTYPE_TEXT_MULTI:
+ {
+ item->hLabel = JabberFormCreateLabel();
+ item->hCtrl = CreateWindowEx( WS_EX_CLIENTEDGE, _T("edit"), valueStr,
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_VSCROLL|ES_LEFT|ES_MULTILINE|ES_AUTOVSCROLL|ES_WANTRETURN,
+ 0, 0, 0, 0,
+ hwndStatic, ( HMENU ) layout_info->id, hInst, NULL );
+ WNDPROC oldWndProc = ( WNDPROC ) SetWindowLongPtr( item->hCtrl, GWLP_WNDPROC, ( LONG_PTR )JabberFormMultiLineWndProc );
+ SetWindowLongPtr( item->hCtrl, GWLP_USERDATA, ( LONG_PTR ) oldWndProc );
+ ++layout_info->id;
+ break;
+ }
+ case JFORM_CTYPE_BOOLEAN:
+ {
+ item->hCtrl = CreateWindowEx( 0, _T("button"), labelStr,
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX|BS_MULTILINE,
+ 0, 0, 0, 0,
+ hwndStatic, ( HMENU ) layout_info->id, hInst, NULL );
+ if ( valueStr && !_tcscmp( valueStr, _T("1")))
+ SendMessage( item->hCtrl, BM_SETCHECK, 1, 0 );
+ ++layout_info->id;
+ break;
+ }
+ case JFORM_CTYPE_LIST_SINGLE:
+ {
+ item->hLabel = JabberFormCreateLabel();
+ item->hCtrl = CreateWindowExA( WS_EX_CLIENTEDGE, "combobox", NULL,
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|CBS_DROPDOWNLIST,
+ 0, 0, 0, 0,
+ hwndStatic, ( HMENU ) layout_info->id, hInst, NULL );
+ ++layout_info->id;
+ break;
+ }
+ case JFORM_CTYPE_LIST_MULTI:
+ {
+ item->hLabel = JabberFormCreateLabel();
+ item->hCtrl = CreateWindowExA( WS_EX_CLIENTEDGE, "listbox",
+ NULL, WS_CHILD|WS_VISIBLE|WS_TABSTOP|LBS_MULTIPLESEL,
+ 0, 0, 0, 0,
+ hwndStatic, ( HMENU ) layout_info->id, hInst, NULL );
+ ++layout_info->id;
+ break;
+ }
+ case JFORM_CTYPE_FIXED:
+ {
+ item->hCtrl = CreateWindow( _T("edit"), valueStr,
+ WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_READONLY|ES_AUTOHSCROLL,
+ 0, 0, 0, 0,
+ hwndStatic, ( HMENU )-1, hInst, NULL );
+ break;
+ }
+ case JFORM_CTYPE_HIDDEN:
+ {
+ break;
+ }
+ case JFORM_CTYPE_TEXT_SINGLE:
+ {
+ item->hLabel = labelStr ? (JabberFormCreateLabel()) : NULL;
+ item->hCtrl = CreateWindowEx( WS_EX_CLIENTEDGE, _T("edit"), valueStr,
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL,
+ 0, 0, 0, 0,
+ hwndStatic, ( HMENU ) layout_info->id, hInst, NULL );
+ ++layout_info->id;
+ break;
+ }
+ }
+
+ HFONT hFont = ( HFONT ) SendMessage( GetParent(hwndStatic), WM_GETFONT, 0, 0 );
+ if (item->hLabel) SendMessage( item->hLabel, WM_SETFONT, ( WPARAM ) hFont, 0 );
+ if (item->hCtrl) SendMessage( item->hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 );
+
+ JabberFormLayoutSingleControl(item, layout_info, labelStr, valueStr);
+
+ controls->insert(item);
+ return item;
+}
+
+void JabberFormAddListItem(TJabberFormControlInfo *item, TCHAR *text, bool selected)
+{
+ DWORD dwIndex;
+ switch (item->type)
+ {
+ case JFORM_CTYPE_LIST_MULTI:
+ dwIndex = SendMessage(item->hCtrl, LB_ADDSTRING, 0, (LPARAM)text);
+ if (selected) SendMessage(item->hCtrl, LB_SETSEL, TRUE, dwIndex);
+ break;
+ case JFORM_CTYPE_LIST_SINGLE:
+ dwIndex = SendMessage(item->hCtrl, CB_ADDSTRING, 0, (LPARAM)text);
+ if (selected) SendMessage(item->hCtrl, CB_SETCURSEL, dwIndex, 0);
+ break;
+ }
+}
+
+void JabberFormLayoutControls(HWND hwndStatic, TJabberFormLayoutInfo *layout_info, int *formHeight)
+{
+ TJabberFormControlList *controls = (TJabberFormControlList *)GetWindowLongPtr(hwndStatic, GWLP_USERDATA);
+ if (!controls) return;
+
+ for (int i = 0; i < controls->getCount(); ++i)
+ {
+ if ((*controls)[i]->hLabel)
+ SetWindowPos((*controls)[i]->hLabel, 0,
+ layout_info->offset+(*controls)[i]->ptLabel.x, layout_info->y_pos+(*controls)[i]->ptLabel.y, 0, 0,
+ SWP_NOZORDER|SWP_NOSIZE);
+ if ((*controls)[i]->hCtrl)
+ SetWindowPos((*controls)[i]->hCtrl, 0,
+ layout_info->offset+(*controls)[i]->ptCtrl.x, layout_info->y_pos+(*controls)[i]->ptCtrl.y, 0, 0,
+ SWP_NOZORDER|SWP_NOSIZE);
+
+ layout_info->y_pos += (*controls)[i]->szBlock.cy;
+ layout_info->y_pos += layout_info->y_spacing;
+ }
+
+ *formHeight = layout_info->y_pos + (layout_info->compact ? 0 : 9);
+}
+
+HJFORMLAYOUT JabberFormCreateLayout(HWND hwndStatic)
+{
+ RECT frameRect;
+ GetClientRect( hwndStatic, &frameRect );
+
+ TJabberFormLayoutInfo *layout_info = (TJabberFormLayoutInfo *)mir_alloc(sizeof(TJabberFormLayoutInfo));
+ layout_info->compact = false;
+ layout_info->ctrlHeight = 20;
+ layout_info->id = 0;
+ layout_info->width = frameRect.right - frameRect.left - 20 - 10;
+ layout_info->y_spacing = 5;
+ layout_info->maxLabelWidth = layout_info->width*2/5;
+ layout_info->offset = 10;
+ layout_info->y_pos = 14;
+ return layout_info;
+}
+
+void JabberFormCreateUI( HWND hwndStatic, HXML xNode, int *formHeight, BOOL bCompact )
+{
+ JabberFormDestroyUI(hwndStatic);
+
+ HXML v, o, vs;
+
+ int i, j, k;
+ const TCHAR* label, *typeName, *varStr, *str, *valueText;
+ TCHAR *labelStr, *valueStr, *p;
+ RECT frameRect;
+
+ if ( xNode==NULL || xmlGetName( xNode )==NULL || lstrcmp( xmlGetName( xNode ), _T("x")) || hwndStatic==NULL ) return;
+
+ GetClientRect( hwndStatic, &frameRect );
+
+ TJabberFormLayoutInfo layout_info;
+ layout_info.compact = bCompact ? true : false;
+ layout_info.ctrlHeight = 20;
+ layout_info.id = 0;
+ layout_info.width = frameRect.right - frameRect.left - 20;
+ if (!bCompact) layout_info.width -= 10;
+ layout_info.y_spacing = bCompact ? 1 : 5;
+ layout_info.maxLabelWidth = layout_info.width*2/5;
+ layout_info.offset = 10;
+ layout_info.y_pos = bCompact ? 0 : 14;
+ for ( i=0; ; i++ ) {
+ HXML n = xmlGetChild( xNode ,i);
+ if ( !n )
+ break;
+
+ if ( xmlGetName( n )) {
+ if ( !lstrcmp( xmlGetName( n ), _T("field"))) {
+ varStr = xmlGetAttrValue( n, _T("var"));
+ if (( typeName = xmlGetAttrValue( n, _T("type"))) != NULL ) {
+ if (( label = xmlGetAttrValue( n, _T("label"))) != NULL )
+ labelStr = mir_tstrdup( label );
+ else
+ labelStr = mir_tstrdup( varStr );
+
+ TJabberFormControlType type = JabberFormTypeNameToId(typeName);
+
+ if (( v = xmlGetChild( n , "value" )) != NULL )
+ {
+ valueText = xmlGetText( v );
+ if (type != JFORM_CTYPE_TEXT_MULTI)
+ {
+ valueStr = mir_tstrdup( valueText );
+ } else
+ {
+ size_t size = 1;
+ for ( j=0; ; j++ ) {
+ v = xmlGetChild( n ,j);
+ if ( !v )
+ break;
+ if ( xmlGetName( v ) && !lstrcmp( xmlGetName( v ), _T("value")) && xmlGetText( v ))
+ size += _tcslen( xmlGetText( v )) + 2;
+ }
+ valueStr = ( TCHAR* )mir_alloc( sizeof(TCHAR)*size );
+ valueStr[0] = '\0';
+ for ( j=0; ; j++ ) {
+ v = xmlGetChild( n ,j);
+ if ( !v )
+ break;
+ if ( xmlGetName( v ) && !lstrcmp( xmlGetName( v ), _T("value")) && xmlGetText( v )) {
+ if ( valueStr[0] )
+ _tcscat( valueStr, _T("\r\n"));
+ _tcscat( valueStr, xmlGetText( v ));
+ } }
+ }
+ } else
+ {
+ valueText = valueStr = NULL;
+ }
+
+ TJabberFormControlInfo *item = JabberFormAppendControl(hwndStatic, &layout_info, type, labelStr, valueStr);
+
+ mir_free( labelStr );
+ mir_free( valueStr );
+
+ if (type == JFORM_CTYPE_LIST_SINGLE)
+ {
+ for ( j=0; ; j++ ) {
+ o = xmlGetChild( n ,j);
+ if ( !o )
+ break;
+ if ( xmlGetName( o ) && !lstrcmp( xmlGetName( o ), _T("option"))) {
+ if (( v = xmlGetChild( o , "value" )) != NULL && xmlGetText( v )) {
+ if (( str = xmlGetAttrValue( o, _T("label"))) == NULL )
+ str = xmlGetText( v );
+ if (( p = mir_tstrdup( str )) != NULL ) {
+ bool selected = false;
+ if ( valueText != NULL && !_tcscmp( valueText, xmlGetText( v )))
+ selected = true;
+ JabberFormAddListItem(item, p, selected);
+ mir_free( p );
+ } } } }
+ } else
+ if (type == JFORM_CTYPE_LIST_MULTI)
+ {
+ for ( j=0; ; j++ ) {
+ o = xmlGetChild( n ,j);
+ if ( !o )
+ break;
+ if ( xmlGetName( o ) && !lstrcmp( xmlGetName( o ), _T("option"))) {
+ if (( v = xmlGetChild( o , "value" )) != NULL && xmlGetText( v )) {
+ if (( str = xmlGetAttrValue( o, _T("label"))) == NULL )
+ str = xmlGetText( v );
+ if (( p = mir_tstrdup( str )) != NULL ) {
+ bool selected = false;
+ for ( k=0; ; k++ ) {
+ vs = xmlGetChild( n ,k);
+ if ( !vs )
+ break;
+ if ( !lstrcmp( xmlGetName( vs ), _T("value")) && xmlGetText( vs ) && !_tcscmp( xmlGetText( vs ), xmlGetText( v )))
+ {
+ selected = true;
+ break;
+ }
+ }
+ JabberFormAddListItem( item, p, selected );
+ mir_free( p );
+ } } } } } } } } }
+
+ JabberFormLayoutControls(hwndStatic, &layout_info, formHeight);
+}
+
+void JabberFormDestroyUI(HWND hwndStatic)
+{
+ TJabberFormControlList *controls = (TJabberFormControlList *)GetWindowLongPtr(hwndStatic, GWLP_USERDATA);
+ if (controls)
+ {
+ for ( int i = 0; i < controls->getCount(); i++ )
+ mir_free((*controls)[i]);
+ controls->destroy();
+ delete controls;
+ SetWindowLongPtr(hwndStatic, GWLP_USERDATA, 0);
+ }
+}
+
+HXML JabberFormGetData( HWND hwndStatic, HXML xNode )
+{
+ HWND hFrame, hCtrl;
+ HXML n, v, o;
+ int id, j, k, len;
+ const TCHAR *varName, *type, *fieldStr, *labelText, *str2;
+ TCHAR *p, *q, *str;
+
+ if ( xNode == NULL || xmlGetName( xNode ) == NULL || lstrcmp( xmlGetName( xNode ), _T("x")) || hwndStatic == NULL )
+ return NULL;
+
+ hFrame = hwndStatic;
+ id = 0;
+ XmlNode x( _T("x"));
+ x << XATTR( _T("xmlns"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("submit"));
+
+ for ( int i=0; ; i++ ) {
+ n = xmlGetChild( xNode ,i);
+ if ( !n )
+ break;
+
+ fieldStr = NULL;
+ if ( lstrcmp( xmlGetName( n ), _T("field")))
+ continue;
+
+ if (( varName = xmlGetAttrValue( n, _T("var"))) == NULL || ( type = xmlGetAttrValue( n, _T("type"))) == NULL )
+ continue;
+
+ hCtrl = GetDlgItem( hFrame, id );
+ HXML field = x << XCHILD( _T("field")) << XATTR( _T("var"), varName );
+
+ if ( !_tcscmp( type, _T("text-multi")) || !_tcscmp( type, _T("jid-multi"))) {
+ len = GetWindowTextLength( GetDlgItem( hFrame, id ));
+ str = ( TCHAR* )mir_alloc( sizeof(TCHAR)*( len+1 ));
+ GetDlgItemText( hFrame, id, str, len+1 );
+ p = str;
+ while ( p != NULL ) {
+ if (( q = _tcsstr( p, _T("\r\n"))) != NULL )
+ *q = '\0';
+ field << XCHILD( _T("value"), p );
+ p = q ? q+2 : NULL;
+ }
+ mir_free( str );
+ id++;
+ }
+ else if ( !_tcscmp( type, _T("boolean"))) {
+ TCHAR buf[ 10 ];
+ _itot( IsDlgButtonChecked( hFrame, id ) == BST_CHECKED ? 1 : 0, buf, 10 );
+ field << XCHILD( _T("value"), buf );
+ id++;
+ }
+ else if ( !_tcscmp( type, _T("list-single"))) {
+ len = GetWindowTextLength( GetDlgItem( hFrame, id ));
+ str = ( TCHAR* )mir_alloc( sizeof( TCHAR )*( len+1 ));
+ GetDlgItemText( hFrame, id, str, len+1 );
+ v = NULL;
+ for ( j=0; ; j++ ) {
+ o = xmlGetChild( n ,j);
+ if ( !o )
+ break;
+
+ if ( !lstrcmp( xmlGetName( o ), _T("option"))) {
+ if (( v = xmlGetChild( o , "value" )) != NULL && xmlGetText( v )) {
+ if (( str2 = xmlGetAttrValue( o, _T("label"))) == NULL )
+ str2 = xmlGetText( v );
+ if ( !lstrcmp( str2, str ))
+ break;
+ } } }
+
+ if ( o )
+ field << XCHILD( _T("value"), xmlGetText( v ));
+
+ mir_free( str );
+ id++;
+ }
+ else if ( !_tcscmp( type, _T("list-multi"))) {
+ int count = SendMessage( hCtrl, LB_GETCOUNT, 0, 0 );
+ for ( j=0; j<count; j++ ) {
+ if ( SendMessage( hCtrl, LB_GETSEL, j, 0 ) > 0 ) {
+ // an entry is selected
+ len = SendMessage( hCtrl, LB_GETTEXTLEN, j, 0 );
+ if (( str = ( TCHAR* )mir_alloc(( len+1 )*sizeof( TCHAR ))) != NULL ) {
+ SendMessage( hCtrl, LB_GETTEXT, j, ( LPARAM )str );
+ for ( k=0; ; k++ ) {
+ o = xmlGetChild( n ,k);
+ if ( !o )
+ break;
+
+ if ( xmlGetName( o ) && !lstrcmp( xmlGetName( o ), _T("option"))) {
+ if (( v = xmlGetChild( o , "value" )) != NULL && xmlGetText( v )) {
+ if (( labelText = xmlGetAttrValue( o, _T("label"))) == NULL )
+ labelText = xmlGetText( v );
+
+ if ( !lstrcmp( labelText, str ))
+ field << XCHILD( _T("value"), xmlGetText( v ));
+ } } }
+ mir_free( str );
+ } } }
+ id++;
+ }
+ else if ( !_tcscmp( type, _T("fixed")) || !_tcscmp( type, _T("hidden"))) {
+ v = xmlGetChild( n , "value" );
+ if ( v != NULL && xmlGetText( v ) != NULL )
+ field << XCHILD( _T("value"), xmlGetText( v ));
+ }
+ else { // everything else is considered "text-single" or "text-private"
+ len = GetWindowTextLength( GetDlgItem( hFrame, id ));
+ str = ( TCHAR* )mir_alloc( sizeof(TCHAR)*( len+1 ));
+ GetDlgItemText( hFrame, id, str, len+1 );
+ field << XCHILD( _T("value"), str );
+ mir_free( str );
+ id++;
+ } }
+
+ return xi.copyNode( x );
+}
+
+struct JABBER_FORM_INFO
+{
+ ~JABBER_FORM_INFO();
+
+ CJabberProto* ppro;
+ HXML xNode;
+ TCHAR defTitle[128]; // Default title if no <title/> in xNode
+ RECT frameRect; // Clipping region of the frame to scroll
+ int frameHeight; // Height of the frame ( can be eliminated, redundant to frameRect )
+ int formHeight; // Actual height of the form
+ int curPos; // Current scroll position
+ JABBER_FORM_SUBMIT_FUNC pfnSubmit;
+ void *userdata;
+};
+
+static INT_PTR CALLBACK JabberFormDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ JABBER_FORM_INFO *jfi;
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ {
+ HXML n;
+ LONG frameExStyle;
+
+ // lParam is ( JABBER_FORM_INFO * )
+ TranslateDialogDefault( hwndDlg );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_FRAME_TEXT ), SW_HIDE );
+ jfi = ( JABBER_FORM_INFO * ) lParam;
+ if ( jfi != NULL ) {
+ // Set dialog title
+ if ( jfi->xNode!=NULL && ( n = xmlGetChild( jfi->xNode , "title" )) != NULL && xmlGetText( n ) != NULL )
+ SetWindowText( hwndDlg, xmlGetText( n ));
+ else if ( jfi->defTitle != NULL )
+ SetWindowText( hwndDlg, TranslateTS( jfi->defTitle ));
+ // Set instruction field
+ if ( jfi->xNode!=NULL && ( n = xmlGetChild( jfi->xNode , "instructions" )) != NULL && xmlGetText( n ) != NULL )
+ JabberFormSetInstruction( hwndDlg, xmlGetText( n ));
+ else
+ {
+ if ( jfi->xNode != NULL && ( n = xmlGetChild( jfi->xNode , "title" )) != NULL && xmlGetText( n ) != NULL )
+ JabberFormSetInstruction( hwndDlg, xmlGetText( n ));
+ else if ( jfi->defTitle != NULL )
+ JabberFormSetInstruction( hwndDlg, TranslateTS( jfi->defTitle ));
+ }
+
+ // Create form
+ if ( jfi->xNode != NULL ) {
+ RECT rect;
+
+ GetClientRect( GetDlgItem( hwndDlg, IDC_FRAME ), &( jfi->frameRect ));
+ GetClientRect( GetDlgItem( hwndDlg, IDC_VSCROLL ), &rect );
+ jfi->frameRect.right -= ( rect.right - rect.left );
+ GetClientRect( GetDlgItem( hwndDlg, IDC_FRAME ), &rect );
+ jfi->frameHeight = rect.bottom - rect.top;
+ JabberFormCreateUI( GetDlgItem( hwndDlg, IDC_FRAME ), jfi->xNode, &( jfi->formHeight ));
+ }
+ }
+
+ if ( jfi->formHeight > jfi->frameHeight ) {
+ HWND hwndScroll;
+
+ hwndScroll = GetDlgItem( hwndDlg, IDC_VSCROLL );
+ EnableWindow( hwndScroll, TRUE );
+ SetScrollRange( hwndScroll, SB_CTL, 0, jfi->formHeight - jfi->frameHeight, FALSE );
+ jfi->curPos = 0;
+ }
+
+ // Enable WS_EX_CONTROLPARENT on IDC_FRAME ( so tab stop goes through all its children )
+ frameExStyle = GetWindowLongPtr( GetDlgItem( hwndDlg, IDC_FRAME ), GWL_EXSTYLE );
+ frameExStyle |= WS_EX_CONTROLPARENT;
+ SetWindowLongPtr( GetDlgItem( hwndDlg, IDC_FRAME ), GWL_EXSTYLE, frameExStyle );
+
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, ( LONG_PTR ) jfi );
+ if ( jfi->pfnSubmit != NULL )
+ EnableWindow( GetDlgItem( hwndDlg, IDC_SUBMIT ), TRUE );
+ }
+ return TRUE;
+
+ case WM_CTLCOLORSTATIC:
+ if ((GetWindowLongPtr((HWND)lParam, GWL_ID) == IDC_WHITERECT) ||
+ (GetWindowLongPtr((HWND)lParam, GWL_ID) == IDC_INSTRUCTION) ||
+ (GetWindowLongPtr((HWND)lParam, GWL_ID) == IDC_TITLE))
+ {
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+ }
+
+ return NULL;
+
+ case WM_MOUSEWHEEL:
+ {
+ int zDelta = GET_WHEEL_DELTA_WPARAM( wParam );
+ if ( zDelta ) {
+ int nScrollLines=0;
+ SystemParametersInfo( SPI_GETWHEELSCROLLLINES, 0, (void*)&nScrollLines, 0 );
+ for (int i = 0; i < ( nScrollLines + 1 ) / 2; i++ )
+ SendMessage( hwndDlg, WM_VSCROLL, ( zDelta < 0 ) ? SB_LINEDOWN : SB_LINEUP, 0 );
+ }
+ }
+ break;
+
+ case WM_VSCROLL:
+ jfi = ( JABBER_FORM_INFO * ) GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+ if ( jfi != NULL ) {
+ int pos = jfi->curPos;
+ switch ( LOWORD( wParam )) {
+ case SB_LINEDOWN:
+ pos += 15;
+ break;
+ case SB_LINEUP:
+ pos -= 15;
+ break;
+ case SB_PAGEDOWN:
+ pos += ( jfi->frameHeight - 10 );
+ break;
+ case SB_PAGEUP:
+ pos -= ( jfi->frameHeight - 10 );
+ break;
+ case SB_THUMBTRACK:
+ pos = HIWORD( wParam );
+ break;
+ }
+ if ( pos > ( jfi->formHeight - jfi->frameHeight ))
+ pos = jfi->formHeight - jfi->frameHeight;
+ if ( pos < 0 )
+ pos = 0;
+ if ( jfi->curPos != pos ) {
+ ScrollWindow( GetDlgItem( hwndDlg, IDC_FRAME ), 0, jfi->curPos - pos, NULL, &( jfi->frameRect ));
+ SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, pos, TRUE );
+ jfi->curPos = pos;
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDC_SUBMIT:
+ jfi = ( JABBER_FORM_INFO * ) GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+ if ( jfi != NULL ) {
+ HXML n = JabberFormGetData( GetDlgItem( hwndDlg, IDC_FRAME ), jfi->xNode );
+ ( jfi->ppro->*(jfi->pfnSubmit))( n, jfi->userdata );
+ xi.destroyNode( n );
+ }
+ // fall through
+ case IDCANCEL:
+ case IDCLOSE:
+ DestroyWindow( hwndDlg );
+ return TRUE;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow( hwndDlg );
+ break;
+
+ case WM_DESTROY:
+ JabberFormDestroyUI( GetDlgItem( hwndDlg, IDC_FRAME ));
+ jfi = ( JABBER_FORM_INFO * ) GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+ delete jfi;
+ break;
+ }
+
+ return FALSE;
+}
+
+static VOID CALLBACK JabberFormCreateDialogApcProc( void* param )
+{
+ CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_FORM ), NULL, JabberFormDlgProc, ( LPARAM )param );
+}
+
+void CJabberProto::FormCreateDialog( HXML xNode, TCHAR* defTitle, JABBER_FORM_SUBMIT_FUNC pfnSubmit, void *userdata )
+{
+ JABBER_FORM_INFO *jfi = new JABBER_FORM_INFO;
+ memset( jfi, 0, sizeof( JABBER_FORM_INFO ));
+ jfi->ppro = this;
+ jfi->xNode = xi.copyNode( xNode );
+ if ( defTitle )
+ _tcsncpy( jfi->defTitle, defTitle, SIZEOF( jfi->defTitle ));
+ jfi->pfnSubmit = pfnSubmit;
+ jfi->userdata = userdata;
+
+ CallFunctionAsync( JabberFormCreateDialogApcProc, jfi );
+}
+
+//=======================================================================================
+
+JABBER_FORM_INFO::~JABBER_FORM_INFO()
+{
+ xi.destroyNode( xNode );
+ mir_free( userdata );
+}
\ No newline at end of file diff --git a/protocols/JabberG/src/jabber_form2.cpp b/protocols/JabberG/src/jabber_form2.cpp new file mode 100644 index 0000000000..f1936c9e73 --- /dev/null +++ b/protocols/JabberG/src/jabber_form2.cpp @@ -0,0 +1,1198 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007-09 Maxim Mluhov
+Copyright ( C ) 2007-09 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_caps.h"
+
+#include "jabber_form2.h"
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// FORM_TYPE Registry
+namespace NSJabberRegistry
+{
+ // http://jabber.org/network/serverinfo
+ static TJabberDataFormRegisry_Field form_type_serverinfo[] =
+ {
+ { _T("abuse-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for communication related to abusive traffic") },
+ { _T("feedback-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for customer feedback") },
+ { _T("sales-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for communication related to sales and marketing") },
+ { _T("security-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for communication related to security concerns") },
+ { _T("support-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for customer support") },
+ };
+
+ // http://jabber.org/protocol/admin
+ static TJabberDataFormRegisry_Field form_type_admin[] =
+ {
+ { _T("accountjid"), JDFT_JID_SINGLE, _T("The Jabber ID of a single entity to which an operation applies") },
+ { _T("accountjids"), JDFT_JID_MULTI, _T("The Jabber ID of one or more entities to which an operation applies") },
+ { _T("activeuserjids"), JDFT_JID_MULTI, _T("The Jabber IDs associated with active sessions") },
+ { _T("activeusersnum"), JDFT_TEXT_SINGLE, _T("The number of online entities that are active") },
+ { _T("adminjids"), JDFT_JID_MULTI, _T("A list of entities with administrative privileges") },
+ { _T("announcement"), JDFT_TEXT_MULTI, _T("The text of an announcement to be sent to active users or all users") },
+ { _T("blacklistjids"), JDFT_JID_MULTI, _T("A list of entities with whom communication is blocked") },
+ { _T("delay"), JDFT_LIST_MULTI, _T("The number of seconds to delay before applying a change") },
+ { _T("disableduserjids"), JDFT_JID_MULTI, _T("The Jabber IDs that have been disabled") },
+ { _T("disabledusersnum"), JDFT_TEXT_SINGLE, _T("The number of disabled entities") },
+ { _T("email"), JDFT_TEXT_SINGLE, _T("The email address for a user") },
+ { _T("given_name"), JDFT_TEXT_SINGLE, _T("The given (first) name of a user") },
+ { _T("idleusersnum"), JDFT_TEXT_SINGLE, _T("The number of online entities that are idle") },
+ { _T("ipaddresses"), JDFT_LIST_MULTI, _T("The IP addresses of an account's online sessions") },
+ { _T("lastlogin"), JDFT_TEXT_SINGLE, _T("The last login time (per XEP-0082) of a user") },
+ { _T("loginsperminute"), JDFT_TEXT_SINGLE, _T("The number of logins per minute for an account") },
+ { _T("max_items"), JDFT_LIST_SINGLE, _T("The maximum number of items associated with a search or list") },
+ { _T("motd"), JDFT_TEXT_MULTI, _T("The text of a message of the day") },
+ { _T("onlineresources"), JDFT_TEXT_SINGLE, _T("The names of an account's online sessions") },
+ { _T("onlineuserjids"), JDFT_JID_MULTI, _T("The Jabber IDs associated with online users") },
+ { _T("onlineusersnum"), JDFT_TEXT_SINGLE, _T("The number of online entities") },
+ { _T("password"), JDFT_TEXT_PRIVATE, _T("The password for an account") },
+ { _T("password-verify"), JDFT_TEXT_PRIVATE, _T("Password verification") },
+ { _T("registereduserjids"), JDFT_JID_MULTI, _T("A list of registered entities") },
+ { _T("registeredusersnum"), JDFT_TEXT_SINGLE, _T("The number of registered entities") },
+ { _T("rostersize"), JDFT_TEXT_SINGLE, _T("Number of roster items for an account") },
+ { _T("stanzaspersecond"), JDFT_TEXT_SINGLE, _T("The number of stanzas being sent per second by an account") },
+ { _T("surname"), JDFT_TEXT_SINGLE, _T("The family (last) name of a user") },
+ { _T("welcome"), JDFT_TEXT_MULTI, _T("The text of a welcome message") },
+ { _T("whitelistjids"), JDFT_JID_MULTI, _T("A list of entities with whom communication is allowed") },
+ };
+
+ // http://jabber.org/protocol/muc#register
+ static TJabberDataFormRegisry_Field form_type_muc_register[] =
+ {
+ { _T("muc#register_first"), JDFT_TEXT_SINGLE, _T("First Name") },
+ { _T("muc#register_last"), JDFT_TEXT_SINGLE, _T("Last Name") },
+ { _T("muc#register_roomnick"), JDFT_TEXT_SINGLE, _T("Desired Nickname") },
+ { _T("muc#register_url"), JDFT_TEXT_SINGLE, _T("Your URL") },
+ { _T("muc#register_email"), JDFT_TEXT_SINGLE, _T("Email Address") },
+ { _T("muc#register_faqentry"), JDFT_TEXT_MULTI, _T("FAQ Entry") },
+ };
+
+ // http://jabber.org/protocol/muc#roomconfig
+ static TJabberDataFormRegisry_Field form_type_muc_roomconfig[] =
+ {
+ { _T("muc#roomconfig_allowinvites"), JDFT_BOOLEAN, _T("Whether to Allow Occupants to Invite Others") },
+ { _T("muc#roomconfig_changesubject"), JDFT_BOOLEAN, _T("Whether to Allow Occupants to Change Subject") },
+ { _T("muc#roomconfig_enablelogging"), JDFT_BOOLEAN, _T("Whether to Enable Logging of Room Conversations") },
+ { _T("muc#roomconfig_lang"), JDFT_TEXT_SINGLE, _T("Natural Language for Room Discussions") },
+ { _T("muc#roomconfig_maxusers"), JDFT_LIST_SINGLE, _T("Maximum Number of Room Occupants") },
+ { _T("muc#roomconfig_membersonly"), JDFT_BOOLEAN, _T("Whether an Make Room Members-Only") },
+ { _T("muc#roomconfig_moderatedroom"), JDFT_BOOLEAN, _T("Whether to Make Room Moderated") },
+ { _T("muc#roomconfig_passwordprotectedroom"), JDFT_BOOLEAN, _T("Whether a Password is Required to Enter") },
+ { _T("muc#roomconfig_persistentroom"), JDFT_BOOLEAN, _T("Whether to Make Room Persistent") },
+ { _T("muc#roomconfig_presencebroadcast"), JDFT_LIST_MULTI, _T("Roles for which Presence is Broadcast") },
+ { _T("muc#roomconfig_publicroom"), JDFT_BOOLEAN, _T("Whether to Allow Public Searching for Room") },
+ { _T("muc#roomconfig_roomadmins"), JDFT_JID_MULTI, _T("Full List of Room Admins") },
+ { _T("muc#roomconfig_roomdesc"), JDFT_TEXT_SINGLE, _T("Short Description of Room") },
+ { _T("muc#roomconfig_roomname"), JDFT_TEXT_SINGLE, _T("Natural-Language Room Name") },
+ { _T("muc#roomconfig_roomowners"), JDFT_JID_MULTI, _T("Full List of Room Owners") },
+ { _T("muc#roomconfig_roomsecret"), JDFT_TEXT_PRIVATE, _T("The Room Password") },
+ { _T("muc#roomconfig_whois"), JDFT_LIST_SINGLE, _T("Affiliations that May Discover Real JIDs of Occupants") },
+ };
+
+ // http://jabber.org/protocol/pubsub#publish-options
+ static TJabberDataFormRegisry_Field form_type_publish_options[] =
+ {
+ { _T("pubsub#access_model"), JDFT_LIST_SINGLE, _T("Precondition: node configuration with the specified access model") },
+ };
+
+ // http://jabber.org/protocol/pubsub#subscribe_authorization
+ static TJabberDataFormRegisry_Field form_type_subscribe_auth[] =
+ {
+ { _T("pubsub#allow"), JDFT_BOOLEAN, _T("Whether to allow the subscription") },
+ { _T("pubsub#subid"), JDFT_TEXT_SINGLE, _T("The SubID of the subscription") },
+ { _T("pubsub#node"), JDFT_TEXT_SINGLE, _T("The NodeID of the relevant node") },
+ { _T("pubsub#subscriber_jid"), JDFT_JID_SINGLE, _T("The address (JID) of the subscriber") },
+ };
+
+ // http://jabber.org/protocol/pubsub#subscribe_options
+ static TJabberDataFormRegisry_Field form_type_subscribe_options[] =
+ {
+ { _T("pubsub#deliver"), JDFT_BOOLEAN, _T("Whether an entity wants to receive or disable notifications") },
+ { _T("pubsub#digest"), JDFT_BOOLEAN, _T("Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually") },
+ { _T("pubsub#digest_frequency"), JDFT_TEXT_SINGLE, _T("The minimum number of milliseconds between sending any two notification digests") },
+ { _T("pubsub#expire"), JDFT_TEXT_SINGLE, _T("The DateTime at which a leased subscription will end or has ended") },
+ { _T("pubsub#include_body"), JDFT_BOOLEAN, _T("Whether an entity wants to receive an XMPP message body in addition to the payload format") },
+ { _T("pubsub#show-values"), JDFT_LIST_MULTI, _T("The presence states for which an entity wants to receive notifications") },
+ { _T("pubsub#subscription_type"), JDFT_LIST_SINGLE, _T("") },
+ { _T("pubsub#subscription_depth"), JDFT_LIST_SINGLE, _T("") },
+ };
+
+ // http://jabber.org/protocol/pubsub#node_config
+ static TJabberDataFormRegisry_Field form_type_node_config[] =
+ {
+ { _T("pubsub#access_model"), JDFT_LIST_SINGLE, _T("Who may subscribe and retrieve items") },
+ { _T("pubsub#body_xslt"), JDFT_TEXT_SINGLE, _T("The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.") },
+ { _T("pubsub#collection"), JDFT_TEXT_SINGLE, _T("The collection with which a node is affiliated") },
+ { _T("pubsub#dataform_xslt"), JDFT_TEXT_SINGLE, _T("The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine") },
+ { _T("pubsub#deliver_payloads"), JDFT_BOOLEAN, _T("Whether to deliver payloads with event notifications") },
+ { _T("pubsub#itemreply"), JDFT_LIST_SINGLE, _T("Whether owners or publisher should receive replies to items") },
+ { _T("pubsub#children_association_policy"), JDFT_LIST_SINGLE, _T("Who may associate leaf nodes with a collection") },
+ { _T("pubsub#children_association_whitelist"), JDFT_JID_MULTI, _T("The list of JIDs that may associated leaf nodes with a collection") },
+ { _T("pubsub#children"), JDFT_TEXT_MULTI, _T("The child nodes (leaf or collection) associated with a collection") },
+ { _T("pubsub#children_max"), JDFT_TEXT_SINGLE, _T("The maximum number of child nodes that can be associated with a collection") },
+ { _T("pubsub#max_items"), JDFT_TEXT_SINGLE, _T("The maximum number of items to persist") },
+ { _T("pubsub#max_payload_size"), JDFT_TEXT_SINGLE, _T("The maximum payload size in bytes") },
+ { _T("pubsub#node_type"), JDFT_LIST_SINGLE, _T("Whether the node is a leaf (default) or a collection") },
+ { _T("pubsub#notify_config"), JDFT_BOOLEAN, _T("Whether to notify subscribers when the node configuration changes") },
+ { _T("pubsub#notify_delete"), JDFT_BOOLEAN, _T("Whether to notify subscribers when the node is deleted") },
+ { _T("pubsub#notify_retract"), JDFT_BOOLEAN, _T("Whether to notify subscribers when items are removed from the node") },
+ { _T("pubsub#persist_items"), JDFT_BOOLEAN, _T("Whether to persist items to storage") },
+ { _T("pubsub#presence_based_delivery"), JDFT_BOOLEAN, _T("Whether to deliver notifications to available users only") },
+ { _T("pubsub#publish_model"), JDFT_LIST_SINGLE, _T("The publisher model") },
+ { _T("pubsub#replyroom"), JDFT_JID_MULTI, _T("The specific multi-user chat rooms to specify for replyroom") },
+ { _T("pubsub#replyto"), JDFT_JID_MULTI, _T("The specific JID(s) to specify for replyto") },
+ { _T("pubsub#roster_groups_allowed"), JDFT_LIST_MULTI, _T("The roster group(s) allowed to subscribe and retrieve items") },
+ { _T("pubsub#send_item_subscribe"), JDFT_BOOLEAN, _T("Whether to send items to new subscribers") },
+ { _T("pubsub#subscribe"), JDFT_BOOLEAN, _T("Whether to allow subscriptions") },
+ { _T("pubsub#title"), JDFT_TEXT_SINGLE, _T("A friendly name for the node") },
+ { _T("pubsub#type"), JDFT_TEXT_SINGLE, _T("The type of node data, usually specified by the namespace of the payload (if any); MAY be list-single rather than text-single") },
+ };
+
+ // http://jabber.org/protocol/pubsub#meta-data
+ static TJabberDataFormRegisry_Field form_type_metadata[] =
+ {
+ { _T("pubsub#contact"), JDFT_JID_MULTI, _T("The JIDs of those to contact with questions") },
+ { _T("pubsub#creation_date"), JDFT_TEXT_SINGLE, _T("The datetime when the node was created") },
+ { _T("pubsub#creator"), JDFT_JID_SINGLE, _T("The JID of the node creator") },
+ { _T("pubsub#description"), JDFT_TEXT_SINGLE, _T("A description of the node") },
+ { _T("pubsub#language"), JDFT_TEXT_SINGLE, _T("The default language of the node") },
+ { _T("pubsub#num_subscribers"), JDFT_TEXT_SINGLE, _T("The number of subscribers to the node") },
+ { _T("pubsub#owner"), JDFT_JID_MULTI, _T("The JIDs of those with an affiliation of owner") },
+ { _T("pubsub#publisher"), JDFT_JID_MULTI, _T("The JIDs of those with an affiliation of publisher") },
+ { _T("pubsub#title"), JDFT_TEXT_SINGLE, _T("The name of the node") },
+ { _T("pubsub#type"), JDFT_TEXT_SINGLE, _T("Payload type") },
+ };
+
+ // http://jabber.org/protocol/rc
+ static TJabberDataFormRegisry_Field form_type_rc[] =
+ {
+ { _T("auto-auth"), JDFT_BOOLEAN, _T("Whether to automatically authorize subscription requests") },
+ { _T("auto-files"), JDFT_BOOLEAN, _T("Whether to automatically accept file transfers") },
+ { _T("auto-msg"), JDFT_BOOLEAN, _T("Whether to automatically open new messages") },
+ { _T("auto-offline"), JDFT_BOOLEAN, _T("Whether to automatically go offline when idle") },
+ { _T("sounds"), JDFT_BOOLEAN, _T("Whether to play sounds") },
+ { _T("files"), JDFT_LIST_MULTI, _T("A list of pending file transfers") },
+ { _T("groupchats"), JDFT_LIST_MULTI, _T("A list of joined groupchat rooms") },
+ { _T("status"), JDFT_LIST_SINGLE, _T("A presence or availability status") },
+ { _T("status-message"), JDFT_TEXT_MULTI, _T("The status message text") },
+ { _T("status-priority"), JDFT_TEXT_SINGLE, _T("The new priority for the client") },
+ };
+
+ // jabber:iq:register
+ static TJabberDataFormRegisry_Field form_type_register[] =
+ {
+ { _T("username"), JDFT_TEXT_SINGLE, _T("Account name associated with the user") },
+ { _T("nick"), JDFT_TEXT_SINGLE, _T("Familiar name of the user") },
+ { _T("password"), JDFT_TEXT_PRIVATE, _T("Password or secret for the user") },
+ { _T("name"), JDFT_TEXT_SINGLE, _T("Full name of the user") },
+ { _T("first"), JDFT_TEXT_SINGLE, _T("First name or given name of the user") },
+ { _T("last"), JDFT_TEXT_SINGLE, _T("Last name, surname, or family name of the user") },
+ { _T("email"), JDFT_TEXT_SINGLE, _T("Email address of the user") },
+ { _T("address"), JDFT_TEXT_SINGLE, _T("Street portion of a physical or mailing address") },
+ { _T("city"), JDFT_TEXT_SINGLE, _T("Locality portion of a physical or mailing address") },
+ { _T("state"), JDFT_TEXT_SINGLE, _T("Region portion of a physical or mailing address") },
+ { _T("zip"), JDFT_TEXT_SINGLE, _T("Postal code portion of a physical or mailing address") },
+ };
+
+ // jabber:iq:search
+ static TJabberDataFormRegisry_Field form_type_search[] =
+ {
+ { _T("first"), JDFT_TEXT_SINGLE, _T("First Name") },
+ { _T("last"), JDFT_TEXT_SINGLE, _T("Family Name") },
+ { _T("nick"), JDFT_TEXT_SINGLE, _T("Nickname") },
+ { _T("email"), JDFT_TEXT_SINGLE, _T("Email Address") },
+ };
+
+ // urn:xmpp:ssn
+ static TJabberDataFormRegisry_Field form_type_ssn[] =
+ {
+ { _T("accept"), JDFT_BOOLEAN, _T("Whether to accept the invitation") },
+ { _T("continue"), JDFT_TEXT_SINGLE, _T("Another resource with which to continue the session") },
+ { _T("disclosure"), JDFT_LIST_SINGLE, _T("Disclosure of content, decryption keys or identities") },
+ { _T("http://jabber.org/protocol/chatstates"), JDFT_LIST_SINGLE, _T("Whether may send Chat State Notifications per XEP-0085") },
+ { _T("http://jabber.org/protocol/xhtml-im"), JDFT_LIST_SINGLE, _T("Whether allowed to use XHTML-IM formatting per XEP-0071") },
+ { _T("language"), JDFT_LIST_SINGLE, _T("Primary written language of the chat (each value appears in order of preference and conforms to RFC 4646 and the IANA registry)") },
+ { _T("logging"), JDFT_LIST_SINGLE, _T("Whether allowed to log messages (i.e., whether Off-The-Record mode is required)") },
+ { _T("renegotiate"), JDFT_BOOLEAN, _T("Whether to renegotiate the session") },
+ { _T("security"), JDFT_LIST_SINGLE, _T("Minimum security level") },
+ { _T("terminate"), JDFT_BOOLEAN, _T("Whether to terminate the session") },
+ { _T("urn:xmpp:receipts"), JDFT_BOOLEAN, _T("Whether to enable Message Receipts per XEP-0184") },
+ };
+
+ TJabberDataFormRegisry_Form form_types[] =
+ {
+ /*0157*/ { _T("http://jabber.org/network/serverinfo"), form_type_serverinfo, SIZEOF(form_type_serverinfo) },
+ /*0133*/ { _T("http://jabber.org/protocol/admin"), form_type_admin, SIZEOF(form_type_admin) },
+ /*0045*/ { _T("http://jabber.org/protocol/muc#register"), form_type_muc_register, SIZEOF(form_type_muc_register) },
+ /*0045*/ { _T("http://jabber.org/protocol/muc#roomconfig"), form_type_muc_roomconfig, SIZEOF(form_type_muc_roomconfig) },
+ /*0060*/ { _T("http://jabber.org/protocol/pubsub#publish-options"), form_type_publish_options, SIZEOF(form_type_publish_options) },
+ /*0060*/ { _T("http://jabber.org/protocol/pubsub#subscribe_authorization"), form_type_subscribe_auth, SIZEOF(form_type_subscribe_auth) },
+ /*0060*/ { _T("http://jabber.org/protocol/pubsub#subscribe_options"), form_type_subscribe_options, SIZEOF(form_type_subscribe_options) },
+ /*0060*/ { _T("http://jabber.org/protocol/pubsub#node_config"), form_type_node_config, SIZEOF(form_type_node_config) },
+ /*0060*/ { _T("http://jabber.org/protocol/pubsub#meta-data"), form_type_metadata, SIZEOF(form_type_metadata) },
+ /*0146*/ { _T("http://jabber.org/protocol/rc"), form_type_rc, SIZEOF(form_type_rc) },
+ /*0077*/ { _T("jabber:iq:register"), form_type_register, SIZEOF(form_type_register) },
+ /*0055*/ { _T("jabber:iq:search"), form_type_search, SIZEOF(form_type_search) },
+ /*0155*/ { _T("urn:xmpp:ssn"), form_type_ssn, SIZEOF(form_type_ssn) },
+ };
+};
+
+CJabberDataField::CJabberDataField(CJabberDataForm *form, XmlNode *node):
+ m_node(node), m_options(5), m_values(1), m_descriptions(1)
+{
+ m_typeName = JabberXmlGetAttrValue(m_node, "type");
+ m_var = JabberXmlGetAttrValue(m_node, "var");
+ m_label = JabberXmlGetAttrValue(m_node, "label");
+ m_required = JabberXmlGetChild(m_node, "required") ? true : false;
+
+ if (m_typeName)
+ {
+ if (!lstrcmp(m_typeName, _T("text-private")))
+ m_type = JDFT_TEXT_PRIVATE;
+ else if (!lstrcmp(m_typeName, _T("text-multi")) || !lstrcmp(m_typeName, _T("jid-multi")))
+ m_type = JDFT_TEXT_MULTI;
+ else if (!lstrcmp(m_typeName, _T("boolean")))
+ m_type = JDFT_BOOLEAN;
+ else if (!lstrcmp(m_typeName, _T("list-single")))
+ m_type = JDFT_LIST_SINGLE;
+ else if (!lstrcmp(m_typeName, _T("list-multi")))
+ m_type = JDFT_LIST_MULTI;
+ else if (!lstrcmp(m_typeName, _T("fixed")))
+ m_type = JDFT_FIXED;
+ else if (!lstrcmp(m_typeName, _T("hidden")))
+ m_type = JDFT_HIDDEN;
+ else
+ m_type = JDFT_TEXT_SINGLE;
+ } else
+ {
+ m_typeName = _T("text-single");
+ m_type = JDFT_TEXT_SINGLE;
+ }
+
+ for (int i = 0; i < m_node->numChild; ++i)
+ {
+ if (!lstrcmpA(m_node->child[i]->name, "value"))
+ {
+ m_values.insert(m_node->child[i]->text, m_values.getCount());
+ } else
+ if (!lstrcmpA(m_node->child[i]->name, "option"))
+ {
+ TOption *opt = new TOption;
+ opt->label = JabberXmlGetAttrValue(m_node->child[i], "label");
+ opt->value = NULL;
+ if (XmlNode *p = JabberXmlGetChild(m_node->child[i], "value"))
+ opt->value = p->text;
+ m_options.insert(opt, m_options.getCount());
+ } else
+ if (!lstrcmpA(m_node->child[i]->name, "desc"))
+ {
+ m_descriptions.insert(m_node->child[i]->text, m_descriptions.getCount());
+ }
+ }
+}
+
+CJabberDataField::~CJabberDataField()
+{
+ m_values.destroy();
+ m_descriptions.destroy();
+}
+
+CJabberDataFieldSet::CJabberDataFieldSet():
+ m_fields(5)
+{
+}
+
+CJabberDataField *CJabberDataFieldSet::GetField(TCHAR *var)
+{
+ for (int i = 0; i < m_fields.getCount(); ++i)
+ if (!lstrcmp(m_fields[i].GetVar(), var))
+ return &(m_fields[i]);
+ return NULL;
+}
+
+CJabberDataForm::CJabberDataForm(XmlNode *node):
+ m_node(node),
+ m_form_type(0),
+ m_form_type_info(0),
+ m_title(0),
+ m_instructions(1),
+ m_items(1)
+{
+ m_typename = JabberXmlGetAttrValue(m_node, "type");
+
+ for (int i = 0; i < m_node->numChild; ++i)
+ {
+ XmlNode *child = m_node->child[i];
+ if (!lstrcmpA(child->name, "field"))
+ {
+ CJabberDataField *field = new CJabberDataField(this, child);
+ m_fields.AddField(field);
+
+ if ((field->GetType() == JDFT_HIDDEN) && !lstrcmp(field->GetVar(), _T("FORM_TYPE")))
+ {
+ using NSJabberRegistry::form_types;
+
+ m_form_type = field->GetValue();
+ for (int j = 0; j < SIZEOF(form_types); ++j)
+ if (!lstrcmp(m_form_type, form_types[j].form_type))
+ {
+ m_form_type_info = form_types + j;
+ break;
+ }
+ }
+ } else
+ if (!lstrcmpA(child->name, "title"))
+ {
+ m_title = child->text;
+ } else
+ if (!lstrcmpA(child->name, "instructions"))
+ {
+ m_instructions.insert(child->text, m_instructions.getCount());
+ } else
+ if (!lstrcmpA(child->name, "reported"))
+ {
+ if (m_reported.GetCount())
+ continue; // ignore second <reported/> -> error!!!!!!!!!!!
+
+ for (int j = 0; j < child->numChild; ++j)
+ {
+ XmlNode *child2 = child->child[i];
+ if (!lstrcmpA(child2->name, "field"))
+ {
+ CJabberDataField *field = new CJabberDataField(this, child2);
+ m_reported.AddField(field);
+ }
+ }
+ } else
+ if (!lstrcmpA(child->name, "item"))
+ {
+ CJabberDataFieldSet *item = new CJabberDataFieldSet;
+ m_items.insert(item);
+
+ for (int j = 0; j < child->numChild; ++j)
+ {
+ XmlNode *child2 = child->child[i];
+ if (!lstrcmpA(child2->name, "field"))
+ {
+ CJabberDataField *field = new CJabberDataField(this, child2);
+ item->AddField(field);
+ }
+ }
+ }
+ }
+}
+
+CJabberDataForm::~CJabberDataForm()
+{
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// UI classes
+
+#define FORM_CONTROL_MINWIDTH 100
+#define FORM_CONTROL_HEIGHT 20
+
+class CFormCtrlBase;
+
+class CJabberDlgDataPage
+{
+public:
+ CJabberDlgDataPage(HWND hwndParent);
+ ~CJabberDlgDataPage();
+
+ void AddField(CJabberDataField *field);
+ XmlNode *FetchData();
+
+ HWND GetHwnd() { return m_hwnd; }
+ void Layout();
+
+ // internal usage
+ int AddControl(CFormCtrlBase *ctrl)
+ {
+ m_controls.insert(ctrl, m_controls.getCount());
+ return m_controls.getCount();
+ }
+
+private:
+ HWND m_hwnd;
+ OBJLIST<CFormCtrlBase> m_controls;
+ int m_scrollPos, m_height, m_dataHeight;
+
+ BOOL DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
+ static BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+};
+
+class CFormCtrlBase
+{
+public:
+ CFormCtrlBase(CJabberDlgDataPage *parent, CJabberDataField *field):
+ m_field(field), m_parent(parent),
+ m_hwnd(NULL), m_hwndLabel(NULL)
+ {
+ }
+
+ HWND GetHwnd() { return m_hwnd; }
+ void Init();
+
+ int Layout(HDWP hdwp, int x, int y, int w);
+ virtual XmlNode *FetchData() = 0;
+
+protected:
+ int m_id;
+ HWND m_hwnd, m_hwndLabel;
+ CJabberDataField *m_field;
+ CJabberDlgDataPage *m_parent;
+
+ virtual void CreateControl() = 0;
+ virtual int GetHeight(int width) = 0;
+ SIZE GetControlTextSize(HWND hwnd, int w);
+
+ void CreateLabel();
+ void SetupFont();
+ XmlNode *CreateNode();
+};
+
+void CFormCtrlBase::Init()
+{
+ m_id = m_parent->AddControl(this);
+ CreateControl();
+ SetupFont();
+}
+
+SIZE CFormCtrlBase::GetControlTextSize(HWND hwnd, int w)
+{
+ int length = GetWindowTextLength(hwnd) + 1;
+ TCHAR *text = (TCHAR *)mir_alloc(sizeof(TCHAR) * length);
+ GetWindowText(hwnd, text, length);
+
+ RECT rc;
+ SetRect(&rc, 0, 0, w, 0);
+ HDC hdc = GetDC(hwnd);
+ HFONT hfntSave = (HFONT)SelectObject(hdc, (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0));
+ DrawText(hdc, text, -1, &rc, DT_CALCRECT|DT_WORDBREAK);
+ SelectObject(hdc, hfntSave);
+ ReleaseDC(hwnd, hdc);
+
+ mir_free(text);
+
+ SIZE res = { rc.right, rc.bottom };
+ return res;
+}
+
+int CFormCtrlBase::Layout(HDWP hdwp, int x, int y, int w)
+{
+ SIZE szLabel = {0}, szCtrl = {0};
+ int h = 0;
+
+ if (m_hwndLabel)
+ {
+ SIZE szLabel = GetControlTextSize(m_hwndLabel, w);
+
+ szCtrl.cx = w - szLabel.cx - 5;
+ szCtrl.cy = GetHeight(szCtrl.cx);
+ if ((szCtrl.cx >= FORM_CONTROL_MINWIDTH) && (szCtrl.cy <= FORM_CONTROL_HEIGHT))
+ {
+ DeferWindowPos(hdwp, m_hwndLabel, NULL, x, y + (szCtrl.cy - szLabel.cy) / 2, szLabel.cx, szLabel.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
+ DeferWindowPos(hdwp, m_hwnd, NULL, x + szLabel.cx + 5, y, szCtrl.cx, szCtrl.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
+
+ h = szCtrl.cy;
+ } else
+ {
+ szCtrl.cx = w - 10;
+ szCtrl.cy = GetHeight(szCtrl.cx);
+
+ DeferWindowPos(hdwp, m_hwndLabel, NULL, x, y, szLabel.cx, szLabel.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
+ DeferWindowPos(hdwp, m_hwnd, NULL, x + 10, y + szLabel.cy + 2, szCtrl.cx, szCtrl.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
+
+ h = szLabel.cy + 2 + szCtrl.cy;
+ }
+
+ } else
+ {
+ h = GetHeight(w);
+ DeferWindowPos(hdwp, m_hwnd, NULL, x, y, w, h, SWP_NOZORDER|SWP_SHOWWINDOW);
+ }
+
+ return h;
+}
+
+void CFormCtrlBase::CreateLabel()
+{
+ if (m_field->GetLabel())
+ {
+ m_hwndLabel = CreateWindow(_T("static"), m_field->GetLabel(),
+ WS_CHILD|WS_VISIBLE/*|SS_CENTERIMAGE*/,
+ 0, 0, 0, 0,
+ m_parent->GetHwnd(), (HMENU)-1, hInst, NULL);
+ }
+}
+
+void CFormCtrlBase::SetupFont()
+{
+ if (m_hwnd)
+ {
+ HFONT hFont = (HFONT)SendMessage(GetParent(m_hwnd), WM_GETFONT, 0, 0);
+ if (m_hwnd) SendMessage(m_hwnd, WM_SETFONT, (WPARAM)hFont, 0);
+ if (m_hwndLabel) SendMessage(m_hwndLabel, WM_SETFONT, (WPARAM) hFont, 0);
+ }
+}
+
+XmlNode *CFormCtrlBase::CreateNode()
+{
+ XmlNode* field = new XmlNode("field");
+ field->addAttr("var", m_field->GetVar());
+ field->addAttr("type", m_field->GetTypeName());
+ return field;
+}
+
+class CFormCtrlBoolean: public CFormCtrlBase
+{
+public:
+ CFormCtrlBoolean(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ m_hwnd = CreateWindowEx(0, _T("button"), m_field->GetLabel(),
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX|BS_MULTILINE,
+ 0, 0, 0, 0,
+ m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
+ if (m_field->GetValue() && !_tcscmp(m_field->GetValue(), _T("1")))
+ SendMessage(m_hwnd, BM_SETCHECK, 1, 0);
+ }
+
+ int GetHeight(int width)
+ {
+ return GetControlTextSize(m_hwnd, width - 20).cy;
+ }
+
+ XmlNode *FetchData()
+ {
+ XmlNode *field = CreateNode();
+ field->addChild("value", (SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED) ? _T("1") : _T("0"));
+ return field;
+ }
+};
+
+class CFormCtrlFixed: public CFormCtrlBase
+{
+public:
+ CFormCtrlFixed(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ CreateLabel();
+ m_hwnd = CreateWindow(_T("edit"), m_field->GetValue(),
+ WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_READONLY,
+ 0, 0, 0, 0,
+ m_parent->GetHwnd(), (HMENU)-1, hInst, NULL);
+ }
+
+ int GetHeight(int width)
+ {
+ return GetControlTextSize(m_hwnd, width - 2).cy;
+ }
+
+ XmlNode *FetchData()
+ {
+ XmlNode *field = CreateNode();
+ for (int i = 0; i < m_field->GetValueCount(); ++i)
+ field->addChild("value", m_field->GetValue(i));
+ return field;
+ }
+};
+
+class CFormCtrlHidden: public CFormCtrlBase
+{
+public:
+ CFormCtrlHidden(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ }
+
+ int GetHeight(int width)
+ {
+ return 0;
+ }
+
+ XmlNode *FetchData()
+ {
+ XmlNode *field = CreateNode();
+ for (int i = 0; i < m_field->GetValueCount(); ++i)
+ field->addChild("value", m_field->GetValue(i));
+ return field;
+ }
+};
+/*
+class CFormCtrlJidMulti: public CFormCtrlBase
+{
+public:
+ CFormCtrlJidMulti(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ }
+
+ int GetHeight(int width)
+ {
+ return 20;
+ }
+
+ XmlNode *FetchData()
+ {
+ return NULL;
+ }
+};
+
+class CFormCtrlJidSingle: public CFormCtrlBase
+{
+public:
+ CFormCtrlJidSingle(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ }
+
+ int GetHeight(int width)
+ {
+ return 20;
+ }
+
+ XmlNode *FetchData()
+ {
+ return NULL;
+ }
+};
+*/
+class CFormCtrlListMulti: public CFormCtrlBase
+{
+public:
+ CFormCtrlListMulti(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ CreateLabel();
+ m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("listbox"),
+ NULL, WS_CHILD|WS_VISIBLE|WS_TABSTOP|LBS_MULTIPLESEL,
+ 0, 0, 0, 0,
+ m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
+
+ for (int i = 0; i < m_field->GetOptionCount(); ++i)
+ {
+ DWORD dwIndex = SendMessage(m_hwnd, LB_ADDSTRING, 0, (LPARAM)m_field->GetOption(i)->label);
+ SendMessage(m_hwnd, LB_SETITEMDATA, dwIndex, (LPARAM)m_field->GetOption(i)->value);
+ for (int j = 0; j < m_field->GetValueCount(); ++j)
+ if (!lstrcmp_null(m_field->GetValue(j), m_field->GetOption(i)->value))
+ {
+ SendMessage(m_hwnd, LB_SETSEL, TRUE, dwIndex);
+ break;
+ }
+ }
+ }
+
+ int GetHeight(int width)
+ {
+ return 20 * 3;
+ }
+
+ XmlNode *FetchData()
+ {
+ XmlNode *field = CreateNode();
+ int count = SendMessage(m_hwnd, LB_GETCOUNT, 0, 0);
+ for (int i = 0; i < count; ++i)
+ if (SendMessage(m_hwnd, LB_GETSEL, i, 0) > 0)
+ field->addChild("value", (TCHAR *)SendMessage(m_hwnd, LB_GETITEMDATA, i, 0));
+ return field;
+ }
+};
+
+class CFormCtrlListSingle: public CFormCtrlBase
+{
+public:
+ CFormCtrlListSingle(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ CreateLabel();
+ m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("combobox"), NULL,
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|CBS_DROPDOWNLIST,
+ 0, 0, 0, 0,
+ m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
+
+ for (int i = 0; i < m_field->GetOptionCount(); ++i)
+ {
+ DWORD dwIndex = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)m_field->GetOption(i)->label);
+ SendMessage(m_hwnd, CB_SETITEMDATA, dwIndex, (LPARAM)m_field->GetOption(i)->value);
+ if (!lstrcmp_null(m_field->GetValue(), m_field->GetOption(i)->value))
+ SendMessage(m_hwnd, CB_SETCURSEL, dwIndex, 0);
+ }
+ }
+
+ int GetHeight(int width)
+ {
+ return 20;
+ }
+
+ XmlNode *FetchData()
+ {
+ XmlNode *field = CreateNode();
+ int sel = SendMessage(m_hwnd, CB_GETCURSEL, 0, 0);
+ if (sel != CB_ERR)
+ field->addChild("value", (TCHAR *)SendMessage(m_hwnd, CB_GETITEMDATA, sel, 0));
+ return field;
+ }
+};
+
+class CFormCtrlTextMulti: public CFormCtrlBase
+{
+public:
+ CFormCtrlTextMulti(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ CreateLabel();
+ int i, length = 1;
+ for (i = 0; i < m_field->GetValueCount(); ++i)
+ length += lstrlen(m_field->GetValue(i)) + 2;
+
+ TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * length);
+ *str = 0;
+ for (i = 0; i < m_field->GetValueCount(); ++i)
+ {
+ if (i) lstrcat(str, _T("\r\n"));
+ lstrcat(str, m_field->GetValue(i));
+ }
+
+ m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("edit"), str,
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_VSCROLL|ES_LEFT|ES_MULTILINE|ES_AUTOVSCROLL|ES_WANTRETURN,
+ 0, 0, 0, 0,
+ m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
+ // WNDPROC oldWndProc = (WNDPROC)SetWindowLongPtr(item->hCtrl, GWL_WNDPROC, (LPARAM)JabberFormMultiLineWndProc);
+ // SetWindowLongPtr(item->hCtrl, GWL_USERDATA, (LONG) oldWndProc);
+
+ mir_free(str);
+ }
+
+ int GetHeight(int width)
+ {
+ return 20 * 3;
+ }
+
+ XmlNode *FetchData()
+ {
+ XmlNode *field = CreateNode();
+ int len = GetWindowTextLength(m_hwnd);
+ TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len+1));
+ GetWindowText(m_hwnd, str, len+1);
+ TCHAR *p = str;
+ while (p != NULL)
+ {
+ TCHAR *q = _tcsstr( p, _T("\r\n"));
+ if (q) *q = '\0';
+ field->addChild("value", p);
+ p = q ? q+2 : NULL;
+ }
+ mir_free(str);
+ return field;
+ }
+};
+
+class CFormCtrlTextSingle: public CFormCtrlBase
+{
+public:
+ CFormCtrlTextSingle(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ CreateLabel();
+ m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("edit"), m_field->GetValue(),
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL,
+ 0, 0, 0, 0,
+ m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
+ }
+
+ int GetHeight(int width)
+ {
+ return 20;
+ }
+
+ XmlNode *FetchData()
+ {
+ XmlNode *field = CreateNode();
+ int len = GetWindowTextLength(m_hwnd);
+ TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len+1));
+ GetWindowText(m_hwnd, str, len+1);
+ field->addChild("value", str);
+ mir_free(str);
+ return field;
+ }
+};
+
+class CFormCtrlTextPrivate: public CFormCtrlBase
+{
+public:
+ CFormCtrlTextPrivate(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
+
+ void CreateControl()
+ {
+ CreateLabel();
+ m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("edit"), m_field->GetValue(),
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL|ES_PASSWORD,
+ 0, 0, 0, 0,
+ m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
+ }
+
+ int GetHeight(int width)
+ {
+ return 20;
+ }
+
+ XmlNode *FetchData()
+ {
+ XmlNode *field = CreateNode();
+ int len = GetWindowTextLength(m_hwnd);
+ TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len+1));
+ GetWindowText(m_hwnd, str, len+1);
+ field->addChild("value", str);
+ mir_free(str);
+ return field;
+ }
+};
+
+CJabberDlgDataPage::CJabberDlgDataPage(HWND hwndParent):
+ m_controls(5)
+{
+ m_hwnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_DATAFORM_PAGE), hwndParent, DlgProc, (LPARAM)this);
+ ShowWindow(m_hwnd, SW_SHOW);
+}
+
+CJabberDlgDataPage::~CJabberDlgDataPage()
+{
+ DestroyWindow(m_hwnd);
+}
+
+void CJabberDlgDataPage::AddField(CJabberDataField *field)
+{
+ CFormCtrlBase *ctrl = NULL;
+
+ switch (field->GetType())
+ {
+ case JDFT_BOOLEAN: ctrl = new CFormCtrlBoolean(this, field); break;
+ case JDFT_FIXED: ctrl = new CFormCtrlFixed(this, field); break;
+ case JDFT_HIDDEN: ctrl = new CFormCtrlHidden(this, field); break;
+ case JDFT_JID_MULTI: ctrl = new CFormCtrlTextMulti(this, field); break;
+ case JDFT_JID_SINGLE: ctrl = new CFormCtrlTextSingle(this, field); break;
+ case JDFT_LIST_MULTI: ctrl = new CFormCtrlListMulti(this, field); break;
+ case JDFT_LIST_SINGLE: ctrl = new CFormCtrlListSingle(this, field); break;
+ case JDFT_TEXT_MULTI: ctrl = new CFormCtrlTextMulti(this, field); break;
+ case JDFT_TEXT_PRIVATE: ctrl = new CFormCtrlTextPrivate(this, field); break;
+ case JDFT_TEXT_SINGLE: ctrl = new CFormCtrlTextSingle(this, field); break;
+ }
+
+ if (ctrl) ctrl->Init();
+}
+
+XmlNode *CJabberDlgDataPage::FetchData()
+{
+ XmlNode *result = new XmlNode("x");
+ result->addAttr("xmlns", JABBER_FEAT_DATA_FORMS);
+ result->addAttr("type", "submit");
+
+ for (int i = 0; i < m_controls.getCount(); ++i)
+ if (XmlNode *field = m_controls[i].FetchData())
+ result->addChild(field);
+
+ return result;
+}
+
+void CJabberDlgDataPage::Layout()
+{
+ RECT rc; GetClientRect(m_hwnd, &rc);
+ int w = rc.right - 20;
+ int x = 10;
+ int y = 10;
+
+ m_height = rc.bottom;
+ m_scrollPos = GetScrollPos(m_hwnd, SB_VERT);
+
+ HDWP hdwp = BeginDeferWindowPos(m_controls.getCount());
+ for (int i = 0; i < m_controls.getCount(); ++i)
+ if (int h = m_controls[i].Layout(hdwp, x, y - m_scrollPos, w))
+ y += h + 5;
+ EndDeferWindowPos(hdwp);
+
+ m_dataHeight = y + 5;
+
+ SCROLLINFO si = {0};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_DISABLENOSCROLL|SIF_PAGE|SIF_RANGE;
+ si.nPage = m_height;
+ si.nMin = 0;
+ si.nMax = m_dataHeight;
+ SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);
+}
+
+BOOL CJabberDlgDataPage::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ SCROLLINFO si = {0};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_DISABLENOSCROLL;
+ SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);
+ m_scrollPos = 0;
+
+ break;
+ }
+
+ case WM_MOUSEWHEEL:
+ {
+ int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
+ if (zDelta)
+ {
+ int i, nScrollLines = 0;
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (void*)&nScrollLines, 0);
+ for (i = 0; i < (nScrollLines + 1) / 2; i++ )
+ SendMessage(m_hwnd, WM_VSCROLL, (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP, 0);
+ }
+
+ SetWindowLongPtr(m_hwnd, DWL_MSGRESULT, 0);
+ return TRUE;
+ }
+
+ case WM_VSCROLL:
+ {
+ int pos = m_scrollPos;
+ switch (LOWORD(wParam))
+ {
+ case SB_LINEDOWN:
+ pos += 15;
+ break;
+ case SB_LINEUP:
+ pos -= 15;
+ break;
+ case SB_PAGEDOWN:
+ pos += m_height - 10;
+ break;
+ case SB_PAGEUP:
+ pos -= m_height - 10;
+ break;
+ case SB_THUMBTRACK:
+ pos = HIWORD(wParam);
+ break;
+ }
+
+ if (pos > m_dataHeight - m_height) pos = m_dataHeight - m_height;
+ if (pos < 0) pos = 0;
+
+ if (m_scrollPos != pos)
+ {
+ ScrollWindow(m_hwnd, 0, m_scrollPos - pos, NULL, NULL);
+ SetScrollPos(m_hwnd, SB_VERT, pos, TRUE);
+ m_scrollPos = pos;
+ }
+ break;
+ }
+
+ case WM_SIZE:
+ Layout();
+ break;
+ }
+
+ return FALSE;
+}
+
+BOOL CJabberDlgDataPage::DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CJabberDlgDataPage *pThis = NULL;
+
+ if (msg == WM_INITDIALOG)
+ {
+ if (pThis = (CJabberDlgDataPage *)lParam)
+ pThis->m_hwnd = hwnd;
+ SetWindowLongPtr(hwnd, GWL_USERDATA, lParam);
+ } else
+ {
+ pThis = (CJabberDlgDataPage *)GetWindowLongPtr(hwnd, GWL_USERDATA);
+ }
+
+ if (pThis)
+ {
+ BOOL result = pThis->DlgProc(msg, wParam, lParam);
+ if (msg == WM_DESTROY)
+ {
+ pThis->m_hwnd = NULL;
+ SetWindowLongPtr(hwnd, GWL_USERDATA, 0);
+ }
+ return result;
+ }
+
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// Data form control -- Window class support
+const TCHAR *CCtrlJabberForm::ClassName = _T("JabberDataFormControl");
+bool CCtrlJabberForm::ClassRegistered = false;
+
+bool CCtrlJabberForm::RegisterClass()
+{
+ if (ClassRegistered) return true;
+
+ WNDCLASSEX wcx = {0};
+ wcx.cbSize = sizeof(wcx);
+ wcx.lpszClassName = ClassName;
+ wcx.lpfnWndProc = DefWindowProc;
+ wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcx.hbrBackground = 0;
+ wcx.style = CS_GLOBALCLASS;
+
+ if (::RegisterClassEx(&wcx))
+ ClassRegistered = true;
+
+ return ClassRegistered;
+}
+
+bool CCtrlJabberForm::UnregisterClass()
+{
+ if (!ClassRegistered) return true;
+
+ if (::UnregisterClass(ClassName, hInst) == 0)
+ ClassRegistered = false;
+
+ return !ClassRegistered;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// Data form control
+CCtrlJabberForm::CCtrlJabberForm(CDlgBase* dlg, int ctrlId):
+ CCtrlBase(dlg, ctrlId), m_pForm(NULL), m_pDlgPage(NULL)
+{
+}
+
+CCtrlJabberForm::~CCtrlJabberForm()
+{
+ if (m_pDlgPage) delete m_pDlgPage;
+}
+
+void CCtrlJabberForm::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+ SetupForm();
+}
+
+void CCtrlJabberForm::SetDataForm(CJabberDataForm *pForm)
+{
+ if (m_pDlgPage)
+ {
+ delete m_pDlgPage;
+ m_pDlgPage = NULL;
+ }
+
+ m_pForm = pForm;
+ SetupForm();
+}
+
+XmlNode *CCtrlJabberForm::FetchData()
+{
+ return m_pDlgPage ? m_pDlgPage->FetchData() : NULL;
+}
+
+void CCtrlJabberForm::SetupForm()
+{
+ if (!m_pForm || !m_hwnd) return;
+
+ m_pDlgPage = new CJabberDlgDataPage(m_hwnd);
+ for (int i = 0; i < m_pForm->GetFields()->GetCount(); ++i)
+ m_pDlgPage->AddField(m_pForm->GetFields()->GetField(i));
+
+ Layout();
+}
+
+void CCtrlJabberForm::Layout()
+{
+ if (!m_pDlgPage) return;
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ SetWindowPos(m_pDlgPage->GetHwnd(), NULL, 0, 0, rc.right, rc.bottom, SWP_NOZORDER);
+}
+
+LRESULT CCtrlJabberForm::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_SIZE:
+ {
+ Layout();
+ break;
+ }
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// testing
+class CJabberDlgFormTest: public CDlgBase
+{
+public:
+ CJabberDlgFormTest(CJabberDataForm *pForm):
+ CDlgBase(IDD_DATAFORM_TEST, NULL),
+ m_frm(this, IDC_DATAFORM)
+ {
+ m_frm.SetDataForm(pForm);
+ }
+
+ int Resizer(UTILRESIZECONTROL *urc)
+ {
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ }
+
+private:
+ CCtrlJabberForm m_frm;
+};
+
+static VOID CALLBACK CreateDialogApcProc(void* param)
+{
+ XmlNode *node = (XmlNode *)param;
+
+ CJabberDataForm form(node);
+
+ CCtrlJabberForm::RegisterClass();
+ CJabberDlgFormTest dlg(&form);
+ dlg.DoModal();
+
+ delete node;
+}
+
+void LaunchForm(XmlNode *node)
+{
+ node = JabberXmlCopyNode(node);
+ CallFunctionAsync(CreateDialogApcProc, node);
+}
diff --git a/protocols/JabberG/src/jabber_form2.h b/protocols/JabberG/src/jabber_form2.h new file mode 100644 index 0000000000..051eb8571f --- /dev/null +++ b/protocols/JabberG/src/jabber_form2.h @@ -0,0 +1,190 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007-09 Maxim Mluhov
+Copyright ( C ) 2007-09 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+enum TJabberDataFormType
+{
+ JDFT_BOOLEAN,
+ JDFT_FIXED,
+ JDFT_HIDDEN,
+ JDFT_JID_MULTI,
+ JDFT_JID_SINGLE,
+ JDFT_LIST_MULTI,
+ JDFT_LIST_SINGLE,
+ JDFT_TEXT_MULTI,
+ JDFT_TEXT_PRIVATE,
+ JDFT_TEXT_SINGLE,
+};
+
+struct TJabberDataFormRegisry_Field
+{
+ TCHAR *field;
+ TJabberDataFormType type;
+ TCHAR *description_en;
+ TCHAR *description_tr;
+};
+
+struct TJabberDataFormRegisry_Form
+{
+ TCHAR *form_type;
+ TJabberDataFormRegisry_Field *fields;
+ int count;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Forwards
+class CJabberDlgDataForm;
+class CJabberDataField;
+class CJabberDataFieldSet;
+class CJabberDataForm;
+class CJabberDlgDataPage;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Data form classes
+class CJabberDataField
+{
+public:
+ struct TOption
+ {
+ TCHAR *label;
+ TCHAR *value;
+ };
+
+ CJabberDataField(CJabberDataForm *form, XmlNode *node);
+ ~CJabberDataField();
+
+ XmlNode *GetNode() { return m_node; }
+
+ TCHAR *GetTypeName() { return m_typeName; }
+ TJabberDataFormType GetType() { return m_type; }
+
+ TCHAR *GetVar() { return m_var; }
+
+ bool IsRequired() { return m_required; }
+ TCHAR *GetDescription(int i) { return m_descriptions[i]; }
+ TCHAR *GetLabel() { return m_label; }
+
+ int GetValueCount() { return m_values.getCount(); }
+ TCHAR *GetValue(int i = 0) { return m_values[i]; }
+
+ int GetOptionCount() { return m_options.getCount(); }
+ TOption *GetOption(int i) { return &(m_options[i]); }
+
+private:
+ XmlNode *m_node;
+ CJabberDataFieldSet *m_fieldset;
+
+ bool m_required;
+ TCHAR *m_var;
+ TCHAR *m_label;
+ TCHAR *m_typeName;
+ TJabberDataFormType m_type;
+
+ OBJLIST<TOption> m_options;
+ LIST<TCHAR> m_values;
+ LIST<TCHAR> m_descriptions;
+};
+
+class CJabberDataFieldSet
+{
+public:
+ CJabberDataFieldSet();
+
+ int GetCount() { return m_fields.getCount(); }
+ CJabberDataField *GetField(int i) { return &(m_fields[i]); }
+ CJabberDataField *GetField(TCHAR *var);
+
+ void AddField(CJabberDataField *field) { m_fields.insert(field, m_fields.getCount()); }
+
+private:
+ OBJLIST<CJabberDataField> m_fields;
+};
+
+class CJabberDataForm
+{
+public:
+ enum TFormType { TYPE_NONE, TYPE_FORM, TYPE_SUBMIT, TYPE_CANCEL, TYPE_RESULT };
+
+ CJabberDataForm(XmlNode *node);
+ ~CJabberDataForm();
+
+ TCHAR *GetTypeName() const { return m_typename; }
+ TFormType GetType() const { return m_type; }
+ TCHAR *GetFormType() const { return m_form_type; }
+ TCHAR *GetTitle() const { return m_title; }
+ int GetInstructionsCount() const { return m_instructions.getCount(); }
+ TCHAR *GetInstructions(int idx=0) const { return m_instructions[idx]; }
+
+ CJabberDataFieldSet *GetFields() { return &m_fields; }
+
+ CJabberDataFieldSet *GetReported() { return &m_reported; }
+ int GetItemCount() { return m_items.getCount(); }
+ CJabberDataFieldSet *GetItem(int i) { return &(m_items[i]); }
+
+private:
+ XmlNode *m_node;
+
+ TCHAR *m_typename;
+ TFormType m_type;
+
+ TCHAR *m_form_type;
+ TJabberDataFormRegisry_Form *m_form_type_info;
+
+ TCHAR *m_title;
+ LIST<TCHAR> m_instructions;
+
+ CJabberDataFieldSet m_fields;
+ CJabberDataFieldSet m_reported;
+ OBJLIST<CJabberDataFieldSet> m_items;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// UI Control
+class CCtrlJabberForm: public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+public:
+ static const TCHAR *ClassName;
+ static bool RegisterClass();
+ static bool UnregisterClass();
+
+ CCtrlJabberForm(CDlgBase* dlg, int ctrlId);
+ ~CCtrlJabberForm();
+
+ void OnInit();
+ void SetDataForm(CJabberDataForm *pForm);
+ XmlNode *FetchData();
+
+protected:
+ virtual LRESULT CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam);
+
+private:
+ static bool ClassRegistered;
+
+ CJabberDataForm *m_pForm;
+ CJabberDlgDataPage *m_pDlgPage;
+
+ void SetupForm();
+ void Layout();
+};
diff --git a/protocols/JabberG/src/jabber_ft.cpp b/protocols/JabberG/src/jabber_ft.cpp new file mode 100644 index 0000000000..4ded28d84d --- /dev/null +++ b/protocols/JabberG/src/jabber_ft.cpp @@ -0,0 +1,551 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include <io.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "jabber_iq.h"
+#include "jabber_byte.h"
+#include "jabber_ibb.h"
+#include "jabber_caps.h"
+
+void CJabberProto::FtCancel( filetransfer* ft )
+{
+ JABBER_LIST_ITEM *item;
+ JABBER_BYTE_TRANSFER *jbt;
+ JABBER_IBB_TRANSFER *jibb;
+
+ Log( "Invoking JabberFtCancel()" );
+
+ // For file sending session that is still in si negotiation phase
+ if ( m_iqManager.ExpireByUserData( ft ))
+ return;
+ // For file receiving session that is still in si negotiation phase
+ LISTFOREACH(i, this, LIST_FTRECV)
+ {
+ item = ListGetItemPtrFromIndex( i );
+ if ( item->ft == ft ) {
+ Log( "Canceling file receiving session while in si negotiation" );
+ ListRemoveByIndex( i );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
+ delete ft;
+ return;
+ }
+ }
+ // For file transfer through bytestream
+ if (( jbt=ft->jbt ) != NULL ) {
+ Log( "Canceling bytestream session" );
+ jbt->state = JBT_ERROR;
+ if ( jbt->hConn ) {
+ Log( "Force closing bytestream session" );
+ Netlib_CloseHandle( jbt->hConn );
+ jbt->hConn = NULL;
+ }
+ if ( jbt->hSendEvent ) SetEvent( jbt->hSendEvent );
+ if ( jbt->hEvent ) SetEvent( jbt->hEvent );
+ if ( jbt->hProxyEvent ) SetEvent( jbt->hProxyEvent );
+ }
+ // For file transfer through IBB
+ if (( jibb=ft->jibb ) != NULL ) {
+ Log( "Canceling IBB session" );
+ jibb->state = JIBB_ERROR;
+ m_iqManager.ExpireByUserData( jibb );
+ }
+}
+
+///////////////// File sending using stream initiation /////////////////////////
+
+void CJabberProto::FtInitiate( TCHAR* jid, filetransfer* ft )
+{
+ TCHAR *rs;
+ TCHAR *filename, *p;
+ int i;
+ TCHAR sid[9];
+
+ if ( jid==NULL || ft==NULL || !m_bJabberOnline || ( rs=ListGetBestClientResourceNamePtr( jid ))==NULL ) {
+ if ( ft ) {
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
+ delete ft;
+ }
+ return;
+ }
+ ft->type = FT_SI;
+ for ( i=0; i<8; i++ )
+ sid[i] = ( rand()%10 ) + '0';
+ sid[8] = '\0';
+ if ( ft->sid != NULL ) mir_free( ft->sid );
+ ft->sid = mir_tstrdup( sid );
+ filename = ft->std.ptszFiles[ ft->std.currentFileNumber ];
+ if (( p = _tcsrchr( filename, '\\' )) != NULL )
+ filename = p+1;
+
+ TCHAR tszJid[ 512 ];
+ mir_sntprintf( tszJid, SIZEOF(tszJid), _T("%s/%s"), jid, rs );
+
+ XmlNodeIq iq( m_iqManager.AddHandler( &CJabberProto::OnFtSiResult, JABBER_IQ_TYPE_SET, tszJid, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_TO, -1, ft ));
+ HXML si = iq << XCHILDNS( _T("si"), _T(JABBER_FEAT_SI)) << XATTR( _T("id"), sid )
+ << XATTR( _T("mime-type"), _T("binary/octet-stream")) << XATTR( _T("profile"), _T(JABBER_FEAT_SI_FT));
+ si << XCHILDNS( _T("file"), _T(JABBER_FEAT_SI_FT)) << XATTR( _T("name"), filename)
+ << XATTRI64( _T("size"), ft->fileSize[ ft->std.currentFileNumber ] ) << XCHILD( _T("desc"), ft->szDescription);
+
+ HXML field = si << XCHILDNS( _T("feature"), _T(JABBER_FEAT_FEATURE_NEG))
+ << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("form"))
+ << XCHILD( _T("field")) << XATTR( _T("var"), _T("stream-method")) << XATTR( _T("type"), _T("list-single"));
+
+ BOOL bDirect = m_options.BsDirect;
+ BOOL bProxy = m_options.BsProxyManual;
+
+ // bytestreams support?
+ if ( bDirect || bProxy )
+ field << XCHILD( _T("option")) << XCHILD( _T("value"), _T(JABBER_FEAT_BYTESTREAMS));
+
+ field << XCHILD( _T("option")) << XCHILD( _T("value"), _T(JABBER_FEAT_IBB));
+ m_ThreadInfo->send( iq );
+}
+
+void CJabberProto::OnFtSiResult( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ HXML siNode, featureNode, xNode, fieldNode, valueNode;
+ filetransfer *ft = (filetransfer *)pInfo->GetUserData();
+ if ( !ft ) return;
+
+ if (( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) && pInfo->m_szFrom && pInfo->m_szTo ) {
+ if (( siNode = xmlGetChild( iqNode , "si" )) != NULL ) {
+
+ // fix for very smart clients, like gajim
+ BOOL bDirect = m_options.BsDirect;
+ BOOL bProxy = m_options.BsProxyManual;
+
+ if (( featureNode = xmlGetChild( siNode , "feature" )) != NULL ) {
+ if (( xNode = xmlGetChildByTag( featureNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS))) != NULL ) {
+ if (( fieldNode = xmlGetChildByTag( xNode, "field", "var", _T("stream-method"))) != NULL ) {
+ if (( valueNode = xmlGetChild( fieldNode , "value" ))!=NULL && xmlGetText( valueNode )!=NULL ) {
+ if (( bDirect || bProxy ) && !_tcscmp( xmlGetText( valueNode ), _T(JABBER_FEAT_BYTESTREAMS))) {
+ // Start Bytestream session
+ JABBER_BYTE_TRANSFER *jbt = new JABBER_BYTE_TRANSFER;
+ ZeroMemory( jbt, sizeof( JABBER_BYTE_TRANSFER ));
+ jbt->srcJID = mir_tstrdup( pInfo->m_szTo );
+ jbt->dstJID = mir_tstrdup( pInfo->m_szFrom );
+ jbt->sid = mir_tstrdup( ft->sid );
+ jbt->pfnSend = &CJabberProto::FtSend;
+ jbt->pfnFinal = &CJabberProto::FtSendFinal;
+ jbt->ft = ft;
+ ft->type = FT_BYTESTREAM;
+ ft->jbt = jbt;
+ JForkThread(( JThreadFunc )&CJabberProto::ByteSendThread, jbt );
+ } else if ( !_tcscmp( xmlGetText( valueNode ), _T(JABBER_FEAT_IBB))) {
+ JABBER_IBB_TRANSFER *jibb = (JABBER_IBB_TRANSFER *) mir_alloc( sizeof ( JABBER_IBB_TRANSFER ));
+ ZeroMemory( jibb, sizeof( JABBER_IBB_TRANSFER ));
+ jibb->srcJID = mir_tstrdup( pInfo->m_szTo );
+ jibb->dstJID = mir_tstrdup( pInfo->m_szFrom );
+ jibb->sid = mir_tstrdup( ft->sid );
+ jibb->pfnSend = &CJabberProto::FtIbbSend;
+ jibb->pfnFinal = &CJabberProto::FtSendFinal;
+ jibb->ft = ft;
+ ft->type = FT_IBB;
+ ft->jibb = jibb;
+ JForkThread(( JThreadFunc )&CJabberProto::IbbSendThread, jibb );
+ } } } } } } }
+ else {
+ Log( "File transfer stream initiation request denied or failed" );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, pInfo->GetIqType() == JABBER_IQ_TYPE_ERROR ? ACKRESULT_DENIED : ACKRESULT_FAILED, ft, 0 );
+ delete ft;
+ }
+}
+
+BOOL CJabberProto::FtSend( HANDLE hConn, filetransfer* ft )
+{
+ struct _stati64 statbuf;
+ int fd;
+ char* buffer;
+ int numRead;
+
+ Log( "Sending [%s]", ft->std.ptszFiles[ ft->std.currentFileNumber ] );
+ _tstati64( ft->std.ptszFiles[ ft->std.currentFileNumber ], &statbuf ); // file size in statbuf.st_size
+ if (( fd = _topen( ft->std.ptszFiles[ ft->std.currentFileNumber ], _O_BINARY|_O_RDONLY )) < 0 ) {
+ Log( "File cannot be opened" );
+ return FALSE;
+ }
+
+ ft->std.flags |= PFTS_SENDING;
+ ft->std.currentFileSize = statbuf.st_size;
+ ft->std.currentFileProgress = 0;
+
+ if (( buffer=( char* )mir_alloc( 2048 )) != NULL ) {
+ while (( numRead=_read( fd, buffer, 2048 )) > 0 ) {
+ if ( Netlib_Send( hConn, buffer, numRead, 0 ) != numRead ) {
+ mir_free( buffer );
+ _close( fd );
+ return FALSE;
+ }
+ ft->std.currentFileProgress += numRead;
+ ft->std.totalProgress += numRead;
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std );
+ }
+ mir_free( buffer );
+ }
+ _close( fd );
+ return TRUE;
+}
+
+BOOL CJabberProto::FtIbbSend( int blocksize, filetransfer* ft )
+{
+ struct _stati64 statbuf;
+ int fd;
+ char* buffer;
+ int numRead;
+
+ Log( "Sending [%s]", ft->std.ptszFiles[ ft->std.currentFileNumber ] );
+ _tstati64( ft->std.ptszFiles[ ft->std.currentFileNumber ], &statbuf ); // file size in statbuf.st_size
+ if (( fd = _topen( ft->std.ptszFiles[ ft->std.currentFileNumber ], _O_BINARY|_O_RDONLY )) < 0 ) {
+ Log( "File cannot be opened" );
+ return FALSE;
+ }
+
+ ft->std.flags |= PFTS_SENDING;
+ ft->std.currentFileSize = statbuf.st_size;
+ ft->std.currentFileProgress = 0;
+
+ if (( buffer=( char* )mir_alloc( blocksize )) != NULL ) {
+ while (( numRead=_read( fd, buffer, blocksize )) > 0 ) {
+ int iqId = SerialNext();
+ XmlNode msg( _T("message"));
+ xmlAddAttr( msg, _T("to"), ft->jibb->dstJID );
+ msg << XATTRID( iqId );
+
+ // let others send data too
+ Sleep(2);
+
+ char *encoded = JabberBase64Encode(buffer, numRead);
+
+ msg << XCHILD( _T("data"), _A2T(encoded)) << XATTR( _T("xmlns"), _T(JABBER_FEAT_IBB))
+ << XATTR( _T("sid"), ft->jibb->sid ) << XATTRI( _T("seq"), ft->jibb->wPacketId );
+
+ HXML ampNode = msg << XCHILDNS( _T("amp"), _T(JABBER_FEAT_AMP));
+ ampNode << XCHILD( _T("rule")) << XATTR( _T("condition"), _T("deliver-at"))
+ << XATTR( _T("value"), _T("stored")) << XATTR( _T("action"), _T("error"));
+ ampNode << XCHILD( _T("rule")) << XATTR( _T("condition"), _T("match-resource"))
+ << XATTR( _T("value"), _T("exact")) << XATTR( _T("action"), _T("error"));
+ ft->jibb->wPacketId++;
+
+ mir_free( encoded );
+
+ if ( ft->jibb->state == JIBB_ERROR || ft->jibb->bStreamClosed || m_ThreadInfo->send( msg ) == SOCKET_ERROR ) {
+ Log( "JabberFtIbbSend unsuccessful exit" );
+ mir_free( buffer );
+ _close( fd );
+ return FALSE;
+ }
+
+ ft->jibb->dwTransferredSize += (DWORD)numRead;
+
+ ft->std.currentFileProgress += numRead;
+ ft->std.totalProgress += numRead;
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std );
+ }
+ mir_free( buffer );
+ }
+ _close( fd );
+ return TRUE;
+}
+
+void CJabberProto::FtSendFinal( BOOL success, filetransfer* ft )
+{
+ if ( !success ) {
+ Log( "File transfer complete with error" );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ft->state == FT_DENIED ? ACKRESULT_DENIED : ACKRESULT_FAILED, ft, 0 );
+ }
+ else {
+ if ( ft->std.currentFileNumber < ft->std.totalFiles-1 ) {
+ ft->std.currentFileNumber++;
+ replaceStrT( ft->std.tszCurrentFile, ft->std.ptszFiles[ ft->std.currentFileNumber ] );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0 );
+ FtInitiate( ft->jid, ft );
+ return;
+ }
+
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0 );
+ }
+
+ delete ft;
+}
+
+///////////////// File receiving through stream initiation /////////////////////////
+
+void CJabberProto::FtHandleSiRequest( HXML iqNode )
+{
+ const TCHAR* from, *sid, *str, *szId, *filename;
+ HXML siNode, fileNode, featureNode, xNode, fieldNode, n;
+ int i;
+ unsigned __int64 filesize;
+
+ if ( !iqNode ||
+ ( from = xmlGetAttrValue( iqNode, _T("from"))) == NULL ||
+ ( str = xmlGetAttrValue( iqNode, _T("type"))) == NULL || _tcscmp( str, _T("set")) ||
+ ( siNode = xmlGetChildByTag( iqNode, "si", "xmlns", _T(JABBER_FEAT_SI))) == NULL )
+ return;
+
+ szId = xmlGetAttrValue( iqNode, _T("id"));
+ if (( sid = xmlGetAttrValue( siNode, _T("id"))) != NULL &&
+ ( fileNode = xmlGetChildByTag( siNode, "file", "xmlns", _T(JABBER_FEAT_SI_FT))) != NULL &&
+ ( filename = xmlGetAttrValue( fileNode, _T("name"))) != NULL &&
+ ( str = xmlGetAttrValue( fileNode, _T("size"))) != NULL ) {
+
+ filesize = _ttoi64( str );
+ if (( featureNode = xmlGetChildByTag( siNode, "feature", "xmlns", _T(JABBER_FEAT_FEATURE_NEG))) != NULL &&
+ ( xNode = xmlGetChildByTag( featureNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS)))!=NULL &&
+ ( fieldNode = xmlGetChildByTag( xNode, "field", "var", _T("stream-method")))!=NULL ) {
+
+ BOOL bIbbOnly = m_options.BsOnlyIBB;
+ HXML optionNode = NULL;
+ JABBER_FT_TYPE ftType = FT_OOB;
+
+ if ( !bIbbOnly ) {
+ for ( i=0; ; i++ ) {
+ optionNode = xmlGetChild( fieldNode ,i);
+ if ( !optionNode )
+ break;
+
+ if ( !lstrcmp( xmlGetName( optionNode ), _T("option"))) {
+ if (( n = xmlGetChild( optionNode , "value" )) != NULL && xmlGetText( n )) {
+ if ( !_tcscmp( xmlGetText( n ), _T(JABBER_FEAT_BYTESTREAMS))) {
+ ftType = FT_BYTESTREAM;
+ break;
+ } } } } }
+
+ // try IBB only if bytestreams support not found or BsOnlyIBB flag exists
+ if ( bIbbOnly || !optionNode ) {
+ for ( i=0; ; i++ ) {
+ optionNode = xmlGetChild( fieldNode ,i);
+ if ( !optionNode )
+ break;
+
+ if ( !lstrcmp( xmlGetName( optionNode ), _T("option"))) {
+ if (( n = xmlGetChild( optionNode , "value" )) != NULL && xmlGetText( n )) {
+ if ( !_tcscmp( xmlGetText( n ), _T(JABBER_FEAT_IBB))) {
+ ftType = FT_IBB;
+ break;
+ } } } } }
+
+ if ( optionNode != NULL ) {
+ // Found known stream mechanism
+ filetransfer* ft = new filetransfer( this );
+ ft->dwExpectedRecvFileSize = filesize;
+ ft->jid = mir_tstrdup( from );
+ ft->std.hContact = HContactFromJID( from );
+ ft->sid = mir_tstrdup( sid );
+ ft->iqId = mir_tstrdup( szId );
+ ft->type = ftType;
+ ft->std.totalFiles = 1;
+ ft->std.tszCurrentFile = mir_tstrdup( filename );
+ ft->std.totalBytes = ft->std.currentFileSize = filesize;
+
+ PROTORECVFILET pre = { 0 };
+ pre.flags = PREF_TCHAR;
+ pre.fileCount = 1;
+ pre.timestamp = time( NULL );
+ pre.ptszFiles = ( TCHAR** )&filename;
+ pre.lParam = ( LPARAM )ft;
+ if (( n = xmlGetChild( fileNode , "desc" )) != NULL )
+ pre.tszDescription = ( TCHAR* )xmlGetText( n );
+
+ CCSDATA ccs = { ft->std.hContact, PSR_FILE, 0, ( LPARAM )&pre };
+ CallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs );
+ return;
+ }
+ else {
+ // Unknown stream mechanism
+ XmlNodeIq iq( _T("error"), szId, from );
+ HXML e = iq << XCHILD( _T("error")) << XATTRI( _T("code"), 400 ) << XATTR( _T("type"), _T("cancel"));
+ e << XCHILDNS( _T("bad-request"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"));
+ e << XCHILDNS( _T("no-valid-streams"), _T(JABBER_FEAT_SI));
+ m_ThreadInfo->send( iq );
+ return;
+ } } }
+
+ // Bad stream initiation, reply with bad-profile
+ XmlNodeIq iq( _T("error"), szId, from );
+ HXML e = iq << XCHILD( _T("error")) << XATTRI( _T("code"), 400 ) << XATTR( _T("type"), _T("cancel"));
+ e << XCHILDNS( _T("bad-request"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"));
+ e << XCHILDNS( _T("bad-profile"), _T(JABBER_FEAT_SI));
+ m_ThreadInfo->send( iq );
+}
+
+void CJabberProto::FtAcceptSiRequest( filetransfer* ft )
+{
+ if ( !m_bJabberOnline || ft==NULL || ft->jid==NULL || ft->sid==NULL ) return;
+
+ JABBER_LIST_ITEM *item;
+ if (( item=ListAdd( LIST_FTRECV, ft->sid )) != NULL ) {
+ item->ft = ft;
+
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), ft->iqId, ft->jid )
+ << XCHILDNS( _T("si"), _T(JABBER_FEAT_SI))
+ << XCHILDNS( _T("feature"), _T(JABBER_FEAT_FEATURE_NEG))
+ << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("submit"))
+ << XCHILD( _T("field")) << XATTR( _T("var"), _T("stream-method"))
+ << XCHILD( _T("value"), _T(JABBER_FEAT_BYTESTREAMS)));
+} }
+
+void CJabberProto::FtAcceptIbbRequest( filetransfer* ft )
+{
+ if ( !m_bJabberOnline || ft==NULL || ft->jid==NULL || ft->sid==NULL ) return;
+
+ JABBER_LIST_ITEM *item;
+ if (( item=ListAdd( LIST_FTRECV, ft->sid )) != NULL ) {
+ item->ft = ft;
+
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), ft->iqId, ft->jid )
+ << XCHILDNS( _T("si"), _T(JABBER_FEAT_SI))
+ << XCHILDNS( _T("feature"), _T(JABBER_FEAT_FEATURE_NEG))
+ << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("submit"))
+ << XCHILD( _T("field")) << XATTR( _T("var"), _T("stream-method"))
+ << XCHILD( _T("value"), _T(JABBER_FEAT_IBB)));
+} }
+
+BOOL CJabberProto::FtHandleBytestreamRequest( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ HXML queryNode = pInfo->GetChildNode();
+
+ const TCHAR* sid;
+ JABBER_LIST_ITEM *item;
+
+ if (( sid = xmlGetAttrValue( queryNode, _T("sid"))) != NULL && ( item = ListGetItemPtr( LIST_FTRECV, sid )) != NULL ) {
+ // Start Bytestream session
+ JABBER_BYTE_TRANSFER *jbt = new JABBER_BYTE_TRANSFER;
+ ZeroMemory( jbt, sizeof( JABBER_BYTE_TRANSFER ));
+ jbt->iqNode = xi.copyNode( iqNode );
+ jbt->pfnRecv = &CJabberProto::FtReceive;
+ jbt->pfnFinal = &CJabberProto::FtReceiveFinal;
+ jbt->ft = item->ft;
+ item->ft->jbt = jbt;
+ JForkThread(( JThreadFunc )&CJabberProto::ByteReceiveThread, jbt );
+ ListRemove( LIST_FTRECV, sid );
+ return TRUE;
+ }
+
+ Log( "File transfer invalid bytestream initiation request received" );
+ return TRUE;
+}
+
+BOOL CJabberProto::FtHandleIbbRequest( HXML iqNode, BOOL bOpen )
+{
+ if ( !iqNode ) return FALSE;
+
+ const TCHAR *id = xmlGetAttrValue( iqNode, _T("id"));
+ const TCHAR *from = xmlGetAttrValue( iqNode, _T("from"));
+ const TCHAR *to = xmlGetAttrValue( iqNode, _T("to"));
+ if ( !id || !from || !to ) return FALSE;
+
+ HXML ibbNode = xmlGetChildByTag( iqNode, bOpen ? "open" : "close", "xmlns", _T(JABBER_FEAT_IBB));
+ if ( !ibbNode ) return FALSE;
+
+ const TCHAR *sid = xmlGetAttrValue( ibbNode, _T("sid"));
+ if ( !sid ) return FALSE;
+
+ // already closed?
+ JABBER_LIST_ITEM *item = ListGetItemPtr( LIST_FTRECV, sid );
+ if ( !item ) {
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("error"), id, from )
+ << XCHILD( _T("error")) << XATTRI( _T("code"), 404 ) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+ return FALSE;
+ }
+
+ // open event
+ if ( bOpen ) {
+ if ( !item->jibb ) {
+ JABBER_IBB_TRANSFER *jibb = ( JABBER_IBB_TRANSFER * ) mir_alloc( sizeof( JABBER_IBB_TRANSFER ));
+ ZeroMemory( jibb, sizeof( JABBER_IBB_TRANSFER ));
+ jibb->srcJID = mir_tstrdup( from );
+ jibb->dstJID = mir_tstrdup( to );
+ jibb->sid = mir_tstrdup( sid );
+ jibb->pfnRecv = &CJabberProto::FtReceive;
+ jibb->pfnFinal = &CJabberProto::FtReceiveFinal;
+ jibb->ft = item->ft;
+ item->ft->jibb = jibb;
+ item->jibb = jibb;
+ JForkThread(( JThreadFunc )&CJabberProto::IbbReceiveThread, jibb );
+
+ m_ThreadInfo->send( XmlNodeIq( _T("result"), id, from ));
+ return TRUE;
+ }
+ // stream already open
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("error"), id, from )
+ << XCHILD( _T("error")) << XATTRI( _T("code"), 404 ) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+ return FALSE;
+ }
+
+ // close event && stream already open
+ if ( item->jibb && item->jibb->hEvent ) {
+ item->jibb->bStreamClosed = TRUE;
+ SetEvent( item->jibb->hEvent );
+
+ m_ThreadInfo->send( XmlNodeIq( _T("result"), id, from ));
+ return TRUE;
+ }
+
+ ListRemove( LIST_FTRECV, sid );
+
+ return FALSE;
+}
+
+int CJabberProto::FtReceive( HANDLE, filetransfer* ft, char* buffer, int datalen )
+{
+ if ( ft->create() == -1 )
+ return -1;
+
+ __int64 remainingBytes = ft->std.currentFileSize - ft->std.currentFileProgress;
+ if ( remainingBytes > 0 ) {
+ int writeSize = ( remainingBytes<datalen ) ? remainingBytes : datalen;
+ if ( _write( ft->fileId, buffer, writeSize ) != writeSize ) {
+ Log( "_write() error" );
+ return -1;
+ }
+
+ ft->std.currentFileProgress += writeSize;
+ ft->std.totalProgress += writeSize;
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std );
+ return ( ft->std.currentFileSize == ft->std.currentFileProgress ) ? 0 : writeSize;
+ }
+
+ return 0;
+}
+
+void CJabberProto::FtReceiveFinal( BOOL success, filetransfer* ft )
+{
+ if ( success ) {
+ Log( "File transfer complete successfully" );
+ ft->complete();
+ }
+ else Log( "File transfer complete with error" );
+
+ delete ft;
+}
diff --git a/protocols/JabberG/src/jabber_groupchat.cpp b/protocols/JabberG/src/jabber_groupchat.cpp new file mode 100644 index 0000000000..b442588fc0 --- /dev/null +++ b/protocols/JabberG/src/jabber_groupchat.cpp @@ -0,0 +1,1374 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+#define GC_SERVER_LIST_SIZE 5
+
+int JabberGcGetStatus(JABBER_GC_AFFILIATION a, JABBER_GC_ROLE r);
+int JabberGcGetStatus(JABBER_RESOURCE_STATUS *r);
+
+struct JabberGcRecentInfo
+{
+ TCHAR *room, *server, *nick, *password;
+ CJabberProto* ppro;
+
+ JabberGcRecentInfo( CJabberProto* proto )
+ {
+ ppro = proto;
+ room = server = nick = password = NULL;
+ }
+ JabberGcRecentInfo( CJabberProto* proto, const TCHAR *_room, const TCHAR *_server, const TCHAR *_nick = NULL, const TCHAR *_password = NULL)
+ {
+ ppro = proto;
+ room = server = nick = password = NULL;
+ fillData(_room, _server, _nick, _password);
+ }
+ JabberGcRecentInfo( CJabberProto* proto, const TCHAR *jid)
+ {
+ ppro = proto;
+ room = server = nick = password = NULL;
+ fillData(jid);
+ }
+ JabberGcRecentInfo( CJabberProto* proto, int iRecent)
+ {
+ ppro = proto;
+ room = server = nick = password = NULL;
+ loadRecent(iRecent);
+ }
+
+ ~JabberGcRecentInfo()
+ {
+ cleanup();
+ }
+
+ void cleanup()
+ {
+ mir_free(room);
+ mir_free(server);
+ mir_free(nick);
+ mir_free(password);
+ room = server = nick = password = NULL;
+ }
+
+ BOOL equals(const TCHAR *room, const TCHAR *server, const TCHAR *nick = NULL, const TCHAR *password = NULL)
+ {
+ return
+ null_strequals(this->room, room) &&
+ null_strequals(this->server, server) &&
+ null_strequals(this->nick, nick) &&
+ null_strequals(this->password, password);
+ }
+
+ BOOL equalsnp(const TCHAR *room, const TCHAR *server, const TCHAR *nick = NULL)
+ {
+ return
+ null_strequals(this->room, room) &&
+ null_strequals(this->server, server) &&
+ null_strequals(this->nick, nick);
+ }
+
+ void fillForm(HWND hwndDlg)
+ {
+ SetDlgItemText(hwndDlg, IDC_SERVER, server ? server : _T(""));
+ SetDlgItemText(hwndDlg, IDC_ROOM, room ? room : _T(""));
+ SetDlgItemText(hwndDlg, IDC_NICK, nick ? nick : _T(""));
+ SetDlgItemText(hwndDlg, IDC_PASSWORD, password ? password : _T(""));
+ }
+
+ void fillData(const TCHAR *room, const TCHAR *server, const TCHAR *nick = NULL, const TCHAR *password = NULL)
+ {
+ cleanup();
+ this->room = room ? mir_tstrdup(room) : NULL;
+ this->server = server ? mir_tstrdup(server) : NULL;
+ this->nick = nick ? mir_tstrdup(nick) : NULL;
+ this->password = password ? mir_tstrdup(password) : NULL;
+ }
+
+ void fillData(const TCHAR *jid)
+ {
+ TCHAR *room, *server, *nick=NULL;
+ room = NEWTSTR_ALLOCA(jid);
+ server = _tcschr(room, _T('@'));
+ if (server)
+ {
+ *server++ = 0;
+ nick = _tcschr(server, _T('/'));
+ if (nick) *nick++ = 0;
+ } else
+ {
+ server = room;
+ room = NULL;
+ }
+
+ fillData(room, server, nick);
+ }
+
+ BOOL loadRecent(int iRecent)
+ {
+ DBVARIANT dbv;
+ char setting[MAXMODULELABELLENGTH];
+
+ cleanup();
+
+ mir_snprintf(setting, sizeof(setting), "rcMuc_%d_server", iRecent);
+ if ( !ppro->JGetStringT( NULL, setting, &dbv )) {
+ server = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+
+ mir_snprintf(setting, sizeof(setting), "rcMuc_%d_room", iRecent);
+ if ( !ppro->JGetStringT( NULL, setting, &dbv )) {
+ room = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant( &dbv );
+ }
+
+ mir_snprintf(setting, sizeof(setting), "rcMuc_%d_nick", iRecent);
+ if ( !ppro->JGetStringT( NULL, setting, &dbv )) {
+ nick = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant( &dbv );
+ }
+
+ mir_snprintf(setting, sizeof(setting), "rcMuc_%d_passwordW", iRecent);
+ password = ppro->JGetStringCrypt(NULL, setting);
+
+ return room || server || nick || password;
+ }
+
+ void saveRecent(int iRecent)
+ {
+ char setting[MAXMODULELABELLENGTH];
+
+ mir_snprintf(setting, sizeof(setting), "rcMuc_%d_server", iRecent);
+ if (server)
+ ppro->JSetStringT(NULL, setting, server);
+ else
+ ppro->JDeleteSetting(NULL, setting);
+
+ mir_snprintf(setting, sizeof(setting), "rcMuc_%d_room", iRecent);
+ if (room)
+ ppro->JSetStringT(NULL, setting, room);
+ else
+ ppro->JDeleteSetting(NULL, setting);
+
+ mir_snprintf(setting, sizeof(setting), "rcMuc_%d_nick", iRecent);
+ if (nick)
+ ppro->JSetStringT(NULL, setting, nick);
+ else
+ ppro->JDeleteSetting(NULL, setting);
+
+ mir_snprintf(setting, sizeof(setting), "rcMuc_%d_passwordW", iRecent);
+ if (password)
+ ppro->JSetStringCrypt(NULL, setting, password);
+ else
+ ppro->JDeleteSetting(NULL, setting);
+ }
+
+private:
+ BOOL null_strequals(const TCHAR *str1, const TCHAR *str2)
+ {
+ if (!str1 && !str2) return TRUE;
+ if (!str1 && str2 && !*str2) return TRUE;
+ if (!str2 && str1 && !*str1) return TRUE;
+
+ if (!str1 && str2) return FALSE;
+ if (!str2 && str1) return FALSE;
+
+ return !lstrcmp(str1, str2);
+ }
+};
+
+JABBER_RESOURCE_STATUS* CJabberProto::GcFindResource(JABBER_LIST_ITEM *item, const TCHAR *resource)
+{
+ JABBER_RESOURCE_STATUS *res = NULL;
+
+ EnterCriticalSection( &m_csLists );
+ JABBER_RESOURCE_STATUS *r = item->resource;
+ for ( int i=0; i<item->resourceCount; i++ ) {
+ if ( !_tcscmp( r[i].resourceName, resource )) {
+ res = &r[i];
+ break;
+ }
+ }
+ LeaveCriticalSection( &m_csLists );
+
+ return res;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleJoinGroupchat( WPARAM, LPARAM )
+{
+ if ( jabberChatDllPresent )
+ GroupchatJoinRoomByJid( NULL, NULL );
+ else
+ JabberChatDllError();
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnJoinChat( WPARAM wParam, LPARAM )
+{
+ DBVARIANT nick, jid;
+ HANDLE hContact = ( HANDLE )wParam;
+ if ( JGetStringT( hContact, "ChatRoomID", &jid ))
+ return 0;
+
+ if ( JGetStringT( hContact, "MyNick", &nick ))
+ if ( JGetStringT( NULL, "Nick", &nick )) {
+ JFreeVariant( &jid );
+ return 0;
+ }
+
+ TCHAR *password = JGetStringCrypt( hContact, "LoginPassword" );
+
+ if ( JGetWord( hContact, "Status", 0 ) != ID_STATUS_ONLINE ) {
+ if ( !jabberChatDllPresent )
+ JabberChatDllError();
+ else {
+ TCHAR* p = _tcschr( jid.ptszVal, '@' );
+ if ( p != NULL ) {
+ *p++ = 0;
+ GroupchatJoinRoom( p, jid.ptszVal, nick.ptszVal, password );
+ } } }
+
+ mir_free( password );
+ JFreeVariant( &nick );
+ JFreeVariant( &jid );
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnLeaveChat( WPARAM wParam, LPARAM )
+{
+ DBVARIANT jid;
+ HANDLE hContact = ( HANDLE )wParam;
+ if ( JGetStringT( hContact, "ChatRoomID", &jid ))
+ return 0;
+
+ if ( JGetWord( hContact, "Status", 0 ) != ID_STATUS_OFFLINE ) {
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_CHATROOM, jid.ptszVal );
+ if ( item != NULL )
+ GcQuit( item, 0, NULL );
+ }
+
+ JFreeVariant( &jid );
+ return 0;
+}
+
+void CJabberProto::GroupchatJoinRoom( const TCHAR* server, const TCHAR* room, const TCHAR* nick, const TCHAR* password, bool autojoin )
+{
+ JabberGcRecentInfo info( this );
+
+ int i = 0;
+ bool found = false;
+ for (i = 0 ; i < 5; ++i)
+ {
+ if (!info.loadRecent(i))
+ continue;
+
+ if (info.equals(room, server, nick, password))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ for (int i = 4; i--; )
+ {
+ if (info.loadRecent(i))
+ info.saveRecent(i + 1);
+ }
+
+ info.fillData(room, server, nick, password);
+ info.saveRecent(0);
+ }
+
+ TCHAR text[512];
+ mir_sntprintf( text, SIZEOF(text), _T("%s@%s/%s"), room, server, nick );
+
+ JABBER_LIST_ITEM* item = ListAdd( LIST_CHATROOM, text );
+ item->bAutoJoin = autojoin;
+ replaceStrT( item->nick, nick );
+ replaceStrT( item->password, info.password );
+
+ int status = ( m_iStatus == ID_STATUS_INVISIBLE ) ? ID_STATUS_ONLINE : m_iStatus;
+ XmlNode x( _T("x")); x << XATTR( _T("xmlns"), _T(JABBER_FEAT_MUC));
+ if ( info.password && info.password[0] )
+ x << XCHILD( _T("password"), info.password );
+
+ if (m_options.GcLogChatHistory) {
+ char setting[MAXMODULELABELLENGTH];
+ mir_snprintf(setting, SIZEOF(setting), "muc_%s@%s_lastevent", _T2A(room), _T2A(server));
+ time_t lasteventtime = this->JGetDword( NULL, setting, 0 );
+ if ( lasteventtime > 0 ) {
+ _tzset();
+ lasteventtime += _timezone + 1;
+ struct tm* time = localtime(&lasteventtime);
+ TCHAR lasteventdate[40];
+ mir_sntprintf(lasteventdate, SIZEOF(lasteventdate), _T("%04d-%02d-%02dT%02d:%02d:%02dZ"),
+ time->tm_year+1900, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec);
+ x << XCHILD( _T("history")) << XATTR( _T("since"), lasteventdate);
+ }
+ }
+
+ SendPresenceTo( status, text, x );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Join Dialog
+
+static int sttTextLineHeight = 16;
+
+struct RoomInfo
+{
+ enum Overlay { ROOM_WAIT, ROOM_FAIL, ROOM_BOOKMARK, ROOM_DEFAULT };
+ Overlay overlay;
+ TCHAR *line1, *line2;
+};
+
+static int sttRoomListAppend(HWND hwndList, RoomInfo::Overlay overlay, const TCHAR *line1, const TCHAR *line2, const TCHAR *name)
+{
+ RoomInfo *info = (RoomInfo *)mir_alloc(sizeof(RoomInfo));
+ info->overlay = overlay;
+ info->line1 = line1 ? mir_tstrdup(line1) : 0;
+ info->line2 = line2 ? mir_tstrdup(line2) : 0;
+
+ int id = SendMessage(hwndList, CB_ADDSTRING, 0, (LPARAM)name);
+ SendMessage(hwndList, CB_SETITEMDATA, id, (LPARAM)info);
+ SendMessage(hwndList, CB_SETITEMHEIGHT, id, sttTextLineHeight * 2);
+ return id;
+}
+
+void CJabberProto::OnIqResultDiscovery(HXML iqNode, CJabberIqInfo *pInfo)
+{
+ if (!iqNode || !pInfo)
+ return;
+
+ HWND hwndList = (HWND)pInfo->GetUserData();
+ SendMessage(hwndList, CB_SHOWDROPDOWN, FALSE, 0);
+ SendMessage(hwndList, CB_RESETCONTENT, 0, 0);
+
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT )
+ {
+ HXML query = xmlGetChild( iqNode , "query" );
+ if ( !query )
+ {
+ sttRoomListAppend(hwndList, RoomInfo::ROOM_FAIL,
+ TranslateT("Jabber Error"),
+ TranslateT("Failed to retrieve room list from server."),
+ _T(""));
+ } else
+ {
+ bool found = false;
+ HXML item;
+ for ( int i = 1; item = xmlGetNthChild( query, _T("item"), i ); i++ )
+ {
+ const TCHAR *jid = xmlGetAttrValue( item, _T("jid"));
+ TCHAR *name = NEWTSTR_ALLOCA(jid);
+ if (name)
+ {
+ if (TCHAR *p = _tcschr(name, _T('@')))
+ *p = 0;
+ } else
+ {
+ name = _T("");
+ }
+
+ sttRoomListAppend(hwndList,
+ ListGetItemPtr(LIST_BOOKMARK, jid) ? RoomInfo::ROOM_BOOKMARK : RoomInfo::ROOM_DEFAULT,
+ xmlGetAttrValue( item, _T("name")),
+ jid, name);
+
+ found = true;
+ }
+
+ if (!found)
+ {
+ sttRoomListAppend(hwndList, RoomInfo::ROOM_FAIL,
+ TranslateT("Jabber Error"),
+ TranslateT("No rooms available on server."),
+ _T(""));
+ }
+ }
+ } else
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_ERROR )
+ {
+ HXML errorNode = xmlGetChild( iqNode , "error" );
+ TCHAR* str = JabberErrorMsg( errorNode );
+ sttRoomListAppend(hwndList, RoomInfo::ROOM_FAIL,
+ TranslateT("Jabber Error"),
+ str,
+ _T(""));
+ mir_free( str );
+ } else
+ {
+ sttRoomListAppend(hwndList, RoomInfo::ROOM_FAIL,
+ TranslateT("Jabber Error"),
+ TranslateT("Room list request timed out."),
+ _T(""));
+ }
+
+ SendMessage(hwndList, CB_SHOWDROPDOWN, TRUE, 0);
+}
+
+static void sttJoinDlgShowRecentItems(HWND hwndDlg, int newCount)
+{
+ RECT rcTitle, rcLastItem;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_TXT_RECENT), &rcTitle);
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_RECENT5), &rcLastItem);
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_RECENT), newCount ? SW_SHOW : SW_HIDE);
+
+ int oldCount = 5;
+ for (int idc = IDC_RECENT1; idc <= IDC_RECENT5; ++idc)
+ ShowWindow(GetDlgItem(hwndDlg, idc), (idc - IDC_RECENT1 < newCount) ? SW_SHOW : SW_HIDE);
+
+ int curRecentHeight = rcLastItem.bottom - rcTitle.top - (5 - oldCount) * (rcLastItem.bottom - rcLastItem.top);
+ int newRecentHeight = rcLastItem.bottom - rcTitle.top - (5 - newCount) * (rcLastItem.bottom - rcLastItem.top);
+ if (!newCount) newRecentHeight = 0;
+ int offset = newRecentHeight - curRecentHeight;
+
+ RECT rc;
+ int ctrls[] = { IDC_BOOKMARKS, IDOK, IDCANCEL };
+ for (int i = 0; i < SIZEOF(ctrls); ++i)
+ {
+ GetWindowRect(GetDlgItem(hwndDlg, ctrls[i]), &rc);
+ MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rc, 2);
+ SetWindowPos(GetDlgItem(hwndDlg, ctrls[i]), NULL, rc.left, rc.top + offset, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
+ }
+
+ GetWindowRect(hwndDlg, &rc);
+ SetWindowPos(hwndDlg, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top+offset, SWP_NOMOVE|SWP_NOZORDER);
+}
+
+class CJabberDlgGcJoin: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+public:
+ CJabberDlgGcJoin(CJabberProto *proto, TCHAR *jid);
+
+protected:
+ TCHAR *m_jid;
+
+ void OnInitDialog();
+ void OnClose();
+ void OnDestroy();
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
+};
+
+CJabberDlgGcJoin::CJabberDlgGcJoin(CJabberProto *proto, TCHAR *jid) :
+ CSuper(proto, IDD_GROUPCHAT_JOIN, NULL),
+ m_jid(mir_tstrdup(jid))
+{
+ m_autoClose = 0;
+}
+
+void CJabberDlgGcJoin::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+
+ WindowSetIcon( m_hwnd, m_proto, "group" );
+
+ JabberGcRecentInfo *info = NULL;
+ if ( m_jid )
+ info = new JabberGcRecentInfo( m_proto, m_jid );
+ else
+ {
+ OpenClipboard(m_hwnd);
+ HANDLE hData = GetClipboardData(CF_UNICODETEXT);
+
+ if (hData)
+ {
+ TCHAR *buf = (TCHAR *)GlobalLock(hData);
+ if (buf && _tcschr(buf, _T('@')) && !_tcschr(buf, _T(' ')))
+ info = new JabberGcRecentInfo( m_proto, buf );
+ GlobalUnlock(hData);
+ }
+ CloseClipboard();
+ }
+
+ if (info)
+ {
+ info->fillForm(m_hwnd);
+ delete info;
+ }
+
+ DBVARIANT dbv;
+ if ( !m_proto->JGetStringT( NULL, "Nick", &dbv )) {
+ SetDlgItemText( m_hwnd, IDC_NICK, dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else {
+ TCHAR* nick = JabberNickFromJID( m_proto->m_szJabberJID );
+ SetDlgItemText( m_hwnd, IDC_NICK, nick );
+ mir_free( nick );
+ }
+
+ {
+ TEXTMETRIC tm = {0};
+ HDC hdc = GetDC(m_hwnd);
+ GetTextMetrics(hdc, &tm);
+ ReleaseDC(m_hwnd, hdc);
+ sttTextLineHeight = tm.tmHeight;
+ SendDlgItemMessage(m_hwnd, IDC_ROOM, CB_SETITEMHEIGHT, -1, sttTextLineHeight-1);
+ }
+
+ {
+ LOGFONT lf = {0};
+ HFONT hfnt = (HFONT)SendDlgItemMessage(m_hwnd, IDC_TXT_RECENT, WM_GETFONT, 0, 0);
+ GetObject(hfnt, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ SendDlgItemMessage(m_hwnd, IDC_TXT_RECENT, WM_SETFONT, (WPARAM)CreateFontIndirect(&lf), TRUE);
+ }
+
+ SendDlgItemMessage(m_hwnd, IDC_BOOKMARKS, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_proto->LoadIconEx("bookmarks"));
+ SendDlgItemMessage(m_hwnd, IDC_BOOKMARKS, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(m_hwnd, IDC_BOOKMARKS, BUTTONADDTOOLTIP, (WPARAM)"Bookmarks", 0);
+ SendDlgItemMessage(m_hwnd, IDC_BOOKMARKS, BUTTONSETASPUSHBTN, TRUE, 0);
+
+ m_proto->ComboLoadRecentStrings(m_hwnd, IDC_SERVER, "joinWnd_rcSvr");
+
+ int i = 0;
+ for ( ; i < 5; ++i)
+ {
+ TCHAR jid[JABBER_MAX_JID_LEN];
+ JabberGcRecentInfo info( m_proto );
+ if (info.loadRecent(i))
+ {
+ mir_sntprintf(jid, SIZEOF(jid), _T("%s@%s (%s)"),
+ info.room, info.server,
+ info.nick ? info.nick : TranslateT("<no nick>"));
+ SetDlgItemText(m_hwnd, IDC_RECENT1+i, jid);
+ } else
+ {
+ break;
+ }
+ }
+ sttJoinDlgShowRecentItems(m_hwnd, i);
+}
+
+void CJabberDlgGcJoin::OnClose()
+{
+ CSuper::OnClose();
+}
+
+void CJabberDlgGcJoin::OnDestroy()
+{
+ g_ReleaseIcon(( HICON )SendDlgItemMessage( m_hwnd, IDC_BOOKMARKS, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ m_proto->m_pDlgJabberJoinGroupchat = NULL;
+ DeleteObject((HFONT)SendDlgItemMessage(m_hwnd, IDC_TXT_RECENT, WM_GETFONT, 0, 0));
+
+ CSuper::OnDestroy();
+
+ mir_free( m_jid ); m_jid = NULL;
+}
+
+INT_PTR CJabberDlgGcJoin::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ TCHAR text[128];
+
+ switch ( msg ) {
+ case WM_DELETEITEM:
+ {
+ LPDELETEITEMSTRUCT lpdis = (LPDELETEITEMSTRUCT)lParam;
+ if (lpdis->CtlID != IDC_ROOM)
+ break;
+
+ RoomInfo *info = (RoomInfo *)lpdis->itemData;
+ if (info->line1) mir_free(info->line1);
+ if (info->line2) mir_free(info->line2);
+ mir_free(info);
+
+ break;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+ if (lpmis->CtlID != IDC_ROOM)
+ break;
+
+ lpmis->itemHeight = 2*sttTextLineHeight;
+ if (lpmis->itemID == -1)
+ lpmis->itemHeight = sttTextLineHeight-1;
+
+ break;
+ }
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ if (lpdis->CtlID != IDC_ROOM)
+ break;
+
+ if (lpdis->itemID < 0)
+ break;
+
+ RoomInfo *info = (RoomInfo *)SendDlgItemMessage(m_hwnd, IDC_ROOM, CB_GETITEMDATA, lpdis->itemID, 0);
+ COLORREF clLine1, clLine2, clBack;
+
+ if (lpdis->itemState & ODS_SELECTED)
+ {
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ clBack = GetSysColor(COLOR_HIGHLIGHT);
+ clLine1 = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ } else
+ {
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_WINDOW));
+ clBack = GetSysColor(COLOR_WINDOW);
+ clLine1 = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ clLine2 = RGB(
+ GetRValue(clLine1) * 0.66 + GetRValue(clBack) * 0.34,
+ GetGValue(clLine1) * 0.66 + GetGValue(clBack) * 0.34,
+ GetBValue(clLine1) * 0.66 + GetBValue(clBack) * 0.34
+ );
+
+ SetBkMode(lpdis->hDC, TRANSPARENT);
+
+ RECT rc;
+
+ rc = lpdis->rcItem;
+ rc.bottom -= (rc.bottom - rc.top) / 2;
+ rc.left += 20;
+ SetTextColor(lpdis->hDC, clLine1);
+ DrawText(lpdis->hDC, info->line1, lstrlen(info->line1), &rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS);
+
+ rc = lpdis->rcItem;
+ rc.top += (rc.bottom - rc.top) / 2;
+ rc.left += 20;
+ SetTextColor(lpdis->hDC, clLine2);
+ DrawText(lpdis->hDC, info->line2, lstrlen(info->line2), &rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS);
+
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.left+1, lpdis->rcItem.top+1, m_proto->LoadIconEx("group"), 16, 16, 0, NULL, DI_NORMAL);
+ switch (info->overlay) {
+ case RoomInfo::ROOM_WAIT:
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.left+1, lpdis->rcItem.top+1, m_proto->LoadIconEx("disco_progress"), 16, 16, 0, NULL, DI_NORMAL);
+ break;
+ case RoomInfo::ROOM_FAIL:
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.left+1, lpdis->rcItem.top+1, m_proto->LoadIconEx("disco_fail"), 16, 16, 0, NULL, DI_NORMAL);
+ break;
+ case RoomInfo::ROOM_BOOKMARK:
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.left+1, lpdis->rcItem.top+1, m_proto->LoadIconEx("disco_ok"), 16, 16, 0, NULL, DI_NORMAL);
+ break;
+ }
+ }
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDC_SERVER:
+ switch (HIWORD(wParam)) {
+ case CBN_EDITCHANGE:
+ case CBN_SELCHANGE:
+ {
+ int iqid = GetWindowLongPtr(GetDlgItem(m_hwnd, IDC_ROOM), GWLP_USERDATA);
+ if (iqid)
+ {
+ m_proto->m_iqManager.ExpireIq(iqid);
+ SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_ROOM), GWLP_USERDATA, 0);
+ }
+ SendDlgItemMessage(m_hwnd, IDC_ROOM, CB_RESETCONTENT, 0, 0);
+ }
+ break;
+ }
+ break;
+
+ case IDC_ROOM:
+ switch (HIWORD(wParam)) {
+ case CBN_DROPDOWN:
+ if (!SendDlgItemMessage(m_hwnd, IDC_ROOM, CB_GETCOUNT, 0, 0))
+ {
+ int iqid = GetWindowLongPtr(GetDlgItem(m_hwnd, IDC_ROOM), GWLP_USERDATA);
+ if (iqid)
+ {
+ m_proto->m_iqManager.ExpireIq(iqid);
+ SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_ROOM), GWLP_USERDATA, 0);
+ }
+
+ SendDlgItemMessage(m_hwnd, IDC_ROOM, CB_RESETCONTENT, 0, 0);
+
+ int len = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_SERVER)) + 1;
+ TCHAR *server = (TCHAR *)_alloca(len * sizeof(TCHAR));
+ GetWindowText(GetDlgItem(m_hwnd, IDC_SERVER), server, len);
+
+ if (*server)
+ {
+ sttRoomListAppend(GetDlgItem(m_hwnd, IDC_ROOM), RoomInfo::ROOM_WAIT, TranslateT("Loading..."), TranslateT("Please wait for room list to download."), _T(""));
+
+ CJabberIqInfo *pInfo = m_proto->m_iqManager.AddHandler( &CJabberProto::OnIqResultDiscovery, JABBER_IQ_TYPE_GET, server, 0, -1, (void *)GetDlgItem(m_hwnd, IDC_ROOM));
+ pInfo->SetTimeout(30000);
+ XmlNodeIq iq(pInfo);
+ iq << XQUERY( _T(JABBER_FEAT_DISCO_ITEMS));
+ m_proto->m_ThreadInfo->send(iq);
+
+ SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_ROOM), GWLP_USERDATA, pInfo->GetIqId());
+ } else
+ {
+ sttRoomListAppend(GetDlgItem(m_hwnd, IDC_ROOM), RoomInfo::ROOM_FAIL,
+ TranslateT("Jabber Error"),
+ TranslateT("Please specify groupchat directory first."),
+ _T(""));
+ }
+ }
+ break;
+ }
+ break;
+
+ case IDC_BOOKMARKS:
+ {
+ HMENU hMenu = CreatePopupMenu();
+
+ LISTFOREACH(i, m_proto, LIST_BOOKMARK)
+ {
+ JABBER_LIST_ITEM *item = 0;
+ if (item = m_proto->ListGetItemPtrFromIndex(i))
+ if (!lstrcmp(item->type, _T("conference")))
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)item, item->name);
+ }
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)-1, TranslateT("Bookmarks..."));
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel"));
+
+ RECT rc; GetWindowRect(GetDlgItem(m_hwnd, IDC_BOOKMARKS), &rc);
+ CheckDlgButton(m_hwnd, IDC_BOOKMARKS, TRUE);
+ int res = TrackPopupMenu(hMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, NULL);
+ CheckDlgButton(m_hwnd, IDC_BOOKMARKS, FALSE);
+ DestroyMenu(hMenu);
+
+ if ( res == -1 )
+ m_proto->OnMenuHandleBookmarks( 0, 0 );
+ else if (res) {
+ JABBER_LIST_ITEM *item = (JABBER_LIST_ITEM *)res;
+ TCHAR *room = NEWTSTR_ALLOCA(item->jid);
+ if (room) {
+ TCHAR *server = _tcschr(room, _T('@'));
+ if (server) {
+ *server++ = 0;
+
+ SendMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_SERVER, CBN_EDITCHANGE), (LPARAM)GetDlgItem(m_hwnd, IDC_SERVER));
+
+ SetDlgItemText(m_hwnd, IDC_SERVER, server);
+ SetDlgItemText(m_hwnd, IDC_ROOM, room);
+ SetDlgItemText(m_hwnd, IDC_NICK, item->nick);
+ SetDlgItemText(m_hwnd, IDC_PASSWORD, item->password);
+ } } } }
+ break;
+
+ case IDC_RECENT1:
+ case IDC_RECENT2:
+ case IDC_RECENT3:
+ case IDC_RECENT4:
+ case IDC_RECENT5:
+ {
+ JabberGcRecentInfo info( m_proto, LOWORD( wParam ) - IDC_RECENT1);
+ info.fillForm(m_hwnd);
+ if (GetAsyncKeyState(VK_CONTROL))
+ break;
+ }
+ // fall through
+
+ case IDOK:
+ {
+ GetDlgItemText( m_hwnd, IDC_SERVER, text, SIZEOF( text ));
+ TCHAR* server = NEWTSTR_ALLOCA( text ), *room;
+
+ m_proto->ComboAddRecentString(m_hwnd, IDC_SERVER, "joinWnd_rcSvr", server);
+
+ GetDlgItemText( m_hwnd, IDC_ROOM, text, SIZEOF( text ));
+ room = NEWTSTR_ALLOCA( text );
+
+ GetDlgItemText( m_hwnd, IDC_NICK, text, SIZEOF( text ));
+ TCHAR* nick = NEWTSTR_ALLOCA( text );
+
+ GetDlgItemText( m_hwnd, IDC_PASSWORD, text, SIZEOF( text ));
+ TCHAR* password = NEWTSTR_ALLOCA( text );
+ m_proto->GroupchatJoinRoom( server, room, nick, password );
+ }
+ // fall through
+ case IDCANCEL:
+ Close();
+ break;
+ }
+ break;
+ case WM_JABBER_CHECK_ONLINE:
+ if ( !m_proto->m_bJabberOnline )
+ EndDialog( m_hwnd, 0 );
+ break;
+ }
+
+ return CSuper::DlgProc(msg, wParam, lParam);
+}
+
+void CJabberProto::GroupchatJoinRoomByJid( HWND, TCHAR *jid )
+{
+ if (m_pDlgJabberJoinGroupchat)
+ SetForegroundWindow(m_pDlgJabberJoinGroupchat->GetHwnd());
+ else {
+ m_pDlgJabberJoinGroupchat = new CJabberDlgGcJoin(this, jid);
+ m_pDlgJabberJoinGroupchat->Show();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGroupchatProcessPresence - handles the group chat presence packet
+
+struct JabberGroupchatChangeNicknameParam
+{
+ JabberGroupchatChangeNicknameParam( CJabberProto* ppro_, const TCHAR* jid_ ) :
+ ppro( ppro_ ),
+ jid( mir_tstrdup( jid_ ))
+ {}
+
+ ~JabberGroupchatChangeNicknameParam()
+ { mir_free( jid );
+ }
+
+ CJabberProto* ppro;
+ TCHAR* jid;
+};
+
+static VOID CALLBACK JabberGroupchatChangeNickname( void* arg )
+{
+ JabberGroupchatChangeNicknameParam* param = ( JabberGroupchatChangeNicknameParam* )arg;
+ if ( param == NULL )
+ return;
+
+ JABBER_LIST_ITEM* item = param->ppro->ListGetItemPtr( LIST_CHATROOM, param->jid );
+ if ( item != NULL ) {
+ TCHAR szBuffer[ 1024 ];
+ TCHAR szCaption[ 1024 ];
+ szBuffer[ 0 ] = _T('\0');
+
+ TCHAR* roomName = item->name ? item->name : item->jid;
+ mir_sntprintf( szCaption, SIZEOF(szCaption), _T("%s <%s>"), TranslateT( "Change nickname in" ), roomName );
+ if ( item->nick )
+ mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s"), item->nick );
+
+ if ( param->ppro->EnterString( szBuffer, SIZEOF(szBuffer), szCaption, JES_COMBO, "gcNick_" )) {
+ TCHAR text[ 1024 ];
+ replaceStrT( item->nick, szBuffer );
+ mir_sntprintf( text, SIZEOF( text ), _T("%s/%s"), item->jid, szBuffer );
+ param->ppro->SendPresenceTo( param->ppro->m_iStatus, text, NULL );
+ } }
+
+ delete param;
+}
+
+static int sttGetStatusCode( HXML node )
+{
+ HXML statusNode = xmlGetChild( node , "status" );
+ if ( statusNode == NULL )
+ return -1;
+
+ const TCHAR* statusCode = xmlGetAttrValue( statusNode, _T("code"));
+ if ( statusCode == NULL )
+ return -1;
+
+ return _ttol( statusCode );
+}
+
+void CJabberProto::RenameParticipantNick( JABBER_LIST_ITEM* item, const TCHAR* oldNick, HXML itemNode )
+{
+ const TCHAR* newNick = xmlGetAttrValue( itemNode, _T("nick"));
+ const TCHAR* jid = xmlGetAttrValue( itemNode, _T("jid"));
+ if ( newNick == NULL )
+ return;
+
+ for ( int i=0; i < item->resourceCount; i++ ) {
+ JABBER_RESOURCE_STATUS& RS = item->resource[i];
+ if ( !lstrcmp( RS.resourceName, oldNick )) {
+ replaceStrT( RS.resourceName, newNick );
+
+ if ( !lstrcmp( item->nick, oldNick )) {
+ replaceStrT( item->nick, newNick );
+
+ HANDLE hContact = HContactFromJID( item->jid );
+ if ( hContact != NULL )
+ JSetStringT( hContact, "MyNick", newNick );
+ }
+
+ GCDEST gcd = { m_szModuleName, NULL, GC_EVENT_CHUID };
+ gcd.ptszID = item->jid;
+
+ GCEVENT gce = {0};
+ gce.cbSize = sizeof(GCEVENT);
+ gce.pDest = &gcd;
+ gce.ptszNick = oldNick;
+ gce.ptszText = newNick;
+ if (jid != NULL)
+ gce.ptszUserInfo = jid;
+ gce.time = time(0);
+ gce.dwFlags = GC_TCHAR;
+ CallServiceSync( MS_GC_EVENT, NULL, ( LPARAM )&gce );
+
+ gcd.iType = GC_EVENT_NICK;
+ gce.ptszNick = oldNick;
+ gce.ptszUID = newNick;
+ gce.ptszText = newNick;
+ CallServiceSync( MS_GC_EVENT, NULL, ( LPARAM )&gce );
+ break;
+} } }
+
+void CJabberProto::GroupchatProcessPresence( HXML node )
+{
+ HXML showNode, statusNode, itemNode, n, priorityNode;
+ const TCHAR* from;
+ int status, newRes = 0;
+ bool bStatusChanged = false;
+ BOOL roomCreated;
+
+ if ( !node || !xmlGetName( node ) || lstrcmp( xmlGetName( node ), _T("presence"))) return;
+ if (( from = xmlGetAttrValue( node, _T("from"))) == NULL ) return;
+
+ const TCHAR* resource = _tcschr( from, '/' );
+ if ( resource == NULL || *++resource == '\0' )
+ return;
+
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_CHATROOM, from );
+ if ( item == NULL )
+ return;
+
+ JABBER_RESOURCE_STATUS* r = GcFindResource(item, resource);
+
+ HXML nNode = xmlGetChildByTag( node, "nick", "xmlns", _T(JABBER_FEAT_NICK));
+ if ( nNode == NULL )
+ nNode = xmlGetChildByTag( node, "nick:nick", "xmlns:nick", _T(JABBER_FEAT_NICK));
+
+ const TCHAR* cnick = nNode ? xmlGetText( nNode ) : NULL;
+ const TCHAR* nick = cnick ? cnick : (r && r->nick ? r->nick : resource);
+
+ // process custom nick change
+ if ( cnick && r && r->nick && _tcscmp( cnick, r->nick ))
+ replaceStrT( r->nick, cnick );
+
+ HXML xNode = xmlGetChildByTag( node, "x", "xmlns", _T(JABBER_FEAT_MUC_USER));
+ HXML xUserNode = xmlGetChildByTag( node, "user:x", "xmlns:user", _T(JABBER_FEAT_MUC_USER));
+
+ itemNode = xmlGetChild( xNode , "item" );
+ if ( itemNode == NULL )
+ itemNode = xmlGetChild( xUserNode , "user:item" );
+
+ const TCHAR* type = xmlGetAttrValue( node, _T("type"));
+
+ // entering room or a usual room presence
+ if ( type == NULL || !_tcscmp( type, _T("available"))) {
+ TCHAR* room = JabberNickFromJID( from );
+ if ( room == NULL )
+ return;
+
+ GcLogCreate( item );
+ item->iChatState = 0;
+
+ // Update status of room participant
+ status = ID_STATUS_ONLINE;
+ if (( showNode = xmlGetChild( node , "show" )) != NULL ) {
+ if ( xmlGetText( showNode ) != NULL ) {
+ if ( !_tcscmp( xmlGetText( showNode ) , _T("away"))) status = ID_STATUS_AWAY;
+ else if ( !_tcscmp( xmlGetText( showNode ) , _T("xa"))) status = ID_STATUS_NA;
+ else if ( !_tcscmp( xmlGetText( showNode ) , _T("dnd"))) status = ID_STATUS_DND;
+ else if ( !_tcscmp( xmlGetText( showNode ) , _T("chat"))) status = ID_STATUS_FREECHAT;
+ } }
+
+ statusNode = xmlGetChild( node , "status" );
+ if ( statusNode == NULL )
+ statusNode = xmlGetChild( node , "user:status" );
+
+ const TCHAR* str = statusNode ? xmlGetText( statusNode ) : NULL;
+
+ char priority = 0;
+ if (( priorityNode = xmlGetChild( node , "priority" )) != NULL && xmlGetText( priorityNode ) != NULL )
+ priority = (char)_ttoi( xmlGetText( priorityNode ));
+
+ if (JABBER_RESOURCE_STATUS *oldRes = ListFindResource(LIST_CHATROOM, from))
+ if ((oldRes->status != status) || lstrcmp_null(oldRes->statusMessage, str))
+ bStatusChanged = true;
+
+ newRes = ( ListAddResource( LIST_CHATROOM, from, status, str, priority, cnick ) == 0 ) ? 0 : GC_EVENT_JOIN;
+
+ roomCreated = FALSE;
+
+ bool bAffiliationChanged = false;
+ bool bRoleChanged = false;
+
+ // Check additional MUC info for this user
+ if ( itemNode != NULL ) {
+ if ( r == NULL )
+ r = GcFindResource(item, resource);
+ if ( r != NULL ) {
+ JABBER_GC_AFFILIATION affiliation = r->affiliation;
+ JABBER_GC_ROLE role = r->role;
+
+ if (( str = xmlGetAttrValue( itemNode, _T("affiliation"))) != NULL ) {
+ if ( !_tcscmp( str, _T("owner"))) affiliation = AFFILIATION_OWNER;
+ else if ( !_tcscmp( str, _T("admin"))) affiliation = AFFILIATION_ADMIN;
+ else if ( !_tcscmp( str, _T("member"))) affiliation = AFFILIATION_MEMBER;
+ else if ( !_tcscmp( str, _T("none"))) affiliation = AFFILIATION_NONE;
+ else if ( !_tcscmp( str, _T("outcast"))) affiliation = AFFILIATION_OUTCAST;
+ }
+ if (( str = xmlGetAttrValue( itemNode, _T("role"))) != NULL ) {
+ if ( !_tcscmp( str, _T("moderator"))) role = ROLE_MODERATOR;
+ else if ( !_tcscmp( str, _T("participant"))) role = ROLE_PARTICIPANT;
+ else if ( !_tcscmp( str, _T("visitor"))) role = ROLE_VISITOR;
+ else role = ROLE_NONE;
+ }
+
+ if ( (role != ROLE_NONE) && (JabberGcGetStatus(r) != JabberGcGetStatus(affiliation, role))) {
+ GcLogUpdateMemberStatus( item, resource, nick, NULL, GC_EVENT_REMOVESTATUS, NULL );
+ if (!newRes) newRes = GC_EVENT_ADDSTATUS;
+ }
+
+ if (affiliation != r->affiliation) {
+ r->affiliation = affiliation;
+ bAffiliationChanged = true;
+ }
+
+ if (role != r->role) {
+ r->role = role;
+ if (r->role != ROLE_NONE)
+ bRoleChanged = true;
+ }
+
+ if ( str = xmlGetAttrValue( itemNode, _T("jid")))
+ replaceStrT( r->szRealJid, str );
+ }
+ }
+
+ if ( sttGetStatusCode( xNode ) == 201 )
+ roomCreated = TRUE;
+
+ // show status change if needed
+ if (bStatusChanged)
+ if (JABBER_RESOURCE_STATUS *res = ListFindResource(LIST_CHATROOM, from))
+ GcLogShowInformation(item, res, INFO_STATUS);
+
+ // Update groupchat log window
+ GcLogUpdateMemberStatus( item, resource, nick, str, newRes, NULL );
+ if (r && bAffiliationChanged) GcLogShowInformation(item, r, INFO_AFFILIATION);
+ if (r && bRoleChanged) GcLogShowInformation(item, r, INFO_ROLE);
+
+ // update clist status
+ HANDLE hContact = HContactFromJID( from );
+ if ( hContact != NULL )
+ JSetWord( hContact, "Status", status );
+
+ // Update room status
+ //if ( item->status != ID_STATUS_ONLINE ) {
+ // item->status = ID_STATUS_ONLINE;
+ // JSetWord( hContact, "Status", ( WORD )ID_STATUS_ONLINE );
+ // JabberLog( "Room %s online", from );
+ //}
+
+ // Check <created/>
+ if ( roomCreated ||
+ (( n = xmlGetChild( node , "created" ))!=NULL &&
+ ( str = xmlGetAttrValue( n, _T("xmlns")))!=NULL &&
+ !_tcscmp( str, _T("http://jabber.org/protocol/muc#owner")))) {
+ // A new room just created by me
+ // Request room config
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetMuc );
+ m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId, item->jid ) << XQUERY( xmlnsOwner ));
+ }
+
+ mir_free( room );
+ }
+
+ // leaving room
+ else if ( !_tcscmp( type, _T("unavailable"))) {
+ const TCHAR* str = 0;
+ if ( xNode != NULL && item->nick != NULL ) {
+ HXML reasonNode = xmlGetChild( itemNode , "reason" );
+ str = xmlGetAttrValue( itemNode, _T( "jid" ));
+
+ int iStatus = sttGetStatusCode( xNode );
+ if (iStatus == 301 && r != NULL)
+ GcLogShowInformation(item, r, INFO_BAN);
+
+ if ( !lstrcmp( resource, item->nick )) {
+ switch( iStatus ) {
+ case 301:
+ case 307:
+ GcQuit( item, iStatus, reasonNode );
+ return;
+
+ case 303:
+ RenameParticipantNick( item, resource, itemNode );
+ return;
+ } }
+ else {
+ switch( iStatus ) {
+ case 303:
+ RenameParticipantNick( item, resource, itemNode );
+ return;
+
+ case 301:
+ case 307:
+ case 322:
+ ListRemoveResource( LIST_CHATROOM, from );
+ GcLogUpdateMemberStatus( item, resource, nick, str, GC_EVENT_KICK, reasonNode, iStatus );
+ return;
+ } } }
+
+ statusNode = xmlGetChild( node , "status" );
+ GcLogUpdateMemberStatus( item, resource, nick, str, GC_EVENT_PART, statusNode );
+ ListRemoveResource( LIST_CHATROOM, from );
+
+ HANDLE hContact = HContactFromJID( from );
+ if ( hContact != NULL )
+ JSetWord( hContact, "Status", ID_STATUS_OFFLINE );
+ }
+
+ // processing room errors
+ else if ( !_tcscmp( type, _T("error"))) {
+ int errorCode = 0;
+ HXML errorNode = xmlGetChild( node , "error" );
+ TCHAR* str = JabberErrorMsg( errorNode, &errorCode );
+
+ if ( errorCode == JABBER_ERROR_CONFLICT ) {
+ TCHAR newNick[256] = { 0 };
+ if (++item->iChatState == 1 &&
+ JGetStringT(NULL, "GcAltNick", newNick, SIZEOF(newNick)) != NULL &&
+ newNick[0] != _T('\0'))
+ {
+ replaceStrT(item->nick, newNick);
+ TCHAR text[1024] = { 0 };
+ mir_sntprintf(text, SIZEOF(text), _T("%s/%s"), item->jid, newNick);
+ SendPresenceTo(m_iStatus, text, NULL);
+ }
+ else {
+ CallFunctionAsync( JabberGroupchatChangeNickname, new JabberGroupchatChangeNicknameParam( this, from ));
+ item->iChatState = 0;
+ }
+ mir_free( str );
+ return;
+ }
+
+ MsgPopup( NULL, str, TranslateT( "Jabber Error" ));
+
+ if ( item != NULL)
+ if ( !item->bChatActive ) ListRemove( LIST_CHATROOM, from );
+ mir_free( str );
+} }
+
+void CJabberProto::GroupchatProcessMessage( HXML node )
+{
+ HXML n, xNode, m;
+ const TCHAR* from, *type, *p, *nick, *resource;
+ JABBER_LIST_ITEM *item;
+
+ if ( !xmlGetName( node ) || lstrcmp( xmlGetName( node ), _T("message"))) return;
+ if (( from = xmlGetAttrValue( node, _T("from"))) == NULL ) return;
+ if (( item = ListGetItemPtr( LIST_CHATROOM, from )) == NULL ) return;
+
+ if (( type = xmlGetAttrValue( node, _T("type"))) == NULL ) return;
+ if ( !lstrcmp( type, _T("error")))
+ return;
+
+ GCDEST gcd = { m_szModuleName, NULL, 0 };
+ gcd.ptszID = item->jid;
+
+ const TCHAR* msgText = NULL;
+
+ resource = _tcschr( from, '/' );
+ if ( resource != NULL && *++resource == '\0' )
+ resource = NULL;
+
+ if (( n = xmlGetChild( node , "subject" )) != NULL ) {
+ msgText = xmlGetText( n );
+
+ if ( msgText == NULL || msgText[0] == '\0' )
+ return;
+
+ gcd.iType = GC_EVENT_TOPIC;
+
+ if ( resource == NULL && ( m = xmlGetChild( node, "body" )) != NULL ) {
+ const TCHAR* tmpnick = xmlGetText( m );
+ if ( tmpnick == NULL || *tmpnick == 0 )
+ return;
+
+ const TCHAR* tmptr = _tcsstr( tmpnick, _T("has set the subject to:")); //ejabberd
+ if ( tmptr == NULL )
+ tmptr = _tcsstr( tmpnick, TranslateT("has set the subject to:")); //ejabberd
+ if ( tmptr != NULL && *tmptr != 0 ) {
+ *(TCHAR*)(--tmptr) = 0;
+ resource = tmpnick;
+ } }
+ replaceStrT( item->itemResource.statusMessage, msgText );
+ }
+ else {
+ if (( n = xmlGetChildByTag( node , "body", "xml:lang", m_tszSelectedLang )) == NULL )
+ if (( n = xmlGetChild( node , "body" )) == NULL )
+ return;
+
+ msgText = xmlGetText( n );
+
+ if ( msgText == NULL )
+ return;
+
+ if ( resource == NULL)
+ gcd.iType = GC_EVENT_INFORMATION;
+ else if ( _tcsncmp( msgText, _T("/me "), 4 ) == 0 && _tcslen( msgText ) > 4 ) {
+ msgText += 4;
+ gcd.iType = GC_EVENT_ACTION;
+ }
+ else gcd.iType = GC_EVENT_MESSAGE;
+ }
+
+ GcLogCreate( item );
+
+ time_t msgTime = 0;
+ for ( int i = 1; ( xNode = xmlGetNthChild( node, _T("x"), i )) != NULL; i++ )
+ if (( p = xmlGetAttrValue( xNode, _T("xmlns"))) != NULL )
+ if ( !_tcscmp( p, _T("jabber:x:delay")) && msgTime==0 )
+ if (( p = xmlGetAttrValue( xNode, _T("stamp"))) != NULL ) {
+ msgTime = JabberIsoToUnixTime( p );
+ if (m_options.GcLogChatHistory && msgTime > 0 ) {
+ char setting[MAXMODULELABELLENGTH];
+ mir_snprintf(setting, sizeof(setting), "muc_%s_lastevent", _T2A(gcd.ptszID));
+ this->JSetDword(NULL, setting, msgTime);
+ } }
+
+ time_t now = time( NULL );
+ if ( msgTime == 0 || msgTime > now )
+ msgTime = now;
+
+ if ( resource != NULL ) {
+ JABBER_RESOURCE_STATUS* r = GcFindResource(item, resource);
+ nick = r && r->nick ? r->nick : resource;
+ }
+ else
+ nick = NULL;
+
+ GCEVENT gce = {0};
+ gce.cbSize = sizeof(GCEVENT);
+ gce.pDest = &gcd;
+ gce.ptszUID = resource;
+ gce.ptszNick = nick;
+ gce.time = msgTime;
+ gce.ptszText = EscapeChatTags( (TCHAR*)msgText );
+ gce.bIsMe = nick == NULL ? FALSE : (lstrcmp( resource, item->nick ) == 0);
+ gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG;
+ CallServiceSync( MS_GC_EVENT, NULL, (LPARAM)&gce );
+
+ item->bChatActive = 2;
+
+ if ( gcd.iType == GC_EVENT_TOPIC ) {
+ gce.dwFlags &= ~GCEF_ADDTOLOG;
+ gcd.iType = GC_EVENT_SETSBTEXT;
+ CallServiceSync( MS_GC_EVENT, NULL, (LPARAM)&gce );
+ }
+
+ mir_free( (void*)gce.pszText ); // Since we processed msgText and created a new string
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Accepting groupchat invitations
+
+class CGroupchatInviteAcceptDlg : public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+ CCtrlButton m_accept;
+ JABBER_GROUPCHAT_INVITE_INFO* m_info;
+
+public:
+ CGroupchatInviteAcceptDlg( CJabberProto* ppro, JABBER_GROUPCHAT_INVITE_INFO* pInfo ) :
+ CSuper( ppro, IDD_GROUPCHAT_INVITE_ACCEPT, NULL ),
+ m_info( pInfo ),
+ m_accept( this, IDC_ACCEPT )
+ {
+ m_accept.OnClick = Callback( this, &CGroupchatInviteAcceptDlg::OnCommand_Accept );
+ }
+
+ void OnInitDialog()
+ {
+ CSuper::OnInitDialog();
+
+ TCHAR buf[256];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s\n%s"), m_info->roomJid, TranslateT("Incoming groupchat invitation."));
+ SetDlgItemText( m_hwnd, IDC_HEADERBAR, buf );
+
+ SetDlgItemText( m_hwnd, IDC_FROM, m_info->from );
+
+ if ( m_info->reason != NULL )
+ SetDlgItemText( m_hwnd, IDC_REASON, m_info->reason );
+
+ TCHAR* myNick = JabberNickFromJID( m_proto->m_szJabberJID );
+ SetDlgItemText( m_hwnd, IDC_NICK, myNick );
+ mir_free( myNick );
+
+ WindowSetIcon( m_hwnd, m_proto, "group" );
+
+ SetFocus(GetDlgItem(m_hwnd, IDC_NICK));
+ }
+
+ void OnCommand_Accept( CCtrlButton* )
+ {
+ TCHAR text[128];
+ GetDlgItemText( m_hwnd, IDC_NICK, text, SIZEOF( text ));
+ m_proto->AcceptGroupchatInvite( m_info->roomJid, text, m_info->password );
+ EndDialog( m_hwnd, 0 );
+ }
+};
+
+void __cdecl CJabberProto::GroupchatInviteAcceptThread( JABBER_GROUPCHAT_INVITE_INFO *inviteInfo )
+{
+ CGroupchatInviteAcceptDlg( this, inviteInfo ).DoModal();
+
+ mir_free( inviteInfo->roomJid );
+ mir_free( inviteInfo->from );
+ mir_free( inviteInfo->reason );
+ mir_free( inviteInfo->password );
+ mir_free( inviteInfo );
+}
+
+void CJabberProto::GroupchatProcessInvite( const TCHAR* roomJid, const TCHAR* from, const TCHAR* reason, const TCHAR* password )
+{
+ if ( roomJid == NULL )
+ return;
+
+ if (ListGetItemPtr( LIST_CHATROOM, roomJid ))
+ return;
+
+ if ( m_options.AutoAcceptMUC == FALSE ) {
+ JABBER_GROUPCHAT_INVITE_INFO* inviteInfo = ( JABBER_GROUPCHAT_INVITE_INFO * ) mir_alloc( sizeof( JABBER_GROUPCHAT_INVITE_INFO ));
+ inviteInfo->roomJid = mir_tstrdup( roomJid );
+ inviteInfo->from = mir_tstrdup( from );
+ inviteInfo->reason = mir_tstrdup( reason );
+ inviteInfo->password = mir_tstrdup( password );
+ JForkThread(( JThreadFunc )&CJabberProto::GroupchatInviteAcceptThread, inviteInfo );
+ }
+ else {
+ TCHAR* myNick = JabberNickFromJID( m_szJabberJID );
+ AcceptGroupchatInvite( roomJid, myNick, password );
+ mir_free( myNick );
+} }
+
+void CJabberProto::AcceptGroupchatInvite( const TCHAR* roomJid, const TCHAR* reason, const TCHAR* password )
+{
+ TCHAR room[256], *server, *p;
+ _tcsncpy( room, roomJid, SIZEOF( room ));
+ p = _tcstok( room, _T( "@" ));
+ server = _tcstok( NULL, _T( "@" ));
+ GroupchatJoinRoom( server, p, reason, password );
+}
diff --git a/protocols/JabberG/src/jabber_ibb.cpp b/protocols/JabberG/src/jabber_ibb.cpp new file mode 100644 index 0000000000..51719a696b --- /dev/null +++ b/protocols/JabberG/src/jabber_ibb.cpp @@ -0,0 +1,193 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_ibb.h"
+#include "jabber_caps.h"
+
+#define JABBER_IBB_BLOCK_SIZE 2048
+
+void JabberIbbFreeJibb( JABBER_IBB_TRANSFER *jibb )
+{
+ if ( jibb ) {
+ filetransfer* pft = jibb->ft;
+ if ( pft )
+ pft->jibb = NULL;
+
+ mir_free( jibb->srcJID );
+ mir_free( jibb->dstJID );
+ mir_free( jibb->sid );
+
+ mir_free( jibb );
+} }
+
+BOOL CJabberProto::OnFtHandleIbbIq( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if ( !_tcscmp( pInfo->GetChildNodeName(), _T("open")))
+ FtHandleIbbRequest( iqNode, TRUE );
+ else if ( !_tcscmp( pInfo->GetChildNodeName(), _T("close")))
+ FtHandleIbbRequest( iqNode, FALSE );
+ else if ( !_tcscmp( pInfo->GetChildNodeName(), _T("data"))) {
+ BOOL bOk = FALSE;
+ const TCHAR *sid = xmlGetAttrValue( pInfo->GetChildNode(), _T("sid"));
+ const TCHAR *seq = xmlGetAttrValue( pInfo->GetChildNode(), _T("seq"));
+ if ( sid && seq && xmlGetText( pInfo->GetChildNode()))
+ bOk = OnIbbRecvdData( xmlGetText( pInfo->GetChildNode()), sid, seq );
+
+ if ( bOk )
+ m_ThreadInfo->send( XmlNodeIq( _T("result"), pInfo ));
+ else
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("error"), pInfo )
+ << XCHILD( _T("error")) << XATTRI( _T("code"), 404 ) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+ }
+ return TRUE;
+}
+
+void CJabberProto::OnIbbInitiateResult( HXML, CJabberIqInfo* pInfo )
+{
+ JABBER_IBB_TRANSFER *jibb = ( JABBER_IBB_TRANSFER * )pInfo->GetUserData();
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT )
+ jibb->bStreamInitialized = TRUE;
+ if ( jibb->hEvent )
+ SetEvent( jibb->hEvent );
+}
+
+void CJabberProto::OnIbbCloseResult( HXML, CJabberIqInfo* pInfo )
+{
+ JABBER_IBB_TRANSFER *jibb = ( JABBER_IBB_TRANSFER * )pInfo->GetUserData();
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT )
+ jibb->bStreamClosed = TRUE;
+ if ( jibb->hEvent )
+ SetEvent( jibb->hEvent );
+}
+
+void CJabberProto::IbbSendThread( JABBER_IBB_TRANSFER *jibb )
+{
+ Log( "Thread started: type=ibb_send" );
+
+ jibb->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ jibb->bStreamInitialized = FALSE;
+ jibb->bStreamClosed = FALSE;
+ jibb->state = JIBB_SENDING;
+
+ m_ThreadInfo->send(
+ XmlNodeIq( m_iqManager.AddHandler( &CJabberProto::OnIbbInitiateResult, JABBER_IQ_TYPE_SET, jibb->dstJID, 0, -1, jibb ))
+ << XCHILDNS( _T("open"), _T(JABBER_FEAT_IBB)) << XATTR( _T("sid"), jibb->sid ) << XATTRI( _T("block-size"), JABBER_IBB_BLOCK_SIZE )
+ << XATTR( _T("stanza"), _T("message")));
+
+ WaitForSingleObject( jibb->hEvent, INFINITE );
+ CloseHandle( jibb->hEvent );
+ jibb->hEvent = NULL;
+
+ if ( jibb->bStreamInitialized ) {
+
+ jibb->wPacketId = 0;
+
+ BOOL bSent = (this->*jibb->pfnSend)( JABBER_IBB_BLOCK_SIZE, jibb->ft );
+
+ if ( !jibb->bStreamClosed )
+ {
+ jibb->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+
+ m_ThreadInfo->send(
+ XmlNodeIq( m_iqManager.AddHandler( &CJabberProto::OnIbbCloseResult, JABBER_IQ_TYPE_SET, jibb->dstJID, 0, -1, jibb ))
+ << XCHILDNS( _T("close"), _T(JABBER_FEAT_IBB)) << XATTR( _T("sid"), jibb->sid ));
+
+ WaitForSingleObject( jibb->hEvent, INFINITE );
+ CloseHandle( jibb->hEvent );
+ jibb->hEvent = NULL;
+
+ if ( jibb->bStreamClosed && bSent )
+ jibb->state = JIBB_DONE;
+
+ } else {
+ jibb->state = JIBB_ERROR;
+ }
+ }
+
+ (this->*jibb->pfnFinal)(( jibb->state==JIBB_DONE )?TRUE:FALSE, jibb->ft );
+ jibb->ft = NULL;
+ JabberIbbFreeJibb( jibb );
+}
+
+void __cdecl CJabberProto::IbbReceiveThread( JABBER_IBB_TRANSFER *jibb )
+{
+ Log( "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"), _T(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 );
+}
+
+BOOL CJabberProto::OnIbbRecvdData( const TCHAR *data, const TCHAR *sid, const TCHAR *seq )
+{
+ JABBER_LIST_ITEM *item = ListGetItemPtr( LIST_FTRECV, sid );
+ if ( !item ) return FALSE;
+
+ WORD wSeq = (WORD)_ttoi(seq);
+ if ( wSeq != item->jibb->wPacketId ) {
+ if ( item->jibb->hEvent )
+ SetEvent( item->jibb->hEvent );
+ return FALSE;
+ }
+
+ item->jibb->wPacketId++;
+
+ int length = 0;
+ char *decodedData = JabberBase64DecodeT( data, &length );
+ if ( !decodedData )
+ return FALSE;
+
+ (this->*item->jibb->pfnRecv)( NULL, item->ft, decodedData, length );
+
+ item->jibb->dwTransferredSize += (DWORD)length;
+
+ mir_free( decodedData );
+
+ return TRUE;
+}
diff --git a/protocols/JabberG/src/jabber_ibb.h b/protocols/JabberG/src/jabber_ibb.h new file mode 100644 index 0000000000..37ecfd1a2e --- /dev/null +++ b/protocols/JabberG/src/jabber_ibb.h @@ -0,0 +1,46 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_IBB_H_
+#define _JABBER_IBB_H_
+
+typedef enum { JIBB_INIT, JIBB_CONNECT, JIBB_SENDING, JIBB_RECVING, JIBB_DONE, JIBB_ERROR } JABBER_IBB_STATE;
+
+typedef struct {
+ TCHAR* sid;
+ TCHAR* srcJID;
+ TCHAR* dstJID;
+ unsigned __int64 dwTransferredSize;
+ JABBER_IBB_STATE state;
+ HANDLE hEvent;
+ BOOL bStreamInitialized;
+ BOOL bStreamClosed;
+ WORD wPacketId;
+ BOOL ( CJabberProto::*pfnSend )( int blocksize, filetransfer* ft );
+ int ( CJabberProto::*pfnRecv )( HANDLE hConn, filetransfer* ft, char* buffer, int datalen );
+ void ( CJabberProto::*pfnFinal )( BOOL success, filetransfer* ft );
+ filetransfer* ft;
+}
+ JABBER_IBB_TRANSFER;
+
+#endif
diff --git a/protocols/JabberG/src/jabber_icolib.cpp b/protocols/JabberG/src/jabber_icolib.cpp new file mode 100644 index 0000000000..f29a827313 --- /dev/null +++ b/protocols/JabberG/src/jabber_icolib.cpp @@ -0,0 +1,733 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+Idea & portions of code by Artem Shpynov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_list.h"
+
+#include <m_icolib.h>
+
+#include <m_cluiframes.h>
+
+#define IDI_ONLINE 104
+#define IDI_OFFLINE 105
+#define IDI_AWAY 128
+#define IDI_FREE4CHAT 129
+#define IDI_INVISIBLE 130
+#define IDI_NA 131
+#define IDI_DND 158
+#define IDI_OCCUPIED 159
+#define IDI_ONTHEPHONE 1002
+#define IDI_OUTTOLUNCH 1003
+
+HIMAGELIST hAdvancedStatusIcon = NULL;
+
+struct CTransportProtoTableItem
+{
+ TCHAR* mask;
+ char* proto;
+};
+
+static CTransportProtoTableItem TransportProtoTable[] =
+{
+ { _T("|*icq*|jit*"), "ICQ" },
+ { _T("msn*"), "MSN" },
+ { _T("yahoo*"), "YAHOO" },
+ { _T("mrim*"), "MRA" },
+ { _T("aim*"), "AIM" },
+ //request #3094
+ { _T("|gg*|gadu*"), "GaduGadu" },
+ { _T("tv*"), "TV" },
+ { _T("dict*"), "Dictionary" },
+ { _T("weather*"), "Weather" },
+ { _T("skype*"), "Skype" },
+ { _T("sms*"), "SMS" },
+ { _T("smtp*"), "SMTP" },
+ //j2j
+ { _T("gtalk.*.*"), "GTalk" },
+ { _T("|xmpp.*.*|j2j.*.*"),"Jabber2Jabber" },
+ //jabbim.cz - services
+ { _T("disk*"), "Jabber Disk" },
+ { _T("irc*"), "IRC" },
+ { _T("rss*"), "RSS" },
+ { _T("tlen*"), "Tlen" },
+
+ // German social networks
+ { _T("studivz*"), "StudiVZ" },
+ { _T("schuelervz*"), "SchuelerVZ" },
+ { _T("meinvz*"), "MeinVZ" },
+ { _T("|fb*|facebook*"), "Facebook" },
+};
+
+static int skinIconStatusToResourceId[] = {IDI_OFFLINE,IDI_ONLINE,IDI_AWAY,IDI_DND,IDI_NA,IDI_NA,/*IDI_OCCUPIED,*/IDI_FREE4CHAT,IDI_INVISIBLE,IDI_ONTHEPHONE,IDI_OUTTOLUNCH};
+static int skinStatusToJabberStatus[] = {0,1,2,3,4,4,6,7,2,2};
+
+///////////////////////////////////////////////////////////////////////////////
+// CIconPool class
+int CIconPool::CPoolItem::cmp(const CPoolItem *p1, const CPoolItem *p2)
+{
+ return lstrcmpA(p1->m_name, p2->m_name);
+}
+
+CIconPool::CPoolItem::CPoolItem():
+ m_name(NULL), m_szIcolibName(NULL), m_hIcolibItem(NULL), m_hClistItem(NULL)
+{
+}
+
+CIconPool::CPoolItem::~CPoolItem()
+{
+ if (m_hIcolibItem && m_szIcolibName)
+ {
+ CallService(MS_SKIN2_REMOVEICON, 0, (LPARAM)m_szIcolibName);
+ mir_free(m_szIcolibName);
+ }
+
+ if (m_name) mir_free(m_name);
+}
+
+CIconPool::CIconPool(CJabberProto *proto):
+ m_proto(proto),
+ m_items(10, CIconPool::CPoolItem::cmp),
+ m_hOnExtraIconsRebuild(NULL)
+{
+}
+
+CIconPool::~CIconPool()
+{
+ if (m_hOnExtraIconsRebuild)
+ {
+ UnhookEvent(m_hOnExtraIconsRebuild);
+ m_hOnExtraIconsRebuild = NULL;
+ }
+}
+
+void CIconPool::RegisterIcon(const char *name, const char *filename, int iconid, TCHAR *szSection, TCHAR *szDescription)
+{
+ char szSettingName[128];
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%s_%s", m_proto->m_szModuleName, name);
+
+ CPoolItem *item = new CPoolItem;
+ item->m_name = mir_strdup(name);
+ item->m_szIcolibName = mir_strdup(szSettingName);
+ item->m_hClistItem = NULL;
+
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.pszDefaultFile = (char *)filename; // kill const flag for compiler to shut up
+ sid.pszName = szSettingName;
+ sid.ptszSection = szSection;
+ sid.ptszDescription = szDescription;
+ sid.flags = SIDF_TCHAR;
+ sid.iDefaultIndex = iconid;
+ item->m_hIcolibItem = Skin_AddIcon(&sid);
+
+ m_items.insert(item);
+}
+
+HANDLE CIconPool::GetIcolibHandle(const char *name)
+{
+ if (CPoolItem *item = FindItemByName(name))
+ return item->m_hIcolibItem;
+
+ return NULL;
+}
+
+char *CIconPool::GetIcolibName(const char *name)
+{
+ if (CPoolItem *item = FindItemByName(name))
+ return item->m_szIcolibName;
+
+ return NULL;
+}
+
+HICON CIconPool::GetIcon(const char *name, bool big)
+{
+ if (CPoolItem *item = FindItemByName(name))
+ return (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, big, (LPARAM)item->m_hIcolibItem);
+
+ return NULL;
+}
+
+HANDLE CIconPool::GetClistHandle(const char *name)
+{
+ if (!name)
+ return (HANDLE)-1;
+
+ if (!ExtraIconsSupported())
+ return (HANDLE)-1;
+
+ if (!m_hOnExtraIconsRebuild)
+ {
+ int (__cdecl CIconPool::*hookProc)(WPARAM, LPARAM);
+ hookProc = &CIconPool::OnExtraIconsRebuild;
+ m_hOnExtraIconsRebuild = HookEventObj(ME_CLIST_EXTRA_LIST_REBUILD, (MIRANDAHOOKOBJ)*(void **)&hookProc, this);
+ }
+
+ if (CPoolItem *item = FindItemByName(name))
+ {
+ if (item->m_hClistItem)
+ return item->m_hClistItem;
+
+ HICON hIcon = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)item->m_hIcolibItem);
+ item->m_hClistItem = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+ g_ReleaseIcon(hIcon);
+ return item->m_hClistItem;
+ }
+
+ return (HANDLE)-1;
+}
+
+CIconPool::CPoolItem *CIconPool::FindItemByName(const char *name)
+{
+ CPoolItem item;
+ item.m_name = mir_strdup(name);
+ return m_items.find(&item);
+}
+
+int CIconPool::OnExtraIconsRebuild(WPARAM, LPARAM)
+{
+ for (int i = 0; i < m_items.getCount(); ++i)
+ m_items[i].m_hClistItem = NULL;
+
+ return 0;
+}
+
+bool CIconPool::ExtraIconsSupported()
+{
+ static int res = -1;
+ if (res < 0) res = ServiceExists(MS_CLIST_EXTRA_ADD_ICON) ? 1 : 0;
+ return res ? true : false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Icons init
+
+struct TIconListItem
+{
+ char* szDescr;
+ char* szName;
+ int defIconID;
+ char* szSection;
+ HANDLE hIcon;
+};
+
+static TIconListItem iconList[] =
+{
+ { LPGEN("%s"), "main", IDI_JABBER, NULL },
+};
+
+void CJabberProto::IconsInit( void )
+{
+ int i;
+
+ m_transportProtoTableStartIndex = (int *)mir_alloc(sizeof(int) * SIZEOF(TransportProtoTable));
+ for (i = 0; i < SIZEOF(TransportProtoTable); ++i)
+ m_transportProtoTableStartIndex[i] = -1;
+
+ SKINICONDESC sid = {0};
+ char szFile[MAX_PATH];
+ GetModuleFileNameA(hInst, szFile, MAX_PATH);
+
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.pszDefaultFile = szFile;
+ sid.flags = SIDF_TCHAR;
+
+ char szSettingName[100];
+ TCHAR szSectionName[100];
+ TCHAR szDescription[100];
+ TCHAR szRootSection[100];
+
+ sid.pszName = szSettingName;
+ sid.ptszSection = szSectionName;
+ sid.ptszDescription = szDescription;
+
+ m_phIconLibItems = ( HANDLE* )mir_alloc( sizeof( HANDLE )*SIZEOF(iconList));
+
+ mir_sntprintf( szRootSection, SIZEOF(szRootSection), _T("%s/%s/%s"), LPGENT("Protocols"), LPGENT("Jabber"), LPGENT("Accounts"));
+
+ for (i = 0; i < SIZEOF(iconList); i++ ) {
+ TCHAR tmp[100];
+
+ if ( iconList[i].szSection ) {
+ mir_sntprintf( szSectionName, SIZEOF(szSectionName), _T("%s/") _T(TCHAR_STR_PARAM), szRootSection, iconList[i].szSection );
+ if (_tcsstr(szSectionName, _T("%s"))) {
+ mir_sntprintf(tmp, SIZEOF(tmp), szSectionName, m_tszUserName);
+ lstrcpy(szSectionName, tmp);
+ }
+ }
+ else {
+ mir_sntprintf( szSectionName, SIZEOF(szSectionName), _T("%s"), szRootSection );
+ }
+
+ if (strstr(iconList[i].szDescr, "%s")) {
+ mir_sntprintf( tmp, SIZEOF(tmp), _T(TCHAR_STR_PARAM), iconList[i].szDescr );
+ mir_sntprintf( szDescription, SIZEOF(szDescription), tmp, m_tszUserName );
+ }
+ else mir_sntprintf( szDescription, SIZEOF(szDescription), _T(TCHAR_STR_PARAM), iconList[i].szDescr );
+
+ mir_snprintf( szSettingName, SIZEOF(szSettingName), "%s_%s", m_szModuleName, iconList[i].szName );
+
+ sid.iDefaultIndex = -iconList[i].defIconID;
+ m_phIconLibItems[i] = Skin_AddIcon(&sid);
+} }
+
+HANDLE CJabberProto::GetIconHandle( int iconId )
+{
+ if (HANDLE result = g_GetIconHandle(iconId))
+ return result;
+
+ for ( int i=0; i < SIZEOF(iconList); i++ )
+ if ( iconList[i].defIconID == iconId )
+ return m_phIconLibItems[i];
+
+ return NULL;
+}
+
+HICON CJabberProto::LoadIconEx( const char* name, bool big )
+{
+ if (HICON result = g_LoadIconEx(name, big))
+ return result;
+
+ char szSettingName[100];
+ mir_snprintf( szSettingName, sizeof( szSettingName ), "%s_%s", m_szModuleName, name );
+ return ( HICON )CallService( MS_SKIN2_GETICON, big, (LPARAM)szSettingName );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// internal functions
+
+static inline TCHAR qtoupper( TCHAR c )
+{
+ return ( c >= 'a' && c <= 'z' ) ? c - 'a' + 'A' : c;
+}
+
+static BOOL WildComparei( const TCHAR* name, const TCHAR* mask )
+{
+ const TCHAR* last='\0';
+ for ( ;; mask++, name++) {
+ if ( *mask != '?' && qtoupper( *mask ) != qtoupper( *name ))
+ break;
+ if ( *name == '\0' )
+ return ((BOOL)!*mask);
+ }
+
+ if ( *mask != '*' )
+ return FALSE;
+
+ for (;; mask++, name++ ) {
+ while( *mask == '*' ) {
+ last = mask++;
+ if ( *mask == '\0' )
+ return ((BOOL)!*mask); /* true */
+ }
+
+ if ( *name == '\0' )
+ return ((BOOL)!*mask); /* *mask == EOS */
+ if ( *mask != '?' && qtoupper( *mask ) != qtoupper( *name ))
+ name -= (size_t)(mask - last) - 1, mask = last;
+} }
+
+static BOOL MatchMask( const TCHAR* name, const TCHAR* mask)
+{
+ if ( !mask || !name )
+ return mask == name;
+
+ if ( *mask != '|' )
+ return WildComparei( name, mask );
+
+ TCHAR* temp = NEWTSTR_ALLOCA(mask);
+ for ( int e=1; mask[e] != '\0'; e++ ) {
+ int s = e;
+ while ( mask[e] != '\0' && mask[e] != '|')
+ e++;
+
+ temp[e]= _T('\0');
+ if ( WildComparei( name, temp+s ))
+ return TRUE;
+
+ if ( mask[e] == 0 )
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+static HICON ExtractIconFromPath(const char *path, BOOL * needFree)
+{
+ char *comma;
+ char file[MAX_PATH],fileFull[MAX_PATH];
+ int n;
+ HICON hIcon;
+ lstrcpynA(file,path,sizeof(file));
+ comma=strrchr(file,',');
+ if(comma==NULL) n=0;
+ else {n=atoi(comma+1); *comma=0;}
+ CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)file, (LPARAM)fileFull);
+ hIcon=NULL;
+ ExtractIconExA(fileFull,n,NULL,&hIcon,1);
+ if (needFree)
+ *needFree=(hIcon!=NULL);
+
+ return hIcon;
+}
+
+static HICON LoadTransportIcon(char *filename,int i,char *IconName,TCHAR *SectName,TCHAR *Description,int internalidx, BOOL * needFree)
+{
+ char szPath[MAX_PATH],szMyPath[MAX_PATH], szFullPath[MAX_PATH],*str;
+ BOOL has_proto_icon=FALSE;
+ SKINICONDESC sid={0};
+ if (needFree) *needFree=FALSE;
+ GetModuleFileNameA(NULL, szPath, MAX_PATH);
+ str=strrchr(szPath,'\\');
+ if(str!=NULL) *str=0;
+ _snprintf(szMyPath, sizeof(szMyPath), "%s\\Icons\\%s", szPath, filename);
+ _snprintf(szFullPath, sizeof(szFullPath), "%s\\Icons\\%s,%d", szPath, filename, i);
+ BOOL nf;
+ HICON hi=ExtractIconFromPath(szFullPath,&nf);
+ if (hi) has_proto_icon=TRUE;
+ if (hi && nf) DestroyIcon(hi);
+ if ( IconName != NULL && SectName != NULL) {
+ sid.cbSize = sizeof(sid);
+ sid.hDefaultIcon = (has_proto_icon)?NULL:(HICON)CallService(MS_SKIN_LOADPROTOICON,(WPARAM)NULL,(LPARAM)(-internalidx));
+ sid.ptszSection = SectName;
+ sid.pszName = IconName;
+ sid.ptszDescription = Description;
+ sid.pszDefaultFile = szMyPath;
+ sid.iDefaultIndex = i;
+ sid.flags = SIDF_TCHAR;
+ Skin_AddIcon(&sid);
+ }
+ return ((HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)IconName));
+}
+
+static HICON LoadSmallIcon(HINSTANCE hInstance, LPCTSTR lpIconName)
+{
+ HICON hIcon=NULL; // icon handle
+ int index=-(int)lpIconName;
+ TCHAR filename[MAX_PATH]={0};
+ GetModuleFileName(hInstance,filename,MAX_PATH);
+ ExtractIconEx(filename,index,NULL,&hIcon,1);
+ return hIcon;
+}
+
+int CJabberProto::LoadAdvancedIcons(int iID)
+{
+ int i;
+ char *proto = TransportProtoTable[iID].proto;
+ char defFile[MAX_PATH] = {0};
+ TCHAR Group[255];
+ char Uname[255];
+ int first=-1;
+ HICON empty=LoadSmallIcon(NULL,MAKEINTRESOURCE(102));
+
+ mir_sntprintf(Group, SIZEOF(Group), _T("Status Icons/%s/") _T(TCHAR_STR_PARAM) _T(" %s"), m_tszUserName, proto, TranslateT("transport"));
+ mir_snprintf(defFile, SIZEOF(defFile), "proto_%s.dll",proto);
+ if (!hAdvancedStatusIcon)
+ hAdvancedStatusIcon=(HIMAGELIST)CallService(MS_CLIST_GETICONSIMAGELIST,0,0);
+
+ EnterCriticalSection( &m_csModeMsgMutex );
+ for (i=0; i<ID_STATUS_ONTHEPHONE-ID_STATUS_OFFLINE; i++) {
+ HICON hicon;
+ BOOL needFree;
+ int n=skinStatusToJabberStatus[i];
+ TCHAR *descr = (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, n+ID_STATUS_OFFLINE, GSMDF_TCHAR);
+ mir_snprintf(Uname, SIZEOF(Uname), "%s_Transport_%s_%d", m_szModuleName, proto, n);
+ hicon=(HICON)LoadTransportIcon(defFile,-skinIconStatusToResourceId[i],Uname,Group,descr,-(n+ID_STATUS_OFFLINE),&needFree);
+ int index=(m_transportProtoTableStartIndex[iID] == -1)?-1:m_transportProtoTableStartIndex[iID]+n;
+ int added=ImageList_ReplaceIcon(hAdvancedStatusIcon,index,hicon?hicon:empty);
+ if (first == -1) first=added;
+ if (hicon && needFree) DestroyIcon(hicon);
+ }
+
+ if ( m_transportProtoTableStartIndex[iID] == -1 )
+ m_transportProtoTableStartIndex[iID] = first;
+ LeaveCriticalSection( &m_csModeMsgMutex );
+ return 0;
+}
+
+int CJabberProto::GetTransportProtoID( TCHAR* TransportDomain )
+{
+ for ( int i=0; i<SIZEOF(TransportProtoTable); i++ )
+ if ( MatchMask( TransportDomain, TransportProtoTable[i].mask ))
+ return i;
+
+ return -1;
+}
+
+int CJabberProto::GetTransportStatusIconIndex(int iID, int Status)
+{
+ if ( iID < 0 || iID >= SIZEOF( TransportProtoTable ))
+ return -1;
+
+ //icons not loaded - loading icons
+ if ( m_transportProtoTableStartIndex[iID] == -1 )
+ LoadAdvancedIcons( iID );
+
+ //some fault on loading icons
+ if ( m_transportProtoTableStartIndex[iID] == -1 )
+ return -1;
+
+ if ( Status < ID_STATUS_OFFLINE )
+ Status = ID_STATUS_OFFLINE;
+
+ return m_transportProtoTableStartIndex[iID] + skinStatusToJabberStatus[ Status - ID_STATUS_OFFLINE ];
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// a hook for the IcoLib plugin
+
+int CJabberProto::OnReloadIcons(WPARAM, LPARAM)
+{
+ for ( int i=0; i < SIZEOF(TransportProtoTable); i++ )
+ if ( m_transportProtoTableStartIndex[i] != -1 )
+ LoadAdvancedIcons(i);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Prototype for Jabber and other protocols to return index of Advanced status
+// wParam - HCONTACT of called protocol
+// lParam - should be 0 (reserverd for futher usage)
+// return value: -1 - no Advanced status
+// : other - index of icons in clcimagelist.
+// if imagelist require advanced painting status overlay(like xStatus)
+// index should be shifted to HIWORD, LOWORD should be 0
+
+INT_PTR __cdecl CJabberProto::JGetAdvancedStatusIcon(WPARAM wParam, LPARAM)
+{
+ HANDLE hContact=(HANDLE) wParam;
+ if ( !hContact )
+ return -1;
+
+ if ( !JGetByte( hContact, "IsTransported", 0 ))
+ return -1;
+
+ DBVARIANT dbv;
+ if ( JGetStringT( hContact, "Transport", &dbv ))
+ return -1;
+
+ int iID = GetTransportProtoID( dbv.ptszVal );
+ DBFreeVariant(&dbv);
+ if ( iID >= 0 ) {
+ WORD Status = ID_STATUS_OFFLINE;
+ Status = JGetWord( hContact, "Status", ID_STATUS_OFFLINE );
+ if ( Status < ID_STATUS_OFFLINE )
+ Status = ID_STATUS_OFFLINE;
+ else if (Status > ID_STATUS_INVISIBLE )
+ Status = ID_STATUS_ONLINE;
+ return GetTransportStatusIconIndex( iID, Status );
+ }
+ return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Transport check functions
+
+BOOL CJabberProto::DBCheckIsTransportedContact(const TCHAR* jid, HANDLE hContact)
+{
+ // check if transport is already set
+ if ( !jid || !hContact )
+ return FALSE;
+
+ // strip domain part from jid
+ TCHAR* domain = _tcschr(( TCHAR* )jid, '@' );
+ BOOL isAgent = (domain == NULL) ? TRUE : FALSE;
+ BOOL isTransported = FALSE;
+ if ( domain!=NULL )
+ domain = NEWTSTR_ALLOCA(domain+1);
+ else
+ domain = NEWTSTR_ALLOCA(jid);
+
+ TCHAR* resourcepos = _tcschr( domain, '/' );
+ if ( resourcepos != NULL )
+ *resourcepos = '\0';
+
+ for ( int i=0; i < SIZEOF(TransportProtoTable); i++ ) {
+ if ( MatchMask( domain, TransportProtoTable[i].mask )) {
+ GetTransportStatusIconIndex( GetTransportProtoID( domain ), ID_STATUS_OFFLINE );
+ isTransported = TRUE;
+ break;
+ } }
+
+ if ( m_lstTransports.getIndex( domain ) == -1 ) {
+ if ( isAgent ) {
+ m_lstTransports.insert( mir_tstrdup(domain));
+ JSetByte( hContact, "IsTransport", 1 );
+ } }
+
+ if ( isTransported ) {
+ JSetStringT( hContact, "Transport", domain );
+ JSetByte( hContact, "IsTransported", 1 );
+ }
+ return isTransported;
+}
+
+void CJabberProto::CheckAllContactsAreTransported()
+{
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL ) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( !lstrcmpA( m_szModuleName, szProto )) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ DBCheckIsTransportedContact( dbv.ptszVal, hContact );
+ JFreeVariant( &dbv );
+ } }
+
+ hContact = db_find_next(hContact);
+} }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Cross-instance shared icons
+
+static TIconListItem sharedIconList[] =
+{
+ { LPGEN("Privacy Lists"), "privacylists", IDI_PRIVACY_LISTS, NULL },
+ { LPGEN("Bookmarks"), "bookmarks", IDI_BOOKMARKS, NULL },
+ { LPGEN("Notes"), "notes", IDI_NOTES, NULL },
+ { LPGEN("Multi-User Conference"), "group", IDI_GROUP, NULL },
+ { LPGEN("Agents list"), "Agents", IDI_AGENTS, NULL },
+
+ { LPGEN("Transports"), "transport", IDI_TRANSPORT, NULL },
+ { LPGEN("Registered transports"), "transport_loc", IDI_TRANSPORTL, NULL },
+ { LPGEN("Change password"), "key", IDI_KEYS, NULL },
+ { LPGEN("Personal vCard"), "vcard", IDI_VCARD, NULL },
+ { LPGEN("Request authorization"), "Request", IDI_REQUEST, NULL },
+ { LPGEN("Grant authorization"), "Grant", IDI_GRANT, NULL },
+ { LPGEN("Revoke authorization"), "Revoke", IDI_AUTHREVOKE, NULL },
+ { LPGEN("Convert to room"), "convert", IDI_USER2ROOM, NULL },
+ { LPGEN("Add to roster"), "addroster", IDI_ADDROSTER, NULL },
+ { LPGEN("Login/logout"), "trlogonoff", IDI_LOGIN, NULL },
+ { LPGEN("Resolve nicks"), "trresolve", IDI_REFRESH, NULL },
+ { LPGEN("Send note"), "sendnote", IDI_SEND_NOTE, NULL },
+ { LPGEN("Service Discovery"), "servicediscovery", IDI_SERVICE_DISCOVERY, NULL },
+ { LPGEN("AdHoc Command"), "adhoc", IDI_COMMAND, NULL },
+ { LPGEN("XML Console"), "xmlconsole", IDI_CONSOLE, NULL },
+ { LPGEN("OpenID Request"), "openid", IDI_HTTP_AUTH, NULL },
+
+ { LPGEN("Discovery succeeded"), "disco_ok", IDI_DISCO_OK, LPGEN("Dialogs") },
+ { LPGEN("Discovery failed"), "disco_fail", IDI_DISCO_FAIL, LPGEN("Dialogs") },
+ { LPGEN("Discovery in progress"), "disco_progress", IDI_DISCO_PROGRESS, LPGEN("Dialogs") },
+ { LPGEN("View as tree"), "sd_view_tree", IDI_VIEW_TREE, LPGEN("Dialogs") },
+ { LPGEN("View as list"), "sd_view_list", IDI_VIEW_LIST, LPGEN("Dialogs") },
+ { LPGEN("Apply filter"), "sd_filter_apply", IDI_FILTER_APPLY, LPGEN("Dialogs") },
+ { LPGEN("Reset filter"), "sd_filter_reset", IDI_FILTER_RESET, LPGEN("Dialogs") },
+
+ { LPGEN("Navigate home"), "sd_nav_home", IDI_NAV_HOME, LPGEN("Dialogs/Discovery") },
+ { LPGEN("Refresh node"), "sd_nav_refresh", IDI_NAV_REFRESH, LPGEN("Dialogs/Discovery") },
+ { LPGEN("Browse node"), "sd_browse", IDI_BROWSE, LPGEN("Dialogs/Discovery") },
+ { LPGEN("RSS service"), "node_rss", IDI_NODE_RSS, LPGEN("Dialogs/Discovery") },
+ { LPGEN("Server"), "node_server", IDI_NODE_SERVER, LPGEN("Dialogs/Discovery") },
+ { LPGEN("Storage service"), "node_store", IDI_NODE_STORE, LPGEN("Dialogs/Discovery") },
+ { LPGEN("Weather service"), "node_weather", IDI_NODE_WEATHER, LPGEN("Dialogs/Discovery") },
+
+ { LPGEN("Generic privacy list"), "pl_list_any", IDI_PL_LIST_ANY, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Active privacy list"), "pl_list_active", IDI_PL_LIST_ACTIVE, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Default privacy list"), "pl_list_default", IDI_PL_LIST_DEFAULT, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Move up"), "arrow_up", IDI_ARROW_UP, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Move down"), "arrow_down", IDI_ARROW_DOWN, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Allow Messages"), "pl_msg_allow", IDI_PL_MSG_ALLOW, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Allow Presences (in)"), "pl_prin_allow", IDI_PL_PRIN_ALLOW, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Allow Presences (out)"), "pl_prout_allow", IDI_PL_PROUT_ALLOW, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Allow Queries"), "pl_iq_allow", IDI_PL_QUERY_ALLOW, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Deny Messages"), "pl_msg_deny", IDI_PL_MSG_DENY, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Deny Presences (in)"), "pl_prin_deny", IDI_PL_PRIN_DENY, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Deny Presences (out)"), "pl_prout_deny", IDI_PL_PROUT_DENY, LPGEN("Dialogs/Privacy") },
+ { LPGEN("Deny Queries"), "pl_iq_deny", IDI_PL_QUERY_DENY, LPGEN("Dialogs/Privacy") },
+};
+
+static void sttProcessIcons( int iAmount )
+{
+ TCHAR szFile[MAX_PATH];
+ GetModuleFileName(hInst, szFile, MAX_PATH);
+
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.ptszDefaultFile = szFile;
+ sid.flags = SIDF_PATH_TCHAR;
+
+ char szRootSection[100];
+ mir_snprintf( szRootSection, SIZEOF(szRootSection), "%s/%s", LPGEN("Protocols"), LPGEN("Jabber"));
+
+ for ( int i = 0; i < iAmount; i++ ) {
+ char szSettingName[100], szSectionName[100];
+
+ mir_snprintf( szSettingName, sizeof( szSettingName ), "%s_%s",
+ GLOBAL_SETTING_PREFIX, sharedIconList[i].szName);
+
+ if ( sharedIconList[i].szSection ) {
+ mir_snprintf( szSectionName, sizeof( szSectionName ), "%s/%s", szRootSection, sharedIconList[i].szSection );
+ sid.pszSection = szSectionName;
+ }
+ else sid.pszSection = szRootSection;
+
+ sid.pszName = szSettingName;
+ sid.pszDescription = sharedIconList[i].szDescr;
+ sid.iDefaultIndex = -sharedIconList[i].defIconID;
+ sharedIconList[i].hIcon = Skin_AddIcon(&sid);
+} }
+
+void g_IconsInit()
+{
+ sttProcessIcons( SIZEOF( sharedIconList ));
+}
+
+HANDLE g_GetIconHandle( int iconId )
+{
+ for ( int i=0; i < SIZEOF(sharedIconList); i++ )
+ if ( sharedIconList[i].defIconID == iconId )
+ return sharedIconList[i].hIcon;
+
+ return NULL;
+}
+
+HICON g_LoadIconEx( const char* name, bool big )
+{
+ char szSettingName[100];
+ mir_snprintf( szSettingName, sizeof( szSettingName ), "%s_%s", GLOBAL_SETTING_PREFIX, name );
+ return ( HICON )CallService( MS_SKIN2_GETICON, big, (LPARAM)szSettingName );
+}
+
+void g_ReleaseIcon( HICON hIcon )
+{
+ if ( hIcon ) CallService(MS_SKIN2_RELEASEICON, (WPARAM)hIcon, 0);
+}
+
+void ImageList_AddIcon_Icolib( HIMAGELIST hIml, HICON hIcon )
+{
+ ImageList_AddIcon( hIml, hIcon );
+ g_ReleaseIcon( hIcon );
+}
+
+void WindowSetIcon(HWND hWnd, CJabberProto *proto, const char* name)
+{
+ SendMessage(hWnd, WM_SETICON, ICON_BIG, ( LPARAM )proto->LoadIconEx( name, true ));
+ SendMessage(hWnd, WM_SETICON, ICON_SMALL, ( LPARAM )proto->LoadIconEx( name ));
+}
+
+void WindowFreeIcon(HWND hWnd)
+{
+ g_ReleaseIcon(( HICON )SendMessage(hWnd, WM_SETICON, ICON_BIG, 0));
+ g_ReleaseIcon(( HICON )SendMessage(hWnd, WM_SETICON, ICON_SMALL, 0));
+}
diff --git a/protocols/JabberG/src/jabber_icolib.h b/protocols/JabberG/src/jabber_icolib.h new file mode 100644 index 0000000000..4b5f7d8e12 --- /dev/null +++ b/protocols/JabberG/src/jabber_icolib.h @@ -0,0 +1,67 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007-09 Maxim Mluhov
+Copyright ( C ) 2007-09 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_ICOLIB_H_
+#define _JABBER_ICOLIB_H_
+
+struct CJabberProto;
+
+class CIconPool
+{
+public:
+ CIconPool(CJabberProto *proto);
+ ~CIconPool();
+
+ void RegisterIcon(const char *name, const char *filename, int iconid, TCHAR *szSection, TCHAR *szDescription);
+
+ HANDLE GetIcolibHandle(const char *name);
+ char *GetIcolibName(const char *name);
+ HICON GetIcon(const char *name, bool big = false);
+ HANDLE GetClistHandle(const char *name);
+
+private:
+ struct CPoolItem
+ {
+ char *m_name;
+ char *m_szIcolibName;
+ HANDLE m_hIcolibItem;
+ HANDLE m_hClistItem;
+
+ static int cmp(const CPoolItem *p1, const CPoolItem *p2);
+
+ CPoolItem();
+ ~CPoolItem();
+ };
+
+ CJabberProto *m_proto;
+ OBJLIST<CPoolItem> m_items;
+ HANDLE m_hOnExtraIconsRebuild;
+
+ CPoolItem *FindItemByName(const char *name);
+
+ int __cdecl OnExtraIconsRebuild(WPARAM, LPARAM);
+ static bool ExtraIconsSupported();
+};
+
+#endif // _JABBER_ICOLIB_H_
diff --git a/protocols/JabberG/src/jabber_iq.cpp b/protocols/JabberG/src/jabber_iq.cpp new file mode 100644 index 0000000000..43084114c2 --- /dev/null +++ b/protocols/JabberG/src/jabber_iq.cpp @@ -0,0 +1,375 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+#include "jabber_privacy.h"
+#include "jabber_ibb.h"
+#include "jabber_rc.h"
+
+void CJabberProto::IqInit()
+{
+ InitializeCriticalSection( &m_csIqList );
+ m_ppIqList = NULL;
+ m_nIqCount = 0;
+ m_nIqAlloced = 0;
+}
+
+void CJabberProto::IqUninit()
+{
+ if ( m_ppIqList ) mir_free( m_ppIqList );
+ m_ppIqList = NULL;
+ m_nIqCount = 0;
+ m_nIqAlloced = 0;
+ DeleteCriticalSection( &m_csIqList );
+}
+
+void CJabberProto::IqRemove( int index )
+{
+ EnterCriticalSection( &m_csIqList );
+ if ( index>=0 && index<m_nIqCount ) {
+ memmove( m_ppIqList+index, m_ppIqList+index+1, sizeof( JABBER_IQ_FUNC )*( m_nIqCount-index-1 ));
+ m_nIqCount--;
+ }
+ LeaveCriticalSection( &m_csIqList );
+}
+
+void CJabberProto::IqExpire()
+{
+ int i;
+ time_t expire;
+
+ EnterCriticalSection( &m_csIqList );
+ expire = time( NULL ) - 120; // 2 minute
+ i = 0;
+ while ( i < m_nIqCount ) {
+ if ( m_ppIqList[i].requestTime < expire )
+ IqRemove( i );
+ else
+ i++;
+ }
+ LeaveCriticalSection( &m_csIqList );
+}
+
+JABBER_IQ_PFUNC CJabberProto::JabberIqFetchFunc( int iqId )
+{
+ int i;
+ JABBER_IQ_PFUNC res;
+
+ EnterCriticalSection( &m_csIqList );
+ IqExpire();
+#ifdef _DEBUG
+ for ( i=0; i<m_nIqCount; i++ )
+ Log( " %04d : %02d : 0x%x", m_ppIqList[i].iqId, m_ppIqList[i].procId, m_ppIqList[i].func );
+#endif
+ for ( i=0; i<m_nIqCount && m_ppIqList[i].iqId!=iqId; i++ );
+ if ( i < m_nIqCount ) {
+ res = m_ppIqList[i].func;
+ IqRemove( i );
+ }
+ else {
+ res = ( JABBER_IQ_PFUNC ) NULL;
+ }
+ LeaveCriticalSection( &m_csIqList );
+ return res;
+}
+
+void CJabberProto::IqAdd( unsigned int iqId, JABBER_IQ_PROCID procId, JABBER_IQ_PFUNC func )
+{
+ int i;
+
+ EnterCriticalSection( &m_csIqList );
+ Log( "IqAdd id=%d, proc=%d, func=0x%x", iqId, procId, func );
+ if ( procId == IQ_PROC_NONE )
+ i = m_nIqCount;
+ else
+ for ( i=0; i<m_nIqCount && m_ppIqList[i].procId!=procId; i++ );
+
+ if ( i>=m_nIqCount && m_nIqCount>=m_nIqAlloced ) {
+ m_nIqAlloced = m_nIqCount + 8;
+ m_ppIqList = ( JABBER_IQ_FUNC * )mir_realloc( m_ppIqList, sizeof( JABBER_IQ_FUNC )*m_nIqAlloced );
+ }
+
+ if ( m_ppIqList != NULL ) {
+ m_ppIqList[i].iqId = iqId;
+ m_ppIqList[i].procId = procId;
+ m_ppIqList[i].func = func;
+ m_ppIqList[i].requestTime = time( NULL );
+ if ( i == m_nIqCount ) m_nIqCount++;
+ }
+ LeaveCriticalSection( &m_csIqList );
+}
+
+BOOL CJabberIqManager::FillPermanentHandlers()
+{
+ // Google Shared Status (http://code.google.com/apis/talk/jep_extensions/shared_status.html)
+ AddPermanentHandler( &CJabberProto::OnIqSetGoogleSharedStatus, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_TO | JABBER_IQ_PARSE_ID_STR, _T("google:shared-status"), FALSE, _T("query"));
+
+ // version requests (XEP-0092)
+ AddPermanentHandler( &CJabberProto::OnIqRequestVersion, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_VERSION), FALSE, _T("query"));
+
+ // last activity (XEP-0012)
+ AddPermanentHandler( &CJabberProto::OnIqRequestLastActivity, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_LAST_ACTIVITY), FALSE, _T("query"));
+
+ // ping requests (XEP-0199)
+ AddPermanentHandler( &CJabberProto::OnIqRequestPing, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_PING), FALSE, _T("ping"));
+
+ // entity time (XEP-0202)
+ AddPermanentHandler( &CJabberProto::OnIqRequestTime, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_ENTITY_TIME), FALSE, _T("time"));
+
+ // entity time (XEP-0090)
+ AddPermanentHandler( &CJabberProto::OnIqProcessIqOldTime, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_ENTITY_TIME_OLD), FALSE, _T("query"));
+
+ // old avatars support (deprecated XEP-0008)
+ AddPermanentHandler( &CJabberProto::OnIqRequestAvatar, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_AVATAR), FALSE, _T("query"));
+
+ // privacy lists (XEP-0016)
+ AddPermanentHandler( &CJabberProto::OnIqRequestPrivacyLists, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_PRIVACY_LISTS), FALSE, _T("query"));
+
+ // in band bytestreams (XEP-0047)
+ AddPermanentHandler( &CJabberProto::OnFtHandleIbbIq, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_CHILD_TAG_NODE | JABBER_IQ_PARSE_CHILD_TAG_NAME | JABBER_IQ_PARSE_CHILD_TAG_XMLNS, _T(JABBER_FEAT_IBB), FALSE, NULL);
+
+ // socks5-bytestreams (XEP-0065)
+ AddPermanentHandler( &CJabberProto::FtHandleBytestreamRequest, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_BYTESTREAMS), FALSE, _T("query"));
+
+ // session initiation (XEP-0095)
+ AddPermanentHandler( &CJabberProto::OnSiRequest, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_SI), FALSE, _T("si"));
+
+ // roster push requests
+ AddPermanentHandler( &CJabberProto::OnRosterPushRequest, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_IQ_ROSTER), FALSE, _T("query"));
+
+ // OOB file transfers
+ AddPermanentHandler( &CJabberProto::OnIqRequestOOB, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_HCONTACT | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_OOB), FALSE, _T("query"));
+
+ // disco#items requests (XEP-0030, XEP-0050)
+ AddPermanentHandler( &CJabberProto::OnHandleDiscoItemsRequest, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_TO | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_DISCO_ITEMS), FALSE, _T("query"));
+
+ // disco#info requests (XEP-0030, XEP-0050, XEP-0115)
+ AddPermanentHandler( &CJabberProto::OnHandleDiscoInfoRequest, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_TO | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_DISCO_INFO), FALSE, _T("query"));
+
+ // ad-hoc commands (XEP-0050) for remote controlling (XEP-0146)
+ AddPermanentHandler( &CJabberProto::HandleAdhocCommandRequest, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_TO | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_COMMANDS), FALSE, _T("command"));
+
+ // http auth (XEP-0070)
+ AddPermanentHandler( &CJabberProto::OnIqHttpAuth, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_HTTP_AUTH), FALSE, _T("confirm"));
+
+ return TRUE;
+}
+
+BOOL CJabberIqManager::Start()
+{
+ if ( m_hExpirerThread || m_bExpirerThreadShutdownRequest )
+ return FALSE;
+
+ m_hExpirerThread = ppro->JForkThreadEx( &CJabberProto::ExpirerThread, this );
+ if ( !m_hExpirerThread )
+ return FALSE;
+
+ return TRUE;
+}
+
+void __cdecl CJabberProto::ExpirerThread( void* pParam )
+{
+ CJabberIqManager *pManager = ( CJabberIqManager * )pParam;
+ pManager->ExpirerThread();
+}
+
+void CJabberIqManager::ExpirerThread()
+{
+ while (!m_bExpirerThreadShutdownRequest)
+ {
+ Lock();
+ CJabberIqInfo* pInfo = DetachExpired();
+ Unlock();
+ if (!pInfo)
+ {
+ for (int i = 0; !m_bExpirerThreadShutdownRequest && (i < 10); i++)
+ Sleep(50);
+
+ // -1 thread :)
+ ppro->m_adhocManager.ExpireSessions();
+ continue;
+ }
+ ExpireInfo( pInfo );
+ delete pInfo;
+ }
+
+ if ( !m_bExpirerThreadShutdownRequest ) {
+ CloseHandle( m_hExpirerThread );
+ m_hExpirerThread = NULL;
+ }
+}
+
+void CJabberIqManager::ExpireInfo( CJabberIqInfo* pInfo, void*)
+{
+ if ( !pInfo )
+ return;
+
+ if ( pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_FROM )
+ pInfo->m_szFrom = pInfo->m_szReceiver;
+ if (( pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_HCONTACT ) && ( pInfo->m_szFrom ))
+ pInfo->m_hContact = ppro->HContactFromJID( pInfo->m_szFrom , 3);
+
+ ppro->Log( "Expiring iq id %d, sent to " TCHAR_STR_PARAM, pInfo->m_nIqId, pInfo->m_szReceiver ? pInfo->m_szReceiver : _T("server"));
+
+ pInfo->m_nIqType = JABBER_IQ_TYPE_FAIL;
+ (ppro->*(pInfo->m_pHandler))( NULL, pInfo );
+}
+
+CJabberIqInfo* CJabberIqManager::AddHandler(JABBER_IQ_HANDLER pHandler, int nIqType, const TCHAR *szReceiver, DWORD dwParamsToParse, int nIqId, void *pUserData, int iPriority)
+{
+ CJabberIqInfo* pInfo = new CJabberIqInfo();
+ if (!pInfo)
+ return NULL;
+
+ pInfo->m_pHandler = pHandler;
+ if (nIqId == -1)
+ nIqId = ppro->SerialNext();
+ pInfo->m_nIqId = nIqId;
+ pInfo->m_nIqType = nIqType;
+ pInfo->m_dwParamsToParse = dwParamsToParse;
+ pInfo->m_pUserData = pUserData;
+ pInfo->m_dwRequestTime = GetTickCount();
+ pInfo->m_dwTimeout = JABBER_DEFAULT_IQ_REQUEST_TIMEOUT;
+ pInfo->m_iPriority = iPriority;
+ pInfo->SetReceiver(szReceiver);
+
+ InsertIq(pInfo);
+
+ return pInfo;
+}
+
+BOOL CJabberIqManager::HandleIq(int nIqId, HXML pNode )
+{
+ if (nIqId == -1 || pNode == NULL)
+ return FALSE;
+
+ const TCHAR *szType = xmlGetAttrValue( pNode, _T("type"));
+ if ( !szType )
+ return FALSE;
+
+ int nIqType = JABBER_IQ_TYPE_FAIL;
+ if (!_tcsicmp(szType, _T("result")))
+ nIqType = JABBER_IQ_TYPE_RESULT;
+ else if (!_tcsicmp(szType, _T("error")))
+ nIqType = JABBER_IQ_TYPE_ERROR;
+ else
+ return FALSE;
+
+ Lock();
+ CJabberIqInfo* pInfo = DetachInfo(nIqId);
+ Unlock();
+ if (pInfo)
+ {
+ pInfo->m_nIqType = nIqType;
+ if (nIqType == JABBER_IQ_TYPE_RESULT) {
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_CHILD_TAG_NODE)
+ pInfo->m_pChildNode = xmlGetChild( pNode , 0 );
+
+ if (pInfo->m_pChildNode && (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_CHILD_TAG_NAME))
+ pInfo->m_szChildTagName = ( TCHAR* )xmlGetName( pInfo->m_pChildNode );
+ if (pInfo->m_pChildNode && (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_CHILD_TAG_XMLNS))
+ pInfo->m_szChildTagXmlns = ( TCHAR* )xmlGetAttrValue( pNode, _T("xmlns"));
+ }
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_TO)
+ pInfo->m_szTo = ( TCHAR* )xmlGetAttrValue( pNode, _T("to"));
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_FROM)
+ pInfo->m_szFrom = ( TCHAR* )xmlGetAttrValue( pNode, _T("from"));
+ if (pInfo->m_szFrom && (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_HCONTACT))
+ pInfo->m_hContact = ppro->HContactFromJID( pInfo->m_szFrom, 3 );
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_ID_STR)
+ pInfo->m_szId = ( TCHAR* )xmlGetAttrValue( pNode, _T("id"));
+
+ (ppro->*(pInfo->m_pHandler))(pNode, pInfo);
+ delete pInfo;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CJabberIqManager::HandleIqPermanent( HXML pNode )
+{
+ BOOL bStopHandling = FALSE;
+ Lock();
+ CJabberIqPermanentInfo *pInfo = m_pPermanentHandlers;
+ while ( pInfo ) {
+ // have to get all data here, in the loop, because there's always possibility that previous handler modified it
+ const TCHAR *szType = xmlGetAttrValue( pNode, _T("type"));
+ if ( !szType )
+ break;
+
+ CJabberIqInfo iqInfo;
+
+ iqInfo.m_nIqType = JABBER_IQ_TYPE_FAIL;
+ if ( !_tcsicmp( szType, _T("get")))
+ iqInfo.m_nIqType = JABBER_IQ_TYPE_GET;
+ else if ( !_tcsicmp( szType, _T("set")))
+ iqInfo.m_nIqType = JABBER_IQ_TYPE_SET;
+ else
+ break;
+
+ if ( pInfo->m_nIqTypes & iqInfo.m_nIqType )
+ {
+ HXML pFirstChild = xmlGetChild( pNode , 0 );
+ if ( !pFirstChild || !xmlGetName( pFirstChild ))
+ break;
+
+ const TCHAR *szTagName = xmlGetName( pFirstChild );
+ const TCHAR *szXmlns = xmlGetAttrValue( pFirstChild, _T("xmlns"));
+
+ if ( (!pInfo->m_szXmlns || ( szXmlns && !_tcscmp( pInfo->m_szXmlns, szXmlns ))) &&
+ ( !pInfo->m_szTag || !_tcscmp( pInfo->m_szTag, szTagName ))) {
+ // node suits handler criteria, call the handler
+ iqInfo.m_pChildNode = pFirstChild;
+ iqInfo.m_szChildTagName = ( TCHAR* )szTagName;
+ iqInfo.m_szChildTagXmlns = ( TCHAR* )szXmlns;
+ iqInfo.m_szId = ( TCHAR* )xmlGetAttrValue( pNode, _T("id"));
+ iqInfo.m_pUserData = pInfo->m_pUserData;
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_TO)
+ iqInfo.m_szTo = ( TCHAR* )xmlGetAttrValue( pNode, _T("to"));
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_FROM)
+ iqInfo.m_szFrom = ( TCHAR* )xmlGetAttrValue( pNode, _T("from"));
+
+ if ((pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_HCONTACT) && (iqInfo.m_szFrom))
+ iqInfo.m_hContact = ppro->HContactFromJID( iqInfo.m_szFrom, 3 );
+
+ ppro->Log( "Handling iq id " TCHAR_STR_PARAM ", type " TCHAR_STR_PARAM ", from " TCHAR_STR_PARAM, iqInfo.m_szId, szType, iqInfo.m_szFrom );
+ if ((ppro->*(pInfo->m_pHandler))(pNode, &iqInfo)) {
+ bStopHandling = TRUE;
+ break;
+ }
+ }
+ }
+
+ pInfo = pInfo->m_pNext;
+ }
+ Unlock();
+
+ return bStopHandling;
+}
diff --git a/protocols/JabberG/src/jabber_iq.h b/protocols/JabberG/src/jabber_iq.h new file mode 100644 index 0000000000..6e145e7db0 --- /dev/null +++ b/protocols/JabberG/src/jabber_iq.h @@ -0,0 +1,526 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_IQ_H_
+#define _JABBER_IQ_H_
+
+#include "jabber_xml.h"
+
+class CJabberIqInfo;
+
+typedef enum {
+ IQ_PROC_NONE,
+ IQ_PROC_GETAGENTS,
+ IQ_PROC_GETREGISTER,
+ IQ_PROC_SETREGISTER,
+ IQ_PROC_GETVCARD,
+ IQ_PROC_SETVCARD,
+ IQ_PROC_GETSEARCH,
+ IQ_PROC_GETSEARCHFIELDS,
+ IQ_PROC_BROWSEROOMS,
+ IQ_PROC_DISCOROOMSERVER,
+ IQ_PROC_DISCOAGENTS,
+ IQ_PROC_DISCOBOOKMARKS,
+ IQ_PROC_SETBOOKMARKS,
+ IQ_PROC_DISCOCOMMANDS,
+ IQ_PROC_EXECCOMMANDS,
+} JABBER_IQ_PROCID;
+
+struct CJabberProto;
+typedef void ( CJabberProto::*JABBER_IQ_PFUNC )( HXML iqNode );
+typedef void ( *IQ_USER_DATA_FREE_FUNC )( void *pUserData );
+
+typedef struct {
+ TCHAR* xmlns;
+ JABBER_IQ_PFUNC func;
+ BOOL allowSubNs; // e.g. #info in disco#info
+} JABBER_IQ_XMLNS_FUNC;
+
+// 2 minutes, milliseconds
+#define JABBER_DEFAULT_IQ_REQUEST_TIMEOUT 120000
+
+typedef void ( CJabberProto::*JABBER_IQ_HANDLER )( HXML iqNode, CJabberIqInfo* pInfo );
+typedef BOOL ( CJabberProto::*JABBER_PERMANENT_IQ_HANDLER )( HXML iqNode, CJabberIqInfo* pInfo );
+
+#define JABBER_IQ_PARSE_CHILD_TAG_NODE (1)
+#define JABBER_IQ_PARSE_CHILD_TAG_NAME ((1<<1)|JABBER_IQ_PARSE_CHILD_TAG_NODE)
+#define JABBER_IQ_PARSE_CHILD_TAG_XMLNS ((1<<2)|JABBER_IQ_PARSE_CHILD_TAG_NODE)
+#define JABBER_IQ_PARSE_FROM (1<<3)
+#define JABBER_IQ_PARSE_HCONTACT ((1<<4)|JABBER_IQ_PARSE_FROM)
+#define JABBER_IQ_PARSE_TO (1<<5)
+#define JABBER_IQ_PARSE_ID_STR (1<<6)
+
+#define JABBER_IQ_PARSE_DEFAULT (JABBER_IQ_PARSE_CHILD_TAG_NODE|JABBER_IQ_PARSE_CHILD_TAG_NAME|JABBER_IQ_PARSE_CHILD_TAG_XMLNS)
+
+class CJabberIqInfo
+{
+protected:
+ friend class CJabberIqManager;
+ JABBER_IQ_HANDLER m_pHandler;
+ CJabberIqInfo* m_pNext;
+
+ int m_nIqId;
+ DWORD m_dwParamsToParse;
+ DWORD m_dwRequestTime;
+ DWORD m_dwTimeout;
+ TCHAR *m_szReceiver;
+ int m_iPriority;
+public:
+ void *m_pUserData;
+public:// parsed data
+ int m_nIqType;
+ TCHAR *m_szFrom;
+ TCHAR *m_szChildTagXmlns;
+ TCHAR *m_szChildTagName;
+ HXML m_pChildNode;
+ HANDLE m_hContact;
+ TCHAR *m_szTo;
+ TCHAR *m_szId;
+public:
+ CJabberIqInfo()
+ {
+ ZeroMemory(this, sizeof(CJabberIqInfo));
+ }
+ ~CJabberIqInfo()
+ {
+ if (m_szReceiver)
+ mir_free(m_szReceiver);
+ }
+ void SetReceiver(const TCHAR *szReceiver)
+ {
+ replaceStrT(m_szReceiver, szReceiver);
+ }
+ TCHAR* GetReceiver()
+ {
+ return m_szReceiver;
+ }
+ void SetParamsToParse(DWORD dwParamsToParse)
+ {
+ m_dwParamsToParse = dwParamsToParse;
+ }
+ void SetTimeout(DWORD dwTimeout)
+ {
+ m_dwTimeout = dwTimeout;
+ }
+ int GetIqId()
+ {
+ return m_nIqId;
+ }
+ DWORD GetRequestTime()
+ {
+ return m_dwRequestTime;
+ }
+ int GetIqType()
+ {
+ return m_nIqType;
+ }
+ void* GetUserData()
+ {
+ return m_pUserData;
+ }
+ TCHAR* GetFrom()
+ {
+ return m_szFrom;
+ }
+ TCHAR* GetTo()
+ {
+ return m_szTo;
+ }
+ TCHAR* GetIdStr()
+ {
+ return m_szId;
+ }
+ HANDLE GetHContact()
+ {
+ return m_hContact;
+ }
+ HXML GetChildNode()
+ {
+ return m_pChildNode;
+ }
+ TCHAR* GetChildNodeName()
+ {
+ return m_szChildTagName;
+ }
+ char* GetCharIqType()
+ {
+ switch (m_nIqType)
+ {
+ case JABBER_IQ_TYPE_SET: return "set";
+ case JABBER_IQ_TYPE_GET: return "get";
+ case JABBER_IQ_TYPE_ERROR: return "error";
+ case JABBER_IQ_TYPE_RESULT: return "result";
+ }
+ return NULL;
+ }
+};
+
+class CJabberIqPermanentInfo
+{
+ friend class CJabberIqManager;
+
+ CJabberIqPermanentInfo* m_pNext;
+
+ JABBER_PERMANENT_IQ_HANDLER m_pHandler;
+ DWORD m_dwParamsToParse;
+ int m_nIqTypes;
+ TCHAR* m_szXmlns;
+ TCHAR* m_szTag;
+ BOOL m_bAllowPartialNs;
+ void *m_pUserData;
+ IQ_USER_DATA_FREE_FUNC m_pUserDataFree;
+ int m_iPriority;
+public:
+ CJabberIqPermanentInfo()
+ {
+ ZeroMemory(this, sizeof(CJabberIqPermanentInfo));
+ }
+ ~CJabberIqPermanentInfo()
+ {
+ if ( m_pUserDataFree )
+ m_pUserDataFree(m_pUserData);
+ mir_free(m_szXmlns);
+ mir_free(m_szTag);
+ }
+};
+
+class CJabberIqManager
+{
+protected:
+ CJabberProto* ppro;
+ CRITICAL_SECTION m_cs;
+ DWORD m_dwLastUsedHandle;
+ CJabberIqInfo* m_pIqs; // list of iqs ordered by priority
+ HANDLE m_hExpirerThread;
+ BOOL m_bExpirerThreadShutdownRequest;
+
+ CJabberIqPermanentInfo* m_pPermanentHandlers;
+
+ CJabberIqInfo* DetachInfo(int nIqId)
+ {
+ if (!m_pIqs)
+ return NULL;
+
+ CJabberIqInfo* pInfo = m_pIqs;
+ if (m_pIqs->m_nIqId == nIqId)
+ {
+ m_pIqs = pInfo->m_pNext;
+ pInfo->m_pNext = NULL;
+ return pInfo;
+ }
+
+ while (pInfo->m_pNext)
+ {
+ if (pInfo->m_pNext->m_nIqId == nIqId)
+ {
+ CJabberIqInfo* pRetVal = pInfo->m_pNext;
+ pInfo->m_pNext = pInfo->m_pNext->m_pNext;
+ pRetVal->m_pNext = NULL;
+ return pRetVal;
+ }
+ pInfo = pInfo->m_pNext;
+ }
+ return NULL;
+ }
+ CJabberIqInfo* DetachInfo(void *pUserData)
+ {
+ if (!m_pIqs)
+ return NULL;
+
+ CJabberIqInfo* pInfo = m_pIqs;
+ if (m_pIqs->m_pUserData == pUserData)
+ {
+ m_pIqs = pInfo->m_pNext;
+ pInfo->m_pNext = NULL;
+ return pInfo;
+ }
+
+ while (pInfo->m_pNext)
+ {
+ if (pInfo->m_pNext->m_pUserData == pUserData)
+ {
+ CJabberIqInfo* pRetVal = pInfo->m_pNext;
+ pInfo->m_pNext = pInfo->m_pNext->m_pNext;
+ pRetVal->m_pNext = NULL;
+ return pRetVal;
+ }
+ pInfo = pInfo->m_pNext;
+ }
+ return NULL;
+ }
+ CJabberIqInfo* DetachExpired()
+ {
+ if (!m_pIqs)
+ return NULL;
+
+ DWORD dwCurrentTime = GetTickCount();
+
+ CJabberIqInfo* pInfo = m_pIqs;
+ if (dwCurrentTime - pInfo->m_dwRequestTime > pInfo->m_dwTimeout )
+ {
+ m_pIqs = pInfo->m_pNext;
+ pInfo->m_pNext = NULL;
+ return pInfo;
+ }
+
+ while (pInfo->m_pNext)
+ {
+ if (dwCurrentTime - pInfo->m_pNext->m_dwRequestTime > pInfo->m_pNext->m_dwTimeout )
+ {
+ CJabberIqInfo* pRetVal = pInfo->m_pNext;
+ pInfo->m_pNext = pInfo->m_pNext->m_pNext;
+ pRetVal->m_pNext = NULL;
+ return pRetVal;
+ }
+ pInfo = pInfo->m_pNext;
+ }
+ return NULL;
+ }
+ void ExpireInfo( CJabberIqInfo* pInfo, void *pUserData = NULL );
+ BOOL InsertIq(CJabberIqInfo* pInfo)
+ { // inserts pInfo at a place determined by pInfo->m_iPriority
+ Lock();
+ if (!m_pIqs)
+ m_pIqs = pInfo;
+ else
+ {
+ if (m_pIqs->m_iPriority > pInfo->m_iPriority) {
+ pInfo->m_pNext = m_pIqs;
+ m_pIqs = pInfo;
+ } else
+ {
+ CJabberIqInfo* pTmp = m_pIqs;
+ while (pTmp->m_pNext && pTmp->m_pNext->m_iPriority <= pInfo->m_iPriority)
+ pTmp = pTmp->m_pNext;
+ pInfo->m_pNext = pTmp->m_pNext;
+ pTmp->m_pNext = pInfo;
+ }
+ }
+ Unlock();
+ return TRUE;
+ }
+public:
+ CJabberIqManager( CJabberProto* proto )
+ {
+ InitializeCriticalSection(&m_cs);
+ m_dwLastUsedHandle = 0;
+ m_pIqs = NULL;
+ m_hExpirerThread = NULL;
+ m_pPermanentHandlers = NULL;
+ ppro = proto;
+ }
+ ~CJabberIqManager()
+ {
+ ExpireAll();
+ Lock();
+ CJabberIqPermanentInfo *pInfo = m_pPermanentHandlers;
+ while ( pInfo )
+ {
+ CJabberIqPermanentInfo *pTmp = pInfo->m_pNext;
+ delete pInfo;
+ pInfo = pTmp;
+ }
+ m_pPermanentHandlers = NULL;
+ Unlock();
+ DeleteCriticalSection(&m_cs);
+ }
+ BOOL Start();
+ BOOL Shutdown()
+ {
+ if ( m_bExpirerThreadShutdownRequest || !m_hExpirerThread )
+ return TRUE;
+
+ m_bExpirerThreadShutdownRequest = TRUE;
+
+ WaitForSingleObject( m_hExpirerThread, INFINITE );
+ CloseHandle( m_hExpirerThread );
+ m_hExpirerThread = NULL;
+
+ return TRUE;
+ }
+ void Lock()
+ {
+ EnterCriticalSection(&m_cs);
+ }
+ void Unlock()
+ {
+ LeaveCriticalSection(&m_cs);
+ }
+ // fucking params, maybe just return CJabberIqRequestInfo pointer ?
+ CJabberIqInfo* AddHandler(JABBER_IQ_HANDLER pHandler, int nIqType = JABBER_IQ_TYPE_GET, const TCHAR *szReceiver = NULL, DWORD dwParamsToParse = 0, int nIqId = -1, void *pUserData = NULL, int iPriority = JH_PRIORITY_DEFAULT);
+ CJabberIqPermanentInfo* AddPermanentHandler(JABBER_PERMANENT_IQ_HANDLER pHandler, int nIqTypes, DWORD dwParamsToParse, const TCHAR* szXmlns, BOOL bAllowPartialNs, const TCHAR* szTag, void *pUserData = NULL, IQ_USER_DATA_FREE_FUNC pUserDataFree = NULL, int iPriority = JH_PRIORITY_DEFAULT)
+ {
+ CJabberIqPermanentInfo* pInfo = new CJabberIqPermanentInfo();
+ if (!pInfo)
+ return NULL;
+
+ pInfo->m_pHandler = pHandler;
+ pInfo->m_nIqTypes = nIqTypes ? nIqTypes : JABBER_IQ_TYPE_ANY;
+ replaceStrT( pInfo->m_szXmlns, szXmlns );
+ pInfo->m_bAllowPartialNs = bAllowPartialNs;
+ replaceStrT( pInfo->m_szTag, szTag );
+ pInfo->m_dwParamsToParse = dwParamsToParse;
+ pInfo->m_pUserData = pUserData;
+ pInfo->m_pUserDataFree = pUserDataFree;
+ pInfo->m_iPriority = iPriority;
+
+ Lock();
+ if (!m_pPermanentHandlers)
+ m_pPermanentHandlers = pInfo;
+ else
+ {
+ if (m_pPermanentHandlers->m_iPriority > pInfo->m_iPriority) {
+ pInfo->m_pNext = m_pPermanentHandlers;
+ m_pPermanentHandlers = pInfo;
+ } else
+ {
+ CJabberIqPermanentInfo* pTmp = m_pPermanentHandlers;
+ while (pTmp->m_pNext && pTmp->m_pNext->m_iPriority <= pInfo->m_iPriority)
+ pTmp = pTmp->m_pNext;
+ pInfo->m_pNext = pTmp->m_pNext;
+ pTmp->m_pNext = pInfo;
+ }
+ }
+ Unlock();
+
+ return pInfo;
+ }
+ BOOL DeletePermanentHandler(CJabberIqPermanentInfo *pInfo)
+ { // returns TRUE when pInfo found, or FALSE otherwise
+ Lock();
+ if (!m_pPermanentHandlers)
+ {
+ Unlock();
+ return FALSE;
+ }
+ if (m_pPermanentHandlers == pInfo) // check first item
+ {
+ m_pPermanentHandlers = m_pPermanentHandlers->m_pNext;
+ delete pInfo;
+ Unlock();
+ return TRUE;
+ } else
+ {
+ CJabberIqPermanentInfo* pTmp = m_pPermanentHandlers;
+ while (pTmp->m_pNext)
+ {
+ if (pTmp->m_pNext == pInfo)
+ {
+ pTmp->m_pNext = pTmp->m_pNext->m_pNext;
+ delete pInfo;
+ Unlock();
+ return TRUE;
+ }
+ pTmp = pTmp->m_pNext;
+ }
+ }
+ Unlock();
+ return FALSE;
+ }
+ BOOL DeleteHandler(CJabberIqInfo *pInfo)
+ { // returns TRUE when pInfo found, or FALSE otherwise
+ Lock();
+ if (!m_pIqs)
+ {
+ Unlock();
+ return FALSE;
+ }
+ if (m_pIqs == pInfo) // check first item
+ {
+ m_pIqs = m_pIqs->m_pNext;
+ Unlock();
+ ExpireInfo(pInfo); // must expire it to allow the handler to free m_pUserData if necessary
+ delete pInfo;
+ return TRUE;
+ } else
+ {
+ CJabberIqInfo* pTmp = m_pIqs;
+ while (pTmp->m_pNext)
+ {
+ if (pTmp->m_pNext == pInfo)
+ {
+ pTmp->m_pNext = pTmp->m_pNext->m_pNext;
+ Unlock();
+ ExpireInfo(pInfo); // must expire it to allow the handler to free m_pUserData if necessary
+ delete pInfo;
+ return TRUE;
+ }
+ pTmp = pTmp->m_pNext;
+ }
+ }
+ Unlock();
+ return FALSE;
+ }
+ BOOL HandleIq(int nIqId, HXML pNode);
+ BOOL HandleIqPermanent(HXML pNode);
+ BOOL ExpireIq(int nIqId)
+ {
+ Lock();
+ CJabberIqInfo* pInfo = DetachInfo(nIqId);
+ Unlock();
+ if (pInfo)
+ {
+ ExpireInfo(pInfo);
+ delete pInfo;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ void ExpirerThread( void );
+ BOOL ExpireByUserData(void *pUserData)
+ {
+ BOOL bRetVal = FALSE;
+ while (1)
+ {
+ Lock();
+ CJabberIqInfo* pInfo = DetachInfo(pUserData);
+ Unlock();
+ if (!pInfo)
+ break;
+ ExpireInfo(pInfo, NULL);
+ delete pInfo;
+ bRetVal = TRUE;
+ }
+ return bRetVal;
+ }
+ BOOL ExpireAll(void *pUserData = NULL)
+ {
+ while (1)
+ {
+ Lock();
+ CJabberIqInfo* pInfo = m_pIqs;
+ if (pInfo)
+ m_pIqs = m_pIqs->m_pNext;
+ Unlock();
+ if (!pInfo)
+ break;
+ pInfo->m_pNext = NULL;
+ ExpireInfo(pInfo, pUserData);
+ delete pInfo;
+ }
+ return TRUE;
+ }
+ BOOL FillPermanentHandlers();
+};
+
+#endif
diff --git a/protocols/JabberG/src/jabber_iq_handlers.cpp b/protocols/JabberG/src/jabber_iq_handlers.cpp new file mode 100644 index 0000000000..7f3385ff6e --- /dev/null +++ b/protocols/JabberG/src/jabber_iq_handlers.cpp @@ -0,0 +1,775 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+#include <io.h>
+#include "version.h"
+#include "jabber_iq.h"
+#include "jabber_rc.h"
+
+extern int bSecureIM;
+
+#ifndef VER_SUITE_WH_SERVER
+ #define VER_SUITE_WH_SERVER 0x00008000
+#endif
+
+#ifndef PRODUCT_ULTIMATE
+ #define PRODUCT_UNDEFINED 0x00000000
+ #define PRODUCT_ULTIMATE 0x00000001
+ #define PRODUCT_HOME_BASIC 0x00000002
+ #define PRODUCT_HOME_PREMIUM 0x00000003
+ #define PRODUCT_ENTERPRISE 0x00000004
+ #define PRODUCT_HOME_BASIC_N 0x00000005
+ #define PRODUCT_BUSINESS 0x00000006
+ #define PRODUCT_STANDARD_SERVER 0x00000007
+ #define PRODUCT_DATACENTER_SERVER 0x00000008
+ #define PRODUCT_SMALLBUSINESS_SERVER 0x00000009
+ #define PRODUCT_ENTERPRISE_SERVER 0x0000000A
+ #define PRODUCT_STARTER 0x0000000B
+ #define PRODUCT_DATACENTER_SERVER_CORE 0x0000000C
+ #define PRODUCT_STANDARD_SERVER_CORE 0x0000000D
+ #define PRODUCT_ENTERPRISE_SERVER_CORE 0x0000000E
+ #define PRODUCT_ENTERPRISE_SERVER_IA64 0x0000000F
+ #define PRODUCT_BUSINESS_N 0x00000010
+ #define PRODUCT_WEB_SERVER 0x00000011
+ #define PRODUCT_CLUSTER_SERVER 0x00000012
+ #define PRODUCT_HOME_SERVER 0x00000013
+ #define PRODUCT_STORAGE_EXPRESS_SERVER 0x00000014
+ #define PRODUCT_STORAGE_STANDARD_SERVER 0x00000015
+ #define PRODUCT_STORAGE_WORKGROUP_SERVER 0x00000016
+ #define PRODUCT_STORAGE_ENTERPRISE_SERVER 0x00000017
+ #define PRODUCT_SERVER_FOR_SMALLBUSINESS 0x00000018
+ #define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM 0x00000019
+ #define PRODUCT_UNLICENSED 0xABCDABCD
+#endif
+
+typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
+typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
+
+#define StringCchCopy(x,y,z) lstrcpyn((x),(z),(y))
+#define StringCchCat(x,y,z) lstrcat((x),(z))
+#define StringCchPrintf mir_sntprintf
+
+// slightly modified sample from MSDN
+BOOL GetOSDisplayString(LPTSTR pszOS, int BUFSIZE)
+{
+ OSVERSIONINFOEX osvi;
+ SYSTEM_INFO si;
+ PGNSI pGNSI;
+ PGPI pGPI;
+
+ DWORD dwType;
+
+ ZeroMemory(&si, sizeof(SYSTEM_INFO));
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ BOOL bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi);
+ if ( !bOsVersionInfoEx )
+ {
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!GetVersionEx((OSVERSIONINFO*)&osvi))
+ return FALSE;
+ }
+
+ // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
+ HMODULE hKernel = GetModuleHandle(TEXT("kernel32.dll"));
+ pGNSI = (PGNSI) GetProcAddress(hKernel,"GetNativeSystemInfo");
+ if(NULL != pGNSI)
+ pGNSI(&si);
+ else GetSystemInfo(&si);
+
+ //Some code from Crash Dumper Plugin :-)
+ if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && osvi.dwMajorVersion > 4 )
+ {
+ StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft "));
+
+ // Test for the specific product.
+ if (osvi.dwMajorVersion == 6)
+ {
+ switch (osvi.dwMinorVersion)
+ {
+ case 0:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista "));
+ else
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 "));
+ break;
+
+ case 1:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 7 "));
+ else
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 "));
+ break;
+
+ default:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8 "));
+ else
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 "));
+ break;
+ }
+
+ pGPI = (PGPI) GetProcAddress(hKernel, "GetProductInfo");
+ if(pGPI != NULL) pGPI( 6, 0, 0, 0, &dwType);
+
+ switch( dwType )
+ {
+ case PRODUCT_ULTIMATE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Ultimate Edition" ));
+ break;
+ case PRODUCT_HOME_PREMIUM:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Home Premium Edition" ));
+ break;
+ case PRODUCT_HOME_BASIC:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Home Basic Edition" ));
+ break;
+ case PRODUCT_ENTERPRISE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
+ break;
+ case PRODUCT_BUSINESS:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Business Edition" ));
+ break;
+ case PRODUCT_STARTER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Starter Edition" ));
+ break;
+ case PRODUCT_CLUSTER_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Cluster Server Edition" ));
+ break;
+ case PRODUCT_DATACENTER_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition" ));
+ break;
+ case PRODUCT_DATACENTER_SERVER_CORE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition (core installation)" ));
+ break;
+ case PRODUCT_ENTERPRISE_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
+ break;
+ case PRODUCT_ENTERPRISE_SERVER_CORE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition (core installation)" ));
+ break;
+ case PRODUCT_ENTERPRISE_SERVER_IA64:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition for Itanium-based Systems" ));
+ break;
+ case PRODUCT_SMALLBUSINESS_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server" ));
+ break;
+ case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server Premium Edition" ));
+ break;
+ case PRODUCT_STANDARD_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition" ));
+ break;
+ case PRODUCT_STANDARD_SERVER_CORE:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition (core installation)" ));
+ break;
+ case PRODUCT_WEB_SERVER:
+ StringCchCat(pszOS, BUFSIZE, TEXT("Web Server Edition" ));
+ break;
+ }
+ if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
+ StringCchCat(pszOS, BUFSIZE, TEXT( ", 64-bit" ));
+ else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL )
+ StringCchCat(pszOS, BUFSIZE, TEXT(", 32-bit"));
+ }
+
+ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
+ {
+ if ( GetSystemMetrics(SM_SERVERR2))
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Server 2003 R2, "));
+ else if ( osvi.wSuiteMask==VER_SUITE_STORAGE_SERVER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Storage Server 2003"));
+ else if ( osvi.wSuiteMask==VER_SUITE_WH_SERVER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Home Server"));
+ else if ( osvi.wProductType == VER_NT_WORKSTATION &&
+ si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Windows XP Professional x64 Edition"));
+ }
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2003, "));
+
+ // Test for the server type.
+ if ( osvi.wProductType != VER_NT_WORKSTATION )
+ {
+ if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 )
+ {
+ if ( osvi.wSuiteMask & VER_SUITE_DATACENTER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition for Itanium-based Systems" ));
+ else if ( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition for Itanium-based Systems" ));
+ }
+
+ else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
+ {
+ if ( osvi.wSuiteMask & VER_SUITE_DATACENTER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter x64 Edition" ));
+ else if ( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise x64 Edition" ));
+ else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard x64 Edition" ));
+ }
+
+ else
+ {
+ if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Compute Cluster Edition" ));
+ else if ( osvi.wSuiteMask & VER_SUITE_DATACENTER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition" ));
+ else if ( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition" ));
+ else if ( osvi.wSuiteMask & VER_SUITE_BLADE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Web Edition" ));
+ else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard Edition" ));
+ }
+ }
+ }
+
+ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows XP "));
+ if ( osvi.wSuiteMask & VER_SUITE_PERSONAL )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Home Edition" ));
+ else StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
+ }
+
+ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 2000 "));
+
+ if ( osvi.wProductType == VER_NT_WORKSTATION )
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
+ }
+ else
+ {
+ if ( osvi.wSuiteMask & VER_SUITE_DATACENTER )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Server" ));
+ else if ( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
+ StringCchCat(pszOS, BUFSIZE, TEXT( "Advanced Server" ));
+ else StringCchCat(pszOS, BUFSIZE, TEXT( "Server" ));
+ }
+ }
+
+ // Include service pack (if any) and build number.
+
+ if ( _tcslen(osvi.szCSDVersion) > 0 )
+ {
+ StringCchCat(pszOS, BUFSIZE, TEXT(" "));
+ StringCchCat(pszOS, BUFSIZE, osvi.szCSDVersion);
+ }
+
+ TCHAR buf[80];
+
+ StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber);
+ StringCchCat(pszOS, BUFSIZE, buf);
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+BOOL CJabberProto::OnIqRequestVersion( HXML, CJabberIqInfo* pInfo )
+{
+ if ( !pInfo->GetFrom())
+ return TRUE;
+
+ if ( !m_options.AllowVersionRequests )
+ return FALSE;
+
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_VERSION));
+ query << XCHILD( _T("name"), _T("Miranda NG Jabber"));
+ query << XCHILD( _T("version"), szCoreVersion);
+
+ if ( m_options.ShowOSVersion )
+ {
+ TCHAR os[256] = {0};
+ if (!GetOSDisplayString(os, SIZEOF(os)))
+ lstrcpyn(os, _T("Microsoft Windows"), SIZEOF(os));
+ query << XCHILD( _T("os"), os );
+ }
+
+ m_ThreadInfo->send( iq );
+ return TRUE;
+}
+
+// last activity (XEP-0012) support
+BOOL CJabberProto::OnIqRequestLastActivity( HXML, CJabberIqInfo *pInfo )
+{
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), pInfo ) << XQUERY( _T(JABBER_FEAT_LAST_ACTIVITY))
+ << XATTRI( _T("seconds"), m_tmJabberIdleStartTime ? time( 0 ) - m_tmJabberIdleStartTime : 0 ));
+ return TRUE;
+}
+
+// XEP-0199: XMPP Ping support
+BOOL CJabberProto::OnIqRequestPing( HXML, CJabberIqInfo *pInfo )
+{
+ m_ThreadInfo->send( XmlNodeIq( _T("result"), pInfo ) << XATTR( _T("from"), m_ThreadInfo->fullJID ));
+ return TRUE;
+}
+
+// Returns the current GMT offset in seconds
+int GetGMTOffset(void)
+{
+ TIME_ZONE_INFORMATION tzinfo;
+ int nOffset = 0;
+
+ DWORD dwResult= GetTimeZoneInformation(&tzinfo);
+
+ switch(dwResult) {
+ case TIME_ZONE_ID_STANDARD:
+ nOffset = tzinfo.Bias + tzinfo.StandardBias;
+ break;
+ case TIME_ZONE_ID_DAYLIGHT:
+ nOffset = tzinfo.Bias + tzinfo.DaylightBias;
+ break;
+ case TIME_ZONE_ID_UNKNOWN:
+ nOffset = tzinfo.Bias;
+ break;
+ case TIME_ZONE_ID_INVALID:
+ default:
+ nOffset = 0;
+ break;
+ }
+
+ return -nOffset;
+}
+
+// entity time (XEP-0202) support
+BOOL CJabberProto::OnIqRequestTime( HXML, CJabberIqInfo *pInfo )
+{
+ TCHAR stime[100];
+ TCHAR szTZ[10];
+
+ tmi.printDateTime(UTC_TIME_HANDLE, _T("I"), stime, SIZEOF(stime), 0);
+
+ int nGmtOffset = GetGMTOffset();
+ mir_sntprintf(szTZ, SIZEOF(szTZ), _T("%+03d:%02d"), nGmtOffset / 60, nGmtOffset % 60 );
+
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML timeNode = iq << XCHILDNS( _T("time"), _T(JABBER_FEAT_ENTITY_TIME));
+ timeNode << XCHILD( _T("utc"), stime); timeNode << XCHILD( _T("tzo"), szTZ );
+ LPCTSTR szTZName = tmi.getTzName( NULL );
+ if ( szTZName )
+ timeNode << XCHILD( _T("tz"), szTZName );
+ m_ThreadInfo->send( iq );
+ return TRUE;
+}
+
+BOOL CJabberProto::OnIqProcessIqOldTime( HXML, CJabberIqInfo *pInfo )
+{
+ struct tm *gmt;
+ time_t ltime;
+ TCHAR stime[ 100 ], *dtime;
+
+ _tzset();
+ time( <ime );
+ gmt = gmtime( <ime );
+ mir_sntprintf( stime, SIZEOF(stime), _T("%.4i%.2i%.2iT%.2i:%.2i:%.2i"),
+ gmt->tm_year + 1900, gmt->tm_mon + 1,
+ gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec );
+ dtime = _tctime( <ime );
+ dtime[ 24 ] = 0;
+
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML queryNode = iq << XQUERY( _T(JABBER_FEAT_ENTITY_TIME_OLD));
+ queryNode << XCHILD( _T("utc"), stime );
+ LPCTSTR szTZName = tmi.getTzName( NULL );
+ if ( szTZName )
+ queryNode << XCHILD( _T("tz"), szTZName );
+ queryNode << XCHILD( _T("display"), dtime );
+ m_ThreadInfo->send( iq );
+ return TRUE;
+}
+
+BOOL CJabberProto::OnIqRequestAvatar( HXML, CJabberIqInfo *pInfo )
+{
+ if ( !m_options.EnableAvatars )
+ return TRUE;
+
+ int pictureType = m_options.AvatarType;
+ if ( pictureType == PA_FORMAT_UNKNOWN )
+ return TRUE;
+
+ TCHAR* szMimeType;
+ switch( pictureType ) {
+ case PA_FORMAT_JPEG: szMimeType = _T("image/jpeg"); break;
+ case PA_FORMAT_GIF: szMimeType = _T("image/gif"); break;
+ case PA_FORMAT_PNG: szMimeType = _T("image/png"); break;
+ case PA_FORMAT_BMP: szMimeType = _T("image/bmp"); break;
+ default: return TRUE;
+ }
+
+ TCHAR szFileName[ MAX_PATH ];
+ GetAvatarFileName( NULL, szFileName, SIZEOF(szFileName));
+
+ FILE* in = _tfopen( szFileName, _T("rb"));
+ if ( in == NULL )
+ return TRUE;
+
+ long bytes = _filelength( _fileno( in ));
+ char* buffer = ( char* )mir_alloc( bytes*4/3 + bytes + 1000 );
+ if ( buffer == NULL ) {
+ fclose( in );
+ return TRUE;
+ }
+
+ fread( buffer, bytes, 1, in );
+ fclose( in );
+
+ char* str = JabberBase64Encode( buffer, bytes );
+ m_ThreadInfo->send( XmlNodeIq( _T("result"), pInfo ) << XQUERY( _T(JABBER_FEAT_AVATAR)) << XCHILD( _T("query"), _A2T(str)) << XATTR( _T("mimetype"), szMimeType ));
+ mir_free( str );
+ mir_free( buffer );
+ return TRUE;
+}
+
+BOOL CJabberProto::OnSiRequest( HXML node, CJabberIqInfo *pInfo )
+{
+ const TCHAR* szProfile = xmlGetAttrValue( pInfo->GetChildNode(), _T("profile"));
+
+ if ( szProfile && !_tcscmp( szProfile, _T(JABBER_FEAT_SI_FT)))
+ FtHandleSiRequest( node );
+ else {
+ XmlNodeIq iq( _T("error"), pInfo );
+ HXML error = iq << XCHILD( _T("error")) << XATTRI( _T("code"), 400 ) << XATTR( _T("type"), _T("cancel"));
+ error << XCHILDNS( _T("bad-request"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"));
+ error << XCHILD( _T("bad-profile"));
+ m_ThreadInfo->send( iq );
+ }
+ return TRUE;
+}
+
+BOOL CJabberProto::OnRosterPushRequest( HXML, CJabberIqInfo *pInfo )
+{
+ HXML queryNode = pInfo->GetChildNode();
+
+ // RFC 3921 #7.2 Business Rules
+ if ( pInfo->GetFrom()) {
+ TCHAR* szFrom = JabberPrepareJid( pInfo->GetFrom());
+ if ( !szFrom )
+ return TRUE;
+
+ TCHAR* szTo = JabberPrepareJid( m_ThreadInfo->fullJID );
+ if ( !szTo ) {
+ mir_free( szFrom );
+ return TRUE;
+ }
+
+ TCHAR* pDelimiter = _tcschr( szFrom, _T('/'));
+ if ( pDelimiter ) *pDelimiter = _T('\0');
+
+ pDelimiter = _tcschr( szTo, _T('/'));
+ if ( pDelimiter ) *pDelimiter = _T('\0');
+
+ BOOL bRetVal = _tcscmp( szFrom, szTo ) == 0;
+
+ mir_free( szFrom );
+ mir_free( szTo );
+
+ // invalid JID
+ if ( !bRetVal ) {
+ Log( "<iq/> attempt to hack via roster push from " TCHAR_STR_PARAM, pInfo->GetFrom());
+ return TRUE;
+ }
+ }
+
+ JABBER_LIST_ITEM *item;
+ HANDLE hContact = NULL;
+ const TCHAR *jid, *str, *name;
+ TCHAR* nick;
+
+ Log( "<iq/> Got roster push, query has %d children", xmlGetChildCount( queryNode ));
+ for ( int i=0; ; i++ ) {
+ HXML itemNode = xmlGetChild( queryNode ,i);
+ if ( !itemNode )
+ break;
+
+ if ( _tcscmp( xmlGetName( itemNode ), _T("item")) != 0 )
+ continue;
+ if (( jid = xmlGetAttrValue( itemNode, _T("jid"))) == NULL )
+ continue;
+ if (( str = xmlGetAttrValue( itemNode, _T("subscription"))) == NULL )
+ continue;
+
+ // we will not add new account when subscription=remove
+ if ( !_tcscmp( str, _T("to")) || !_tcscmp( str, _T("both")) || !_tcscmp( str, _T("from")) || !_tcscmp( str, _T("none"))) {
+ if (( name = xmlGetAttrValue( itemNode, _T("name"))) != NULL )
+ nick = mir_tstrdup( name );
+ else
+ nick = JabberNickFromJID( jid );
+
+ if ( nick != NULL ) {
+ if (( item=ListAdd( LIST_ROSTER, jid )) != NULL ) {
+ replaceStrT( item->nick, nick );
+
+ HXML groupNode = xmlGetChild( itemNode , "group" );
+ replaceStrT( item->group, ( groupNode ) ? xmlGetText( groupNode ) : NULL );
+
+ if (( hContact=HContactFromJID( jid, 0 )) == NULL ) {
+ // Received roster has a new JID.
+ // Add the jid ( with empty resource ) to Miranda contact list.
+ hContact = DBCreateContact( jid, nick, FALSE, FALSE );
+ }
+ else JSetStringT( hContact, "jid", jid );
+
+ if ( name != NULL ) {
+ DBVARIANT dbnick;
+ if ( !JGetStringT( hContact, "Nick", &dbnick )) {
+ if ( _tcscmp( nick, dbnick.ptszVal ) != 0 )
+ DBWriteContactSettingTString( hContact, "CList", "MyHandle", nick );
+ else
+ DBDeleteContactSetting( hContact, "CList", "MyHandle" );
+
+ JFreeVariant( &dbnick );
+ }
+ else DBWriteContactSettingTString( hContact, "CList", "MyHandle", nick );
+ }
+ else DBDeleteContactSetting( hContact, "CList", "MyHandle" );
+
+ if (!m_options.IgnoreRosterGroups)
+ {
+ if ( item->group != NULL ) {
+ JabberContactListCreateGroup( item->group );
+ DBWriteContactSettingTString( hContact, "CList", "Group", item->group );
+ }
+ else
+ DBDeleteContactSetting( hContact, "CList", "Group" );
+ }
+ }
+ mir_free( nick );
+ } }
+
+ if (( item=ListGetItemPtr( LIST_ROSTER, jid )) != NULL ) {
+ if ( !_tcscmp( str, _T("both"))) item->subscription = SUB_BOTH;
+ else if ( !_tcscmp( str, _T("to"))) item->subscription = SUB_TO;
+ else if ( !_tcscmp( str, _T("from"))) item->subscription = SUB_FROM;
+ else item->subscription = SUB_NONE;
+ Log( "Roster push for jid=" TCHAR_STR_PARAM ", set subscription to " TCHAR_STR_PARAM, jid, str );
+ // subscription = remove is to remove from roster list
+ // but we will just set the contact to offline and not actually
+ // remove, so that history will be retained.
+ if ( !_tcscmp( str, _T("remove"))) {
+ if (( hContact=HContactFromJID( jid )) != NULL ) {
+ SetContactOfflineStatus( hContact );
+ ListRemove( LIST_ROSTER, jid );
+ } }
+ else if ( JGetByte( hContact, "ChatRoom", 0 ))
+ DBDeleteContactSetting( hContact, "CList", "Hidden" );
+ else
+ UpdateSubscriptionInfo( hContact, item );
+ } }
+
+ UI_SAFE_NOTIFY(m_pDlgServiceDiscovery, WM_JABBER_TRANSPORT_REFRESH);
+ RebuildInfoFrame();
+ return TRUE;
+}
+
+BOOL CJabberProto::OnIqRequestOOB( HXML, CJabberIqInfo *pInfo )
+{
+ if ( !pInfo->GetFrom() || !pInfo->GetHContact())
+ return TRUE;
+
+ HXML n = xmlGetChild( pInfo->GetChildNode(), "url" );
+ if ( !n || !xmlGetText( n ))
+ return TRUE;
+
+ if ( m_options.BsOnlyIBB ) {
+ // reject
+ XmlNodeIq iq( _T("error"), pInfo );
+ HXML e = xmlAddChild( iq, _T("error"), _T("File transfer refused")); xmlAddAttr( e, _T("code"), 406 );
+ m_ThreadInfo->send( iq );
+ return TRUE;
+ }
+
+ TCHAR text[ 1024 ];
+ TCHAR *str, *p, *q;
+
+ str = ( TCHAR* )xmlGetText( n ); // URL of the file to get
+ filetransfer* ft = new filetransfer( this );
+ ft->std.totalFiles = 1;
+ ft->jid = mir_tstrdup( pInfo->GetFrom());
+ ft->std.hContact = pInfo->GetHContact();
+ ft->type = FT_OOB;
+ ft->httpHostName = NULL;
+ ft->httpPort = 80;
+ ft->httpPath = NULL;
+
+ // Parse the URL
+ if ( !_tcsnicmp( str, _T("http://"), 7 )) {
+ p = str + 7;
+ if (( q = _tcschr( p, '/' )) != NULL ) {
+ if ( q-p < SIZEOF( text )) {
+ _tcsncpy( text, p, q-p );
+ text[q-p] = '\0';
+ if (( p = _tcschr( text, ':' )) != NULL ) {
+ ft->httpPort = ( WORD )_ttoi( p+1 );
+ *p = '\0';
+ }
+ ft->httpHostName = mir_t2a( text );
+ } } }
+
+ if ( pInfo->GetIdStr())
+ ft->iqId = mir_tstrdup( pInfo->GetIdStr());
+
+ if ( ft->httpHostName && ft->httpPath ) {
+ TCHAR* desc = NULL;
+
+ Log( "Host=%s Port=%d Path=%s", ft->httpHostName, ft->httpPort, ft->httpPath );
+ if (( n = xmlGetChild( pInfo->GetChildNode(), "desc" )) != NULL )
+ desc = ( TCHAR* )xmlGetText( n );
+
+ TCHAR* str2;
+ Log( "description = %s", desc );
+ if (( str2 = _tcsrchr( ft->httpPath, '/' )) != NULL )
+ str2++;
+ else
+ str2 = ft->httpPath;
+ str2 = mir_tstrdup( str2 );
+ JabberHttpUrlDecode( str2 );
+
+ PROTORECVFILET pre;
+ pre.flags = PREF_TCHAR;
+ pre.timestamp = time( NULL );
+ pre.tszDescription = desc;
+ pre.ptszFiles = &str2;
+ pre.fileCount = 1;
+ pre.lParam = ( LPARAM )ft;
+
+ CCSDATA ccs = { ft->std.hContact, PSR_FILE, 0, ( LPARAM )&pre };
+ CallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs );
+ mir_free( str2 );
+ }
+ else {
+ // reject
+ XmlNodeIq iq( _T("error"), pInfo );
+ HXML e = xmlAddChild( iq, _T("error"), _T("File transfer refused")); xmlAddAttr( e, _T("code"), 406 );
+ m_ThreadInfo->send( iq );
+ delete ft;
+ }
+ return TRUE;
+}
+
+BOOL CJabberProto::OnHandleDiscoInfoRequest( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if ( !pInfo->GetChildNode())
+ return TRUE;
+
+ const TCHAR* szNode = xmlGetAttrValue( pInfo->GetChildNode(), _T("node"));
+ // caps hack
+ if ( m_clientCapsManager.HandleInfoRequest( iqNode, pInfo, szNode ))
+ return TRUE;
+
+ // ad-hoc hack:
+ if ( szNode && m_adhocManager.HandleInfoRequest( iqNode, pInfo, szNode ))
+ return TRUE;
+
+ // another request, send empty result
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("error"), pInfo )
+ << XCHILD( _T("error")) << XATTRI( _T("code"), 404 ) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+ return TRUE;
+}
+
+BOOL CJabberProto::OnHandleDiscoItemsRequest( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if ( !pInfo->GetChildNode())
+ return TRUE;
+
+ // ad-hoc commands check:
+ const TCHAR* szNode = xmlGetAttrValue( pInfo->GetChildNode(), _T("node"));
+ if ( szNode && m_adhocManager.HandleItemsRequest( iqNode, pInfo, szNode ))
+ return TRUE;
+
+ // another request, send empty result
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML resultQuery = iq << XQUERY( _T(JABBER_FEAT_DISCO_ITEMS));
+ if ( szNode )
+ xmlAddAttr( resultQuery, _T("node"), szNode );
+
+ if ( !szNode && m_options.EnableRemoteControl )
+ resultQuery << XCHILD( _T("item")) << XATTR( _T("jid"), m_ThreadInfo->fullJID )
+ << XATTR( _T("node"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("name"), _T("Ad-hoc commands"));
+
+ m_ThreadInfo->send( iq );
+ return TRUE;
+}
+
+BOOL CJabberProto::AddClistHttpAuthEvent( CJabberHttpAuthParams *pParams )
+{
+ CLISTEVENT cle = {0};
+ char szService[256];
+ mir_snprintf( szService, sizeof(szService),"%s%s", m_szModuleName, JS_HTTP_AUTH );
+ cle.cbSize = sizeof(CLISTEVENT);
+ cle.hIcon = (HICON) LoadIconEx("openid");
+ cle.flags = CLEF_PROTOCOLGLOBAL | CLEF_TCHAR;
+ cle.hDbEvent = (HANDLE)("test");
+ cle.lParam = (LPARAM) pParams;
+ cle.pszService = szService;
+ cle.ptszTooltip = TranslateT("Http authentication request received");
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle);
+
+ return TRUE;
+}
+
+BOOL CJabberProto::OnIqHttpAuth( HXML node, CJabberIqInfo* pInfo )
+{
+ if ( !m_options.AcceptHttpAuth )
+ return TRUE;
+
+ if ( !node || !pInfo->GetChildNode() || !pInfo->GetFrom() || !pInfo->GetIdStr())
+ return TRUE;
+
+ HXML pConfirm = xmlGetChild( node , "confirm" );
+ if ( !pConfirm )
+ return TRUE;
+
+ const TCHAR *szId = xmlGetAttrValue( pConfirm, _T("id"));
+ const TCHAR *szMethod = xmlGetAttrValue( pConfirm, _T("method"));
+ const TCHAR *szUrl = xmlGetAttrValue( pConfirm, _T("url"));
+
+ if ( !szId || !szMethod || !szUrl )
+ return TRUE;
+
+ CJabberHttpAuthParams *pParams = (CJabberHttpAuthParams *)mir_alloc( sizeof( CJabberHttpAuthParams ));
+ if ( !pParams )
+ return TRUE;
+ ZeroMemory( pParams, sizeof( CJabberHttpAuthParams ));
+ pParams->m_nType = CJabberHttpAuthParams::IQ;
+ pParams->m_szFrom = mir_tstrdup( pInfo->GetFrom());
+ pParams->m_szId = mir_tstrdup( szId );
+ pParams->m_szMethod = mir_tstrdup( szMethod );
+ pParams->m_szUrl = mir_tstrdup( szUrl );
+
+ AddClistHttpAuthEvent( pParams );
+
+ return TRUE;
+}
diff --git a/protocols/JabberG/src/jabber_iqid.cpp b/protocols/JabberG/src/jabber_iqid.cpp new file mode 100644 index 0000000000..7679450d59 --- /dev/null +++ b/protocols/JabberG/src/jabber_iqid.cpp @@ -0,0 +1,1742 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_list.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+#include "jabber_privacy.h"
+
+#include "m_genmenu.h"
+#include "m_clistint.h"
+
+void CJabberProto::OnIqResultServerDiscoInfo( HXML iqNode )
+{
+ if ( !iqNode )
+ return;
+
+ const TCHAR *type = xmlGetAttrValue( iqNode, _T("type"));
+ int i;
+
+ if ( !_tcscmp( type, _T("result"))) {
+ HXML query = xmlGetChildByTag( iqNode, "query", "xmlns", _T(JABBER_FEAT_DISCO_INFO));
+ if ( !query )
+ return;
+
+ HXML identity;
+ for ( i = 1; ( identity = xmlGetNthChild( query, _T("identity"), i )) != NULL; i++ ) {
+ const TCHAR *identityCategory = xmlGetAttrValue( identity, _T("category"));
+ const TCHAR *identityType = xmlGetAttrValue( identity, _T("type"));
+ const TCHAR *identityName = xmlGetAttrValue( identity, _T("name"));
+ if ( identityCategory && identityType && !_tcscmp( identityCategory, _T("pubsub")) && !_tcscmp( identityType, _T("pep"))) {
+ m_bPepSupported = TRUE;
+
+ EnableMenuItems( TRUE );
+ RebuildInfoFrame();
+ }
+ else if ( identityCategory && identityType && identityName &&
+ !_tcscmp( identityCategory, _T("server")) &&
+ !_tcscmp( identityType, _T("im")) &&
+ !_tcscmp( identityName, _T("Google Talk"))) {
+ m_ThreadInfo->jabberServerCaps |= JABBER_CAPS_PING;
+ m_bGoogleTalk = true;
+
+ // Google Shared Status
+ m_ThreadInfo->send(
+ XmlNodeIq(m_iqManager.AddHandler(&CJabberProto::OnIqResultGoogleSharedStatus, JABBER_IQ_TYPE_GET))
+ << XQUERY(_T(JABBER_FEAT_GTALK_SHARED_STATUS)) << XATTR(_T("version"), _T("2")));
+ }
+ }
+ if ( m_ThreadInfo ) {
+ HXML feature;
+ for ( i = 1; ( feature = xmlGetNthChild( query, _T("feature"), i )) != NULL; i++ ) {
+ const TCHAR *featureName = xmlGetAttrValue( feature, _T("var"));
+ if ( featureName ) {
+ for ( int j = 0; g_JabberFeatCapPairs[j].szFeature; j++ ) {
+ if ( !_tcscmp( g_JabberFeatCapPairs[j].szFeature, featureName )) {
+ m_ThreadInfo->jabberServerCaps |= g_JabberFeatCapPairs[j].jcbCap;
+ break;
+ } } } } }
+
+ OnProcessLoginRq( m_ThreadInfo, JABBER_LOGIN_SERVERINFO);
+} }
+
+void CJabberProto::OnIqResultNestedRosterGroups( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ const TCHAR *szGroupDelimeter = NULL;
+ BOOL bPrivateStorageSupport = FALSE;
+
+ if ( iqNode && pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) {
+ bPrivateStorageSupport = TRUE;
+ szGroupDelimeter = XPathFmt( iqNode, _T("query[@xmlns='%s']/roster[@xmlns='%s']"), _T(JABBER_FEAT_PRIVATE_STORAGE), _T( JABBER_FEAT_NESTED_ROSTER_GROUPS ));
+ if ( szGroupDelimeter && !szGroupDelimeter[0] )
+ szGroupDelimeter = NULL; // "" as roster delimeter is not supported :)
+ }
+
+ // global fuckup
+ if ( !m_ThreadInfo )
+ return;
+
+ // is our default delimiter?
+ if (( !szGroupDelimeter && bPrivateStorageSupport ) || ( szGroupDelimeter && _tcscmp( szGroupDelimeter, _T("\\"))))
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("set"), SerialNext()) << XQUERY( _T(JABBER_FEAT_PRIVATE_STORAGE))
+ << XCHILD( _T("roster"), _T("\\")) << XATTR( _T("xmlns"), _T(JABBER_FEAT_NESTED_ROSTER_GROUPS)));
+
+ // roster request
+ TCHAR *szUserData = mir_tstrdup( szGroupDelimeter ? szGroupDelimeter : _T("\\"));
+ m_ThreadInfo->send(
+ XmlNodeIq( m_iqManager.AddHandler( &CJabberProto::OnIqResultGetRoster, JABBER_IQ_TYPE_GET, NULL, 0, -1, (void *)szUserData ))
+ << XCHILDNS( _T("query"), _T(JABBER_FEAT_IQ_ROSTER)));
+}
+
+void CJabberProto::OnIqResultNotes( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if ( iqNode && pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) {
+ HXML hXmlData = XPathFmt( iqNode, _T("query[@xmlns='%s']/storage[@xmlns='%s']"),
+ _T(JABBER_FEAT_PRIVATE_STORAGE), _T(JABBER_FEAT_MIRANDA_NOTES));
+ if (hXmlData) m_notes.LoadXml(hXmlData);
+ }
+}
+
+void CJabberProto::OnProcessLoginRq( ThreadData* info, DWORD rq )
+{
+ if ( info == NULL )
+ return;
+
+ info->dwLoginRqs |= rq;
+
+ if ((info->dwLoginRqs & JABBER_LOGIN_ROSTER) && (info->dwLoginRqs & JABBER_LOGIN_BOOKMARKS) &&
+ (info->dwLoginRqs & JABBER_LOGIN_SERVERINFO) && !(info->dwLoginRqs & JABBER_LOGIN_BOOKMARKS_AJ))
+ {
+ if ( jabberChatDllPresent && m_options.AutoJoinBookmarks) {
+ LIST<JABBER_LIST_ITEM> ll( 10 );
+ LISTFOREACH(i, this, LIST_BOOKMARK)
+ {
+ JABBER_LIST_ITEM* item = ListGetItemPtrFromIndex( i );
+ if ( item != NULL && !lstrcmp( item->type, _T("conference")) && item->bAutoJoin )
+ ll.insert( item );
+ }
+
+ for ( int j=0; j < ll.getCount(); j++ ) {
+ JABBER_LIST_ITEM* item = ll[j];
+
+ TCHAR room[256], *server, *p;
+ TCHAR text[128];
+ _tcsncpy( text, item->jid, SIZEOF( text ));
+ _tcsncpy( room, text, SIZEOF( room ));
+ p = _tcstok( room, _T( "@" ));
+ server = _tcstok( NULL, _T( "@" ));
+ if ( item->nick && item->nick[0] != 0 )
+ GroupchatJoinRoom( server, p, item->nick, item->password, true );
+ else {
+ TCHAR* nick = JabberNickFromJID( m_szJabberJID );
+ GroupchatJoinRoom( server, p, nick, item->password, true );
+ mir_free( nick );
+ } }
+
+ ll.destroy();
+ }
+
+ OnProcessLoginRq( info, JABBER_LOGIN_BOOKMARKS_AJ );
+} }
+
+void CJabberProto::OnLoggedIn()
+{
+ m_bJabberOnline = TRUE;
+ m_tmJabberLoggedInTime = time(0);
+
+ m_ThreadInfo->dwLoginRqs = 0;
+
+ // XEP-0083 support
+ {
+ CJabberIqInfo* pIqInfo = m_iqManager.AddHandler( &CJabberProto::OnIqResultNestedRosterGroups, JABBER_IQ_TYPE_GET );
+ // ugly hack to prevent hangup during login process
+ pIqInfo->SetTimeout( 30000 );
+ m_ThreadInfo->send(
+ XmlNodeIq( pIqInfo ) << XQUERY( _T(JABBER_FEAT_PRIVATE_STORAGE))
+ << XCHILDNS( _T("roster"), _T(JABBER_FEAT_NESTED_ROSTER_GROUPS)));
+ }
+
+ // Server-side notes
+ {
+ m_ThreadInfo->send(
+ XmlNodeIq(m_iqManager.AddHandler(&CJabberProto::OnIqResultNotes, JABBER_IQ_TYPE_GET))
+ << XQUERY( _T(JABBER_FEAT_PRIVATE_STORAGE))
+ << XCHILDNS( _T("storage"), _T(JABBER_FEAT_MIRANDA_NOTES)));
+ }
+
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_DISCOBOOKMARKS, &CJabberProto::OnIqResultDiscoBookmarks);
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("get"), iqId) << XQUERY( _T(JABBER_FEAT_PRIVATE_STORAGE))
+ << XCHILDNS( _T("storage"), _T("storage:bookmarks")));
+
+ m_bPepSupported = FALSE;
+ m_ThreadInfo->jabberServerCaps = JABBER_RESOURCE_CAPS_NONE;
+ iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultServerDiscoInfo );
+ m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId, _A2T(m_ThreadInfo->server)) << XQUERY( _T(JABBER_FEAT_DISCO_INFO)));
+
+ QueryPrivacyLists( m_ThreadInfo );
+
+ char szServerName[ sizeof(m_ThreadInfo->server) ];
+ if ( JGetStaticString( "LastLoggedServer", NULL, szServerName, sizeof(szServerName)))
+ SendGetVcard( m_szJabberJID );
+ else if ( strcmp( m_ThreadInfo->server, szServerName ))
+ SendGetVcard( m_szJabberJID );
+ JSetString( NULL, "LastLoggedServer", m_ThreadInfo->server );
+
+ m_pepServices.ResetPublishAll();
+}
+
+void CJabberProto::OnIqResultGetAuth( HXML iqNode )
+{
+ // RECVED: result of the request for authentication method
+ // ACTION: send account authentication information to log in
+ Log( "<iq/> iqIdGetAuth" );
+
+ HXML queryNode;
+ const TCHAR* type;
+ if (( type=xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+ if (( queryNode=xmlGetChild( iqNode , "query" )) == NULL ) return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultSetAuth );
+
+ XmlNodeIq iq( _T("set"), iqId );
+ HXML query = iq << XQUERY( _T("jabber:iq:auth"));
+ query << XCHILD( _T("username"), m_ThreadInfo->username );
+ if ( xmlGetChild( queryNode, "digest" ) != NULL && m_szStreamId ) {
+ char* str = mir_utf8encodeT( m_ThreadInfo->password );
+ char text[200];
+ mir_snprintf( text, SIZEOF(text), "%s%s", m_szStreamId, str );
+ mir_free( str );
+ if (( str=JabberSha1( text )) != NULL ) {
+ query << XCHILD( _T("digest"), _A2T(str));
+ mir_free( str );
+ }
+ }
+ else if ( xmlGetChild( queryNode, "password" ) != NULL )
+ query << XCHILD( _T("password"), m_ThreadInfo->password );
+ else {
+ Log( "No known authentication mechanism accepted by the server." );
+
+ m_ThreadInfo->send( "</stream:stream>" );
+ return;
+ }
+
+ if ( xmlGetChild( queryNode , "resource" ) != NULL )
+ query << XCHILD( _T("resource"), m_ThreadInfo->resource );
+
+ m_ThreadInfo->send( iq );
+ }
+ else if ( !lstrcmp( type, _T("error"))) {
+ m_ThreadInfo->send( "</stream:stream>" );
+
+ TCHAR text[128];
+ mir_sntprintf( text, SIZEOF( text ), _T("%s %s."), TranslateT( "Authentication failed for" ), m_ThreadInfo->username );
+ MsgPopup( NULL, text, TranslateT( "Jabber Authentication" ));
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD );
+ m_ThreadInfo = NULL; // To disallow auto reconnect
+} }
+
+void CJabberProto::OnIqResultSetAuth( HXML iqNode )
+{
+ const TCHAR* type;
+
+ // RECVED: authentication result
+ // ACTION: if successfully logged in, continue by requesting roster list and set my initial status
+ Log( "<iq/> iqIdSetAuth" );
+ if (( type=xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ DBVARIANT dbv;
+ if ( JGetStringT( NULL, "Nick", &dbv ))
+ JSetStringT( NULL, "Nick", m_ThreadInfo->username );
+ else
+ JFreeVariant( &dbv );
+
+ OnLoggedIn();
+ }
+ // What to do if password error? etc...
+ else if ( !lstrcmp( type, _T("error"))) {
+ TCHAR text[128];
+
+ m_ThreadInfo->send( "</stream:stream>" );
+ mir_sntprintf( text, SIZEOF( text ), _T("%s %s."), TranslateT( "Authentication failed for" ), m_ThreadInfo->username );
+ MsgPopup( NULL, text, TranslateT( "Jabber Authentication" ));
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD );
+ m_ThreadInfo = NULL; // To disallow auto reconnect
+} }
+
+void CJabberProto::OnIqResultBind( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if ( !m_ThreadInfo || !iqNode )
+ return;
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) {
+ LPCTSTR szJid = XPathT( iqNode, "bind[@xmlns='urn:ietf:params:xml:ns:xmpp-bind']/jid" );
+ if ( szJid ) {
+ if ( !_tcsncmp( m_ThreadInfo->fullJID, szJid, SIZEOF( m_ThreadInfo->fullJID )))
+ Log( "Result Bind: " TCHAR_STR_PARAM " confirmed ", m_ThreadInfo->fullJID );
+ else {
+ Log( "Result Bind: " TCHAR_STR_PARAM " changed to " TCHAR_STR_PARAM, m_ThreadInfo->fullJID, szJid);
+ _tcsncpy( m_ThreadInfo->fullJID, szJid, SIZEOF( m_ThreadInfo->fullJID ));
+ }
+ }
+ if ( m_ThreadInfo->bIsSessionAvailable )
+ m_ThreadInfo->send(
+ XmlNodeIq( m_iqManager.AddHandler( &CJabberProto::OnIqResultSession, JABBER_IQ_TYPE_SET ))
+ << XCHILDNS( _T("session"), _T("urn:ietf:params:xml:ns:xmpp-session" )));
+ else
+ OnLoggedIn();
+ }
+ else {
+ //rfc3920 page 39
+ m_ThreadInfo->send( "</stream:stream>" );
+ m_ThreadInfo = NULL; // To disallow auto reconnect
+ }
+}
+
+void CJabberProto::OnIqResultSession( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT)
+ OnLoggedIn();
+}
+
+void CJabberProto::GroupchatJoinByHContact( HANDLE hContact, bool autojoin )
+{
+ TCHAR* roomjid = JGetStringT( hContact, "ChatRoomID" );
+ if ( !roomjid ) return;
+
+ TCHAR* room = roomjid;
+ TCHAR* server = _tcschr( roomjid, '@' );
+ if ( !server ) {
+ mir_free( roomjid );
+ return;
+ }
+ server[0] = 0; server++;
+
+ TCHAR *nick = JGetStringT( hContact, "MyNick" );
+ if ( !nick ) {
+ nick = JabberNickFromJID( m_szJabberJID );
+ if ( !nick ) {
+ mir_free( roomjid );
+ return;
+ }
+ }
+
+ TCHAR *password = JGetStringCrypt( hContact, "LoginPassword" );
+
+ GroupchatJoinRoom( server, room, nick, password, autojoin );
+
+ mir_free( password );
+ mir_free( nick );
+ mir_free( roomjid );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberIqResultGetRoster - populates LIST_ROSTER and creates contact for any new rosters
+
+void CJabberProto::OnIqResultGetRoster( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ Log( "<iq/> iqIdGetRoster" );
+ TCHAR *szGroupDelimeter = (TCHAR *)pInfo->GetUserData();
+ if ( pInfo->GetIqType() != JABBER_IQ_TYPE_RESULT ) {
+ mir_free( szGroupDelimeter );
+ return;
+ }
+
+ HXML queryNode = xmlGetChild( iqNode , "query" );
+ if ( queryNode == NULL ) {
+ mir_free( szGroupDelimeter );
+ return;
+ }
+
+ if ( lstrcmp( xmlGetAttrValue( queryNode, _T("xmlns")), _T(JABBER_FEAT_IQ_ROSTER))) {
+ mir_free( szGroupDelimeter );
+ return;
+ }
+
+ if ( !_tcscmp( szGroupDelimeter, _T("\\"))) {
+ mir_free( szGroupDelimeter );
+ szGroupDelimeter = NULL;
+ }
+
+ TCHAR* nick;
+ int i;
+ LIST<void> chatRooms(10);
+ OBJLIST<JABBER_HTTP_AVATARS> *httpavatars = new OBJLIST<JABBER_HTTP_AVATARS>(20, JABBER_HTTP_AVATARS::compare);
+
+ for ( i=0; ; i++ ) {
+ BOOL bIsTransport=FALSE;
+
+ HXML itemNode = xmlGetChild( queryNode ,i);
+ if ( !itemNode )
+ break;
+
+ if ( _tcscmp( xmlGetName( itemNode ), _T("item")))
+ continue;
+
+ const TCHAR* str = xmlGetAttrValue( itemNode, _T("subscription")), *name;
+
+ JABBER_SUBSCRIPTION sub;
+ if ( str == NULL ) sub = SUB_NONE;
+ else if ( !_tcscmp( str, _T("both"))) sub = SUB_BOTH;
+ else if ( !_tcscmp( str, _T("to"))) sub = SUB_TO;
+ else if ( !_tcscmp( str, _T("from"))) sub = SUB_FROM;
+ else sub = SUB_NONE;
+
+ const TCHAR* jid = xmlGetAttrValue( itemNode, _T("jid"));
+ if ( jid == NULL )
+ continue;
+ if ( _tcschr( jid, '@' ) == NULL )
+ bIsTransport = TRUE;
+
+ if (( name = xmlGetAttrValue( itemNode, _T("name"))) != NULL )
+ nick = mir_tstrdup( name );
+ else
+ nick = JabberNickFromJID( jid );
+
+ if ( nick == NULL )
+ continue;
+
+ JABBER_LIST_ITEM* item = ListAdd( LIST_ROSTER, jid );
+ item->subscription = sub;
+
+ mir_free( item->nick ); item->nick = nick;
+
+ HXML groupNode = xmlGetChild( itemNode , "group" );
+ replaceStrT( item->group, ( groupNode ) ? xmlGetText( groupNode ) : NULL );
+
+ // check group delimiters:
+ if ( item->group && szGroupDelimeter ) {
+ TCHAR *szPos = NULL;
+ while ( szPos = _tcsstr( item->group, szGroupDelimeter )) {
+ *szPos = 0;
+ szPos += _tcslen( szGroupDelimeter );
+ TCHAR *szNewGroup = (TCHAR *)mir_alloc( sizeof(TCHAR) * ( _tcslen( item->group ) + _tcslen( szPos ) + 2));
+ _tcscpy( szNewGroup, item->group );
+ _tcscat( szNewGroup, _T("\\"));
+ _tcscat( szNewGroup, szPos );
+ mir_free( item->group );
+ item->group = szNewGroup;
+ }
+ }
+
+ HANDLE hContact = HContactFromJID( jid );
+ if ( hContact == NULL ) {
+ // Received roster has a new JID.
+ // Add the jid ( with empty resource ) to Miranda contact list.
+ hContact = DBCreateContact( jid, nick, FALSE, FALSE );
+ }
+
+ if ( name != NULL ) {
+ DBVARIANT dbNick;
+ if ( !JGetStringT( hContact, "Nick", &dbNick )) {
+ if ( lstrcmp( nick, dbNick.ptszVal ) != 0 )
+ DBWriteContactSettingTString( hContact, "CList", "MyHandle", nick );
+ else
+ DBDeleteContactSetting( hContact, "CList", "MyHandle" );
+
+ JFreeVariant( &dbNick );
+ }
+ else DBWriteContactSettingTString( hContact, "CList", "MyHandle", nick );
+ }
+ else DBDeleteContactSetting( hContact, "CList", "MyHandle" );
+
+ if ( JGetByte( hContact, "ChatRoom", 0 )) {
+ GCSESSION gcw = {0};
+ gcw.cbSize = sizeof(GCSESSION);
+ gcw.iType = GCW_CHATROOM;
+ gcw.pszModule = m_szModuleName;
+ gcw.dwFlags = GC_TCHAR;
+ gcw.ptszID = jid;
+ gcw.ptszName = NEWTSTR_ALLOCA( jid );
+
+ TCHAR* p = (TCHAR*)_tcschr( gcw.ptszName, '@' );
+ if ( p )
+ *p = 0;
+
+ CallServiceSync( MS_GC_NEWSESSION, 0, ( LPARAM )&gcw );
+
+ DBDeleteContactSetting( hContact, "CList", "Hidden" );
+ chatRooms.insert( hContact );
+ } else
+ {
+ UpdateSubscriptionInfo(hContact, item);
+ }
+
+ if (!m_options.IgnoreRosterGroups) {
+ if ( item->group != NULL ) {
+ JabberContactListCreateGroup( item->group );
+
+ // Don't set group again if already correct, or Miranda may show wrong group count in some case
+ DBVARIANT dbv;
+ if ( !DBGetContactSettingTString( hContact, "CList", "Group", &dbv )) {
+ if ( lstrcmp( dbv.ptszVal, item->group ))
+ DBWriteContactSettingTString( hContact, "CList", "Group", item->group );
+ JFreeVariant( &dbv );
+ }
+ else DBWriteContactSettingTString( hContact, "CList", "Group", item->group );
+ }
+ else
+ DBDeleteContactSetting( hContact, "CList", "Group" );
+ }
+
+ if ( hContact != NULL ) {
+ if ( bIsTransport)
+ JSetByte( hContact, "IsTransport", TRUE );
+ else
+ JSetByte( hContact, "IsTransport", FALSE );
+ }
+
+ const TCHAR* imagepath = xmlGetAttrValue(itemNode, _T("vz:img"));
+ if (imagepath)
+ httpavatars->insert(new JABBER_HTTP_AVATARS(imagepath, hContact));
+ }
+
+ if (httpavatars->getCount())
+ JForkThread(&CJabberProto::LoadHttpAvatars, httpavatars);
+ else
+ delete httpavatars;
+
+ // Delete orphaned contacts ( if roster sync is enabled )
+ if ( m_options.RosterSync == TRUE ) {
+ int listSize = 0, listAllocSize = 0;
+ HANDLE* list = NULL;
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL ) {
+ char* str = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( str != NULL && !strcmp( str, m_szModuleName )) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ if ( !ListExist( LIST_ROSTER, dbv.ptszVal )) {
+ Log( "Syncing roster: preparing to delete " TCHAR_STR_PARAM " ( hContact=0x%x )", dbv.ptszVal, hContact );
+ if ( listSize >= listAllocSize ) {
+ listAllocSize = listSize + 100;
+ if (( list=( HANDLE * ) mir_realloc( list, listAllocSize * sizeof( HANDLE ))) == NULL ) {
+ listSize = 0;
+ break;
+ } }
+
+ list[listSize++] = hContact;
+ }
+ JFreeVariant( &dbv );
+ } }
+
+ hContact = db_find_next(hContact);
+ }
+
+ for ( i=0; i < listSize; i++ ) {
+ Log( "Syncing roster: deleting 0x%x", list[i] );
+ CallService( MS_DB_CONTACT_DELETE, ( WPARAM ) list[i], 0 );
+ }
+ if ( list != NULL )
+ mir_free( list );
+ }
+
+ EnableMenuItems( TRUE );
+
+ Log( "Status changed via THREADSTART" );
+ m_bModeMsgStatusChangePending = FALSE;
+ SetServerStatus( m_iDesiredStatus );
+
+ if ( m_options.AutoJoinConferences ) {
+ for ( i=0; i < chatRooms.getCount(); i++ )
+ GroupchatJoinByHContact(( HANDLE )chatRooms[i], true);
+ }
+ chatRooms.destroy();
+
+ //UI_SAFE_NOTIFY(m_pDlgJabberJoinGroupchat, WM_JABBER_CHECK_ONLINE);
+ //UI_SAFE_NOTIFY(m_pDlgBookmarks, WM_JABBER_CHECK_ONLINE);
+ UI_SAFE_NOTIFY_HWND(m_hwndJabberAddBookmark, WM_JABBER_CHECK_ONLINE);
+ WindowNotify(WM_JABBER_CHECK_ONLINE);
+
+ UI_SAFE_NOTIFY(m_pDlgServiceDiscovery, WM_JABBER_TRANSPORT_REFRESH);
+
+ if ( szGroupDelimeter )
+ mir_free( szGroupDelimeter );
+
+ OnProcessLoginRq(m_ThreadInfo, JABBER_LOGIN_ROSTER);
+ RebuildInfoFrame();
+}
+
+void CJabberProto::OnIqResultGetRegister( HXML iqNode )
+{
+ // RECVED: result of the request for ( agent ) registration mechanism
+ // ACTION: activate ( agent ) registration input dialog
+ Log( "<iq/> iqIdGetRegister" );
+
+ HXML queryNode;
+ const TCHAR *type;
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+ if (( queryNode = xmlGetChild( iqNode , "query" )) == NULL ) return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ if ( m_hwndAgentRegInput )
+ SendMessage( m_hwndAgentRegInput, WM_JABBER_REGINPUT_ACTIVATE, 1 /*success*/, ( LPARAM )xi.copyNode( iqNode ));
+ }
+ else if ( !lstrcmp( type, _T("error"))) {
+ if ( m_hwndAgentRegInput ) {
+ HXML errorNode = xmlGetChild( iqNode , "error" );
+ TCHAR* str = JabberErrorMsg( errorNode );
+ SendMessage( m_hwndAgentRegInput, WM_JABBER_REGINPUT_ACTIVATE, 0 /*error*/, ( LPARAM )str );
+ mir_free( str );
+} } }
+
+void CJabberProto::OnIqResultSetRegister( HXML iqNode )
+{
+ // RECVED: result of registration process
+ // ACTION: notify of successful agent registration
+ Log( "<iq/> iqIdSetRegister" );
+
+ const TCHAR *type, *from;
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+ if (( from = xmlGetAttrValue( iqNode, _T("from"))) == NULL ) return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ HANDLE hContact = HContactFromJID( from );
+ if ( hContact != NULL )
+ JSetByte( hContact, "IsTransport", TRUE );
+
+ if ( m_hwndRegProgress )
+ SendMessage( m_hwndRegProgress, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Registration successful" ));
+ }
+ else if ( !lstrcmp( type, _T("error"))) {
+ if ( m_hwndRegProgress ) {
+ HXML errorNode = xmlGetChild( iqNode , "error" );
+ TCHAR* str = JabberErrorMsg( errorNode );
+ SendMessage( m_hwndRegProgress, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )str );
+ mir_free( str );
+} } }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberIqResultGetVcard - processes the server-side v-card
+
+void CJabberProto::OnIqResultGetVcardPhoto( const TCHAR* jid, HXML n, HANDLE hContact, BOOL& hasPhoto )
+{
+ Log( "JabberIqResultGetVcardPhoto: %d", hasPhoto );
+ if ( hasPhoto )
+ return;
+
+ HXML o = xmlGetChild( n , "BINVAL" );
+ if ( o == NULL || xmlGetText( o ) == NULL )
+ return;
+
+ int bufferLen;
+ char* buffer = JabberBase64DecodeT( xmlGetText( o ), &bufferLen );
+ if ( buffer == NULL )
+ return;
+
+ const TCHAR* szPicType;
+ HXML m = xmlGetChild( n , "TYPE" );
+ if ( m == NULL || xmlGetText( m ) == NULL ) {
+LBL_NoTypeSpecified:
+ switch( JabberGetPictureType( buffer )) {
+ case PA_FORMAT_GIF: szPicType = _T("image/gif"); break;
+ case PA_FORMAT_BMP: szPicType = _T("image/bmp"); break;
+ case PA_FORMAT_PNG: szPicType = _T("image/png"); break;
+ case PA_FORMAT_JPEG: szPicType = _T("image/jpeg"); break;
+ default:
+LBL_Ret:
+ mir_free( buffer );
+ return;
+ }
+ }
+ else {
+ const TCHAR* tszType = xmlGetText( m );
+ if ( !_tcscmp( tszType, _T("image/jpeg")) ||
+ !_tcscmp( tszType, _T("image/png")) ||
+ !_tcscmp( tszType, _T("image/gif")) ||
+ !_tcscmp( tszType, _T("image/bmp")))
+ szPicType = tszType;
+ else
+ goto LBL_NoTypeSpecified;
+ }
+
+ TCHAR szAvatarFileName[MAX_PATH];
+ GetAvatarFileName( hContact, szAvatarFileName, SIZEOF( szAvatarFileName ));
+
+ Log( "Picture file name set to " TCHAR_STR_PARAM, szAvatarFileName );
+ HANDLE hFile = CreateFile( szAvatarFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+ if ( hFile == INVALID_HANDLE_VALUE )
+ goto LBL_Ret;
+
+ Log( "Writing %d bytes", bufferLen );
+ DWORD nWritten;
+ if ( !WriteFile( hFile, buffer, bufferLen, &nWritten, NULL ))
+ goto LBL_Ret;
+
+ CloseHandle( hFile );
+
+ Log( "%d bytes written", nWritten );
+ if ( hContact == NULL ) {
+ hasPhoto = TRUE;
+ CallService( MS_AV_SETMYAVATART, ( WPARAM )m_szModuleName, ( LPARAM )szAvatarFileName );
+
+ Log( "My picture saved to " TCHAR_STR_PARAM, szAvatarFileName );
+ }
+ else {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ JABBER_LIST_ITEM *item = ListGetItemPtr( LIST_ROSTER, jid );
+ if ( item == NULL ) {
+ item = ListAdd( LIST_VCARD_TEMP, jid ); // adding to the temp list to store information about photo
+ item->bUseResource = TRUE;
+ }
+ if ( item != NULL ) {
+ hasPhoto = TRUE;
+ if ( item->photoFileName )
+ DeleteFile( item->photoFileName );
+ replaceStrT( item->photoFileName, szAvatarFileName );
+ Log( "Contact's picture saved to " TCHAR_STR_PARAM, szAvatarFileName );
+
+ if (JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) == ID_STATUS_OFFLINE) {
+ char szHashValue[ MAX_PATH ];
+ if ( JGetStaticString( "AvatarHash", hContact, szHashValue, sizeof( szHashValue )))
+ OnIqResultGotAvatar( hContact, o, xmlGetText( m ));
+ } }
+
+ JFreeVariant( &dbv );
+ } }
+
+ if ( !hasPhoto )
+ DeleteFile( szAvatarFileName );
+
+ goto LBL_Ret;
+}
+
+static TCHAR* sttGetText( HXML node, char* tag )
+{
+ HXML n = xmlGetChild( node , tag );
+ if ( n == NULL )
+ return NULL;
+
+ return ( TCHAR* )xmlGetText( n );
+}
+
+void CJabberProto::OnIqResultGetVcard( HXML iqNode )
+{
+ HXML vCardNode, m, n, o;
+ const TCHAR* type, *jid;
+ HANDLE hContact;
+ TCHAR text[128];
+ DBVARIANT dbv;
+
+ Log( "<iq/> iqIdGetVcard" );
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+ if (( jid = xmlGetAttrValue( iqNode, _T("from"))) == NULL ) return;
+ int id = JabberGetPacketID( iqNode );
+
+ if ( id == m_nJabberSearchID ) {
+ m_nJabberSearchID = -1;
+
+ if (( vCardNode = xmlGetChild( iqNode , "vCard" )) != NULL ) {
+ if ( !lstrcmp( type, _T("result"))) {
+ JABBER_SEARCH_RESULT jsr = { 0 };
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+ jsr.hdr.flags = PSR_TCHAR;
+ jsr.hdr.nick = sttGetText( vCardNode, "NICKNAME" );
+ jsr.hdr.firstName = sttGetText( vCardNode, "FN" );
+ jsr.hdr.lastName = _T("");
+ jsr.hdr.email = sttGetText( vCardNode, "EMAIL" );
+ _tcsncpy( jsr.jid, jid, SIZEOF( jsr.jid ));
+ jsr.jid[ SIZEOF( jsr.jid )-1 ] = '\0';
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, ( HANDLE )id, ( LPARAM )&jsr );
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE )id, 0 );
+ }
+ else if ( !lstrcmp( type, _T("error")))
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE )id, 0 );
+ }
+ else JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE )id, 0 );
+ return;
+ }
+
+ size_t len = _tcslen( m_szJabberJID );
+ if ( !_tcsnicmp( jid, m_szJabberJID, len ) && ( jid[len]=='/' || jid[len]=='\0' )) {
+ hContact = NULL;
+ Log( "Vcard for myself" );
+ }
+ else {
+ if (( hContact = HContactFromJID( jid )) == NULL )
+ return;
+ Log( "Other user's vcard" );
+ }
+
+ if ( !lstrcmp( type, _T("result"))) {
+ BOOL hasFn, hasNick, hasGiven, hasFamily, hasMiddle, hasBday, hasGender;
+ BOOL hasPhone, hasFax, hasCell, hasUrl;
+ BOOL hasHome, hasHomeStreet, hasHomeStreet2, hasHomeLocality, hasHomeRegion, hasHomePcode, hasHomeCtry;
+ BOOL hasWork, hasWorkStreet, hasWorkStreet2, hasWorkLocality, hasWorkRegion, hasWorkPcode, hasWorkCtry;
+ BOOL hasOrgname, hasOrgunit, hasRole, hasTitle;
+ BOOL hasDesc, hasPhoto;
+ int nEmail, nPhone, nYear, nMonth, nDay;
+
+ hasFn = hasNick = hasGiven = hasFamily = hasMiddle = hasBday = hasGender = FALSE;
+ hasPhone = hasFax = hasCell = hasUrl = FALSE;
+ hasHome = hasHomeStreet = hasHomeStreet2 = hasHomeLocality = hasHomeRegion = hasHomePcode = hasHomeCtry = FALSE;
+ hasWork = hasWorkStreet = hasWorkStreet2 = hasWorkLocality = hasWorkRegion = hasWorkPcode = hasWorkCtry = FALSE;
+ hasOrgname = hasOrgunit = hasRole = hasTitle = FALSE;
+ hasDesc = hasPhoto = FALSE;
+ nEmail = nPhone = 0;
+
+ if (( vCardNode = xmlGetChild( iqNode , "vCard" )) != NULL ) {
+ for ( int i=0; ; i++ ) {
+ n = xmlGetChild( vCardNode ,i);
+ if ( !n )
+ break;
+ if ( xmlGetName( n ) == NULL ) continue;
+ if ( !_tcscmp( xmlGetName( n ), _T("FN"))) {
+ if ( xmlGetText( n ) != NULL ) {
+ hasFn = TRUE;
+ JSetStringT( hContact, "FullName", xmlGetText( n ));
+ }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("NICKNAME"))) {
+ if ( xmlGetText( n ) != NULL ) {
+ hasNick = TRUE;
+ JSetStringT( hContact, "Nick", xmlGetText( n ));
+ }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("N"))) {
+ // First/Last name
+ if ( !hasGiven && !hasFamily && !hasMiddle ) {
+ if (( m=xmlGetChild( n , "GIVEN" )) != NULL && xmlGetText( m )!=NULL ) {
+ hasGiven = TRUE;
+ JSetStringT( hContact, "FirstName", xmlGetText( m ));
+ }
+ if (( m=xmlGetChild( n , "FAMILY" )) != NULL && xmlGetText( m )!=NULL ) {
+ hasFamily = TRUE;
+ JSetStringT( hContact, "LastName", xmlGetText( m ));
+ }
+ if (( m=xmlGetChild( n , "MIDDLE" )) != NULL && xmlGetText( m ) != NULL ) {
+ hasMiddle = TRUE;
+ JSetStringT( hContact, "MiddleName", xmlGetText( m ));
+ } }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("EMAIL"))) {
+ // E-mail address( es )
+ if (( m=xmlGetChild( n , "USERID" )) == NULL ) // Some bad client put e-mail directly in <EMAIL/> instead of <USERID/>
+ m = n;
+ if ( xmlGetText( m ) != NULL ) {
+ char text[100];
+ if ( hContact != NULL ) {
+ if ( nEmail == 0 )
+ strcpy( text, "e-mail" );
+ else
+ sprintf( text, "e-mail%d", nEmail-1 );
+ }
+ else sprintf( text, "e-mail%d", nEmail );
+ JSetStringT( hContact, text, xmlGetText( m ));
+
+ if ( hContact == NULL ) {
+ sprintf( text, "e-mailFlag%d", nEmail );
+ int nFlag = 0;
+ if ( xmlGetChild( n , "HOME" ) != NULL ) nFlag |= JABBER_VCEMAIL_HOME;
+ if ( xmlGetChild( n , "WORK" ) != NULL ) nFlag |= JABBER_VCEMAIL_WORK;
+ if ( xmlGetChild( n , "INTERNET" ) != NULL ) nFlag |= JABBER_VCEMAIL_INTERNET;
+ if ( xmlGetChild( n , "X400" ) != NULL ) nFlag |= JABBER_VCEMAIL_X400;
+ JSetWord( NULL, text, nFlag );
+ }
+ nEmail++;
+ }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("BDAY"))) {
+ // Birthday
+ if ( !hasBday && xmlGetText( n )!=NULL ) {
+ if ( hContact != NULL ) {
+ if ( _stscanf( xmlGetText( n ), _T("%d-%d-%d"), &nYear, &nMonth, &nDay ) == 3 ) {
+ hasBday = TRUE;
+ JSetWord( hContact, "BirthYear", ( WORD )nYear );
+ JSetByte( hContact, "BirthMonth", ( BYTE ) nMonth );
+ JSetByte( hContact, "BirthDay", ( BYTE ) nDay );
+
+ SYSTEMTIME sToday = {0};
+ GetLocalTime(&sToday);
+ int nAge = sToday.wYear - nYear;
+ if (sToday.wMonth < nMonth || (sToday.wMonth == nMonth && sToday.wDay < nDay))
+ nAge--;
+ if (nAge)
+ JSetWord( hContact, "Age", ( WORD )nAge );
+ }
+ }
+ else {
+ hasBday = TRUE;
+ JSetStringT( NULL, "BirthDate", xmlGetText( n ));
+ } }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("GENDER"))) {
+ // Gender
+ if ( !hasGender && xmlGetText( n )!=NULL ) {
+ if ( hContact != NULL ) {
+ if ( xmlGetText( n )[0] && strchr( "mMfF", xmlGetText( n )[0] )!=NULL ) {
+ hasGender = TRUE;
+ JSetByte( hContact, "Gender", ( BYTE ) toupper( xmlGetText( n )[0] ));
+ }
+ }
+ else {
+ hasGender = TRUE;
+ JSetStringT( NULL, "GenderString", xmlGetText( n ));
+ } }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("ADR"))) {
+ if ( !hasHome && xmlGetChild( n , "HOME" )!=NULL ) {
+ // Home address
+ hasHome = TRUE;
+ if (( m=xmlGetChild( n , "STREET" )) != NULL && xmlGetText( m ) != NULL ) {
+ hasHomeStreet = TRUE;
+ if ( hContact != NULL ) {
+ if (( o=xmlGetChild( n , "EXTADR" )) != NULL && xmlGetText( o ) != NULL )
+ mir_sntprintf( text, SIZEOF( text ), _T("%s\r\n%s"), xmlGetText( m ), xmlGetText( o ));
+ else if (( o=xmlGetChild( n , "EXTADD" ))!=NULL && xmlGetText( o )!=NULL )
+ mir_sntprintf( text, SIZEOF( text ), _T("%s\r\n%s"), xmlGetText( m ), xmlGetText( o ));
+ else
+ _tcsncpy( text, xmlGetText( m ), SIZEOF( text ));
+ text[SIZEOF(text)-1] = '\0';
+ JSetStringT( hContact, "Street", text );
+ }
+ else {
+ JSetStringT( hContact, "Street", xmlGetText( m ));
+ if (( m=xmlGetChild( n , "EXTADR" )) == NULL )
+ m = xmlGetChild( n , "EXTADD" );
+ if ( m!=NULL && xmlGetText( m )!=NULL ) {
+ hasHomeStreet2 = TRUE;
+ JSetStringT( hContact, "Street2", xmlGetText( m ));
+ } } }
+
+ if (( m=xmlGetChild( n , "LOCALITY" ))!=NULL && xmlGetText( m )!=NULL ) {
+ hasHomeLocality = TRUE;
+ JSetStringT( hContact, "City", xmlGetText( m ));
+ }
+ if (( m=xmlGetChild( n , "REGION" ))!=NULL && xmlGetText( m )!=NULL ) {
+ hasHomeRegion = TRUE;
+ JSetStringT( hContact, "State", xmlGetText( m ));
+ }
+ if (( m=xmlGetChild( n , "PCODE" ))!=NULL && xmlGetText( m )!=NULL ) {
+ hasHomePcode = TRUE;
+ JSetStringT( hContact, "ZIP", xmlGetText( m ));
+ }
+ if (( m=xmlGetChild( n , "CTRY" ))==NULL || xmlGetText( m )==NULL ) // Some bad client use <COUNTRY/> instead of <CTRY/>
+ m = xmlGetChild( n , "COUNTRY" );
+ if ( m!=NULL && xmlGetText( m )!=NULL ) {
+ hasHomeCtry = TRUE;
+ JSetStringT( hContact, "Country", xmlGetText( m ));
+ } }
+
+ if ( !hasWork && xmlGetChild( n , "WORK" )!=NULL ) {
+ // Work address
+ hasWork = TRUE;
+ if (( m=xmlGetChild( n , "STREET" ))!=NULL && xmlGetText( m )!=NULL ) {
+ hasWorkStreet = TRUE;
+ if ( hContact != NULL ) {
+ if (( o=xmlGetChild( n , "EXTADR" ))!=NULL && xmlGetText( o )!=NULL )
+ mir_sntprintf( text, SIZEOF( text ), _T("%s\r\n%s"), xmlGetText( m ), xmlGetText( o ));
+ else if (( o=xmlGetChild( n , "EXTADD" ))!=NULL && xmlGetText( o )!=NULL )
+ mir_sntprintf( text, SIZEOF( text ), _T("%s\r\n%s"), xmlGetText( m ), xmlGetText( o ));
+ else
+ _tcsncpy( text, xmlGetText( m ), SIZEOF( text ));
+ text[SIZEOF( text )-1] = '\0';
+ JSetStringT( hContact, "CompanyStreet", text );
+ }
+ else {
+ JSetStringT( hContact, "CompanyStreet", xmlGetText( m ));
+ if (( m=xmlGetChild( n , "EXTADR" )) == NULL )
+ m = xmlGetChild( n , "EXTADD" );
+ if ( m!=NULL && xmlGetText( m )!=NULL ) {
+ hasWorkStreet2 = TRUE;
+ JSetStringT( hContact, "CompanyStreet2", xmlGetText( m ));
+ } } }
+
+ if (( m=xmlGetChild( n , "LOCALITY" ))!=NULL && xmlGetText( m )!=NULL ) {
+ hasWorkLocality = TRUE;
+ JSetStringT( hContact, "CompanyCity", xmlGetText( m ));
+ }
+ if (( m=xmlGetChild( n , "REGION" ))!=NULL && xmlGetText( m )!=NULL ) {
+ hasWorkRegion = TRUE;
+ JSetStringT( hContact, "CompanyState", xmlGetText( m ));
+ }
+ if (( m=xmlGetChild( n , "PCODE" ))!=NULL && xmlGetText( m )!=NULL ) {
+ hasWorkPcode = TRUE;
+ JSetStringT( hContact, "CompanyZIP", xmlGetText( m ));
+ }
+ if (( m=xmlGetChild( n , "CTRY" ))==NULL || xmlGetText( m )==NULL ) // Some bad client use <COUNTRY/> instead of <CTRY/>
+ m = xmlGetChild( n , "COUNTRY" );
+ if ( m!=NULL && xmlGetText( m )!=NULL ) {
+ hasWorkCtry = TRUE;
+ JSetStringT( hContact, "CompanyCountry", xmlGetText( m ));
+ } }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("TEL"))) {
+ // Telephone/Fax/Cellular
+ if (( m=xmlGetChild( n , "NUMBER" ))!=NULL && xmlGetText( m )!=NULL ) {
+ if ( hContact != NULL ) {
+ if ( !hasFax && xmlGetChild( n , "FAX" )!=NULL ) {
+ hasFax = TRUE;
+ JSetStringT( hContact, "Fax", xmlGetText( m ));
+ }
+ else if ( !hasCell && xmlGetChild( n , "CELL" )!=NULL ) {
+ hasCell = TRUE;
+ JSetStringT( hContact, "Cellular", xmlGetText( m ));
+ }
+ else if ( !hasPhone &&
+ ( xmlGetChild( n , "HOME" )!=NULL ||
+ xmlGetChild( n , "WORK" )!=NULL ||
+ xmlGetChild( n , "VOICE" )!=NULL ||
+ ( xmlGetChild( n , "FAX" )==NULL &&
+ xmlGetChild( n , "PAGER" )==NULL &&
+ xmlGetChild( n , "MSG" )==NULL &&
+ xmlGetChild( n , "CELL" )==NULL &&
+ xmlGetChild( n , "VIDEO" )==NULL &&
+ xmlGetChild( n , "BBS" )==NULL &&
+ xmlGetChild( n , "MODEM" )==NULL &&
+ xmlGetChild( n , "ISDN" )==NULL &&
+ xmlGetChild( n , "PCS" )==NULL ))) {
+ hasPhone = TRUE;
+ JSetStringT( hContact, "Phone", xmlGetText( m ));
+ }
+ }
+ else {
+ char text[ 100 ];
+ sprintf( text, "Phone%d", nPhone );
+ JSetStringT( NULL, text, xmlGetText( m ));
+
+ sprintf( text, "PhoneFlag%d", nPhone );
+ int nFlag = 0;
+ if ( xmlGetChild( n ,"HOME" ) != NULL ) nFlag |= JABBER_VCTEL_HOME;
+ if ( xmlGetChild( n ,"WORK" ) != NULL ) nFlag |= JABBER_VCTEL_WORK;
+ if ( xmlGetChild( n ,"VOICE" ) != NULL ) nFlag |= JABBER_VCTEL_VOICE;
+ if ( xmlGetChild( n ,"FAX" ) != NULL ) nFlag |= JABBER_VCTEL_FAX;
+ if ( xmlGetChild( n ,"PAGER" ) != NULL ) nFlag |= JABBER_VCTEL_PAGER;
+ if ( xmlGetChild( n ,"MSG" ) != NULL ) nFlag |= JABBER_VCTEL_MSG;
+ if ( xmlGetChild( n ,"CELL" ) != NULL ) nFlag |= JABBER_VCTEL_CELL;
+ if ( xmlGetChild( n ,"VIDEO" ) != NULL ) nFlag |= JABBER_VCTEL_VIDEO;
+ if ( xmlGetChild( n ,"BBS" ) != NULL ) nFlag |= JABBER_VCTEL_BBS;
+ if ( xmlGetChild( n ,"MODEM" ) != NULL ) nFlag |= JABBER_VCTEL_MODEM;
+ if ( xmlGetChild( n ,"ISDN" ) != NULL ) nFlag |= JABBER_VCTEL_ISDN;
+ if ( xmlGetChild( n ,"PCS" ) != NULL ) nFlag |= JABBER_VCTEL_PCS;
+ JSetWord( NULL, text, nFlag );
+ nPhone++;
+ } }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("URL"))) {
+ // Homepage
+ if ( !hasUrl && xmlGetText( n )!=NULL ) {
+ hasUrl = TRUE;
+ JSetStringT( hContact, "Homepage", xmlGetText( n ));
+ }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("ORG"))) {
+ if ( !hasOrgname && !hasOrgunit ) {
+ if (( m=xmlGetChild( n ,"ORGNAME" ))!=NULL && xmlGetText( m )!=NULL ) {
+ hasOrgname = TRUE;
+ JSetStringT( hContact, "Company", xmlGetText( m ));
+ }
+ if (( m=xmlGetChild( n ,"ORGUNIT" ))!=NULL && xmlGetText( m )!=NULL ) { // The real vCard can have multiple <ORGUNIT/> but we will only display the first one
+ hasOrgunit = TRUE;
+ JSetStringT( hContact, "CompanyDepartment", xmlGetText( m ));
+ } }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("ROLE"))) {
+ if ( !hasRole && xmlGetText( n )!=NULL ) {
+ hasRole = TRUE;
+ JSetStringT( hContact, "Role", xmlGetText( n ));
+ }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("TITLE"))) {
+ if ( !hasTitle && xmlGetText( n )!=NULL ) {
+ hasTitle = TRUE;
+ JSetStringT( hContact, "CompanyPosition", xmlGetText( n ));
+ }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("DESC"))) {
+ if ( !hasDesc && xmlGetText( n )!=NULL ) {
+ hasDesc = TRUE;
+ TCHAR* szMemo = JabberUnixToDosT( xmlGetText( n ));
+ JSetStringT( hContact, "About", szMemo );
+ mir_free( szMemo );
+ }
+ }
+ else if ( !lstrcmp( xmlGetName( n ), _T("PHOTO")))
+ OnIqResultGetVcardPhoto( jid, n, hContact, hasPhoto );
+ } }
+
+ if ( hasFn && !hasNick ) {
+ TCHAR *name = JGetStringT( hContact, "FullName" );
+ TCHAR *nick = JGetStringT( hContact, "Nick" );
+ TCHAR *jidNick = JabberNickFromJID(jid);
+ if ( !nick || ( jidNick && !_tcsicmp( nick, jidNick )))
+ JSetStringT( hContact, "Nick", name );
+
+ mir_free( jidNick );
+ mir_free( nick );
+ mir_free( name );
+ }
+ if ( !hasFn )
+ JDeleteSetting( hContact, "FullName" );
+ // We are not deleting "Nick"
+// if ( !hasNick )
+// JDeleteSetting( hContact, "Nick" );
+ if ( !hasGiven )
+ JDeleteSetting( hContact, "FirstName" );
+ if ( !hasFamily )
+ JDeleteSetting( hContact, "LastName" );
+ if ( !hasMiddle )
+ JDeleteSetting( hContact, "MiddleName" );
+ if ( hContact != NULL ) {
+ while ( true ) {
+ if ( nEmail <= 0 )
+ JDeleteSetting( hContact, "e-mail" );
+ else {
+ char text[ 100 ];
+ sprintf( text, "e-mail%d", nEmail-1 );
+ if ( DBGetContactSettingString( hContact, m_szModuleName, text, &dbv )) break;
+ JFreeVariant( &dbv );
+ JDeleteSetting( hContact, text );
+ }
+ nEmail++;
+ }
+ }
+ else {
+ while ( true ) {
+ char text[ 100 ];
+ sprintf( text, "e-mail%d", nEmail );
+ if ( DBGetContactSettingString( NULL, m_szModuleName, text, &dbv )) break;
+ JFreeVariant( &dbv );
+ JDeleteSetting( NULL, text );
+ sprintf( text, "e-mailFlag%d", nEmail );
+ JDeleteSetting( NULL, text );
+ nEmail++;
+ } }
+
+ if ( !hasBday ) {
+ JDeleteSetting( hContact, "BirthYear" );
+ JDeleteSetting( hContact, "BirthMonth" );
+ JDeleteSetting( hContact, "BirthDay" );
+ JDeleteSetting( hContact, "BirthDate" );
+ JDeleteSetting( hContact, "Age" );
+ }
+ if ( !hasGender ) {
+ if ( hContact != NULL )
+ JDeleteSetting( hContact, "Gender" );
+ else
+ JDeleteSetting( NULL, "GenderString" );
+ }
+ if ( hContact != NULL ) {
+ if ( !hasPhone )
+ JDeleteSetting( hContact, "Phone" );
+ if ( !hasFax )
+ JDeleteSetting( hContact, "Fax" );
+ if ( !hasCell )
+ JDeleteSetting( hContact, "Cellular" );
+ }
+ else {
+ while ( true ) {
+ char text[ 100 ];
+ sprintf( text, "Phone%d", nPhone );
+ if ( DBGetContactSettingString( NULL, m_szModuleName, text, &dbv )) break;
+ JFreeVariant( &dbv );
+ JDeleteSetting( NULL, text );
+ sprintf( text, "PhoneFlag%d", nPhone );
+ JDeleteSetting( NULL, text );
+ nPhone++;
+ } }
+
+ if ( !hasHomeStreet )
+ JDeleteSetting( hContact, "Street" );
+ if ( !hasHomeStreet2 && hContact==NULL )
+ JDeleteSetting( hContact, "Street2" );
+ if ( !hasHomeLocality )
+ JDeleteSetting( hContact, "City" );
+ if ( !hasHomeRegion )
+ JDeleteSetting( hContact, "State" );
+ if ( !hasHomePcode )
+ JDeleteSetting( hContact, "ZIP" );
+ if ( !hasHomeCtry )
+ JDeleteSetting( hContact, "Country" );
+ if ( !hasWorkStreet )
+ JDeleteSetting( hContact, "CompanyStreet" );
+ if ( !hasWorkStreet2 && hContact==NULL )
+ JDeleteSetting( hContact, "CompanyStreet2" );
+ if ( !hasWorkLocality )
+ JDeleteSetting( hContact, "CompanyCity" );
+ if ( !hasWorkRegion )
+ JDeleteSetting( hContact, "CompanyState" );
+ if ( !hasWorkPcode )
+ JDeleteSetting( hContact, "CompanyZIP" );
+ if ( !hasWorkCtry )
+ JDeleteSetting( hContact, "CompanyCountry" );
+ if ( !hasUrl )
+ JDeleteSetting( hContact, "Homepage" );
+ if ( !hasOrgname )
+ JDeleteSetting( hContact, "Company" );
+ if ( !hasOrgunit )
+ JDeleteSetting( hContact, "CompanyDepartment" );
+ if ( !hasRole )
+ JDeleteSetting( hContact, "Role" );
+ if ( !hasTitle )
+ JDeleteSetting( hContact, "CompanyPosition" );
+ if ( !hasDesc )
+ JDeleteSetting( hContact, "About" );
+
+ if ( id == m_ThreadInfo->resolveID ) {
+ const TCHAR* p = _tcschr( jid, '@' );
+ ResolveTransportNicks(( p != NULL ) ? p+1 : jid );
+ }
+ else {
+ if (( hContact = HContactFromJID( jid )) != NULL )
+ JSendBroadcast( hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, ( HANDLE ) 1, 0 );
+ WindowNotify(WM_JABBER_REFRESH_VCARD);
+ }
+ }
+ else if ( !lstrcmp( type, _T("error"))) {
+ if (( hContact = HContactFromJID( jid )) != NULL )
+ JSendBroadcast( hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, ( HANDLE ) 1, 0 );
+ }
+}
+
+void CJabberProto::OnIqResultSetVcard( HXML iqNode )
+{
+ Log( "<iq/> iqIdSetVcard" );
+ if ( !xmlGetAttrValue( iqNode, _T("type")))
+ return;
+
+ WindowNotify(WM_JABBER_REFRESH_VCARD);
+}
+
+void CJabberProto::OnIqResultSetSearch( HXML iqNode )
+{
+ HXML queryNode, n;
+ const TCHAR* type, *jid;
+ int i, id;
+ JABBER_SEARCH_RESULT jsr;
+
+ Log( "<iq/> iqIdGetSearch" );
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+ if (( id = JabberGetPacketID( iqNode )) == -1 ) return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ if (( queryNode=xmlGetChild( iqNode , "query" )) == NULL ) return;
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+ for ( i=0; ; i++ ) {
+ HXML itemNode = xmlGetChild( queryNode ,i);
+ if ( !itemNode )
+ break;
+
+ if ( !lstrcmp( xmlGetName( itemNode ), _T("item"))) {
+ if (( jid=xmlGetAttrValue( itemNode, _T("jid"))) != NULL ) {
+ _tcsncpy( jsr.jid, jid, SIZEOF( jsr.jid ));
+ jsr.jid[ SIZEOF( jsr.jid )-1] = '\0';
+ jsr.hdr.id = (TCHAR*)jid;
+ Log( "Result jid = " TCHAR_STR_PARAM, jid );
+ if (( n=xmlGetChild( itemNode , "nick" ))!=NULL && xmlGetText( n )!=NULL )
+ jsr.hdr.nick = ( TCHAR* )xmlGetText( n );
+ else
+ jsr.hdr.nick = _T( "" );
+ if (( n=xmlGetChild( itemNode , "first" ))!=NULL && xmlGetText( n )!=NULL )
+ jsr.hdr.firstName = ( TCHAR* )xmlGetText( n );
+ else
+ jsr.hdr.firstName = _T( "" );
+ if (( n=xmlGetChild( itemNode , "last" ))!=NULL && xmlGetText( n )!=NULL )
+ jsr.hdr.lastName = ( TCHAR* )xmlGetText( n );
+ else
+ jsr.hdr.lastName = _T( "" );
+ if (( n=xmlGetChild( itemNode , "email" ))!=NULL && xmlGetText( n )!=NULL )
+ jsr.hdr.email = ( TCHAR* )xmlGetText( n );
+ else
+ jsr.hdr.email = _T( "" );
+ jsr.hdr.flags = PSR_TCHAR;
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, ( HANDLE ) id, ( LPARAM )&jsr );
+ } } }
+
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 );
+ }
+ else if ( !lstrcmp( type, _T("error")))
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 );
+}
+
+void CJabberProto::OnIqResultExtSearch( HXML iqNode )
+{
+ HXML queryNode;
+ const TCHAR* type;
+ int id;
+
+ Log( "<iq/> iqIdGetExtSearch" );
+ if (( type=xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+ if (( id = JabberGetPacketID( iqNode )) == -1 ) return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ if (( queryNode=xmlGetChild( iqNode , "query" )) == NULL ) return;
+ if (( queryNode=xmlGetChild( queryNode , "x" )) == NULL ) return;
+ for ( int i=0; ; i++ ) {
+ HXML itemNode = xmlGetChild( queryNode ,i);
+ if ( !itemNode )
+ break;
+ if ( lstrcmp( xmlGetName( itemNode ), _T("item")))
+ continue;
+
+ JABBER_SEARCH_RESULT jsr = { 0 };
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+ jsr.hdr.flags = PSR_TCHAR;
+// jsr.hdr.firstName = "";
+
+ for ( int j=0; ; j++ ) {
+ HXML fieldNode = xmlGetChild( itemNode ,j);
+ if ( !fieldNode )
+ break;
+
+ if ( lstrcmp( xmlGetName( fieldNode ), _T("field")))
+ continue;
+
+ const TCHAR* fieldName = xmlGetAttrValue( fieldNode, _T("var"));
+ if ( fieldName == NULL )
+ continue;
+
+ HXML n = xmlGetChild( fieldNode , "value" );
+ if ( n == NULL )
+ continue;
+
+ if ( !lstrcmp( fieldName, _T("jid"))) {
+ _tcsncpy( jsr.jid, xmlGetText( n ), SIZEOF( jsr.jid ));
+ jsr.jid[SIZEOF( jsr.jid )-1] = '\0';
+ Log( "Result jid = " TCHAR_STR_PARAM, jsr.jid );
+ }
+ else if ( !lstrcmp( fieldName, _T("nickname")))
+ jsr.hdr.nick = ( xmlGetText( n ) != NULL ) ? ( TCHAR* )xmlGetText( n ) : _T( "" );
+ else if ( !lstrcmp( fieldName, _T("fn")))
+ jsr.hdr.firstName = ( xmlGetText( n ) != NULL ) ? ( TCHAR* )xmlGetText( n ) : _T( "" );
+ else if ( !lstrcmp( fieldName, _T("given")))
+ jsr.hdr.firstName = ( xmlGetText( n ) != NULL ) ? ( TCHAR* )xmlGetText( n ) : _T( "" );
+ else if ( !lstrcmp( fieldName, _T("family")))
+ jsr.hdr.lastName = ( xmlGetText( n ) != NULL ) ? ( TCHAR* )xmlGetText( n ) : _T( "" );
+ else if ( !lstrcmp( fieldName, _T("email")))
+ jsr.hdr.email = ( xmlGetText( n ) != NULL ) ? ( TCHAR* )xmlGetText( n ) : _T( "" );
+ }
+
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, ( HANDLE ) id, ( LPARAM )&jsr );
+ }
+
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 );
+ }
+ else if ( !lstrcmp( type, _T("error")))
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 );
+}
+
+void CJabberProto::OnIqResultSetPassword( HXML iqNode )
+{
+ Log( "<iq/> iqIdSetPassword" );
+
+ const TCHAR* type = xmlGetAttrValue( iqNode, _T("type"));
+ if ( type == NULL )
+ return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ _tcsncpy( m_ThreadInfo->password, m_ThreadInfo->newPassword, SIZEOF( m_ThreadInfo->password ));
+ MessageBox( NULL, TranslateT( "Password is successfully changed. Don't forget to update your password in the Jabber protocol option." ), TranslateT( "Change Password" ), MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND );
+ }
+ else if ( !lstrcmp( type, _T("error")))
+ MessageBox( NULL, TranslateT( "Password cannot be changed." ), TranslateT( "Change Password" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND );
+}
+/*
+void CJabberProto::OnIqResultDiscoAgentItems( HXML iqNode, void *userdata )
+{
+ if ( !m_options.EnableAvatars )
+ return;
+}
+*/
+void CJabberProto::OnIqResultGetVCardAvatar( HXML iqNode )
+{
+ const TCHAR* type;
+
+ Log( "<iq/> OnIqResultGetVCardAvatar" );
+
+ const TCHAR* from = xmlGetAttrValue( iqNode, _T("from"));
+ if ( from == NULL )
+ return;
+ HANDLE hContact = HContactFromJID( from );
+ if ( hContact == NULL )
+ return;
+
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+ if ( _tcscmp( type, _T("result"))) return;
+
+ HXML vCard = xmlGetChild( iqNode , "vCard" );
+ if (vCard == NULL) return;
+ vCard = xmlGetChild( vCard , "PHOTO" );
+ if (vCard == NULL) return;
+
+ if ( xmlGetChildCount( vCard ) == 0 ) {
+ JDeleteSetting( hContact, "AvatarHash" );
+ DBVARIANT dbv = {0};
+ if ( !JGetStringT( hContact, "AvatarSaved", &dbv )) {
+ JFreeVariant( &dbv );
+ JDeleteSetting( hContact, "AvatarSaved" );
+ JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, NULL, NULL );
+ }
+
+ return;
+ }
+
+ HXML typeNode = xmlGetChild( vCard , "TYPE" );
+ const TCHAR* mimeType = NULL;
+ if (typeNode != NULL) mimeType = xmlGetText( typeNode );
+ HXML n = xmlGetChild( vCard , "BINVAL" );
+ if ( n == NULL )
+ return;
+
+ JSetByte( hContact, "AvatarXVcard", 1 );
+ OnIqResultGotAvatar( hContact, n, mimeType);
+}
+
+void CJabberProto::OnIqResultGetClientAvatar( HXML iqNode )
+{
+ const TCHAR* type;
+
+ Log( "<iq/> iqIdResultGetClientAvatar" );
+
+ const TCHAR* from = xmlGetAttrValue( iqNode, _T("from"));
+ if ( from == NULL )
+ return;
+ HANDLE hContact = HContactFromJID( from );
+ if ( hContact == NULL )
+ return;
+
+ HXML n = NULL;
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) != NULL && !_tcscmp( type, _T("result"))) {
+ HXML queryNode = xmlGetChild( iqNode , "query" );
+ if ( queryNode != NULL ) {
+ const TCHAR* xmlns = xmlGetAttrValue( queryNode, _T("xmlns"));
+ if ( !lstrcmp( xmlns, _T(JABBER_FEAT_AVATAR))) {
+ n = xmlGetChild( queryNode , "data" );
+ }
+ }
+ }
+
+ if ( n == NULL ) {
+ TCHAR szJid[ JABBER_MAX_JID_LEN ];
+ lstrcpyn(szJid, from, SIZEOF(szJid));
+ TCHAR *res = _tcschr(szJid, _T('/'));
+ if ( res != NULL )
+ *res = 0;
+
+ // Try server stored avatar
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetServerAvatar );
+
+ XmlNodeIq iq( _T("get"), iqId, szJid );
+ iq << XQUERY( _T(JABBER_FEAT_SERVER_AVATAR));
+ m_ThreadInfo->send( iq );
+
+ return;
+ }
+
+ const TCHAR* mimeType = mimeType = xmlGetAttrValue( n, _T("mimetype"));
+
+ OnIqResultGotAvatar( hContact, n, mimeType);
+}
+
+
+void CJabberProto::OnIqResultGetServerAvatar( HXML iqNode )
+{
+ const TCHAR* type;
+
+ Log( "<iq/> iqIdResultGetServerAvatar" );
+
+ const TCHAR* from = xmlGetAttrValue( iqNode, _T("from"));
+ if ( from == NULL )
+ return;
+ HANDLE hContact = HContactFromJID( from );
+ if ( hContact == NULL )
+ return;
+
+ HXML n = NULL;
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) != NULL && !_tcscmp( type, _T("result"))) {
+ HXML queryNode = xmlGetChild( iqNode , "query" );
+ if ( queryNode != NULL ) {
+ const TCHAR* xmlns = xmlGetAttrValue( queryNode, _T("xmlns"));
+ if ( !lstrcmp( xmlns, _T(JABBER_FEAT_SERVER_AVATAR))) {
+ n = xmlGetChild( queryNode , "data" );
+ }
+ }
+ }
+
+ if ( n == NULL ) {
+ TCHAR szJid[ JABBER_MAX_JID_LEN ];
+ lstrcpyn(szJid, from, SIZEOF(szJid));
+ TCHAR *res = _tcschr(szJid, _T('/'));
+ if ( res != NULL )
+ *res = 0;
+
+ // Try VCard photo
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetVCardAvatar );
+
+ XmlNodeIq iq( _T("get"), iqId, szJid );
+ iq << XCHILDNS( _T("vCard"), _T(JABBER_FEAT_VCARD_TEMP));
+ m_ThreadInfo->send( iq );
+
+ return;
+ }
+
+ const TCHAR* mimeType = xmlGetAttrValue( n, _T("mimetype"));
+
+ OnIqResultGotAvatar( hContact, n, mimeType);
+}
+
+
+void CJabberProto::OnIqResultGotAvatar( HANDLE hContact, HXML n, const TCHAR* mimeType )
+{
+ int resultLen = 0;
+ char* body = JabberBase64DecodeT( xmlGetText( n ), &resultLen );
+
+ int pictureType;
+ if ( mimeType != NULL ) {
+ if ( !lstrcmp( mimeType, _T("image/jpeg"))) pictureType = PA_FORMAT_JPEG;
+ else if ( !lstrcmp( mimeType, _T("image/png"))) pictureType = PA_FORMAT_PNG;
+ else if ( !lstrcmp( mimeType, _T("image/gif"))) pictureType = PA_FORMAT_GIF;
+ else if ( !lstrcmp( mimeType, _T("image/bmp"))) pictureType = PA_FORMAT_BMP;
+ else {
+LBL_ErrFormat:
+ Log( "Invalid mime type specified for picture: " TCHAR_STR_PARAM, mimeType );
+ mir_free( body );
+ return;
+ } }
+ else if (( pictureType = JabberGetPictureType( body )) == PA_FORMAT_UNKNOWN )
+ goto LBL_ErrFormat;
+
+ TCHAR tszFileName[ MAX_PATH ];
+
+ PROTO_AVATAR_INFORMATIONT AI;
+ AI.cbSize = sizeof AI;
+ AI.format = pictureType;
+ AI.hContact = hContact;
+
+ if ( JGetByte( hContact, "AvatarType", PA_FORMAT_UNKNOWN ) != (unsigned char)pictureType ) {
+ GetAvatarFileName( hContact, tszFileName, SIZEOF(tszFileName));
+ DeleteFile( tszFileName );
+ }
+
+ JSetByte( hContact, "AvatarType", pictureType );
+
+ char buffer[ 41 ];
+ mir_sha1_byte_t digest[20];
+ mir_sha1_ctx sha;
+ mir_sha1_init( &sha );
+ mir_sha1_append( &sha, ( mir_sha1_byte_t* )body, resultLen );
+ mir_sha1_finish( &sha, digest );
+ for ( int i=0; i<20; i++ )
+ sprintf( buffer+( i<<1 ), "%02x", digest[i] );
+
+ GetAvatarFileName( hContact, tszFileName, SIZEOF(tszFileName));
+ _tcsncpy( AI.filename, tszFileName, SIZEOF(AI.filename));
+
+ FILE* out = _tfopen( tszFileName, _T("wb"));
+ if ( out != NULL ) {
+ fwrite( body, resultLen, 1, out );
+ fclose( out );
+ JSetString( hContact, "AvatarSaved", buffer );
+ JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, HANDLE( &AI ), NULL );
+ Log("Broadcast new avatar: %s",AI.filename);
+ }
+ else JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, HANDLE( &AI ), NULL );
+
+ mir_free( body );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Bookmarks
+
+void CJabberProto::OnIqResultDiscoBookmarks( HXML iqNode )
+{
+ HXML storageNode;//, nickNode, passNode;
+ const TCHAR* type, *jid, *name;
+
+ // RECVED: list of bookmarks
+ // ACTION: refresh bookmarks dialog
+ Log( "<iq/> iqIdGetBookmarks" );
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ if ( m_ThreadInfo && !( m_ThreadInfo->jabberServerCaps & JABBER_CAPS_PRIVATE_STORAGE )) {
+ m_ThreadInfo->jabberServerCaps |= JABBER_CAPS_PRIVATE_STORAGE;
+ EnableMenuItems( TRUE );
+ }
+
+ if ( storageNode = XPathT( iqNode, "query/storage[@xmlns='storage:bookmarks']" )) {
+ ListRemoveList( LIST_BOOKMARK );
+
+ HXML itemNode;
+ for ( int i = 0; itemNode = xmlGetChild( storageNode, i ); i++ ) {
+ if ( name = xmlGetName( itemNode)) {
+ if ( !_tcscmp( name, _T("conference")) && (jid = xmlGetAttrValue( itemNode, _T("jid")))) {
+ JABBER_LIST_ITEM* item = ListAdd( LIST_BOOKMARK, jid );
+ item->name = mir_tstrdup( xmlGetAttrValue( itemNode, _T("name")));
+ item->type = mir_tstrdup( _T( "conference" ));
+ item->bUseResource = TRUE;
+ item->nick = mir_tstrdup( XPathT( itemNode, "nick" ));
+ item->password = mir_tstrdup( XPathT( itemNode, "password" ));
+
+ const TCHAR* autoJ = xmlGetAttrValue( itemNode, _T("autojoin"));
+ if ( autoJ != NULL )
+ item->bAutoJoin = ( !lstrcmp( autoJ, _T("true")) || !lstrcmp( autoJ, _T("1"))) ? true : false;
+ }
+ else if ( !_tcscmp( name, _T("url")) && (jid = xmlGetAttrValue( itemNode, _T("url") ))) {
+ JABBER_LIST_ITEM* item = ListAdd( LIST_BOOKMARK, jid );
+ item->bUseResource = TRUE;
+ item->name = mir_tstrdup( xmlGetAttrValue( itemNode, _T("name")));
+ item->type = mir_tstrdup( _T("url"));
+ }
+ }
+ }
+
+ UI_SAFE_NOTIFY(m_pDlgBookmarks, WM_JABBER_REFRESH);
+ m_ThreadInfo->bBookmarksLoaded = TRUE;
+ OnProcessLoginRq(m_ThreadInfo, JABBER_LOGIN_BOOKMARKS);
+ }
+ }
+ else if ( !lstrcmp( type, _T("error"))) {
+ if ( m_ThreadInfo->jabberServerCaps & JABBER_CAPS_PRIVATE_STORAGE ) {
+ m_ThreadInfo->jabberServerCaps &= ~JABBER_CAPS_PRIVATE_STORAGE;
+ EnableMenuItems( TRUE );
+ UI_SAFE_NOTIFY(m_pDlgBookmarks, WM_JABBER_ACTIVATE);
+ return;
+ }
+} }
+
+void CJabberProto::SetBookmarkRequest (XmlNodeIq& iq)
+{
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVATE_STORAGE));
+ HXML storage = query << XCHILDNS( _T("storage"), _T("storage:bookmarks"));
+
+ LISTFOREACH(i, this, LIST_BOOKMARK)
+ {
+ JABBER_LIST_ITEM* item = ListGetItemPtrFromIndex( i );
+ if ( item == NULL )
+ continue;
+
+ if ( item->jid == NULL )
+ continue;
+ if ( !lstrcmp( item->type, _T("conference"))) {
+ HXML itemNode = storage << XCHILD( _T("conference")) << XATTR( _T("jid"), item->jid );
+ if ( item->name )
+ itemNode << XATTR( _T("name"), item->name );
+ if ( item->bAutoJoin )
+ itemNode << XATTRI( _T("autojoin"), 1 );
+ if ( item->nick )
+ itemNode << XCHILD( _T("nick"), item->nick );
+ if ( item->password )
+ itemNode << XCHILD( _T("password"), item->password );
+ }
+ if ( !lstrcmp( item->type, _T("url"))) {
+ HXML itemNode = storage << XCHILD( _T("url")) << XATTR( _T("url"), item->jid );
+ if ( item->name )
+ itemNode << XATTR( _T("name"), item->name );
+ }
+ }
+}
+
+void CJabberProto::OnIqResultSetBookmarks( HXML iqNode )
+{
+ // RECVED: server's response
+ // ACTION: refresh bookmarks list dialog
+
+ Log( "<iq/> iqIdSetBookmarks" );
+
+ const TCHAR* type = xmlGetAttrValue( iqNode, _T("type"));
+ if ( type == NULL )
+ return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ UI_SAFE_NOTIFY(m_pDlgBookmarks, WM_JABBER_REFRESH);
+ }
+ else if ( !lstrcmp( type, _T("error"))) {
+ HXML errorNode = xmlGetChild( iqNode , "error" );
+ TCHAR* str = JabberErrorMsg( errorNode );
+ MessageBox( NULL, str, TranslateT( "Jabber Bookmarks Error" ), MB_OK|MB_SETFOREGROUND );
+ mir_free( str );
+ UI_SAFE_NOTIFY(m_pDlgBookmarks, WM_JABBER_ACTIVATE);
+} }
+
+// last activity (XEP-0012) support
+void CJabberProto::OnIqResultLastActivity( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ JABBER_RESOURCE_STATUS *r = ResourceInfoFromJID( pInfo->m_szFrom );
+ if ( !r )
+ return;
+
+ time_t lastActivity = -1;
+ if ( pInfo->m_nIqType == JABBER_IQ_TYPE_RESULT ) {
+ LPCTSTR szSeconds = XPathT( iqNode, "query[@xmlns='jabber:iq:last']/@seconds" );
+ if ( szSeconds ) {
+ int nSeconds = _ttoi( szSeconds );
+ if ( nSeconds > 0 )
+ lastActivity = time( 0 ) - nSeconds;
+ }
+
+ LPCTSTR szLastStatusMessage = XPathT( iqNode, "query[@xmlns='jabber:iq:last']" );
+ if ( szLastStatusMessage ) // replace only if it exists
+ replaceStrT( r->statusMessage, szLastStatusMessage );
+ }
+
+ r->idleStartTime = lastActivity;
+
+ JabberUserInfoUpdate(pInfo->GetHContact());
+}
+
+// entity time (XEP-0202) support
+void CJabberProto::OnIqResultEntityTime( HXML pIqNode, CJabberIqInfo* pInfo )
+{
+ if ( !pInfo->m_hContact )
+ return;
+
+ if ( pInfo->m_nIqType == JABBER_IQ_TYPE_RESULT ) {
+ LPCTSTR szTzo = XPathFmt( pIqNode, _T("time[@xmlns='%s']/tzo"), _T( JABBER_FEAT_ENTITY_TIME ));
+ if ( szTzo && szTzo[0] ) {
+ LPCTSTR szMin = _tcschr( szTzo, ':' );
+ int nTz = _ttoi( szTzo ) * -2;
+ nTz += ( nTz < 0 ? -1 : 1 ) * ( szMin ? _ttoi( szMin + 1 ) / 30 : 0 );
+
+ TIME_ZONE_INFORMATION tzinfo;
+ if ( GetTimeZoneInformation( &tzinfo ) == TIME_ZONE_ID_DAYLIGHT )
+ nTz -= tzinfo.DaylightBias / 30;
+
+ JSetByte( pInfo->m_hContact, "Timezone", (signed char)nTz );
+
+ LPCTSTR szTz = XPathFmt( pIqNode, _T("time[@xmlns='%s']/tz"), _T( JABBER_FEAT_ENTITY_TIME ));
+ if (szTz)
+ JSetStringT( pInfo->m_hContact, "TzName", szTz );
+ else
+ JDeleteSetting( pInfo->m_hContact, "TzName" );
+ return;
+ }
+ }
+ else if ( pInfo->m_nIqType == JABBER_IQ_TYPE_ERROR )
+ {
+ if ( JGetWord( pInfo->m_hContact, "Status", ID_STATUS_OFFLINE ) == ID_STATUS_OFFLINE )
+ return;
+ }
+
+ JDeleteSetting( pInfo->m_hContact, "Timezone" );
+ JDeleteSetting( pInfo->m_hContact, "TzName" );
+}
diff --git a/protocols/JabberG/src/jabber_iqid_muc.cpp b/protocols/JabberG/src/jabber_iqid_muc.cpp new file mode 100644 index 0000000000..3621506d29 --- /dev/null +++ b/protocols/JabberG/src/jabber_iqid_muc.cpp @@ -0,0 +1,560 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_list.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+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 );
+} }
+
+void LaunchForm(HXML node);
+
+void CJabberProto::OnIqResultGetMuc( HXML iqNode )
+{
+ HXML queryNode, xNode;
+ const TCHAR *type, *from, *str;
+
+ // RECVED: room config form
+ // ACTION: show the form
+ Log( "<iq/> iqIdGetMuc" );
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+ if (( from = xmlGetAttrValue( iqNode, _T("from"))) == NULL ) return;
+
+ if ( !_tcscmp( type, _T("result"))) {
+ if (( queryNode = xmlGetChild( iqNode , "query" )) != NULL ) {
+ str = xmlGetAttrValue( queryNode, _T("xmlns"));
+ if ( !lstrcmp( str, _T("http://jabber.org/protocol/muc#owner" ))) {
+ if (( xNode = xmlGetChild( queryNode , "x" )) != NULL ) {
+ str = xmlGetAttrValue( xNode, _T("xmlns"));
+ if ( !lstrcmp( str, _T(JABBER_FEAT_DATA_FORMS)))
+ //LaunchForm(xNode);
+ FormCreateDialog( xNode, _T("Jabber Conference Room Configuration"), &CJabberProto::SetMucConfig, mir_tstrdup( from ));
+} } } } }
+
+static void sttFillJidList(HWND hwndDlg)
+{
+ JABBER_MUC_JIDLIST_INFO *jidListInfo;
+ HXML iqNode, queryNode;
+ const TCHAR* from, *jid, *reason, *nick;
+ LVITEM lvi;
+ HWND hwndList;
+ int count, i;
+
+ TCHAR *filter = NULL;
+ if (GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_FILTER), GWLP_USERDATA))
+ {
+ int filterLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_FILTER)) + 1;
+ filter = (TCHAR *)_alloca(filterLength * sizeof(TCHAR));
+ GetDlgItemText(hwndDlg, IDC_FILTER, filter, filterLength);
+ }
+
+ jidListInfo = ( JABBER_MUC_JIDLIST_INFO * ) GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+ if ( !jidListInfo )
+ return;
+
+ hwndList = GetDlgItem( hwndDlg, IDC_LIST );
+ SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
+
+ count = ListView_GetItemCount( hwndList );
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ for ( i=0; i<count; i++ ) {
+ lvi.iItem = i;
+ if ( ListView_GetItem( hwndList, &lvi ) == TRUE ) {
+ if ( lvi.lParam!=( LPARAM )( -1 ) && lvi.lParam!=( LPARAM )( NULL )) {
+ mir_free(( void * ) lvi.lParam );
+ }
+ }
+ }
+ ListView_DeleteAllItems( hwndList );
+
+ // Populate displayed list from iqNode
+ if (( iqNode = jidListInfo->iqNode ) != NULL ) {
+ if (( from = xmlGetAttrValue( iqNode, _T("from"))) != NULL ) {
+ if (( queryNode = xmlGetChild( iqNode , "query" )) != NULL ) {
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iSubItem = 0;
+ lvi.iItem = 0;
+ for ( i=0; ; i++ ) {
+ HXML itemNode = xmlGetChild( queryNode ,i);
+ if ( !itemNode )
+ break;
+
+ if (( jid = xmlGetAttrValue( itemNode, _T("jid"))) != NULL ) {
+ lvi.pszText = ( TCHAR* )jid;
+ if ( jidListInfo->type == MUC_BANLIST ) {
+ if (( reason = xmlGetText(xmlGetChild( itemNode , "reason" ))) != NULL ) {
+ TCHAR jidreason[ JABBER_MAX_JID_LEN + 256 ];
+ mir_sntprintf( jidreason, SIZEOF( jidreason ), _T("%s (%s)") , jid, reason );
+ lvi.pszText = jidreason;
+ } }
+
+ if ( jidListInfo->type == MUC_VOICELIST || jidListInfo->type == MUC_MODERATORLIST ) {
+ if (( nick = xmlGetAttrValue( itemNode, _T("nick"))) != NULL ) {
+ TCHAR nickjid[ JABBER_MAX_JID_LEN + 256 ];
+ mir_sntprintf( nickjid, SIZEOF( nickjid ), _T("%s (%s)") , nick, jid );
+ lvi.pszText = nickjid;
+ } }
+
+ if (filter && *filter && !JabberStrIStr(lvi.pszText, filter))
+ continue;
+
+ lvi.lParam = ( LPARAM )mir_tstrdup( jid );
+
+ ListView_InsertItem( hwndList, &lvi );
+ lvi.iItem++;
+ } } } } }
+
+ lvi.mask = LVIF_PARAM;
+ lvi.lParam = ( LPARAM )( -1 );
+ ListView_InsertItem( hwndList, &lvi );
+
+ SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
+ RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE);
+}
+
+static int sttJidListResizer(HWND, LPARAM, UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId)
+ {
+ case IDC_LIST:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ case IDC_FILTER:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM|RD_ANCHORX_WIDTH;
+ case IDC_BTN_FILTERRESET:
+ case IDC_BTN_FILTERAPPLY:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP;
+}
+
+static INT_PTR CALLBACK JabberMucJidListDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ JABBER_MUC_JIDLIST_INFO* dat = (JABBER_MUC_JIDLIST_INFO*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch( msg ) {
+ case WM_INITDIALOG:
+ {
+ LVCOLUMN lvc;
+ RECT rc;
+ HWND hwndList;
+
+ TranslateDialogDefault( hwndDlg );
+
+ hwndList = GetDlgItem( hwndDlg, IDC_LIST );
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
+ GetClientRect( hwndList, &rc );
+ //rc.right -= GetSystemMetrics( SM_CXVSCROLL );
+ lvc.mask = LVCF_WIDTH;
+ lvc.cx = rc.right - 20;
+ ListView_InsertColumn( hwndList, 0, &lvc );
+ lvc.cx = 20;
+ ListView_InsertColumn( hwndList, 1, &lvc );
+ SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, lParam );
+ dat = (JABBER_MUC_JIDLIST_INFO*)lParam;
+
+ static struct
+ {
+ int idc;
+ char *title;
+ char *icon;
+ bool push;
+ } buttons[] =
+ {
+ {IDC_BTN_FILTERAPPLY, "Apply filter", "sd_filter_apply", false},
+ {IDC_BTN_FILTERRESET, "Reset filter", "sd_filter_reset", false},
+ };
+ for (int i = 0; i < SIZEOF(buttons); ++i)
+ {
+ SendDlgItemMessage(hwndDlg, buttons[i].idc, BM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->ppro->LoadIconEx(buttons[i].icon));
+ SendDlgItemMessage(hwndDlg, buttons[i].idc, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, buttons[i].idc, BUTTONADDTOOLTIP, (WPARAM)buttons[i].title, 0);
+ if (buttons[i].push)
+ SendDlgItemMessage(hwndDlg, buttons[i].idc, BUTTONSETASPUSHBTN, TRUE, 0);
+ }
+
+ Utils_RestoreWindowPosition(hwndDlg, NULL, dat->ppro->m_szModuleName, "jidListWnd_");
+ }
+ return TRUE;
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd = {0};
+ urd.cbSize = sizeof(urd);
+ urd.hInstance = hInst;
+ urd.hwndDlg = hwndDlg;
+ urd.lpTemplate = MAKEINTRESOURCEA(IDD_JIDLIST);
+ urd.pfnResizer = sttJidListResizer;
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd);
+
+ RECT listrc;
+ LVCOLUMN lvc;
+ HWND hwndList = GetDlgItem( hwndDlg, IDC_LIST );
+ GetClientRect( hwndList, &listrc );
+ lvc.mask = LVCF_WIDTH;
+ //listrc.right -= GetSystemMetrics( SM_CXVSCROLL );
+ lvc.cx = listrc.right - 20;
+ SendMessage(hwndList, LVM_SETCOLUMN, 0, (LPARAM)&lvc);
+ break;
+ }
+ break;
+
+ case WM_JABBER_REFRESH:
+ {
+ // lParam is ( JABBER_MUC_JIDLIST_INFO * )
+ HXML iqNode, queryNode;
+ const TCHAR* from;
+ TCHAR title[256];
+
+ // Clear current GWL_USERDATA, if any
+ if ( dat != NULL )
+ delete dat;
+
+ // Set new GWL_USERDATA
+ dat = ( JABBER_MUC_JIDLIST_INFO * ) lParam;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, ( LONG_PTR ) dat );
+
+ // Populate displayed list from iqNode
+ lstrcpyn( title, TranslateT( "JID List" ), SIZEOF( title ));
+ if (( dat=( JABBER_MUC_JIDLIST_INFO * ) lParam ) != NULL ) {
+ if (( iqNode = dat->iqNode ) != NULL ) {
+ if (( from = xmlGetAttrValue( iqNode, _T("from"))) != NULL ) {
+ dat->roomJid = mir_tstrdup( from );
+
+ if (( queryNode = xmlGetChild( iqNode , "query" )) != NULL ) {
+ TCHAR* localFrom = mir_tstrdup( from );
+ mir_sntprintf( title, SIZEOF( title ), TranslateT("%s, %d items (%s)"),
+ ( dat->type == MUC_VOICELIST ) ? TranslateT( "Voice List" ) :
+ ( dat->type == MUC_MEMBERLIST ) ? TranslateT( "Member List" ) :
+ ( dat->type == MUC_MODERATORLIST ) ? TranslateT( "Moderator List" ) :
+ ( dat->type == MUC_BANLIST ) ? TranslateT( "Ban List" ) :
+ ( dat->type == MUC_ADMINLIST ) ? TranslateT( "Admin List" ) :
+ ( dat->type == MUC_OWNERLIST ) ? TranslateT( "Owner List" ) :
+ TranslateT( "JID List" ), xmlGetChildCount(queryNode), localFrom );
+ mir_free( localFrom );
+ } } } }
+ SetWindowText( hwndDlg, title );
+
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_FILTER), GWLP_USERDATA, 0);
+ sttFillJidList(hwndDlg);
+ }
+ break;
+ case WM_NOTIFY:
+ if (( ( LPNMHDR )lParam )->idFrom == IDC_LIST ) {
+ switch (( ( LPNMHDR )lParam )->code ) {
+ case NM_CUSTOMDRAW:
+ {
+ NMLVCUSTOMDRAW *nm = ( NMLVCUSTOMDRAW * ) lParam;
+
+ switch ( nm->nmcd.dwDrawStage ) {
+ case CDDS_PREPAINT:
+ case CDDS_ITEMPREPAINT:
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW );
+ return TRUE;
+ case CDDS_SUBITEM|CDDS_ITEMPREPAINT:
+ {
+ RECT rc;
+ HICON hIcon;
+
+ ListView_GetSubItemRect( nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, LVIR_LABEL, &rc );
+ if ( nm->iSubItem == 1 ) {
+ if ( nm->nmcd.lItemlParam == ( LPARAM )( -1 ))
+ hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_ADDCONTACT ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 );
+ else
+ hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_DELETE ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 );
+ DrawIconEx( nm->nmcd.hdc, ( rc.left+rc.right-GetSystemMetrics( SM_CXSMICON ))/2, ( rc.top+rc.bottom-GetSystemMetrics( SM_CYSMICON ))/2,hIcon, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0, GetSysColorBrush(COLOR_WINDOW), DI_NORMAL );
+ DestroyIcon( hIcon );
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT );
+ return TRUE;
+ } } } }
+ break;
+ case NM_CLICK:
+ {
+ NMLISTVIEW *nm = ( NMLISTVIEW * ) lParam;
+ LVITEM lvi;
+ LVHITTESTINFO hti;
+ TCHAR text[128];
+
+ if ( nm->iSubItem < 1 )
+ break;
+
+ hti.pt.x = ( short ) LOWORD( GetMessagePos());
+ hti.pt.y = ( short ) HIWORD( GetMessagePos());
+ ScreenToClient( nm->hdr.hwndFrom, &hti.pt );
+ if ( ListView_SubItemHitTest( nm->hdr.hwndFrom, &hti ) == -1 )
+ break;
+
+ if ( hti.iSubItem != 1 )
+ break;
+
+ lvi.mask = LVIF_PARAM | LVIF_TEXT;
+ lvi.iItem = hti.iItem;
+ lvi.iSubItem = 0;
+ lvi.pszText = text;
+ lvi.cchTextMax = sizeof( text );
+ ListView_GetItem( nm->hdr.hwndFrom, &lvi );
+ if ( lvi.lParam == ( LPARAM )( -1 )) {
+ TCHAR szBuffer[ 1024 ];
+ _tcscpy( szBuffer, dat->type2str());
+ if ( !dat->ppro->EnterString(szBuffer, SIZEOF(szBuffer), NULL, JES_COMBO, "gcAddNick_"))
+ break;
+
+ // Trim leading and trailing whitespaces
+ TCHAR *p = szBuffer, *q;
+ for ( p = szBuffer; *p!='\0' && isspace( BYTE( *p )); p++);
+ for ( q = p; *q!='\0' && !isspace( BYTE( *q )); q++);
+ if (*q != '\0') *q = '\0';
+ if (*p == '\0')
+ break;
+ TCHAR rsn[ 1024 ];
+ _tcscpy( rsn, dat->type2str());
+ if ( dat->type == MUC_BANLIST ) {
+ dat->ppro->EnterString(rsn, SIZEOF(rsn), TranslateT("Reason to ban") , JES_COMBO, "gcAddReason_");
+ if ( szBuffer )
+ dat->ppro->AddMucListItem( dat, p , rsn);
+ else
+ dat->ppro->AddMucListItem( dat, p );
+ }
+ else dat->ppro->AddMucListItem( dat, p );
+ }
+ else {
+ //delete
+ TCHAR msgText[128];
+
+ mir_sntprintf( msgText, SIZEOF( msgText ), _T("%s %s?"), TranslateT( "Removing" ), text );
+ if ( MessageBox( hwndDlg, msgText, dat->type2str(), MB_YESNO|MB_SETFOREGROUND ) == IDYES ) {
+ dat->ppro->DeleteMucListItem( dat, ( TCHAR* )lvi.lParam );
+ mir_free(( void * )lvi.lParam );
+ ListView_DeleteItem( nm->hdr.hwndFrom, hti.iItem );
+ } } }
+ break;
+ }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ if ((LOWORD(wParam) == IDC_BTN_FILTERAPPLY) ||
+ ((LOWORD(wParam) == IDOK) && (GetFocus() == GetDlgItem(hwndDlg, IDC_FILTER))))
+ {
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_FILTER), GWLP_USERDATA, 1);
+ sttFillJidList(hwndDlg);
+ } else
+ if ((LOWORD(wParam) == IDC_BTN_FILTERRESET) ||
+ ((LOWORD(wParam) == IDCANCEL) && (GetFocus() == GetDlgItem(hwndDlg, IDC_FILTER))))
+ {
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_FILTER), GWLP_USERDATA, 0);
+ sttFillJidList(hwndDlg);
+ }
+ break;
+/* case WM_SETCURSOR:
+ if ( LOWORD( LPARAM )!= HTCLIENT ) break;
+ if ( GetForegroundWindow() == GetParent( hwndDlg )) {
+ POINT pt;
+ GetCursorPos( &pt );
+ ScreenToClient( hwndDlg,&pt );
+ SetFocus( ChildWindowFromPoint( hwndDlg,pt )); //ugly hack because listviews ignore their first click
+ }
+ break;
+*/ case WM_CLOSE:
+ {
+ HWND hwndList;
+ int count, i;
+ LVITEM lvi;
+
+ // Free lParam of the displayed list items
+ hwndList = GetDlgItem( hwndDlg, IDC_LIST );
+ count = ListView_GetItemCount( hwndList );
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ for ( i=0; i<count; i++ ) {
+ lvi.iItem = i;
+ if ( ListView_GetItem( hwndList, &lvi ) == TRUE ) {
+ if ( lvi.lParam!=( LPARAM )( -1 ) && lvi.lParam!=( LPARAM )( NULL )) {
+ mir_free(( void * ) lvi.lParam );
+ }
+ }
+ }
+ ListView_DeleteAllItems( hwndList );
+
+ CJabberProto* ppro = dat->ppro;
+ switch ( dat->type ) {
+ case MUC_VOICELIST:
+ ppro->m_hwndMucVoiceList = NULL;
+ break;
+ case MUC_MEMBERLIST:
+ ppro->m_hwndMucMemberList = NULL;
+ break;
+ case MUC_MODERATORLIST:
+ ppro->m_hwndMucModeratorList = NULL;
+ break;
+ case MUC_BANLIST:
+ ppro->m_hwndMucBanList = NULL;
+ break;
+ case MUC_ADMINLIST:
+ ppro->m_hwndMucAdminList = NULL;
+ break;
+ case MUC_OWNERLIST:
+ ppro->m_hwndMucOwnerList = NULL;
+ break;
+ }
+
+ DestroyWindow( hwndDlg );
+ }
+ break;
+
+ case WM_DESTROY:
+ // Clear GWL_USERDATA
+ if ( dat != NULL ) {
+ Utils_SaveWindowPosition(hwndDlg, NULL, dat->ppro->m_szModuleName, "jidListWnd_");
+ delete dat;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static void CALLBACK JabberMucJidListCreateDialogApcProc( void* param )
+{
+ HXML iqNode, queryNode;
+ const TCHAR* from;
+ HWND *pHwndJidList;
+ JABBER_MUC_JIDLIST_INFO *jidListInfo = (JABBER_MUC_JIDLIST_INFO *)param;
+
+ if ( jidListInfo == NULL ) return;
+ if (( iqNode = jidListInfo->iqNode ) == NULL ) return;
+ if (( from = xmlGetAttrValue( iqNode, _T("from"))) == NULL ) return;
+ if (( queryNode = xmlGetChild( iqNode , "query" )) == NULL ) return;
+
+ CJabberProto* ppro = jidListInfo->ppro;
+ switch ( jidListInfo->type ) {
+ case MUC_VOICELIST:
+ pHwndJidList = &ppro->m_hwndMucVoiceList;
+ break;
+ case MUC_MEMBERLIST:
+ pHwndJidList = &ppro->m_hwndMucMemberList;
+ break;
+ case MUC_MODERATORLIST:
+ pHwndJidList = &ppro->m_hwndMucModeratorList;
+ break;
+ case MUC_BANLIST:
+ pHwndJidList = &ppro->m_hwndMucBanList;
+ break;
+ case MUC_ADMINLIST:
+ pHwndJidList = &ppro->m_hwndMucAdminList;
+ break;
+ case MUC_OWNERLIST:
+ pHwndJidList = &ppro->m_hwndMucOwnerList;
+ break;
+ default:
+ mir_free( jidListInfo );
+ return;
+ }
+
+ if ( *pHwndJidList!=NULL && IsWindow( *pHwndJidList )) {
+ SetForegroundWindow( *pHwndJidList );
+ SendMessage( *pHwndJidList, WM_JABBER_REFRESH, 0, ( LPARAM )jidListInfo );
+ }
+ else *pHwndJidList = CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_JIDLIST ), GetForegroundWindow(), JabberMucJidListDlgProc, ( LPARAM )jidListInfo );
+}
+
+void CJabberProto::OnIqResultMucGetJidList( HXML iqNode, JABBER_MUC_JIDLIST_TYPE listType )
+{
+ const TCHAR* type;
+ JABBER_MUC_JIDLIST_INFO *jidListInfo;
+
+ if (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL ) return;
+
+ if ( !lstrcmp( type, _T("result" ))) {
+ if (( jidListInfo = new JABBER_MUC_JIDLIST_INFO ) != NULL ) {
+ jidListInfo->type = listType;
+ jidListInfo->ppro = this;
+ jidListInfo->roomJid = NULL; // Set in the dialog procedure
+ if (( jidListInfo->iqNode = xi.copyNode( iqNode )) != NULL )
+ CallFunctionAsync( JabberMucJidListCreateDialogApcProc, jidListInfo );
+ else
+ mir_free( jidListInfo );
+} } }
+
+void CJabberProto::OnIqResultMucGetVoiceList( HXML iqNode )
+{
+ Log( "<iq/> iqResultMucGetVoiceList" );
+ OnIqResultMucGetJidList( iqNode, MUC_VOICELIST );
+}
+
+void CJabberProto::OnIqResultMucGetMemberList( HXML iqNode )
+{
+ Log( "<iq/> iqResultMucGetMemberList" );
+ OnIqResultMucGetJidList( iqNode, MUC_MEMBERLIST );
+}
+
+void CJabberProto::OnIqResultMucGetModeratorList( HXML iqNode )
+{
+ Log( "<iq/> iqResultMucGetModeratorList" );
+ OnIqResultMucGetJidList( iqNode, MUC_MODERATORLIST );
+}
+
+void CJabberProto::OnIqResultMucGetBanList( HXML iqNode )
+{
+ Log( "<iq/> iqResultMucGetBanList" );
+ OnIqResultMucGetJidList( iqNode, MUC_BANLIST );
+}
+
+void CJabberProto::OnIqResultMucGetAdminList( HXML iqNode )
+{
+ Log( "<iq/> iqResultMucGetAdminList" );
+ OnIqResultMucGetJidList( iqNode, MUC_ADMINLIST );
+}
+
+void CJabberProto::OnIqResultMucGetOwnerList( HXML iqNode )
+{
+ Log( "<iq/> iqResultMucGetOwnerList" );
+ OnIqResultMucGetJidList( iqNode, MUC_OWNERLIST );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+JABBER_MUC_JIDLIST_INFO::~JABBER_MUC_JIDLIST_INFO()
+{
+ xi.destroyNode( iqNode );
+ mir_free( roomJid );
+}
+
+TCHAR* JABBER_MUC_JIDLIST_INFO::type2str() const
+{
+ switch( type ) {
+ case MUC_VOICELIST: return TranslateT( "Voice List" );
+ case MUC_MEMBERLIST: return TranslateT( "Member List" );
+ case MUC_MODERATORLIST: return TranslateT( "Moderator List" );
+ case MUC_BANLIST: return TranslateT( "Ban List" );
+ case MUC_ADMINLIST: return TranslateT( "Admin List" );
+ case MUC_OWNERLIST: return TranslateT( "Owner List" );
+ }
+
+ return TranslateT( "JID List" );
+}
diff --git a/protocols/JabberG/src/jabber_libstr.cpp b/protocols/JabberG/src/jabber_libstr.cpp new file mode 100644 index 0000000000..4517d77272 --- /dev/null +++ b/protocols/JabberG/src/jabber_libstr.cpp @@ -0,0 +1,31 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+int lstrcmp_null(const TCHAR *s1, const TCHAR *s2)
+{
+ if (!s1 && !s2) return 0;
+ if (!s1) return -1;
+ if (!s2) return 1;
+ return lstrcmp(s1, s2);
+}
diff --git a/protocols/JabberG/src/jabber_list.cpp b/protocols/JabberG/src/jabber_list.cpp new file mode 100644 index 0000000000..8a956ebc47 --- /dev/null +++ b/protocols/JabberG/src/jabber_list.cpp @@ -0,0 +1,474 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_list.h"
+
+void MenuUpdateSrmmIcon(JABBER_LIST_ITEM *item);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// List item freeing
+
+static void JabberListFreeResourceInternal( JABBER_RESOURCE_STATUS *r)
+{
+ if ( r->resourceName ) mir_free( r->resourceName );
+ if ( r->nick ) mir_free( r->nick );
+ if ( r->statusMessage ) mir_free( r->statusMessage );
+ if ( r->software ) mir_free( r->software );
+ if ( r->version ) mir_free( r->version );
+ if ( r->system ) mir_free( r->system );
+ if ( r->szCapsNode ) mir_free( r->szCapsNode );
+ if ( r->szCapsVer ) mir_free( r->szCapsVer );
+ if ( r->szCapsExt ) mir_free( r->szCapsExt );
+ if ( r->szRealJid ) mir_free( r->szRealJid );
+ if ( r->pSoftwareInfo) delete r->pSoftwareInfo;
+}
+
+static void JabberListFreeItemInternal( JABBER_LIST_ITEM *item )
+{
+ if ( item == NULL )
+ return;
+
+ if ( item->jid ) mir_free( item->jid );
+ if ( item->nick ) mir_free( item->nick );
+
+ JABBER_RESOURCE_STATUS* r = item->resource;
+ for ( int i=0; i < item->resourceCount; i++, r++ )
+ JabberListFreeResourceInternal( r );
+ if ( item->resource ) mir_free( item->resource );
+
+ JabberListFreeResourceInternal( &item->itemResource );
+
+ if ( item->group ) mir_free( item->group );
+ if ( item->photoFileName ) {
+ DeleteFile( item->photoFileName );
+ mir_free( item->photoFileName );
+ }
+ if ( item->messageEventIdStr ) mir_free( item->messageEventIdStr );
+ if ( item->name ) mir_free( item->name );
+ if ( item->type ) mir_free( item->type );
+ if ( item->service ) mir_free( item->service );
+ if ( item->password ) mir_free( item->password );
+ if ( item->list==LIST_ROSTER && item->ft ) delete item->ft;
+ mir_free( item );
+}
+
+void CJabberProto::ListWipe( void )
+{
+ int i;
+
+ EnterCriticalSection( &m_csLists );
+ for ( i=0; i < m_lstRoster.getCount(); i++ )
+ JabberListFreeItemInternal( m_lstRoster[i] );
+
+ m_lstRoster.destroy();
+ LeaveCriticalSection( &m_csLists );
+}
+
+int CJabberProto::ListExist( JABBER_LIST list, const TCHAR* jid )
+{
+ JABBER_LIST_ITEM tmp;
+ tmp.list = list;
+ tmp.jid = (TCHAR*)jid;
+ tmp.bUseResource = FALSE;
+
+ EnterCriticalSection( &m_csLists );
+
+ //fyr
+ if ( list == LIST_ROSTER )
+ {
+ tmp.list = LIST_CHATROOM;
+ int id = m_lstRoster.getIndex( &tmp );
+ if ( id != -1)
+ tmp.bUseResource = TRUE;
+ tmp.list = list;
+ }
+
+ int idx = m_lstRoster.getIndex( &tmp );
+
+ if ( idx == -1 ) {
+ LeaveCriticalSection( &m_csLists );
+ return 0;
+ }
+
+ LeaveCriticalSection( &m_csLists );
+ return idx+1;
+}
+
+JABBER_LIST_ITEM *CJabberProto::ListAdd( JABBER_LIST list, const TCHAR* jid )
+{
+ JABBER_LIST_ITEM* item;
+ BOOL bUseResource=FALSE;
+ EnterCriticalSection( &m_csLists );
+ if (( item = ListGetItemPtr( list, jid )) != NULL ) {
+ LeaveCriticalSection( &m_csLists );
+ return item;
+ }
+
+ TCHAR *s = mir_tstrdup( jid );
+ TCHAR *q = NULL;
+ // strip resource name if any
+ //fyr
+ if ( !((list== LIST_ROSTER ) && ListExist(LIST_CHATROOM, jid))) { // but only if it is not chat room contact
+ if ( list != LIST_VCARD_TEMP ) {
+ TCHAR *p;
+ if (( p = _tcschr( s, '@' )) != NULL )
+ if (( q = _tcschr( p, '/' )) != NULL )
+ *q = '\0';
+ }
+ } else {
+ bUseResource=TRUE;
+ }
+
+ if ( !bUseResource && list== LIST_ROSTER )
+ {
+ //if it is a chat room keep resource and made it resource sensitive
+ if ( ChatRoomHContactFromJID( s ))
+ {
+ if (q != NULL) *q='/';
+ bUseResource=TRUE;
+ }
+ }
+ item = ( JABBER_LIST_ITEM* )mir_alloc( sizeof( JABBER_LIST_ITEM ));
+ ZeroMemory( item, sizeof( JABBER_LIST_ITEM ));
+ item->list = list;
+ item->jid = s;
+ item->itemResource.status = ID_STATUS_OFFLINE;
+ item->resource = NULL;
+ item->resourceMode = RSMODE_LASTSEEN;
+ item->lastSeenResource = -1;
+ item->manualResource = -1;
+ item->bUseResource = bUseResource;
+
+ m_lstRoster.insert( item );
+ LeaveCriticalSection( &m_csLists );
+
+ MenuUpdateSrmmIcon(item);
+ return item;
+}
+
+void CJabberProto::ListRemove( JABBER_LIST list, const TCHAR* jid )
+{
+ EnterCriticalSection( &m_csLists );
+ int i = ListExist( list, jid );
+ if ( i != 0 ) {
+ JabberListFreeItemInternal( m_lstRoster[ --i ] );
+ m_lstRoster.remove( i );
+ }
+ LeaveCriticalSection( &m_csLists );
+}
+
+void CJabberProto::ListRemoveList( JABBER_LIST list )
+{
+ int i = 0;
+ while (( i=ListFindNext( list, i )) >= 0 )
+ ListRemoveByIndex( i );
+}
+
+void CJabberProto::ListRemoveByIndex( int index )
+{
+ EnterCriticalSection( &m_csLists );
+ if ( index >= 0 && index < m_lstRoster.getCount()) {
+ JabberListFreeItemInternal( m_lstRoster[index] );
+ m_lstRoster.remove( index );
+ }
+ LeaveCriticalSection( &m_csLists );
+}
+
+JABBER_RESOURCE_STATUS *CJabberProto::ListFindResource( JABBER_LIST list, const TCHAR* jid )
+{
+ JABBER_RESOURCE_STATUS *result = NULL;
+
+ EnterCriticalSection( &m_csLists );
+ int i = ListExist( list, jid );
+ if ( !i ) {
+ LeaveCriticalSection( &m_csLists );
+ return 0;
+ }
+
+ JABBER_LIST_ITEM* LI = m_lstRoster[i-1];
+
+ const TCHAR* p = _tcschr( jid, '@' );
+ const TCHAR* q = _tcschr(( p == NULL ) ? jid : p, '/' );
+ if (q)
+ {
+ const TCHAR *resource = q+1;
+ if (*resource)
+ for ( int j=0; j < LI->resourceCount; j++ )
+ if ( !_tcscmp( LI->resource[j].resourceName, resource ))
+ {
+ result = LI->resource + j;
+ break;
+ }
+ }
+
+ LeaveCriticalSection( &m_csLists );
+
+ return result;
+}
+
+int CJabberProto::ListAddResource( JABBER_LIST list, const TCHAR* jid, int status, const TCHAR* statusMessage, char priority, const TCHAR* nick )
+{
+ EnterCriticalSection( &m_csLists );
+ int i = ListExist( list, jid );
+ if ( !i ) {
+ LeaveCriticalSection( &m_csLists );
+ return 0;
+ }
+
+ JABBER_LIST_ITEM* LI = m_lstRoster[i-1];
+ int bIsNewResource = false, j;
+
+ const TCHAR* p = _tcschr( jid, '@' );
+ const TCHAR* q = _tcschr(( p == NULL ) ? jid : p, '/' );
+ if ( q ) {
+ const TCHAR* resource = q+1;
+ if ( resource[0] ) {
+ JABBER_RESOURCE_STATUS* r = LI->resource;
+ for ( j=0; j < LI->resourceCount; j++, r++ ) {
+ if ( !_tcscmp( r->resourceName, resource )) {
+ // Already exist, update status and statusMessage
+ r->status = status;
+ replaceStrT( r->statusMessage, statusMessage );
+ r->priority = priority;
+ break;
+ } }
+
+ if ( j >= LI->resourceCount ) {
+ // Not already exist, add new resource
+ LI->resource = ( JABBER_RESOURCE_STATUS * ) mir_realloc( LI->resource, ( LI->resourceCount+1 )*sizeof( JABBER_RESOURCE_STATUS ));
+ bIsNewResource = true;
+ r = LI->resource + LI->resourceCount++;
+ memset( r, 0, sizeof( JABBER_RESOURCE_STATUS ));
+ r->status = status;
+ r->affiliation = AFFILIATION_NONE;
+ r->role = ROLE_NONE;
+ r->resourceName = mir_tstrdup( resource );
+ r->nick = mir_tstrdup( nick );
+ if ( statusMessage )
+ r->statusMessage = mir_tstrdup( statusMessage );
+ r->priority = priority;
+ } }
+ }
+ // No resource, update the main statusMessage
+ else {
+ LI->itemResource.status = status;
+ replaceStrT( LI->itemResource.statusMessage, statusMessage );
+ }
+
+ LeaveCriticalSection( &m_csLists );
+
+ MenuUpdateSrmmIcon(LI);
+ return bIsNewResource;
+}
+
+void CJabberProto::ListRemoveResource( JABBER_LIST list, const TCHAR* jid )
+{
+ EnterCriticalSection( &m_csLists );
+ int i = ListExist( list, jid );
+ JABBER_LIST_ITEM* LI = m_lstRoster[i-1];
+ if ( !i || LI == NULL ) {
+ LeaveCriticalSection( &m_csLists );
+ return;
+ }
+
+ const TCHAR* p = _tcschr( jid, '@' );
+ const TCHAR* q = _tcschr(( p == NULL ) ? jid : p, '/' );
+ if ( q ) {
+ const TCHAR* resource = q+1;
+ if ( resource[0] ) {
+ JABBER_RESOURCE_STATUS* r = LI->resource;
+ int j;
+ for ( j=0; j < LI->resourceCount; j++, r++ ) {
+ if ( !_tcsicmp( r->resourceName, resource ))
+ break;
+ }
+ if ( j < LI->resourceCount ) {
+ // Found last seen resource ID to be removed
+ if ( LI->lastSeenResource == j )
+ LI->lastSeenResource = -1;
+ else if ( LI->lastSeenResource > j )
+ LI->lastSeenResource--;
+ // update manually selected resource ID
+ if (LI->resourceMode == RSMODE_MANUAL)
+ {
+ if ( LI->manualResource == j )
+ {
+ LI->resourceMode = RSMODE_LASTSEEN;
+ LI->manualResource = -1;
+ } else if ( LI->manualResource > j )
+ LI->manualResource--;
+ }
+
+ // Update MirVer due to possible resource changes
+ UpdateMirVer(LI);
+
+ JabberListFreeResourceInternal( r );
+
+ if ( LI->resourceCount-- == 1 ) {
+ mir_free( r );
+ LI->resource = NULL;
+ }
+ else {
+ memmove( r, r+1, ( LI->resourceCount-j )*sizeof( JABBER_RESOURCE_STATUS ));
+ LI->resource = ( JABBER_RESOURCE_STATUS* )mir_realloc( LI->resource, LI->resourceCount*sizeof( JABBER_RESOURCE_STATUS ));
+ } } } }
+
+ LeaveCriticalSection( &m_csLists );
+
+ MenuUpdateSrmmIcon(LI);
+}
+
+TCHAR* CJabberProto::ListGetBestResourceNamePtr( const TCHAR* jid )
+{
+ EnterCriticalSection( &m_csLists );
+ int i = ListExist( LIST_ROSTER, jid );
+ if ( !i ) {
+ LeaveCriticalSection( &m_csLists );
+ return NULL;
+ }
+
+ TCHAR* res = NULL;
+
+ JABBER_LIST_ITEM* LI = m_lstRoster[i-1];
+ if ( LI->resourceCount > 1 ) {
+ if ( LI->resourceMode == RSMODE_LASTSEEN && LI->lastSeenResource>=0 && LI->lastSeenResource < LI->resourceCount )
+ res = LI->resource[ LI->lastSeenResource ].resourceName;
+ else if (LI->resourceMode == RSMODE_MANUAL && LI->manualResource>=0 && LI->manualResource < LI->resourceCount )
+ res = LI->resource[ LI->manualResource ].resourceName;
+ else {
+ int nBestPos = -1, nBestPri = -200, j;
+ for ( j = 0; j < LI->resourceCount; j++ ) {
+ if ( LI->resource[ j ].priority > nBestPri ) {
+ nBestPri = LI->resource[ j ].priority;
+ nBestPos = j;
+ }
+ }
+ if ( nBestPos != -1 )
+ res = LI->resource[ nBestPos ].resourceName;
+ }
+ }
+
+ if ( !res && LI->resource)
+ res = LI->resource[0].resourceName;
+
+ LeaveCriticalSection( &m_csLists );
+ return res;
+}
+
+TCHAR* CJabberProto::ListGetBestClientResourceNamePtr( const TCHAR* jid )
+{
+ EnterCriticalSection( &m_csLists );
+ int i = ListExist( LIST_ROSTER, jid );
+ if ( !i ) {
+ LeaveCriticalSection( &m_csLists );
+ return NULL;
+ }
+
+ JABBER_LIST_ITEM* LI = m_lstRoster[i-1];
+ TCHAR* res = ListGetBestResourceNamePtr( jid );
+ if ( res == NULL ) {
+ JABBER_RESOURCE_STATUS* r = LI->resource;
+ int status = ID_STATUS_OFFLINE;
+ res = NULL;
+ for ( i=0; i < LI->resourceCount; i++ ) {
+ int s = r[i].status;
+ BOOL foundBetter = FALSE;
+ switch ( s ) {
+ case ID_STATUS_FREECHAT:
+ foundBetter = TRUE;
+ break;
+ case ID_STATUS_ONLINE:
+ if ( status != ID_STATUS_FREECHAT )
+ foundBetter = TRUE;
+ break;
+ case ID_STATUS_DND:
+ if ( status != ID_STATUS_FREECHAT && status != ID_STATUS_ONLINE )
+ foundBetter = TRUE;
+ break;
+ case ID_STATUS_AWAY:
+ if ( status != ID_STATUS_FREECHAT && status != ID_STATUS_ONLINE && status != ID_STATUS_DND )
+ foundBetter = TRUE;
+ break;
+ case ID_STATUS_NA:
+ if ( status != ID_STATUS_FREECHAT && status != ID_STATUS_ONLINE && status != ID_STATUS_DND && status != ID_STATUS_AWAY )
+ foundBetter = TRUE;
+ break;
+ }
+ if ( foundBetter ) {
+ res = r[i].resourceName;
+ status = s;
+ } } }
+
+ LeaveCriticalSection( &m_csLists );
+ return res;
+}
+
+int CJabberProto::ListFindNext( JABBER_LIST list, int fromOffset )
+{
+ EnterCriticalSection( &m_csLists );
+ int i = ( fromOffset >= 0 ) ? fromOffset : 0;
+ for ( ; i<m_lstRoster.getCount(); i++ )
+ if ( m_lstRoster[i]->list == list ) {
+ LeaveCriticalSection( &m_csLists );
+ return i;
+ }
+ LeaveCriticalSection( &m_csLists );
+ return -1;
+}
+
+JABBER_LIST_ITEM *CJabberProto::ListGetItemPtr( JABBER_LIST list, const TCHAR* jid )
+{
+ EnterCriticalSection( &m_csLists );
+ int i = ListExist( list, jid );
+ if ( !i ) {
+ LeaveCriticalSection( &m_csLists );
+ return NULL;
+ }
+ i--;
+ LeaveCriticalSection( &m_csLists );
+ return m_lstRoster[i];
+}
+
+JABBER_LIST_ITEM *CJabberProto::ListGetItemPtrFromIndex( int index )
+{
+ EnterCriticalSection( &m_csLists );
+ if ( index >= 0 && index < m_lstRoster.getCount()) {
+ LeaveCriticalSection( &m_csLists );
+ return m_lstRoster[index];
+ }
+ LeaveCriticalSection( &m_csLists );
+ return NULL;
+}
+
+BOOL CJabberProto::ListLock()
+{
+ EnterCriticalSection( &m_csLists );
+ return TRUE;
+}
+
+BOOL CJabberProto::ListUnlock()
+{
+ LeaveCriticalSection( &m_csLists );
+ return TRUE;
+}
diff --git a/protocols/JabberG/src/jabber_list.h b/protocols/JabberG/src/jabber_list.h new file mode 100644 index 0000000000..503a479ece --- /dev/null +++ b/protocols/JabberG/src/jabber_list.h @@ -0,0 +1,202 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_LIST_H_
+#define _JABBER_LIST_H_
+
+#include "jabber_caps.h"
+
+typedef enum {
+ LIST_ROSTER, // Roster list
+ LIST_CHATROOM, // Groupchat room currently joined
+ LIST_ROOM, // Groupchat room list to show on the Jabber groupchat dialog
+ LIST_FILE, // Current file transfer session
+ LIST_BYTE, // Bytestream sending connection
+ LIST_FTRECV,
+ LIST_BOOKMARK,
+ LIST_VCARD_TEMP,
+ LIST_FTIQID
+} JABBER_LIST;
+
+typedef enum {
+ SUB_NONE,
+ SUB_TO,
+ SUB_FROM,
+ SUB_BOTH
+} JABBER_SUBSCRIPTION;
+
+typedef enum {
+ AFFILIATION_NONE,
+ AFFILIATION_OUTCAST,
+ AFFILIATION_MEMBER,
+ AFFILIATION_ADMIN,
+ AFFILIATION_OWNER
+} JABBER_GC_AFFILIATION;
+
+typedef enum {
+ ROLE_NONE,
+ ROLE_VISITOR,
+ ROLE_PARTICIPANT,
+ ROLE_MODERATOR
+} JABBER_GC_ROLE;
+
+typedef enum { // initial default to RSMODE_LASTSEEN
+ RSMODE_SERVER, // always let server decide ( always send correspondence without resouce name )
+ RSMODE_LASTSEEN, // use the last seen resource ( or let server decide if haven't seen anything yet )
+ RSMODE_MANUAL // specify resource manually ( see the defaultResource field - must not be NULL )
+} JABBER_RESOURCE_MODE;
+
+
+struct JABBER_XEP0232_SOFTWARE_INFO
+{
+ TCHAR* szOs;
+ TCHAR* szOsVersion;
+ TCHAR* szSoftware;
+ TCHAR* szSoftwareVersion;
+ TCHAR* szXMirandaCoreVersion;
+ BOOL bXMirandaIsUnicode;
+ BOOL bXMirandaIsAlpha;
+ BOOL bXMirandaIsDebug;
+ JABBER_XEP0232_SOFTWARE_INFO() {
+ ZeroMemory( this, sizeof( JABBER_XEP0232_SOFTWARE_INFO ));
+ }
+ ~JABBER_XEP0232_SOFTWARE_INFO() {
+ mir_free(szOs);
+ mir_free(szOsVersion);
+ mir_free(szSoftware);
+ mir_free(szSoftwareVersion);
+ mir_free(szXMirandaCoreVersion);
+ }
+};
+
+struct JABBER_RESOURCE_STATUS
+{
+ int status;
+ TCHAR* resourceName;
+ TCHAR* nick;
+ TCHAR* statusMessage;
+ TCHAR* software;
+ TCHAR* version;
+ TCHAR* system;
+ signed char priority; // resource priority, -128..+127
+ time_t idleStartTime;// XEP-0012 support
+ JABBER_GC_AFFILIATION affiliation;
+ JABBER_GC_ROLE role;
+ TCHAR* szRealJid; // real jid for jabber conferences
+
+ // XEP-0115 support
+ TCHAR* szCapsNode;
+ TCHAR* szCapsVer;
+ TCHAR* szCapsExt;
+ DWORD dwVersionRequestTime;
+ DWORD dwDiscoInfoRequestTime;
+ JabberCapsBits jcbCachedCaps;
+ JabberCapsBits jcbManualDiscoveredCaps;
+
+ // XEP-0085 gone event support
+ BOOL bMessageSessionActive;
+ JABBER_XEP0232_SOFTWARE_INFO* pSoftwareInfo;
+};
+
+struct JABBER_LIST_ITEM
+{
+ JABBER_LIST list;
+ TCHAR* jid;
+
+ // LIST_ROSTER
+ // jid = jid of the contact
+ TCHAR* nick;
+ int resourceCount;
+ JABBER_RESOURCE_STATUS *resource;
+ JABBER_RESOURCE_STATUS itemResource; // resource for jids without /resource node
+ int lastSeenResource; // index to resource[x] which was last seen active
+ int manualResource; // manually set index to resource[x]
+// int defaultResource; // index to resource[x] which is the default, negative ( -1 ) means no resource is chosen yet
+ JABBER_RESOURCE_MODE resourceMode;
+ JABBER_SUBSCRIPTION subscription;
+ TCHAR* group;
+ TCHAR* photoFileName;
+ TCHAR* messageEventIdStr;
+
+ // LIST_AGENT
+ // jid = jid of the agent
+ TCHAR* name;
+ TCHAR* service;
+
+ // LIST_ROOM
+ // jid = room JID
+ // char* name; // room name
+ TCHAR* type; // room type
+
+ // LIST_CHATROOM
+ // jid = room JID
+ // char* nick; // my nick in this chat room ( SPECIAL: in TXT )
+ // JABBER_RESOURCE_STATUS *resource; // participant nicks in this room
+ BOOL bChatActive;
+ HWND hwndGcListBan;
+ HWND hwndGcListAdmin;
+ HWND hwndGcListOwner;
+ int iChatState;
+ // BOOL bAutoJoin; // chat sessio was started via auto-join
+
+ // LIST_FILE
+ // jid = string representation of port number
+ filetransfer* ft;
+ WORD port;
+
+ // LIST_BYTE
+ // jid = string representation of port number
+ JABBER_BYTE_TRANSFER *jbt;
+
+ JABBER_IBB_TRANSFER *jibb;
+
+ // LIST_FTRECV
+ // jid = string representation of stream id ( sid )
+ // ft = file transfer data
+
+ //LIST_BOOKMARK
+ // jid = room JID
+ // TCHAR* nick; // my nick in this chat room
+ // TCHAR* name; // name of the bookmark
+ // TCHAR* type; // type of bookmark ("url" or "conference")
+ TCHAR* password; // password for room
+ BOOL bAutoJoin;
+
+ BOOL bUseResource;
+};
+
+struct JABBER_HTTP_AVATARS
+{
+ char * Url;
+ HANDLE hContact;
+
+ JABBER_HTTP_AVATARS(const TCHAR* tUrl, HANDLE thContact)
+ : Url(mir_t2a(tUrl)), hContact(thContact) {}
+
+ ~JABBER_HTTP_AVATARS() { mir_free(Url); }
+
+ static int compare(const JABBER_HTTP_AVATARS *p1, const JABBER_HTTP_AVATARS *p2)
+ { return strcmp(p1->Url, p2->Url); }
+};
+
+#endif
diff --git a/protocols/JabberG/src/jabber_menu.cpp b/protocols/JabberG/src/jabber_menu.cpp new file mode 100644 index 0000000000..6bc4c1f8c8 --- /dev/null +++ b/protocols/JabberG/src/jabber_menu.cpp @@ -0,0 +1,1324 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_list.h"
+#include "jabber_caps.h"
+#include "jabber_privacy.h"
+#include "jabber_disco.h"
+
+#include <m_genmenu.h>
+#include <m_contacts.h>
+#include <m_hotkeys.h>
+#include <m_icolib.h>
+
+#include "m_toptoolbar.h"
+
+#define MENUITEM_LASTSEEN 1
+#define MENUITEM_SERVER 2
+#define MENUITEM_RESOURCES 10
+
+static HANDLE hChooserMenu;
+static int iChooserMenuPos = 30000;
+
+static HANDLE hPrebuildMenuHook;
+SortedList arServices;
+
+static HGENMENU g_hMenuRequestAuth;
+static HGENMENU g_hMenuGrantAuth;
+static HGENMENU g_hMenuRevokeAuth;
+static HGENMENU g_hMenuConvert;
+static HGENMENU g_hMenuRosterAdd;
+static HGENMENU g_hMenuAddBookmark;
+static HGENMENU g_hMenuLogin;
+static HGENMENU g_hMenuRefresh;
+static HGENMENU g_hMenuCommands;
+static HGENMENU g_hMenuSendNote;
+static HGENMENU g_hMenuResourcesRoot;
+static HGENMENU g_hMenuResourcesActive;
+static HGENMENU g_hMenuResourcesServer;
+
+static struct
+{
+ int icon;
+ int mode;
+} PresenceModeArray[] =
+{
+ { SKINICON_STATUS_ONLINE, ID_STATUS_ONLINE },
+ { SKINICON_STATUS_AWAY, ID_STATUS_AWAY },
+ { SKINICON_STATUS_NA, ID_STATUS_NA },
+ { SKINICON_STATUS_DND, ID_STATUS_DND },
+ { SKINICON_STATUS_FREE4CHAT, ID_STATUS_FREECHAT },
+};
+static HGENMENU g_hMenuDirectPresence[SIZEOF(PresenceModeArray) + 1];
+
+static INT_PTR JabberMenuChooseService( WPARAM wParam, LPARAM lParam )
+{
+ if ( lParam )
+ *( void** )lParam = ( void* )wParam;
+ return 0;
+}
+
+static CJabberProto* JabberGetInstanceByHContact( HANDLE hContact )
+{
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto == NULL )
+ return NULL;
+
+ for ( int i=0; i < g_Instances.getCount(); i++ )
+ if ( !strcmp( szProto, g_Instances[i]->m_szModuleName ))
+ return g_Instances[i];
+
+ return NULL;
+}
+
+static INT_PTR JabberMenuHandleRequestAuth( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuHandleRequestAuth( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberMenuHandleGrantAuth( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuHandleGrantAuth( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberMenuRevokeAuth( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuRevokeAuth( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberMenuConvertChatContact( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuConvertChatContact( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberMenuRosterAdd( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuRosterAdd( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberMenuBookmarkAdd( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuBookmarkAdd( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberMenuTransportLogin( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuTransportLogin( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberMenuTransportResolve( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuTransportResolve( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberContactMenuRunCommands( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->ContactMenuRunCommands( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberMenuSendNote( WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuSendNote( wParam, lParam ) : 0;
+}
+
+static INT_PTR JabberMenuHandleResource( WPARAM wParam, LPARAM lParam, LPARAM lRes )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuHandleResource( wParam, lParam, lRes ) : 0;
+}
+
+static INT_PTR JabberMenuHandleDirectPresence( WPARAM wParam, LPARAM lParam, LPARAM lRes )
+{
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnMenuHandleDirectPresence( wParam, lParam, lRes ) : 0;
+}
+
+static void sttEnableMenuItem( HANDLE hMenuItem, BOOL bEnable )
+{
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_FLAGS;
+ if ( !bEnable )
+ clmi.flags |= CMIF_HIDDEN;
+
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )hMenuItem, ( LPARAM )&clmi );
+}
+
+static int JabberPrebuildContactMenu( WPARAM wParam, LPARAM lParam )
+{
+ sttEnableMenuItem( g_hMenuRequestAuth, FALSE );
+ sttEnableMenuItem( g_hMenuGrantAuth, FALSE );
+ sttEnableMenuItem( g_hMenuRevokeAuth, FALSE );
+ sttEnableMenuItem( g_hMenuCommands, FALSE );
+ sttEnableMenuItem( g_hMenuSendNote, FALSE );
+ sttEnableMenuItem( g_hMenuConvert, FALSE );
+ sttEnableMenuItem( g_hMenuRosterAdd, FALSE );
+ sttEnableMenuItem( g_hMenuLogin, FALSE );
+ sttEnableMenuItem( g_hMenuRefresh, FALSE );
+ sttEnableMenuItem( g_hMenuAddBookmark, FALSE );
+ sttEnableMenuItem( g_hMenuResourcesRoot, FALSE );
+ sttEnableMenuItem( g_hMenuDirectPresence[0], FALSE );
+
+ CJabberProto* ppro = JabberGetInstanceByHContact(( HANDLE )wParam );
+ return( ppro ) ? ppro->OnPrebuildContactMenu( wParam, lParam ) : 0;
+}
+
+void g_MenuInit( void )
+{
+ arServices.increment = 10;
+
+ hPrebuildMenuHook = HookEvent( ME_CLIST_PREBUILDCONTACTMENU, JabberPrebuildContactMenu );
+
+ List_InsertPtr( &arServices, CreateServiceFunction( "Jabber/MenuChoose", JabberMenuChooseService ));
+
+ TMenuParam mnu = {0};
+ mnu.cbSize = sizeof(mnu);
+ mnu.name = "JabberAccountChooser";
+ mnu.ExecService = "Jabber/MenuChoose";
+ hChooserMenu = (HANDLE)CallService( MO_CREATENEWMENUOBJECT, 0, (LPARAM)&mnu );
+
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof( tmi );
+ tmi.flags = CMIF_ICONFROMICOLIB;
+ tmi.pszName = "Cancel";
+ tmi.position = 9999999;
+ tmi.hIcolibItem = LoadSkinnedIconHandle(SKINICON_OTHER_DELETE);
+ CallService( MO_ADDNEWMENUITEM, (WPARAM)hChooserMenu, ( LPARAM )&tmi );
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // Contact menu initialization
+
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(CLISTMENUITEM);
+
+ // "Request authorization"
+ mi.pszName = LPGEN("Request authorization");
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.position = -2000001000;
+ mi.icolibItem = g_GetIconHandle( IDI_REQUEST );
+ mi.pszService = "Jabber/ReqAuth";
+ g_hMenuRequestAuth = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberMenuHandleRequestAuth ));
+
+ // "Grant authorization"
+ mi.pszService = "Jabber/GrantAuth";
+ mi.pszName = LPGEN("Grant authorization");
+ mi.position = -2000001001;
+ mi.icolibItem = g_GetIconHandle( IDI_GRANT );
+ g_hMenuGrantAuth = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberMenuHandleGrantAuth ));
+
+ // Revoke auth
+ mi.pszService = "Jabber/RevokeAuth";
+ mi.pszName = LPGEN("Revoke authorization");
+ mi.position = -2000001002;
+ mi.icolibItem = g_GetIconHandle( IDI_AUTHREVOKE );
+ g_hMenuRevokeAuth = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberMenuRevokeAuth ));
+
+ // "Convert Chat/Contact"
+ mi.pszService = "Jabber/ConvertChatContact";
+ mi.pszName = LPGEN("Convert");
+ mi.position = -1999901004;
+ mi.icolibItem = g_GetIconHandle( IDI_USER2ROOM );
+ g_hMenuConvert = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberMenuConvertChatContact ));
+
+ // "Add to roster"
+ mi.pszService = "Jabber/AddToRoster";
+ mi.pszName = LPGEN("Add to roster");
+ mi.position = -1999901005;
+ mi.icolibItem = g_GetIconHandle( IDI_ADDROSTER );
+ g_hMenuRosterAdd = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberMenuRosterAdd ));
+
+ // "Add to Bookmarks"
+ mi.pszService = "Jabber/AddToBookmarks";
+ mi.pszName = LPGEN("Add to Bookmarks");
+ mi.position = -1999901006;
+ mi.icolibItem = g_GetIconHandle( IDI_BOOKMARKS);
+ g_hMenuAddBookmark = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberMenuBookmarkAdd ));
+
+ // Login/logout
+ mi.pszService = "Jabber/TransportLogin";
+ mi.pszName = LPGEN("Login/logout");
+ mi.position = -1999901007;
+ mi.icolibItem = g_GetIconHandle( IDI_LOGIN );
+ g_hMenuLogin = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberMenuTransportLogin ));
+
+ // Retrieve nicks
+ mi.pszService = "Jabber/TransportGetNicks";
+ mi.pszName = LPGEN("Resolve nicks");
+ mi.position = -1999901008;
+ mi.icolibItem = g_GetIconHandle( IDI_REFRESH );
+ g_hMenuRefresh = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberMenuTransportResolve ));
+
+ // Run Commands
+ mi.pszService = "Jabber/RunCommands";
+ mi.pszName = LPGEN("Commands");
+ mi.position = -1999901009;
+ mi.icolibItem = g_GetIconHandle( IDI_COMMAND );
+ g_hMenuCommands = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberContactMenuRunCommands ));
+
+ // Send Note
+ mi.pszService = "Jabber/SendNote";
+ mi.pszName = LPGEN("Send Note");
+ mi.position = -1999901010;
+ mi.icolibItem = g_GetIconHandle( IDI_SEND_NOTE);
+ g_hMenuSendNote = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunction( mi.pszService, JabberMenuSendNote ));
+
+ // Direct Presence
+ mi.pszService = "Jabber/DirectPresenceDummySvc";
+ mi.pszName = LPGEN("Send Presence");
+ mi.position = -1999901011;
+ mi.pszPopupName = (char *)-1;
+ mi.icolibItem = g_GetIconHandle( IDI_NOTES );
+ g_hMenuDirectPresence[0] = Menu_AddContactMenuItem(&mi);
+
+ mi.flags |= CMIF_ROOTHANDLE;
+ mi.flags &= ~CMIF_ICONFROMICOLIB;
+
+ for (int i = 0; i < SIZEOF(PresenceModeArray); ++i)
+ {
+ char buf[] = "Jabber/DirectPresenceX";
+ buf[SIZEOF(buf)-2] = '0' + i;
+ mi.pszService = buf;
+ mi.pszName = (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, PresenceModeArray[i].mode, 0);
+ mi.position = -1999901000;
+ mi.hParentMenu = g_hMenuDirectPresence[0];
+ mi.icolibItem = LoadSkinnedIcon(PresenceModeArray[i].icon);
+ g_hMenuDirectPresence[i+1] = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunctionParam( mi.pszService, JabberMenuHandleDirectPresence, PresenceModeArray[i].mode ));
+ }
+
+ mi.flags &= ~CMIF_ROOTHANDLE;
+ mi.flags |= CMIF_ICONFROMICOLIB;
+
+ // Resource selector
+ mi.pszService = "Jabber/ResourceSelectorDummySvc";
+ mi.pszName = LPGEN("Jabber Resource");
+ mi.position = -1999901011;
+ mi.pszPopupName = (char *)-1;
+ mi.icolibItem = g_GetIconHandle( IDI_JABBER );
+ g_hMenuResourcesRoot = Menu_AddContactMenuItem(&mi);
+
+ mi.pszService = "Jabber/UseResource_last";
+ mi.pszName = LPGEN("Last Active");
+ mi.position = -1999901000;
+ mi.hParentMenu = g_hMenuResourcesRoot;
+ mi.icolibItem = g_GetIconHandle( IDI_JABBER );
+ mi.flags |= CMIF_ROOTHANDLE;
+ g_hMenuResourcesActive = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunctionParam( mi.pszService, JabberMenuHandleResource, MENUITEM_LASTSEEN ));
+
+ mi.pszService = "Jabber/UseResource_server";
+ mi.pszName = LPGEN("Server's Choice");
+ mi.position = -1999901000;
+ mi.pszPopupName = (char *)g_hMenuResourcesRoot;
+ mi.icolibItem = g_GetIconHandle( IDI_NODE_SERVER );
+ g_hMenuResourcesServer = Menu_AddContactMenuItem(&mi);
+ List_InsertPtr( &arServices, CreateServiceFunctionParam( mi.pszService, JabberMenuHandleResource, MENUITEM_SERVER ));
+}
+
+void g_MenuUninit( void )
+{
+ CallService( MS_CLIST_REMOVECONTACTMENUITEM, ( WPARAM )g_hMenuRequestAuth, 0 );
+ CallService( MS_CLIST_REMOVECONTACTMENUITEM, ( WPARAM )g_hMenuGrantAuth, 0 );
+ CallService( MS_CLIST_REMOVECONTACTMENUITEM, ( WPARAM )g_hMenuRevokeAuth, 0 );
+ CallService( MS_CLIST_REMOVECONTACTMENUITEM, ( WPARAM )g_hMenuConvert, 0 );
+ CallService( MS_CLIST_REMOVECONTACTMENUITEM, ( WPARAM )g_hMenuRosterAdd, 0 );
+ CallService( MS_CLIST_REMOVECONTACTMENUITEM, ( WPARAM )g_hMenuLogin, 0 );
+ CallService( MS_CLIST_REMOVECONTACTMENUITEM, ( WPARAM )g_hMenuRefresh, 0 );
+ CallService( MS_CLIST_REMOVECONTACTMENUITEM, ( WPARAM )g_hMenuAddBookmark, 0 );
+
+ UnhookEvent( hPrebuildMenuHook );
+ for (int i = 0; i < arServices.realCount; i++)
+ DestroyServiceFunction( arServices.items[i] );
+ List_Destroy( &arServices );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// contact menu services
+
+int CJabberProto::OnPrebuildContactMenu( WPARAM wParam, LPARAM )
+{
+ HANDLE hContact;
+ if (( hContact=( HANDLE )wParam ) == NULL )
+ return 0;
+
+ BYTE bIsChatRoom = (BYTE)JGetByte( hContact, "ChatRoom", 0 );
+ BYTE bIsTransport = (BYTE)JGetByte( hContact, "IsTransport", 0 );
+
+ if ((bIsChatRoom == GCW_CHATROOM) || bIsChatRoom == 0 ) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, bIsChatRoom?(char*)"ChatRoomID":(char*)"jid", &dbv )) {
+ JFreeVariant( &dbv );
+ CLISTMENUITEM clmi = { 0 };
+ sttEnableMenuItem( g_hMenuConvert, TRUE );
+ clmi.cbSize = sizeof( clmi );
+ clmi.pszName = bIsChatRoom ? (char *)LPGEN("&Convert to Contact") : (char *)LPGEN("&Convert to Chat Room");
+ clmi.flags = CMIM_NAME | CMIM_FLAGS;
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )g_hMenuConvert, ( LPARAM )&clmi );
+ } }
+
+ if (!m_bJabberOnline)
+ return 0;
+
+ sttEnableMenuItem( g_hMenuDirectPresence[0], TRUE );
+ for (int i = 0; i < SIZEOF(PresenceModeArray); ++i)
+ {
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_ICON|CMIM_FLAGS;
+ clmi.hIcon = (HICON)LoadSkinnedProtoIcon(m_szModuleName, PresenceModeArray[i].mode);
+ CallService(MS_CLIST_MODIFYMENUITEM, ( WPARAM )g_hMenuDirectPresence[i+1], ( LPARAM )&clmi );
+ }
+
+ if ( bIsChatRoom ) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "ChatRoomID", &dbv )) {
+ sttEnableMenuItem( g_hMenuRosterAdd, FALSE );
+
+ if ( ListGetItemPtr( LIST_BOOKMARK, dbv.ptszVal ) == NULL )
+ if ( m_ThreadInfo && m_ThreadInfo->jabberServerCaps & JABBER_CAPS_PRIVATE_STORAGE )
+ sttEnableMenuItem( g_hMenuAddBookmark, TRUE );
+
+ JFreeVariant( &dbv );
+ } }
+
+ if ( bIsChatRoom == GCW_CHATROOM )
+ return 0;
+
+ if ( bIsTransport ) {
+ sttEnableMenuItem( g_hMenuLogin, TRUE );
+ sttEnableMenuItem( g_hMenuRefresh, TRUE );
+ }
+
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ JabberCapsBits jcb = GetTotalJidCapabilites(dbv.ptszVal );
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_ROSTER, dbv.ptszVal );
+ JFreeVariant( &dbv );
+ if ( item != NULL ) {
+ BOOL bCtrlPressed = ( GetKeyState( VK_CONTROL)&0x8000 ) != 0;
+ sttEnableMenuItem( g_hMenuRequestAuth, item->subscription == SUB_FROM || item->subscription == SUB_NONE || bCtrlPressed );
+ sttEnableMenuItem( g_hMenuGrantAuth, bCtrlPressed );
+ sttEnableMenuItem( g_hMenuRevokeAuth, item->subscription == SUB_FROM || item->subscription == SUB_BOTH || bCtrlPressed );
+ sttEnableMenuItem( g_hMenuCommands, (( jcb & JABBER_CAPS_COMMANDS ) != 0) || bCtrlPressed);
+ sttEnableMenuItem( g_hMenuSendNote, TRUE );
+
+ if ( item->resourceCount >= 1 ) {
+ sttEnableMenuItem( g_hMenuResourcesRoot, TRUE );
+
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(CLISTMENUITEM);
+ mi.flags = CMIM_ICON|CMIM_FLAGS;
+ mi.icolibItem = GetIconHandle( IDI_JABBER );
+ CallService(MS_CLIST_MODIFYMENUITEM, ( WPARAM )g_hMenuResourcesRoot, ( LPARAM )&mi );
+ CallService(MS_CLIST_MODIFYMENUITEM, ( WPARAM )g_hMenuResourcesActive, ( LPARAM )&mi );
+
+ int nMenuResourceItemsNew = m_nMenuResourceItems;
+ if ( m_nMenuResourceItems < item->resourceCount ) {
+ m_phMenuResourceItems = (HANDLE *)mir_realloc( m_phMenuResourceItems, item->resourceCount * sizeof(HANDLE));
+ nMenuResourceItemsNew = item->resourceCount;
+ }
+
+ char text[ 256 ];
+ strcpy( text, m_szModuleName );
+ size_t nModuleNameLength = strlen( text );
+ char* tDest = text + nModuleNameLength;
+
+ mi.cbSize = sizeof(CLISTMENUITEM);
+ mi.flags = CMIF_CHILDPOPUP;
+ mi.position = 0;
+ mi.icolibItem = GetIconHandle( IDI_REQUEST );
+ mi.pszService = text;
+ mi.pszContactOwner = m_szModuleName;
+
+ TCHAR szTmp[512];
+ for (int i = 0; i < nMenuResourceItemsNew; ++i) {
+ mir_snprintf( tDest, SIZEOF(text) - nModuleNameLength, "/UseResource_%d", i );
+ if ( i >= m_nMenuResourceItems ) {
+ JCreateServiceParam( tDest, &CJabberProto::OnMenuHandleResource, MENUITEM_RESOURCES+i );
+ mi.pszName = "";
+ mi.position = i;
+ mi.pszPopupName = (char *)g_hMenuResourcesRoot;
+ m_phMenuResourceItems[i] = Menu_AddContactMenuItem(&mi);
+ }
+ if ( i < item->resourceCount ) {
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_NAME|CMIM_FLAGS | CMIF_CHILDPOPUP|CMIF_TCHAR;
+ if ((item->resourceMode == RSMODE_MANUAL) && (item->manualResource == i))
+ clmi.flags |= CMIF_CHECKED;
+ if (ServiceExists(MS_FP_GETCLIENTICONT)) {
+ clmi.flags |= CMIM_ICON;
+ FormatMirVer(&item->resource[i], szTmp, SIZEOF(szTmp));
+ clmi.hIcon = (HICON)CallService( MS_FP_GETCLIENTICONT, (WPARAM)szTmp, 0 );
+ }
+ mir_sntprintf(szTmp, SIZEOF(szTmp), _T("%s [%s, %d]"),
+ item->resource[i].resourceName,
+ (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, item->resource[i].status, GSMDF_TCHAR),
+ item->resource[i].priority);
+ clmi.ptszName = szTmp;
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_phMenuResourceItems[i], ( LPARAM )&clmi );
+ DestroyIcon(clmi.hIcon);
+ }
+ else sttEnableMenuItem( m_phMenuResourceItems[i], FALSE );
+ }
+
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(CLISTMENUITEM);
+
+ mi.flags = CMIM_FLAGS | CMIF_CHILDPOPUP|CMIF_ICONFROMICOLIB |
+ ((item->resourceMode == RSMODE_LASTSEEN) ? CMIF_CHECKED : 0);
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )g_hMenuResourcesActive, ( LPARAM )&mi );
+
+ mi.flags = CMIM_FLAGS | CMIF_CHILDPOPUP|CMIF_ICONFROMICOLIB |
+ ((item->resourceMode == RSMODE_SERVER) ? CMIF_CHECKED : 0);
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )g_hMenuResourcesServer, ( LPARAM )&mi );
+
+ m_nMenuResourceItems = nMenuResourceItemsNew;
+ }
+
+ return 0;
+ } }
+
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuConvertChatContact( WPARAM wParam, LPARAM )
+{
+ BYTE bIsChatRoom = (BYTE)JGetByte( (HANDLE ) wParam, "ChatRoom", 0 );
+ if ((bIsChatRoom == GCW_CHATROOM) || bIsChatRoom == 0 ) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( (HANDLE ) wParam, (bIsChatRoom == GCW_CHATROOM)?(char*)"ChatRoomID":(char*)"jid", &dbv )) {
+ JDeleteSetting( (HANDLE ) wParam, (bIsChatRoom == GCW_CHATROOM)?"ChatRoomID":"jid");
+ JSetStringT( (HANDLE ) wParam, (bIsChatRoom != GCW_CHATROOM)?"ChatRoomID":"jid", dbv.ptszVal);
+ JFreeVariant( &dbv );
+ JSetByte((HANDLE ) wParam, "ChatRoom", (bIsChatRoom == GCW_CHATROOM)?0:GCW_CHATROOM);
+ } }
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuRosterAdd( WPARAM wParam, LPARAM )
+{
+ DBVARIANT dbv;
+ if ( !wParam ) return 0; // we do not add ourself to the roster. (buggy situation - should not happen)
+ if ( !JGetStringT( ( HANDLE ) wParam, "ChatRoomID", &dbv )) {
+ TCHAR *roomID = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant( &dbv );
+ if ( ListGetItemPtr( LIST_ROSTER, roomID ) == NULL ) {
+ TCHAR *nick = 0;
+ TCHAR *group = 0;
+ if ( !DBGetContactSettingTString( ( HANDLE ) wParam, "CList", "Group", &dbv )) {
+ group = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant( &dbv );
+ }
+ if ( !JGetStringT( ( HANDLE ) wParam, "Nick", &dbv )) {
+ nick = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant( &dbv );
+ }
+ AddContactToRoster( roomID, nick, group );
+ if ( m_options.AddRoster2Bookmarks == TRUE ) {
+
+ JABBER_LIST_ITEM* item = NULL;
+
+ item = ListGetItemPtr(LIST_BOOKMARK, roomID);
+ if (!item) {
+ item = ( JABBER_LIST_ITEM* )mir_alloc( sizeof( JABBER_LIST_ITEM ));
+ ZeroMemory( item, sizeof( JABBER_LIST_ITEM ));
+ item->jid = mir_tstrdup(roomID);
+ item->name = mir_tstrdup(nick);
+ if ( !JGetStringT( ( HANDLE ) wParam, "MyNick", &dbv )) {
+ item->nick = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant( &dbv );
+ }
+ AddEditBookmark( item );
+ mir_free(item);
+ }
+ }
+ if (nick) mir_free(nick);
+ if (nick) mir_free(group);
+ }
+ mir_free(roomID);
+ }
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleRequestAuth( WPARAM wParam, LPARAM )
+{
+ HANDLE hContact;
+ DBVARIANT dbv;
+
+ if (( hContact=( HANDLE ) wParam )!=NULL && m_bJabberOnline ) {
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ m_ThreadInfo->send( XmlNode( _T("presence")) << XATTR( _T("to"), dbv.ptszVal ) << XATTR( _T("type"), _T("subscribe")));
+ JFreeVariant( &dbv );
+ } }
+
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleGrantAuth( WPARAM wParam, LPARAM )
+{
+ HANDLE hContact;
+ DBVARIANT dbv;
+
+ if (( hContact=( HANDLE ) wParam )!=NULL && m_bJabberOnline ) {
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ m_ThreadInfo->send( XmlNode( _T("presence")) << XATTR( _T("to"), dbv.ptszVal ) << XATTR( _T("type"), _T("subscribed")));
+ JFreeVariant( &dbv );
+ } }
+
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuRevokeAuth( WPARAM wParam, LPARAM )
+{
+ HANDLE hContact;
+ DBVARIANT dbv;
+
+ if (( hContact=( HANDLE ) wParam ) != NULL && m_bJabberOnline ) {
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ m_ThreadInfo->send( XmlNode( _T("presence")) << XATTR( _T("to"), dbv.ptszVal ) << XATTR( _T("type"), _T("unsubscribed")));
+ JFreeVariant( &dbv );
+ } }
+
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuTransportLogin( WPARAM wParam, LPARAM )
+{
+ HANDLE hContact = ( HANDLE )wParam;
+ if ( !JGetByte( hContact, "IsTransport", 0 ))
+ return 0;
+
+ DBVARIANT jid;
+ if ( JGetStringT( hContact, "jid", &jid ))
+ return 0;
+
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_ROSTER, jid.ptszVal );
+ if ( item != NULL ) {
+ XmlNode p( _T("presence")); xmlAddAttr( p, _T("to"), item->jid );
+ if ( item->itemResource.status == ID_STATUS_ONLINE )
+ xmlAddAttr( p, _T("type"), _T("unavailable"));
+ m_ThreadInfo->send( p );
+ }
+
+ JFreeVariant( &jid );
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuTransportResolve( WPARAM wParam, LPARAM )
+{
+ HANDLE hContact = ( HANDLE )wParam;
+ if ( !JGetByte( hContact, "IsTransport", 0 ))
+ return 0;
+
+ DBVARIANT jid;
+ if ( !JGetStringT( hContact, "jid", &jid )) {
+ ResolveTransportNicks( jid.ptszVal );
+ JFreeVariant( &jid );
+ }
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuBookmarkAdd( WPARAM wParam, LPARAM )
+{
+ DBVARIANT dbv;
+ if ( !wParam ) return 0; // we do not add ourself to the roster. (buggy situation - should not happen)
+ if ( !JGetStringT( ( HANDLE ) wParam, "ChatRoomID", &dbv )) {
+ TCHAR *roomID = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant( &dbv );
+ if ( ListGetItemPtr( LIST_BOOKMARK, roomID ) == NULL ) {
+ TCHAR *nick = 0;
+ if ( !JGetStringT( ( HANDLE ) wParam, "Nick", &dbv )) {
+ nick = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant( &dbv );
+ }
+ JABBER_LIST_ITEM* item = NULL;
+
+ item = ( JABBER_LIST_ITEM* )mir_alloc( sizeof( JABBER_LIST_ITEM ));
+ ZeroMemory( item, sizeof( JABBER_LIST_ITEM ));
+ item->jid = mir_tstrdup(roomID);
+ item->name = ( TCHAR* )CallService( MS_CLIST_GETCONTACTDISPLAYNAME, wParam, GCDNF_TCHAR );
+ item->type = _T("conference");
+ if ( !JGetStringT(( HANDLE ) wParam, "MyNick", &dbv )) {
+ item->nick = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant( &dbv );
+ }
+ AddEditBookmark( item );
+ mir_free(item);
+
+ if (nick) mir_free(nick);
+ }
+ mir_free(roomID);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// status menu
+
+void CJabberProto::MenuInit()
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(CLISTMENUITEM);
+
+ char text[ 200 ];
+ strcpy( text, m_szModuleName );
+ char* tDest = text + strlen( text );
+ mi.pszService = text;
+
+ HGENMENU hJabberRoot = MO_GetProtoRootMenu( m_szModuleName );
+ if ( hJabberRoot == NULL ) {
+ mi.ptszName = m_tszUserName;
+ mi.position = -1999901006;
+ mi.hParentMenu = HGENMENU_ROOT;
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTPOPUP | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ mi.icolibItem = GetIconHandle( IDI_JABBER );
+ hJabberRoot = m_hMenuRoot = Menu_AddProtoMenuItem(&mi);
+ }
+ else {
+ if ( m_hMenuRoot )
+ CallService( MS_CLIST_REMOVEMAINMENUITEM, ( WPARAM )m_hMenuRoot, 0 );
+ m_hMenuRoot = NULL;
+ }
+
+ // "Bookmarks..."
+ JCreateService( "/Bookmarks", &CJabberProto::OnMenuHandleBookmarks );
+ strcpy( tDest, "/Bookmarks" );
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_CHILDPOPUP;
+ mi.hParentMenu = hJabberRoot;
+ mi.pszName = LPGEN("Bookmarks");
+ mi.position = 200001;
+ mi.icolibItem = GetIconHandle( IDI_BOOKMARKS );
+ m_hMenuBookmarks = Menu_AddProtoMenuItem(&mi);
+
+ // "Options..."
+ JCreateService( "/Options", &CJabberProto::OnMenuOptions );
+ strcpy( tDest, "/Options" );
+ mi.pszName = LPGEN("Options...");
+ mi.position = 200002;
+ mi.icolibItem = LoadSkinnedIconHandle(SKINICON_OTHER_OPTIONS);
+ Menu_AddProtoMenuItem(&mi);
+
+ // "Services..."
+ mi.pszName = LPGEN("Services...");
+ strcpy( tDest, "/Services" );
+ mi.position = 200003;
+ mi.icolibItem = GetIconHandle( IDI_SERVICE_DISCOVERY );
+ HGENMENU hMenuServicesRoot = Menu_AddProtoMenuItem(&mi);
+
+ // "Service Discovery..."
+ JCreateService( "/ServiceDiscovery", &CJabberProto::OnMenuHandleServiceDiscovery );
+ strcpy( tDest, "/ServiceDiscovery" );
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE;
+ mi.pszName = LPGEN("Service Discovery");
+ mi.position = 2000050001;
+ mi.icolibItem = GetIconHandle( IDI_SERVICE_DISCOVERY );
+ mi.hParentMenu = hMenuServicesRoot;
+ m_hMenuServiceDiscovery = Menu_AddProtoMenuItem(&mi);
+
+ JCreateService( "/SD/MyTransports", &CJabberProto::OnMenuHandleServiceDiscoveryMyTransports );
+ strcpy( tDest, "/SD/MyTransports" );
+ mi.pszName = LPGEN("Registered Transports");
+ mi.position = 2000050003;
+ mi.icolibItem = GetIconHandle( IDI_TRANSPORTL );
+ m_hMenuSDMyTransports = Menu_AddProtoMenuItem(&mi);
+
+ JCreateService( "/SD/Transports", &CJabberProto::OnMenuHandleServiceDiscoveryTransports );
+ strcpy( tDest, "/SD/Transports" );
+ mi.pszName = LPGEN("Local Server Transports");
+ mi.position = 2000050004;
+ mi.icolibItem = GetIconHandle( IDI_TRANSPORT );
+ m_hMenuSDTransports = Menu_AddProtoMenuItem(&mi);
+
+ JCreateService( "/SD/Conferences", &CJabberProto::OnMenuHandleServiceDiscoveryConferences );
+ strcpy( tDest, "/SD/Conferences" );
+ mi.pszName = LPGEN("Browse Chatrooms");
+ mi.position = 2000050005;
+ mi.icolibItem = GetIconHandle( IDI_GROUP );
+ m_hMenuSDConferences = Menu_AddProtoMenuItem(&mi);
+
+ JCreateService( "/Groupchat", &CJabberProto::OnMenuHandleJoinGroupchat );
+ strcpy( tDest, "/Groupchat" );
+ mi.pszName = LPGEN("Create/Join groupchat");
+ mi.position = 2000050006;
+ mi.icolibItem = GetIconHandle( IDI_GROUP );
+ m_hMenuGroupchat = Menu_AddProtoMenuItem(&mi);
+
+ // "Change Password..."
+ JCreateService( "/ChangePassword", &CJabberProto::OnMenuHandleChangePassword );
+ strcpy( tDest, "/ChangePassword" );
+ mi.pszName = LPGEN("Change Password");
+ mi.position = 2000050007;
+ mi.icolibItem = GetIconHandle( IDI_KEYS );
+ m_hMenuChangePassword = Menu_AddProtoMenuItem(&mi);
+
+ // "Roster editor"
+ JCreateService( "/RosterEditor", &CJabberProto::OnMenuHandleRosterControl );
+ strcpy( tDest, "/RosterEditor" );
+ mi.pszName = LPGEN("Roster editor");
+ mi.position = 2000050009;
+ mi.icolibItem = GetIconHandle( IDI_AGENTS );
+ m_hMenuRosterControl = Menu_AddProtoMenuItem(&mi);
+
+ // "XML Console"
+ JCreateService( "/XMLConsole", &CJabberProto::OnMenuHandleConsole );
+ strcpy( tDest, "/XMLConsole" );
+ mi.pszName = LPGEN("XML Console");
+ mi.position = 2000050010;
+ mi.icolibItem = GetIconHandle( IDI_CONSOLE );
+ Menu_AddProtoMenuItem(&mi);
+
+ JCreateService( "/Notes", &CJabberProto::OnMenuHandleNotes );
+ strcpy( tDest, "/Notes" );
+ mi.pszName = LPGEN("Notes");
+ mi.position = 2000050011;
+ mi.icolibItem = GetIconHandle( IDI_NOTES);
+ m_hMenuNotes = Menu_AddProtoMenuItem(&mi);
+
+ BuildPrivacyMenu();
+ if ( m_menuItemsStatus )
+ BuildPrivacyListsMenu( false );
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // build priority menu
+
+ m_priorityMenuVal = 0;
+ m_priorityMenuValSet = false;
+
+ mi.position = 200006;
+ mi.pszContactOwner = m_szModuleName;
+ mi.hParentMenu = hJabberRoot;
+ mi.pszName = LPGEN("Resource priority");
+ mi.flags = CMIF_ROOTPOPUP | CMIF_HIDDEN;
+ m_hMenuPriorityRoot = Menu_AddProtoMenuItem(&mi);
+
+ char szName[128], srvFce[MAX_PATH + 64], *svcName = srvFce+strlen( m_szModuleName );
+ mi.pszService = srvFce;
+ mi.pszName = szName;
+ mi.position = 2000040000;
+ mi.flags = CMIF_CHILDPOPUP | CMIF_ICONFROMICOLIB;
+ mi.hParentMenu = m_hMenuPriorityRoot;
+
+ mir_snprintf(srvFce, sizeof(srvFce), "%s/menuSetPriority/0", m_szModuleName);
+ bool needServices = !ServiceExists(srvFce);
+ if ( needServices )
+ JCreateServiceParam(svcName, &CJabberProto::OnMenuSetPriority, (LPARAM)0);
+
+ int steps[] = { 10, 5, 1, 0, -1, -5, -10 };
+ for (int i = 0; i < SIZEOF(steps); ++i) {
+ if ( !steps[i] ) {
+ mi.position += 100000;
+ continue;
+ }
+
+ mi.icolibItem = (steps[i] > 0) ? GetIconHandle(IDI_ARROW_UP) : GetIconHandle(IDI_ARROW_DOWN);
+
+ mir_snprintf(srvFce, sizeof(srvFce), "%s/menuSetPriority/%d", m_szModuleName, steps[i]);
+ mir_snprintf(szName, sizeof(szName), (steps[i] > 0) ? "Increase priority by %d" : "Decrease priority by %d", abs(steps[i]));
+
+ if ( needServices )
+ JCreateServiceParam(svcName, &CJabberProto::OnMenuSetPriority, (LPARAM)steps[i]);
+
+ mi.position++;
+ Menu_AddProtoMenuItem(&mi);
+ }
+
+ UpdatePriorityMenu((short)JGetWord(NULL, "Priority", 0));
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // finalize status menu
+
+ m_pepServices.RebuildMenu();
+ CheckMenuItems();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// priority popup in status menu
+
+INT_PTR CJabberProto::OnMenuSetPriority(WPARAM, LPARAM, LPARAM dwDelta)
+{
+ int iDelta = (int)dwDelta;
+ short priority = 0;
+ priority = (short)JGetWord(NULL, "Priority", 0) + iDelta;
+ if (priority > 127) priority = 127;
+ else if (priority < -128) priority = -128;
+ JSetWord(NULL, "Priority", priority);
+ SendPresence(m_iStatus, true);
+ return 0;
+}
+
+void CJabberProto::UpdatePriorityMenu(short priority)
+{
+ if (!m_hMenuPriorityRoot || m_priorityMenuValSet && (priority == m_priorityMenuVal))
+ return;
+
+ TCHAR szName[128];
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_TCHAR | CMIM_NAME;
+ mi.ptszName = szName;
+ mir_sntprintf(szName, SIZEOF(szName), TranslateT("Resource priority [%d]"), (int)priority);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)m_hMenuPriorityRoot, (LPARAM)&mi);
+
+ m_priorityMenuVal = priority;
+ m_priorityMenuValSet = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CJabberProto::GlobalMenuInit()
+{
+ //////////////////////////////////////////////////////////////////////////////////////
+ // Account chooser menu
+
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ tmi.ownerdata = this;
+ tmi.position = iChooserMenuPos++;
+ tmi.ptszName = m_tszUserName;
+ m_hChooseMenuItem = (HANDLE)CallService( MO_ADDNEWMENUITEM, (WPARAM)hChooserMenu, ( LPARAM )&tmi );
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // Hotkeys
+
+ char text[ 200 ];
+ strcpy( text, m_szModuleName );
+ char* tDest = text + strlen( text );
+
+ HOTKEYDESC hkd = {0};
+ hkd.cbSize = sizeof(hkd);
+ hkd.pszName = text;
+ hkd.pszService = text;
+ hkd.ptszSection = m_tszUserName;
+ hkd.dwFlags = HKD_TCHAR;
+
+ strcpy(tDest, "/Groupchat");
+ hkd.ptszDescription = _T("Join conference");
+ Hotkey_Register(&hkd);
+
+ strcpy(tDest, "/Bookmarks");
+ hkd.ptszDescription = _T("Open bookmarks");
+ Hotkey_Register(&hkd);
+
+ strcpy(tDest, "/PrivacyLists");
+ hkd.ptszDescription = _T("Privacy lists");
+ Hotkey_Register(&hkd);
+
+ strcpy(tDest, "/ServiceDiscovery");
+ hkd.ptszDescription = _T("Service discovery");
+ Hotkey_Register(&hkd);
+}
+
+static INT_PTR g_ToolbarHandleJoinGroupchat(WPARAM w, LPARAM l)
+{
+ if (CJabberProto *ppro = JabberChooseInstance())
+ return ppro->OnMenuHandleJoinGroupchat(w, l);
+ return 0;
+}
+
+static INT_PTR g_ToolbarHandleBookmarks(WPARAM w, LPARAM l)
+{
+ if (CJabberProto *ppro = JabberChooseInstance())
+ return ppro->OnMenuHandleBookmarks(w, l);
+ return 0;
+}
+
+static INT_PTR g_ToolbarHandleServiceDiscovery(WPARAM w, LPARAM l)
+{
+ if (CJabberProto *ppro = JabberChooseInstance())
+ return ppro->OnMenuHandleServiceDiscovery(w, l);
+ return 0;
+}
+
+int g_OnToolbarInit(WPARAM, LPARAM)
+{
+ if ( g_Instances.getCount() == 0 )
+ return 0;
+
+ TTBButton button = {0};
+ button.cbSize = sizeof(button);
+ button.dwFlags = TTBBF_SHOWTOOLTIP | TTBBF_VISIBLE;
+
+ List_InsertPtr( &arServices, CreateServiceFunction("JABBER/*/Groupchat", g_ToolbarHandleJoinGroupchat ));
+ button.pszService = "JABBER/*/Groupchat";
+ button.pszTooltipUp = button.name = LPGEN("Join conference");
+ button.hIconHandleUp = g_GetIconHandle(IDI_GROUP);
+ TopToolbar_AddButton(&button);
+
+ List_InsertPtr( &arServices, CreateServiceFunction("JABBER/*/Bookmarks", g_ToolbarHandleBookmarks ));
+ button.pszService = "JABBER/*/Bookmarks";
+ button.pszTooltipUp = button.name = LPGEN("Open bookmarks");
+ button.hIconHandleUp = g_GetIconHandle(IDI_BOOKMARKS);
+ TopToolbar_AddButton(&button);
+
+ List_InsertPtr( &arServices, CreateServiceFunction("JABBER/*/ServiceDiscovery", g_ToolbarHandleServiceDiscovery ));
+ button.pszService = "JABBER/*/ServiceDiscovery";
+ button.pszTooltipUp = button.name = LPGEN("Service discovery");
+ button.hIconHandleUp = g_GetIconHandle(IDI_SERVICE_DISCOVERY);
+ TopToolbar_AddButton(&button);
+ return 0;
+}
+
+void CJabberProto::GlobalMenuUninit()
+{
+ if ( m_phMenuResourceItems ) {
+ for ( int i=0; i < m_nMenuResourceItems; i++ )
+ CallService( MS_CLIST_REMOVECONTACTMENUITEM, ( WPARAM )m_phMenuResourceItems[i], 0 );
+ mir_free(m_phMenuResourceItems);
+ m_phMenuResourceItems = NULL;
+ }
+ m_nMenuResourceItems = 0;
+
+ if ( m_hMenuRoot )
+ CallService( MS_CLIST_REMOVEMAINMENUITEM, ( WPARAM )m_hMenuRoot, 0 );
+ m_hMenuRoot = NULL;
+}
+
+void CJabberProto::EnableMenuItems( BOOL bEnable )
+{
+ m_menuItemsStatus = bEnable;
+ CheckMenuItems();
+}
+
+void CJabberProto::CheckMenuItems()
+{
+ CLISTMENUITEM clmi = { 0 };
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_FLAGS;
+ if ( !m_menuItemsStatus )
+ clmi.flags |= CMIF_GRAYED;
+
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuChangePassword, ( LPARAM )&clmi );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuGroupchat, ( LPARAM )&clmi );
+
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuPrivacyLists, ( LPARAM )&clmi );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuRosterControl, ( LPARAM )&clmi );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuServiceDiscovery, ( LPARAM )&clmi );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuSDMyTransports, ( LPARAM )&clmi );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuSDTransports, ( LPARAM )&clmi );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuSDConferences, ( LPARAM )&clmi );
+
+ clmi.flags = CMIM_FLAGS | (( m_ThreadInfo && ( m_ThreadInfo->jabberServerCaps & JABBER_CAPS_PRIVATE_STORAGE)) ? 0 : CMIF_HIDDEN );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuBookmarks, ( LPARAM )&clmi );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuNotes, ( LPARAM )&clmi );
+
+ clmi.flags = CMIM_FLAGS | (( m_ThreadInfo && ( m_ThreadInfo->jabberServerCaps & JABBER_CAPS_PRIVACY_LISTS)) ? 0 : CMIF_HIDDEN );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hPrivacyMenuRoot, ( LPARAM )&clmi );
+
+ clmi.flags = CMIM_FLAGS | ( m_menuItemsStatus ? 0 : CMIF_HIDDEN);
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuPriorityRoot, ( LPARAM )&clmi );
+
+ if ( !m_bPepSupported )
+ clmi.flags |= CMIF_HIDDEN;
+ for ( int i=0; i < m_pepServices.getCount(); i++ )
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_pepServices[i].GetMenu(), ( LPARAM )&clmi );
+
+ JabberUpdateDialogs( m_menuItemsStatus );
+}
+
+//////////////////////////////////////////////////////////////////////////
+// resource menu
+
+static HANDLE hDialogsList = NULL;
+
+void CJabberProto::MenuHideSrmmIcon(HANDLE hContact)
+{
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = m_szModuleName;
+ sid.flags = MBF_HIDDEN;
+ CallService(MS_MSG_MODIFYICON, (WPARAM)hContact, (LPARAM)&sid);
+}
+
+void CJabberProto::MenuUpdateSrmmIcon(JABBER_LIST_ITEM *item)
+{
+ if ( item->list != LIST_ROSTER || !ServiceExists( MS_MSG_MODIFYICON ))
+ return;
+
+ HANDLE hContact = HContactFromJID(item->jid);
+ if ( !hContact )
+ return;
+
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = m_szModuleName;
+ sid.flags = item->resourceCount ? 0 : MBF_HIDDEN;
+ CallService(MS_MSG_MODIFYICON, (WPARAM)hContact, (LPARAM)&sid);
+}
+
+int CJabberProto::OnProcessSrmmEvent( WPARAM, LPARAM lParam )
+{
+ MessageWindowEventData *event = (MessageWindowEventData *)lParam;
+
+ if ( event->uType == MSG_WINDOW_EVT_OPEN ) {
+ if ( !hDialogsList )
+ hDialogsList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0);
+ WindowList_Add(hDialogsList, event->hwndWindow, event->hContact);
+ }
+ else if ( event->uType == MSG_WINDOW_EVT_CLOSING ) {
+ if (hDialogsList)
+ WindowList_Remove(hDialogsList, event->hwndWindow);
+
+ DBVARIANT dbv;
+ BOOL bSupportTyping = FALSE;
+ if ( !DBGetContactSetting( event->hContact, "SRMsg", "SupportTyping", &dbv )) {
+ bSupportTyping = dbv.bVal == 1;
+ JFreeVariant( &dbv );
+ } else if ( !DBGetContactSetting( NULL, "SRMsg", "DefaultTyping", &dbv )) {
+ bSupportTyping = dbv.bVal == 1;
+ JFreeVariant( &dbv );
+ }
+ if ( bSupportTyping && !JGetStringT( event->hContact, "jid", &dbv )) {
+ TCHAR jid[ JABBER_MAX_JID_LEN ];
+ GetClientJID( dbv.ptszVal, jid, SIZEOF( jid ));
+ JFreeVariant( &dbv );
+
+ JABBER_RESOURCE_STATUS *r = ResourceInfoFromJID( jid );
+
+ if ( r && r->bMessageSessionActive ) {
+ r->bMessageSessionActive = FALSE;
+ JabberCapsBits jcb = GetResourceCapabilites( jid, TRUE );
+
+ if ( jcb & JABBER_CAPS_CHATSTATES ) {
+ int iqId = SerialNext();
+ m_ThreadInfo->send(
+ XmlNode( _T("message")) << XATTR( _T("to"), jid ) << XATTR( _T("type"), _T("chat")) << XATTRID( iqId )
+ << XCHILDNS( _T("gone"), _T(JABBER_FEAT_CHATSTATES)));
+ } } } }
+
+ return 0;
+}
+
+int CJabberProto::OnProcessSrmmIconClick( WPARAM wParam, LPARAM lParam )
+{
+ StatusIconClickData *sicd = (StatusIconClickData *)lParam;
+ if (lstrcmpA(sicd->szModule, m_szModuleName))
+ return 0;
+
+ HANDLE hContact = (HANDLE)wParam;
+ if (!hContact)
+ return 0;
+
+ DBVARIANT dbv;
+ if (JGetStringT(hContact, "jid", &dbv))
+ return 0;
+
+ JABBER_LIST_ITEM *LI = ListGetItemPtr(LIST_ROSTER, dbv.ptszVal);
+ JFreeVariant( &dbv );
+
+ if ( !LI )
+ return 0;
+
+ HMENU hMenu = CreatePopupMenu();
+ TCHAR buf[256];
+
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s (%s)"), TranslateT("Last active"),
+ ((LI->lastSeenResource>=0) && (LI->lastSeenResource < LI->resourceCount)) ?
+ LI->resource[LI->lastSeenResource].resourceName : TranslateT("No activity yet, use server's choice"));
+ AppendMenu(hMenu, MF_STRING, MENUITEM_LASTSEEN, buf);
+
+ AppendMenu(hMenu, MF_STRING, MENUITEM_SERVER, TranslateT("Highest priority (server's choice)"));
+
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ for (int i = 0; i < LI->resourceCount; ++i)
+ AppendMenu(hMenu, MF_STRING, MENUITEM_RESOURCES+i, LI->resource[i].resourceName);
+
+ if (LI->resourceMode == RSMODE_LASTSEEN)
+ CheckMenuItem(hMenu, MENUITEM_LASTSEEN, MF_BYCOMMAND|MF_CHECKED);
+ else if (LI->resourceMode == RSMODE_SERVER)
+ CheckMenuItem(hMenu, MENUITEM_SERVER, MF_BYCOMMAND|MF_CHECKED);
+ else
+ CheckMenuItem(hMenu, MENUITEM_RESOURCES+LI->manualResource, MF_BYCOMMAND|MF_CHECKED);
+
+ int res = TrackPopupMenu(hMenu, TPM_RETURNCMD, sicd->clickLocation.x, sicd->clickLocation.y, 0, WindowList_Find(hDialogsList, hContact), NULL);
+
+ if ( res == MENUITEM_LASTSEEN ) {
+ LI->manualResource = -1;
+ LI->resourceMode = RSMODE_LASTSEEN;
+ }
+ else if (res == MENUITEM_SERVER) {
+ LI->manualResource = -1;
+ LI->resourceMode = RSMODE_SERVER;
+ }
+ else if (res >= MENUITEM_RESOURCES) {
+ LI->manualResource = res - MENUITEM_RESOURCES;
+ LI->resourceMode = RSMODE_MANUAL;
+ }
+
+ UpdateMirVer(LI);
+ MenuUpdateSrmmIcon(LI);
+
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleResource(WPARAM wParam, LPARAM, LPARAM res)
+{
+ if ( !m_bJabberOnline || !wParam )
+ return 0;
+
+ HANDLE hContact = (HANDLE)wParam;
+
+ DBVARIANT dbv;
+ if (JGetStringT(hContact, "jid", &dbv))
+ return 0;
+
+ JABBER_LIST_ITEM *LI = ListGetItemPtr(LIST_ROSTER, dbv.ptszVal);
+ JFreeVariant( &dbv );
+
+ if ( !LI )
+ return 0;
+
+ if ( res == MENUITEM_LASTSEEN ) {
+ LI->manualResource = -1;
+ LI->resourceMode = RSMODE_LASTSEEN;
+ }
+ else if (res == MENUITEM_SERVER) {
+ LI->manualResource = -1;
+ LI->resourceMode = RSMODE_SERVER;
+ }
+ else if (res >= MENUITEM_RESOURCES) {
+ LI->manualResource = res - MENUITEM_RESOURCES;
+ LI->resourceMode = RSMODE_MANUAL;
+ }
+
+ UpdateMirVer(LI);
+ MenuUpdateSrmmIcon(LI);
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleDirectPresence(WPARAM wParam, LPARAM lParam, LPARAM res)
+{
+ if ( !m_bJabberOnline || !wParam )
+ return 0;
+
+ HANDLE hContact = (HANDLE)wParam;
+
+ TCHAR *jid, text[ 1024 ];
+
+ DBVARIANT dbv;
+ int result = JGetStringT( hContact, "jid", &dbv );
+ if (result)
+ {
+ result = JGetStringT( hContact, "ChatRoomID", &dbv );
+ if ( result ) return 0;
+
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_CHATROOM, dbv.ptszVal );
+ if ( !item ) return 0;
+
+ mir_sntprintf( text, SIZEOF( text ), _T("%s/%s"), item->jid, item->nick );
+ jid = text;
+ }
+ else
+ jid = dbv.ptszVal;
+
+ TCHAR buf[1024] = _T("");
+ EnterString(buf, SIZEOF(buf), TranslateT("Status Message"), JES_MULTINE);
+
+ SendPresenceTo(res, jid, NULL, buf);
+ JFreeVariant(&dbv);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Choose protocol instance
+CJabberProto *JabberChooseInstance(bool bIsLink)
+{
+ if ( g_Instances.getCount() == 0 )
+ return NULL;
+
+ if ( g_Instances.getCount() == 1 ) {
+ if ( g_Instances[0]->m_iStatus != ID_STATUS_OFFLINE && g_Instances[0]->m_iStatus != ID_STATUS_CONNECTING )
+ return g_Instances[0];
+ return NULL;
+ }
+
+ if ( bIsLink ) {
+ for ( int i = 0; i < g_Instances.getCount(); i++ )
+ if ( g_Instances[i]->m_options.ProcessXMPPLinks )
+ return g_Instances[i];
+ }
+
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+
+ int nItems = 0, lastItemId = 0;
+ for (int i = 0; i < g_Instances.getCount(); ++i) {
+ clmi.flags = CMIM_FLAGS;
+
+ CJabberProto* ppro = g_Instances[i];
+ if ( ppro->m_iStatus != ID_STATUS_OFFLINE && ppro->m_iStatus != ID_STATUS_CONNECTING ) {
+ ++nItems;
+ lastItemId = i+1;
+ clmi.flags |= CMIM_ICON;
+ clmi.hIcon = LoadSkinnedProtoIcon(ppro->m_szModuleName, ppro->m_iStatus);
+ }
+ else clmi.flags |= CMIF_HIDDEN;
+
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )ppro->m_hChooseMenuItem, ( LPARAM )&clmi );
+ }
+
+ if ( nItems > 1 ) {
+ ListParam param = { 0 };
+ param.MenuObjectHandle = hChooserMenu;
+ HMENU hMenu = CreatePopupMenu();
+ CallService( MO_BUILDMENU, ( WPARAM )hMenu, ( LPARAM )¶m );
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ int res = TrackPopupMenu( hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, pcli->hwndContactList, NULL );
+ DestroyMenu( hMenu );
+
+ if ( res ) {
+ CJabberProto* pro = NULL;
+ CallService( MO_PROCESSCOMMANDBYMENUIDENT, res, ( LPARAM )&pro );
+ return pro;
+ }
+
+ return NULL;
+ }
+
+ return lastItemId ? g_Instances[lastItemId-1] : NULL;
+}
diff --git a/protocols/JabberG/src/jabber_message_handlers.cpp b/protocols/JabberG/src/jabber_message_handlers.cpp new file mode 100644 index 0000000000..a3801f274f --- /dev/null +++ b/protocols/JabberG/src/jabber_message_handlers.cpp @@ -0,0 +1,87 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-08 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2008-09 Dmitriy Chervov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_message_manager.h"
+
+BOOL CJabberProto::OnMessageError( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo )
+{
+ // we check if is message delivery failure
+ int id = JabberGetPacketID( node );
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_ROSTER, pInfo->GetFrom());
+ if ( item == NULL )
+ item = ListGetItemPtr( LIST_CHATROOM, pInfo->GetFrom());
+ if ( item != NULL ) { // yes, it is
+ TCHAR *szErrText = JabberErrorMsg( pInfo->GetChildNode());
+ if ( id != -1 ) {
+ char *errText = mir_t2a(szErrText);
+ JSendBroadcast( pInfo->GetHContact(), ACKTYPE_MESSAGE, ACKRESULT_FAILED, ( HANDLE ) id, (LPARAM)errText );
+ mir_free(errText);
+ } else {
+ TCHAR buf[512];
+ HXML bodyNode = xmlGetChild( node, "body" );
+ if (bodyNode)
+ mir_sntprintf( buf, SIZEOF( buf ), _T( "%s:\n%s\n%s" ), pInfo->GetFrom(), xmlGetText( bodyNode ), szErrText );
+ else
+ mir_sntprintf( buf, SIZEOF( buf ), _T( "%s:\n%s" ), pInfo->GetFrom(), szErrText );
+
+ MsgPopup( NULL, buf, TranslateT( "Jabber Error" ));
+ }
+ mir_free(szErrText);
+ }
+ return TRUE;
+}
+
+BOOL CJabberProto::OnMessageIbb( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo )
+{
+ BOOL bOk = FALSE;
+ const TCHAR *sid = xmlGetAttrValue( pInfo->GetChildNode(), _T("sid"));
+ const TCHAR *seq = xmlGetAttrValue( pInfo->GetChildNode(), _T("seq"));
+ if ( sid && seq && xmlGetText( pInfo->GetChildNode()) ) {
+ bOk = OnIbbRecvdData( xmlGetText( pInfo->GetChildNode()), sid, seq );
+ }
+ return TRUE;
+}
+
+BOOL CJabberProto::OnMessagePubsubEvent( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo )
+{
+ OnProcessPubsubEvent( node );
+ return TRUE;
+}
+
+BOOL CJabberProto::OnMessageGroupchat( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo )
+{
+ JABBER_LIST_ITEM *chatItem = ListGetItemPtr( LIST_CHATROOM, pInfo->GetFrom());
+ if ( chatItem )
+ { // process GC message
+ GroupchatProcessMessage( node );
+ } else
+ { // got message from unknown conference... let's leave it :)
+// TCHAR *conference = NEWTSTR_ALLOCA(from);
+// if (TCHAR *s = _tcschr(conference, _T('/'))) *s = 0;
+// XmlNode p( "presence" ); xmlAddAttr( p, "to", conference ); xmlAddAttr( p, "type", "unavailable" );
+// info->send( p );
+ }
+ return TRUE;
+}
diff --git a/protocols/JabberG/src/jabber_message_manager.cpp b/protocols/JabberG/src/jabber_message_manager.cpp new file mode 100644 index 0000000000..67e9982eb3 --- /dev/null +++ b/protocols/JabberG/src/jabber_message_manager.cpp @@ -0,0 +1,107 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-08 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2008-09 Dmitriy Chervov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_message_manager.h"
+
+BOOL CJabberMessageManager::FillPermanentHandlers()
+{
+ AddPermanentHandler( &CJabberProto::OnMessageError, JABBER_MESSAGE_TYPE_ERROR, JABBER_MESSAGE_PARSE_FROM | JABBER_MESSAGE_PARSE_HCONTACT, NULL, FALSE, _T("error"));
+ AddPermanentHandler( &CJabberProto::OnMessageIbb, 0, 0, _T(JABBER_FEAT_IBB), FALSE, _T("data"));
+ AddPermanentHandler( &CJabberProto::OnMessagePubsubEvent, 0, 0, _T(JABBER_FEAT_PUBSUB_EVENT), FALSE, _T("event"));
+ AddPermanentHandler( &CJabberProto::OnMessageGroupchat, JABBER_MESSAGE_TYPE_GROUPCHAT, JABBER_MESSAGE_PARSE_FROM, NULL, FALSE, NULL );
+ return TRUE;
+}
+
+BOOL CJabberMessageManager::HandleMessagePermanent(HXML node, ThreadData *pThreadData)
+{
+ BOOL bStopHandling = FALSE;
+ Lock();
+ CJabberMessagePermanentInfo *pInfo = m_pPermanentHandlers;
+ while ( pInfo && !bStopHandling ) {
+ // have to get all data here, in the loop, because there's always possibility that previous handler modified it
+ CJabberMessageInfo messageInfo;
+
+ LPCTSTR szType = xmlGetAttrValue(node, _T("type"));
+ if ( szType )
+ {
+ if ( !_tcsicmp( szType, _T("normal")))
+ messageInfo.m_nMessageType = JABBER_MESSAGE_TYPE_NORMAL;
+ else if ( !_tcsicmp( szType, _T("error")))
+ messageInfo.m_nMessageType = JABBER_MESSAGE_TYPE_ERROR;
+ else if ( !_tcsicmp( szType, _T("chat")))
+ messageInfo.m_nMessageType = JABBER_MESSAGE_TYPE_CHAT;
+ else if ( !_tcsicmp( szType, _T("groupchat")))
+ messageInfo.m_nMessageType = JABBER_MESSAGE_TYPE_GROUPCHAT;
+ else if ( !_tcsicmp( szType, _T("headline")))
+ messageInfo.m_nMessageType = JABBER_MESSAGE_TYPE_HEADLINE;
+ else
+ break; // m_nMessageType = JABBER_MESSAGE_TYPE_FAIL;
+ }
+ else {
+ messageInfo.m_nMessageType = JABBER_MESSAGE_TYPE_NORMAL;
+ }
+
+ if ( (pInfo->m_nMessageTypes & messageInfo.m_nMessageType )) {
+ int i;
+ for ( i = xmlGetChildCount( node ) - 1; i >= 0; i-- ) {
+ // enumerate all children and see whether this node suits handler criteria
+ HXML child = xmlGetChild( node, i );
+
+ LPCTSTR szTagName = xmlGetName(child);
+ LPCTSTR szXmlns = xmlGetAttrValue( child, _T("xmlns"));
+
+ if ( (!pInfo->m_szXmlns || ( szXmlns && !_tcscmp( pInfo->m_szXmlns, szXmlns ))) &&
+ ( !pInfo->m_szTag || !_tcscmp( pInfo->m_szTag, szTagName ))) {
+ // node suits handler criteria, call the handler
+ messageInfo.m_hChildNode = child;
+ messageInfo.m_szChildTagName = szTagName;
+ messageInfo.m_szChildTagXmlns = szXmlns;
+ messageInfo.m_pUserData = pInfo->m_pUserData;
+ messageInfo.m_szFrom = xmlGetAttrValue( node, _T("from")); // is necessary for ppro->Log() below, that's why we must parse it even if JABBER_MESSAGE_PARSE_FROM flag is not set
+
+ if (pInfo->m_dwParamsToParse & JABBER_MESSAGE_PARSE_ID_STR)
+ messageInfo.m_szId = xmlGetAttrValue( node, _T("id"));
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_TO)
+ messageInfo.m_szTo = xmlGetAttrValue( node, _T("to"));
+
+ if (pInfo->m_dwParamsToParse & JABBER_MESSAGE_PARSE_HCONTACT)
+ messageInfo.m_hContact = ppro->HContactFromJID( messageInfo.m_szFrom, 3 );
+
+ if (messageInfo.m_szFrom)
+ ppro->Log( "Handling message from " TCHAR_STR_PARAM, messageInfo.m_szFrom );
+ if ((ppro->*(pInfo->m_pHandler))(node, pThreadData, &messageInfo)) {
+ bStopHandling = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ pInfo = pInfo->m_pNext;
+ }
+ Unlock();
+
+ return bStopHandling;
+}
diff --git a/protocols/JabberG/src/jabber_message_manager.h b/protocols/JabberG/src/jabber_message_manager.h new file mode 100644 index 0000000000..dd52b50683 --- /dev/null +++ b/protocols/JabberG/src/jabber_message_manager.h @@ -0,0 +1,250 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-08 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2008-09 Dmitriy Chervov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_MESSAGE_MANAGER_H_
+#define _JABBER_MESSAGE_MANAGER_H_
+
+#include "jabber_xml.h"
+
+struct CJabberProto;
+typedef void ( CJabberProto::*JABBER_MESSAGE_PFUNC )( HXML messageNode, void *usedata );
+typedef void ( *MESSAGE_USER_DATA_FREE_FUNC )( void *pUserData );
+
+class CJabberMessageInfo;
+
+typedef BOOL ( CJabberProto::*JABBER_PERMANENT_MESSAGE_HANDLER )( HXML messageNode, ThreadData *pThreadData, CJabberMessageInfo* pInfo );
+
+#define JABBER_MESSAGE_PARSE_FROM (1<<3)
+#define JABBER_MESSAGE_PARSE_HCONTACT ((1<<4)|JABBER_MESSAGE_PARSE_FROM)
+#define JABBER_MESSAGE_PARSE_TO (1<<5)
+#define JABBER_MESSAGE_PARSE_ID_STR (1<<6)
+
+class CJabberMessageInfo
+{
+protected:
+ friend class CJabberMessageManager;
+ JABBER_PERMANENT_MESSAGE_HANDLER m_pHandler;
+ CJabberMessageInfo* m_pNext;
+
+public:
+ void *m_pUserData;
+// parsed data
+ int m_nMessageType;
+ LPCTSTR m_szFrom;
+ LPCTSTR m_szChildTagXmlns;
+ LPCTSTR m_szChildTagName;
+ HXML m_hChildNode;
+ HANDLE m_hContact;
+ LPCTSTR m_szTo;
+ LPCTSTR m_szId;
+
+public:
+ CJabberMessageInfo()
+ {
+ ZeroMemory(this, sizeof(*this));
+ }
+ ~CJabberMessageInfo()
+ {
+ }
+ int GetMessageType()
+ {
+ return m_nMessageType;
+ }
+ void* GetUserData()
+ {
+ return m_pUserData;
+ }
+ LPCTSTR GetFrom()
+ {
+ return m_szFrom;
+ }
+ LPCTSTR GetTo()
+ {
+ return m_szTo;
+ }
+ LPCTSTR GetIdStr()
+ {
+ return m_szId;
+ }
+ HANDLE GetHContact()
+ {
+ return m_hContact;
+ }
+ HXML GetChildNode()
+ {
+ return m_hChildNode;
+ }
+ LPCTSTR GetChildNodeName()
+ {
+ return m_szChildTagName;
+ }
+};
+
+class CJabberMessagePermanentInfo
+{
+ friend class CJabberMessageManager;
+
+ CJabberMessagePermanentInfo* m_pNext;
+
+ JABBER_PERMANENT_MESSAGE_HANDLER m_pHandler;
+ DWORD m_dwParamsToParse;
+ int m_nMessageTypes;
+ LPTSTR m_szXmlns;
+ LPTSTR m_szTag;
+ BOOL m_bAllowPartialNs;
+ void *m_pUserData;
+ MESSAGE_USER_DATA_FREE_FUNC m_pUserDataFree;
+ int m_iPriority;
+public:
+ CJabberMessagePermanentInfo()
+ {
+ ZeroMemory(this, sizeof(CJabberMessagePermanentInfo));
+ }
+ ~CJabberMessagePermanentInfo()
+ {
+ if ( m_pUserDataFree )
+ m_pUserDataFree(m_pUserData);
+ mir_free(m_szXmlns);
+ mir_free(m_szTag);
+ }
+};
+
+class CJabberMessageManager
+{
+protected:
+ CJabberProto* ppro;
+ CRITICAL_SECTION m_cs;
+ CJabberMessagePermanentInfo* m_pPermanentHandlers;
+
+public:
+ CJabberMessageManager( CJabberProto* proto )
+ {
+ InitializeCriticalSection(&m_cs);
+ m_pPermanentHandlers = NULL;
+ ppro = proto;
+ }
+ ~CJabberMessageManager()
+ {
+ Lock();
+ CJabberMessagePermanentInfo *pInfo = m_pPermanentHandlers;
+ while ( pInfo )
+ {
+ CJabberMessagePermanentInfo *pTmp = pInfo->m_pNext;
+ delete pInfo;
+ pInfo = pTmp;
+ }
+ m_pPermanentHandlers = NULL;
+ Unlock();
+ DeleteCriticalSection(&m_cs);
+ }
+ BOOL Start()
+ {
+ return TRUE;
+ }
+ BOOL Shutdown()
+ {
+ return TRUE;
+ }
+ void Lock()
+ {
+ EnterCriticalSection(&m_cs);
+ }
+ void Unlock()
+ {
+ LeaveCriticalSection(&m_cs);
+ }
+ CJabberMessagePermanentInfo* AddPermanentHandler(JABBER_PERMANENT_MESSAGE_HANDLER pHandler, int nMessageTypes, DWORD dwParamsToParse, const TCHAR* szXmlns, BOOL bAllowPartialNs, const TCHAR* szTag, void *pUserData = NULL, MESSAGE_USER_DATA_FREE_FUNC pUserDataFree = NULL, int iPriority = JH_PRIORITY_DEFAULT)
+ {
+ CJabberMessagePermanentInfo* pInfo = new CJabberMessagePermanentInfo();
+ if (!pInfo)
+ return NULL;
+
+ pInfo->m_pHandler = pHandler;
+ pInfo->m_nMessageTypes = nMessageTypes ? nMessageTypes : JABBER_MESSAGE_TYPE_ANY;
+ replaceStrT( pInfo->m_szXmlns, szXmlns );
+ pInfo->m_bAllowPartialNs = bAllowPartialNs;
+ replaceStrT( pInfo->m_szTag, szTag );
+ pInfo->m_dwParamsToParse = dwParamsToParse;
+ pInfo->m_pUserData = pUserData;
+ pInfo->m_pUserDataFree = pUserDataFree;
+ pInfo->m_iPriority = iPriority;
+
+ Lock();
+ if (!m_pPermanentHandlers)
+ m_pPermanentHandlers = pInfo;
+ else
+ {
+ if (m_pPermanentHandlers->m_iPriority > pInfo->m_iPriority) {
+ pInfo->m_pNext = m_pPermanentHandlers;
+ m_pPermanentHandlers = pInfo;
+ } else
+ {
+ CJabberMessagePermanentInfo* pTmp = m_pPermanentHandlers;
+ while (pTmp->m_pNext && pTmp->m_pNext->m_iPriority <= pInfo->m_iPriority)
+ pTmp = pTmp->m_pNext;
+ pInfo->m_pNext = pTmp->m_pNext;
+ pTmp->m_pNext = pInfo;
+ }
+ }
+ Unlock();
+
+ return pInfo;
+ }
+ BOOL DeletePermanentHandler(CJabberMessagePermanentInfo *pInfo)
+ { // returns TRUE when pInfo found, or FALSE otherwise
+ Lock();
+ if (!m_pPermanentHandlers)
+ {
+ Unlock();
+ return FALSE;
+ }
+ if (m_pPermanentHandlers == pInfo) // check first item
+ {
+ m_pPermanentHandlers = m_pPermanentHandlers->m_pNext;
+ delete pInfo;
+ Unlock();
+ return TRUE;
+ } else
+ {
+ CJabberMessagePermanentInfo* pTmp = m_pPermanentHandlers;
+ while (pTmp->m_pNext)
+ {
+ if (pTmp->m_pNext == pInfo)
+ {
+ pTmp->m_pNext = pTmp->m_pNext->m_pNext;
+ delete pInfo;
+ Unlock();
+ return TRUE;
+ }
+ pTmp = pTmp->m_pNext;
+ }
+ }
+ Unlock();
+ return FALSE;
+ }
+ BOOL HandleMessagePermanent(HXML node, ThreadData *pThreadData);
+ BOOL FillPermanentHandlers();
+};
+
+#endif
diff --git a/protocols/JabberG/src/jabber_misc.cpp b/protocols/JabberG/src/jabber_misc.cpp new file mode 100644 index 0000000000..e13c0d450d --- /dev/null +++ b/protocols/JabberG/src/jabber_misc.cpp @@ -0,0 +1,642 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_list.h"
+#include "jabber_caps.h"
+
+#include <m_popup.h>
+#include "m_folders.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberAddContactToRoster() - adds a contact to the roster
+
+void CJabberProto::AddContactToRoster( const TCHAR* jid, const TCHAR* nick, const TCHAR* grpName )
+{
+ XmlNodeIq iq( _T("set"), SerialNext());
+ HXML query = iq << XQUERY( _T(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 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberChatDllError() - missing CHAT.DLL
+
+void JabberChatDllError()
+{
+ MessageBox( NULL,
+ TranslateT( "CHAT plugin is required for conferences. Install it before chatting" ),
+ TranslateT( "Jabber Error" ), MB_OK|MB_SETFOREGROUND );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberCompareJids
+
+int JabberCompareJids( const TCHAR* jid1, const TCHAR* jid2 )
+{
+ if ( !lstrcmpi( jid1, jid2 ))
+ return 0;
+
+ // match only node@domain part
+ TCHAR szTempJid1[ JABBER_MAX_JID_LEN ], szTempJid2[ JABBER_MAX_JID_LEN ];
+ return lstrcmpi(
+ JabberStripJid( jid1, szTempJid1, SIZEOF(szTempJid1)),
+ JabberStripJid( jid2, szTempJid2, SIZEOF(szTempJid2)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberContactListCreateGroup()
+
+static void JabberContactListCreateClistGroup( TCHAR* groupName )
+{
+ char str[33];
+ int i;
+ DBVARIANT dbv;
+
+ for ( i=0;;i++ ) {
+ _itoa( i, str, 10 );
+ if ( DBGetContactSettingTString( NULL, "CListGroups", str, &dbv ))
+ break;
+ TCHAR* name = dbv.ptszVal;
+ if ( name[0]!='\0' && !_tcscmp( name+1, groupName )) {
+ // Already exists, no need to create
+ JFreeVariant( &dbv );
+ return;
+ }
+ JFreeVariant( &dbv );
+ }
+
+ // Create new group with id = i ( str is the text representation of i )
+ TCHAR newName[128];
+ newName[0] = 1 | GROUPF_EXPANDED;
+ _tcsncpy( newName+1, groupName, SIZEOF( newName )-1 );
+ newName[ SIZEOF( newName )-1] = '\0';
+ DBWriteContactSettingTString( NULL, "CListGroups", str, newName );
+ CallService( MS_CLUI_GROUPADDED, i+1, 0 );
+}
+
+void JabberContactListCreateGroup( TCHAR* groupName )
+{
+ TCHAR name[128], *p;
+
+ if ( groupName==NULL || groupName[0]=='\0' || groupName[0]=='\\' ) return;
+
+ _tcsncpy( name, groupName, SIZEOF( name ));
+ name[ SIZEOF( name )-1] = '\0';
+ for ( p=name; *p!='\0'; p++ ) {
+ if ( *p == '\\' ) {
+ *p = '\0';
+ JabberContactListCreateClistGroup( name );
+ *p = '\\';
+ }
+ }
+ JabberContactListCreateClistGroup( name );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberDBAddAuthRequest()
+
+void CJabberProto::DBAddAuthRequest( const TCHAR* jid, const TCHAR* nick )
+{
+ HANDLE hContact = DBCreateContact( jid, NULL, TRUE, TRUE );
+ JDeleteSetting( hContact, "Hidden" );
+ //JSetStringT( hContact, "Nick", nick );
+
+ char* szJid = mir_utf8encodeT( jid );
+ char* szNick = mir_utf8encodeT( nick );
+
+ //blob is: uin(DWORD), hContact(DWORD), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ)
+ //blob is: 0( DWORD ), hContact(DWORD), nick( ASCIIZ ), ""( ASCIIZ ), ""( ASCIIZ ), email( ASCIIZ ), ""( ASCIIZ )
+ DBEVENTINFO dbei = { sizeof(DBEVENTINFO) };
+ dbei.szModule = m_szModuleName;
+ dbei.timestamp = ( DWORD )time( NULL );
+ dbei.flags = DBEF_UTF;
+ dbei.eventType = EVENTTYPE_AUTHREQUEST;
+ dbei.cbBlob = (DWORD)(sizeof(DWORD)*2 + strlen( szNick ) + strlen( szJid ) + 5);
+ PBYTE pCurBlob = dbei.pBlob = (PBYTE)mir_alloc(dbei.cbBlob);
+ *((PDWORD)pCurBlob) = 0; pCurBlob += sizeof(DWORD);
+ *((PDWORD)pCurBlob) = (DWORD)hContact; pCurBlob += sizeof(DWORD);
+ strcpy(( char* )pCurBlob, szNick ); pCurBlob += strlen( szNick )+1;
+ *pCurBlob = '\0'; pCurBlob++; //firstName
+ *pCurBlob = '\0'; pCurBlob++; //lastName
+ strcpy(( char* )pCurBlob, szJid ); pCurBlob += strlen( szJid )+1;
+ *pCurBlob = '\0'; //reason
+
+ CallService( MS_DB_EVENT_ADD, ( WPARAM ) ( HANDLE ) NULL, ( LPARAM )&dbei );
+ Log( "Setup DBAUTHREQUEST with nick='%s' jid='%s'", szNick, szJid );
+
+ mir_free( szJid );
+ mir_free( szNick );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberDBCreateContact()
+
+HANDLE CJabberProto::DBCreateContact( const TCHAR* jid, const TCHAR* nick, BOOL temporary, BOOL stripResource )
+{
+ TCHAR* s, *p, *q;
+ size_t len;
+ char* szProto;
+
+ if ( jid==NULL || jid[0]=='\0' )
+ return NULL;
+
+ s = mir_tstrdup( jid );
+ q = NULL;
+ // strip resource if present
+ if (( p = _tcschr( s, '@' )) != NULL )
+ if (( q = _tcschr( p, '/' )) != NULL )
+ *q = '\0';
+
+ if ( !stripResource && q!=NULL ) // so that resource is not stripped
+ *q = '/';
+ len = _tcslen( s );
+
+ // We can't use JabberHContactFromJID() here because of the stripResource option
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL ) {
+ szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto!=NULL && !strcmp( m_szModuleName, szProto )) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ p = dbv.ptszVal;
+ if ( p && _tcslen( p )>=len && ( p[len]=='\0'||p[len]=='/' ) && !_tcsnicmp( p, s, len )) {
+ JFreeVariant( &dbv );
+ break;
+ }
+ JFreeVariant( &dbv );
+ } }
+ hContact = db_find_next(hContact);
+ }
+
+ if ( hContact == NULL ) {
+ hContact = ( HANDLE ) CallService( MS_DB_CONTACT_ADD, 0, 0 );
+ CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM ) hContact, ( LPARAM )m_szModuleName );
+ JSetStringT( hContact, "jid", s );
+ if ( nick != NULL && *nick != '\0' )
+ JSetStringT( hContact, "Nick", nick );
+ if ( temporary )
+ DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 );
+ else
+ SendGetVcard( s );
+ Log( "Create Jabber contact jid=" TCHAR_STR_PARAM ", nick=" TCHAR_STR_PARAM, s, nick );
+ DBCheckIsTransportedContact(s,hContact);
+ }
+
+ mir_free( s );
+ return hContact;
+}
+
+BOOL CJabberProto::AddDbPresenceEvent(HANDLE hContact, BYTE btEventType)
+{
+ if ( !hContact )
+ return FALSE;
+
+ switch ( btEventType ) {
+ case JABBER_DB_EVENT_PRESENCE_SUBSCRIBE:
+ case JABBER_DB_EVENT_PRESENCE_SUBSCRIBED:
+ case JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBE:
+ case JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBED:
+ if ( !m_options.LogPresence )
+ return FALSE;
+ break;
+
+ case JABBER_DB_EVENT_PRESENCE_ERROR:
+ if ( !m_options.LogPresenceErrors )
+ return FALSE;
+ break;
+ }
+
+ DBEVENTINFO dbei;
+ dbei.cbSize = sizeof( dbei );
+ dbei.pBlob = &btEventType;
+ dbei.cbBlob = sizeof( btEventType );
+ dbei.eventType = JABBER_DB_EVENT_TYPE_PRESENCE;
+ dbei.flags = DBEF_READ;
+ dbei.timestamp = time( NULL );
+ dbei.szModule = m_szModuleName;
+ CallService( MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei );
+
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberGetAvatarFileName() - gets a file name for the avatar image
+
+static HANDLE hJabberAvatarsFolder = NULL;
+static bool bInitDone = false;
+
+void CJabberProto::InitCustomFolders( void )
+{
+ if ( bInitDone )
+ return;
+
+ bInitDone = true;
+ if ( ServiceExists( MS_FOLDERS_REGISTER_PATH )) {
+ TCHAR AvatarsFolder[MAX_PATH];
+ mir_sntprintf( AvatarsFolder, SIZEOF( AvatarsFolder ), _T("%%miranda_avatarcache%%\\Jabber"));
+ hJabberAvatarsFolder = FoldersRegisterCustomPathT( m_szModuleName, "Avatars", AvatarsFolder ); // title!!!!!!!!!!!
+} }
+
+void CJabberProto::GetAvatarFileName( HANDLE hContact, TCHAR* pszDest, size_t cbLen )
+{
+ size_t tPathLen;
+ TCHAR* path = ( TCHAR* )alloca( cbLen * sizeof( TCHAR ));
+
+ InitCustomFolders();
+
+ if ( hJabberAvatarsFolder == NULL || FoldersGetCustomPathT( hJabberAvatarsFolder, path, (int)cbLen, _T(""))) {
+ TCHAR *tmpPath = Utils_ReplaceVarsT( _T("%miranda_avatarcache%"));
+ tPathLen = mir_sntprintf( pszDest, cbLen, _T("%s\\Jabber"), tmpPath );
+ mir_free(tmpPath);
+ }
+ else tPathLen = mir_sntprintf( pszDest, cbLen, _T("%s"), path );
+
+ DWORD dwAttributes = GetFileAttributes( pszDest );
+ if ( dwAttributes == 0xffffffff || ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 )
+ CallService( MS_UTILS_CREATEDIRTREET, 0, ( LPARAM )pszDest );
+
+ pszDest[ tPathLen++ ] = '\\';
+
+ char* szFileType = NULL;
+ switch( JGetByte( hContact, "AvatarType", PA_FORMAT_PNG )) {
+ case PA_FORMAT_JPEG: szFileType = "jpg"; break;
+ case PA_FORMAT_PNG: szFileType = "png"; break;
+ case PA_FORMAT_GIF: szFileType = "gif"; break;
+ case PA_FORMAT_BMP: szFileType = "bmp"; break;
+ }
+
+ if ( hContact != NULL ) {
+ char str[ 256 ];
+ DBVARIANT dbv;
+ if ( !JGetStringUtf( hContact, "jid", &dbv )) {
+ strncpy( str, dbv.pszVal, sizeof str );
+ str[ sizeof(str)-1 ] = 0;
+ JFreeVariant( &dbv );
+ }
+ else _i64toa(( LONG_PTR )hContact, str, 10 );
+
+ char* hash = JabberSha1( str );
+ mir_sntprintf( pszDest + tPathLen, MAX_PATH - tPathLen, _T(TCHAR_STR_PARAM) _T(".") _T(TCHAR_STR_PARAM), hash, szFileType );
+ mir_free( hash );
+ }
+ else if ( m_ThreadInfo != NULL ) {
+ mir_sntprintf( pszDest + tPathLen, MAX_PATH - tPathLen, _T("%s@") _T(TCHAR_STR_PARAM) _T(" avatar.") _T(TCHAR_STR_PARAM),
+ m_ThreadInfo->username, m_ThreadInfo->server, szFileType );
+ }
+ else {
+ DBVARIANT dbv1, dbv2;
+ BOOL res1 = DBGetContactSettingString( NULL, m_szModuleName, "LoginName", &dbv1 );
+ BOOL res2 = DBGetContactSettingString( NULL, m_szModuleName, "LoginServer", &dbv2 );
+ mir_sntprintf( pszDest + tPathLen, MAX_PATH - tPathLen, _T(TCHAR_STR_PARAM) _T("@") _T(TCHAR_STR_PARAM) _T(" avatar.") _T(TCHAR_STR_PARAM),
+ res1 ? "noname" : dbv1.pszVal,
+ res2 ? m_szModuleName : dbv2.pszVal,
+ szFileType );
+ if (!res1) JFreeVariant( &dbv1 );
+ if (!res2) JFreeVariant( &dbv2 );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberResolveTransportNicks - massive vcard update
+
+void CJabberProto::ResolveTransportNicks( const TCHAR* jid )
+{
+ // Set all contacts to offline
+ HANDLE hContact = m_ThreadInfo->resolveContact;
+ if ( hContact == NULL )
+ hContact = ( HANDLE ) db_find_first();
+
+ for ( ; hContact != NULL; hContact = db_find_next(hContact)) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( lstrcmpA( szProto, m_szModuleName ))
+ continue;
+
+ if ( !JGetByte( hContact, "IsTransported", 0 ))
+ continue;
+
+ DBVARIANT dbv, nick;
+ if ( JGetStringT( hContact, "jid", &dbv ))
+ continue;
+ if ( JGetStringT( hContact, "Nick", &nick )) {
+ JFreeVariant( &dbv );
+ continue;
+ }
+
+ TCHAR* p = _tcschr( dbv.ptszVal, '@' );
+ if ( p ) {
+ *p = 0;
+ if ( !lstrcmp( jid, p+1 ) && !lstrcmp( dbv.ptszVal, nick.ptszVal )) {
+ *p = '@';
+ m_ThreadInfo->resolveID = SendGetVcard( dbv.ptszVal );
+ m_ThreadInfo->resolveContact = hContact;
+ JFreeVariant( &dbv );
+ JFreeVariant( &nick );
+ return;
+ } }
+
+ JFreeVariant( &dbv );
+ JFreeVariant( &nick );
+ }
+
+ m_ThreadInfo->resolveID = -1;
+ m_ThreadInfo->resolveContact = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberSetServerStatus()
+
+void CJabberProto::SetServerStatus( int iNewStatus )
+{
+ if ( !m_bJabberOnline )
+ return;
+
+ // change status
+ int oldStatus = m_iStatus;
+ switch ( iNewStatus ) {
+ case ID_STATUS_ONLINE:
+ case ID_STATUS_NA:
+ case ID_STATUS_FREECHAT:
+ case ID_STATUS_INVISIBLE:
+ m_iStatus = iNewStatus;
+ break;
+ case ID_STATUS_AWAY:
+ case ID_STATUS_ONTHEPHONE:
+ case ID_STATUS_OUTTOLUNCH:
+ m_iStatus = ID_STATUS_AWAY;
+ break;
+ case ID_STATUS_DND:
+ case ID_STATUS_OCCUPIED:
+ m_iStatus = ID_STATUS_DND;
+ break;
+ default:
+ return;
+ }
+
+ if ( m_iStatus == oldStatus )
+ return;
+
+ // send presence update
+ SendPresence( m_iStatus, true );
+ JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, m_iStatus );
+}
+
+// Process a string, and double all % characters, according to chat.dll's restrictions
+// Returns a pointer to the new string (old one is not freed)
+TCHAR* EscapeChatTags(TCHAR* pszText)
+{
+ int nChars = 0;
+ for ( TCHAR* p = pszText; ( p = _tcschr( p, '%' )) != NULL; p++ )
+ nChars++;
+
+ if ( nChars == 0 )
+ return mir_tstrdup( pszText );
+
+ TCHAR* pszNewText = (TCHAR*)mir_alloc( sizeof(TCHAR)*(_tcslen( pszText ) + 1 + nChars )), *s, *d;
+ if ( pszNewText == NULL )
+ return mir_tstrdup( pszText );
+
+ for ( s = pszText, d = pszNewText; *s; s++ ) {
+ if ( *s == '%' )
+ *d++ = '%';
+ *d++ = *s;
+ }
+ *d = 0;
+ return pszNewText;
+}
+
+TCHAR* UnEscapeChatTags(TCHAR* str_in)
+{
+ TCHAR* s = str_in, *d = str_in;
+ while ( *s ) {
+ if ( *s == '%' && s[1] == '%' )
+ s++;
+ *d++ = *s++;
+ }
+ *d = 0;
+ return str_in;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// update MirVer with data for active resource
+
+struct
+{
+ TCHAR *node;
+ TCHAR *name;
+}
+static sttCapsNodeToName_Map[] =
+{
+ { _T("http://miranda-im.org"), _T("Miranda IM Jabber") },
+ { _T("http://miranda-ng.org"), _T("Miranda NG Jabber") },
+ { _T("http://www.google.com"), _T("GTalk") },
+ { _T("http://mail.google.com"), _T("GMail") },
+ { _T("http://talk.google.com/xmpp/bot"), _T("GTalk Bot") },
+ { _T("http://www.android.com"), _T("Android") },
+};
+
+void CJabberProto::UpdateMirVer(JABBER_LIST_ITEM *item)
+{
+ HANDLE hContact = HContactFromJID(item->jid);
+ if (!hContact)
+ return;
+
+ Log("JabberUpdateMirVer: for jid " TCHAR_STR_PARAM, item->jid);
+
+ int resource = -1;
+ if (item->resourceMode == RSMODE_LASTSEEN)
+ resource = item->lastSeenResource;
+ else if (item->resourceMode == RSMODE_MANUAL)
+ resource = item->manualResource;
+ if ((resource < 0) || (resource >= item->resourceCount))
+ return;
+
+ UpdateMirVer( hContact, &item->resource[resource] );
+}
+
+void CJabberProto::FormatMirVer(JABBER_RESOURCE_STATUS *resource, TCHAR *buf, int bufSize)
+{
+ if ( !buf || !bufSize ) return;
+ buf[ 0 ] = _T('\0');
+ if ( !resource ) return;
+
+ // jabber:iq:version info requested and exists?
+ if ( resource->dwVersionRequestTime && resource->software ) {
+ Log("JabberUpdateMirVer: for iq:version rc " TCHAR_STR_PARAM ": " TCHAR_STR_PARAM, resource->resourceName, resource->software);
+ if ( !resource->version || _tcsstr(resource->software, resource->version))
+ lstrcpyn(buf, resource->software, bufSize);
+ else
+ mir_sntprintf(buf, bufSize, _T("%s %s"), resource->software, resource->version);
+ }
+ // no version info and no caps info? set MirVer = resource name
+ else if ( !resource->szCapsNode || !resource->szCapsVer ) {
+ Log("JabberUpdateMirVer: for rc " TCHAR_STR_PARAM ": " TCHAR_STR_PARAM, resource->resourceName, resource->resourceName);
+ if ( resource->resourceName )
+ lstrcpyn(buf, resource->resourceName, bufSize);
+ }
+ // XEP-0115 caps mode
+ else {
+ Log("JabberUpdateMirVer: for rc " TCHAR_STR_PARAM ": " TCHAR_STR_PARAM "#" TCHAR_STR_PARAM, resource->resourceName, resource->szCapsNode, resource->szCapsVer);
+
+ int i;
+
+ // search through known software list
+ for (i = 0; i < SIZEOF(sttCapsNodeToName_Map); ++i)
+ if ( _tcsstr( resource->szCapsNode, sttCapsNodeToName_Map[i].node ))
+ {
+ mir_sntprintf( buf, bufSize, _T("%s %s"), sttCapsNodeToName_Map[i].name, resource->szCapsVer );
+ break;
+ }
+
+ // unknown software
+ if (i == SIZEOF(sttCapsNodeToName_Map))
+ mir_sntprintf( buf, bufSize, _T("%s %s"), resource->szCapsNode, resource->szCapsVer );
+ }
+
+ // attach additional info for fingerprint plguin
+ if (resource->resourceName && !_tcsstr(buf, resource->resourceName))
+ {
+ if (_tcsstr(buf, _T("Miranda IM")) || _tcsstr(buf, _T("Miranda NG")) || m_options.ShowForeignResourceInMirVer )
+ {
+ int offset = lstrlen(buf);
+ mir_sntprintf(buf + offset, bufSize - offset, _T(" [%s]"), resource->resourceName);
+ }
+ }
+
+ if (resource->szCapsExt && _tcsstr(resource->szCapsExt, _T(JABBER_EXT_SECUREIM)) && !_tcsstr(buf, _T("(SecureIM)")))
+ {
+ int offset = lstrlen(buf);
+ mir_sntprintf(buf + offset, bufSize - offset, _T(" (SecureIM)"));
+ }
+}
+
+
+void CJabberProto::UpdateMirVer(HANDLE hContact, JABBER_RESOURCE_STATUS *resource)
+{
+ TCHAR szMirVer[ 512 ];
+ FormatMirVer(resource, szMirVer, SIZEOF(szMirVer));
+ if ( szMirVer[0] )
+ JSetStringT( hContact, "MirVer", szMirVer );
+// else
+// JDeleteSetting( hContact, "MirVer" );
+
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ TCHAR szFullJid[ JABBER_MAX_JID_LEN ];
+ if ( resource->resourceName )
+ mir_sntprintf( szFullJid, SIZEOF( szFullJid ), _T("%s/%s"), dbv.ptszVal, resource->resourceName );
+ else
+ lstrcpyn( szFullJid, dbv.ptszVal, SIZEOF(szFullJid));
+ JSetStringT( hContact, DBSETTING_DISPLAY_UID, szFullJid );
+ JFreeVariant( &dbv );
+ }
+}
+
+void CJabberProto::UpdateSubscriptionInfo(HANDLE hContact, JABBER_LIST_ITEM *item)
+{
+ switch (item->subscription)
+ {
+ case SUB_TO:
+ JSetStringT(hContact, "SubscriptionText", TranslateT("To"));
+ JSetString(hContact, "Subscription", "to");
+ JSetByte(hContact, "Auth", 0);
+ JSetByte(hContact, "Grant", 1);
+ break;
+ case SUB_FROM:
+ JSetStringT(hContact, "SubscriptionText", TranslateT("From"));
+ JSetString(hContact, "Subscription", "from");
+ JSetByte(hContact, "Auth", 1);
+ JSetByte(hContact, "Grant", 0);
+ break;
+ case SUB_BOTH:
+ JSetStringT(hContact, "SubscriptionText", TranslateT("Both"));
+ JSetString(hContact, "Subscription", "both");
+ JSetByte(hContact, "Auth", 0);
+ JSetByte(hContact, "Grant", 0);
+ break;
+ case SUB_NONE:
+ JSetStringT(hContact, "SubscriptionText", TranslateT("None"));
+ JSetString(hContact, "Subscription", "none");
+ JSetByte(hContact, "Auth", 1);
+ JSetByte(hContact, "Grant", 1);
+ break;
+ }
+}
+
+void CJabberProto::SetContactOfflineStatus( HANDLE hContact )
+{
+ if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != ID_STATUS_OFFLINE )
+ JSetWord( hContact, "Status", ID_STATUS_OFFLINE );
+
+ JDeleteSetting( hContact, DBSETTING_XSTATUSID );
+ JDeleteSetting( hContact, DBSETTING_XSTATUSNAME );
+ JDeleteSetting( hContact, DBSETTING_XSTATUSMSG );
+ JDeleteSetting( hContact, DBSETTING_DISPLAY_UID );
+
+ ResetAdvStatus( hContact, ADVSTATUS_MOOD );
+ ResetAdvStatus( hContact, ADVSTATUS_TUNE );
+
+ //JabberUpdateContactExtraIcon(hContact);
+}
+
+void CJabberProto::InitPopups(void)
+{
+ TCHAR desc[256];
+ char name[256];
+
+ POPUPCLASS ppc = {0};
+ ppc.cbSize = sizeof(ppc);
+ ppc.flags = PCF_TCHAR;
+
+ ppc.ptszDescription = desc;
+ ppc.pszName = name;
+ ppc.hIcon = LoadIconEx("main");
+ ppc.colorBack = RGB(191, 0, 0); //Red
+ ppc.colorText = RGB(255, 245, 225); //Yellow
+ ppc.iSeconds = 60;
+ mir_sntprintf(desc, SIZEOF(desc), _T("%s %s"), m_tszUserName, TranslateT("Errors"));
+ mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Error");
+
+ CallService(MS_POPUP_REGISTERCLASS, 0, (WPARAM)&ppc);
+}
+
+void CJabberProto::MsgPopup(HANDLE hContact, const TCHAR *szMsg, const TCHAR *szTitle)
+{
+ if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) {
+ char name[256];
+
+ POPUPDATACLASS ppd = { sizeof(ppd) };
+ ppd.ptszTitle = szTitle;
+ ppd.ptszText = szMsg;
+ ppd.pszClassName = name;
+ ppd.hContact = hContact;
+ mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Error");
+
+ CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&ppd);
+ } else {
+ DWORD mtype = MB_OK | MB_SETFOREGROUND | MB_ICONSTOP;
+ MessageBox(NULL, szMsg, szTitle, mtype);
+ }
+}
diff --git a/protocols/JabberG/src/jabber_notes.cpp b/protocols/JabberG/src/jabber_notes.cpp new file mode 100644 index 0000000000..d684541f0a --- /dev/null +++ b/protocols/JabberG/src/jabber_notes.cpp @@ -0,0 +1,865 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007-09 Maxim Mluhov
+Copyright ( C ) 2007-09 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_list.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+#include "jabber_privacy.h"
+#include "jabber_notes.h"
+
+static TCHAR *StrTrimCopy(TCHAR *str)
+{
+ if (!str) return 0;
+ while (*str && _istspace(*str)) ++str;
+ if (!*str) return mir_tstrdup(str);
+
+ TCHAR *res = mir_tstrdup(str);
+ for (TCHAR *p = res + lstrlen(res) - 1; p >= res; --p)
+ {
+ if (_istspace(*p))
+ *p = 0;
+ else
+ break;
+ }
+
+ return res;
+}
+
+CNoteItem::CNoteItem()
+{
+ m_szTitle =
+ m_szFrom =
+ m_szText =
+ m_szTags =
+ m_szTagsStr = NULL;
+}
+
+CNoteItem::CNoteItem(HXML hXml, TCHAR *szFrom)
+{
+ m_szTitle =
+ m_szFrom =
+ m_szText =
+ m_szTags =
+ m_szTagsStr = NULL;
+
+ SetData(
+ XPathT(hXml, "title"),
+ szFrom ? szFrom : XPathT(hXml, "@from"),
+ XPathT(hXml, "text"),
+ XPathT(hXml, "@tags"));
+}
+
+CNoteItem::~CNoteItem()
+{
+ mir_free(m_szTitle);
+ mir_free(m_szFrom);
+ mir_free(m_szText);
+ mir_free(m_szTags);
+ mir_free(m_szTagsStr);
+}
+
+void CNoteItem::SetData(TCHAR *title, TCHAR *from, TCHAR *text, TCHAR *tags)
+{
+ mir_free(m_szTitle);
+ mir_free(m_szFrom);
+ mir_free(m_szText);
+ mir_free(m_szTags);
+ mir_free(m_szTagsStr);
+
+ m_szTitle = StrTrimCopy(title);
+ m_szText = JabberStrFixLines(text);
+ m_szFrom = StrTrimCopy(from);
+
+ const TCHAR *szTags = tags;
+ TCHAR *p = m_szTags = (TCHAR *)mir_alloc((lstrlen(szTags) + 2 /*for double zero*/) * sizeof(TCHAR));
+ TCHAR *q = m_szTagsStr = (TCHAR *)mir_alloc((lstrlen(szTags) + 1) * sizeof(TCHAR));
+ for ( ; szTags && *szTags; ++szTags)
+ {
+ if (_istspace(*szTags))
+ continue;
+
+ if (*szTags == _T(','))
+ {
+ *q++ = _T(',');
+ *p++ = 0;
+ continue;
+ }
+
+ *q++ = *p++ = *szTags;
+ }
+
+ q[0] = p[0] = p[1] = 0;
+}
+
+bool CNoteItem::HasTag(const TCHAR *szTag)
+{
+ if (!szTag || !*szTag)
+ return true;
+
+ for (TCHAR *p = m_szTags; p && *p; p = p + lstrlen(p) + 1)
+ if (!lstrcmp(p, szTag))
+ return true;
+
+ return false;
+}
+
+int CNoteItem::cmp(const CNoteItem *p1, const CNoteItem *p2)
+{
+ int ret = 0;
+ if (ret = lstrcmp(p1->m_szTitle, p2->m_szTitle)) return ret;
+ if (ret = lstrcmp(p1->m_szText, p2->m_szText)) return ret;
+ if (ret = lstrcmp(p1->m_szTagsStr, p2->m_szTagsStr)) return ret;
+ if (p1 < p2) return -1;
+ if (p1 > p2) return 1;
+ return 0;
+}
+
+void CNoteList::AddNote(HXML hXml, TCHAR *szFrom)
+{
+ m_bIsModified = true;
+ insert(new CNoteItem(hXml, szFrom));
+}
+
+void CNoteList::LoadXml(HXML hXml)
+{
+ destroy();
+ m_bIsModified = false;
+
+ int count = xmlGetChildCount(hXml);
+ for (int i = 0; i < count; ++i)
+ {
+ CNoteItem *pNote = new CNoteItem(xi.getChild(hXml, i));
+ if (pNote->IsNotEmpty())
+ insert(pNote);
+ else
+ delete pNote;
+ }
+}
+
+void CNoteList::SaveXml(HXML hXmlParent)
+{
+ m_bIsModified = false;
+ CNoteList &me = *this;
+
+ for (int i = 0; i < getCount(); ++i)
+ {
+ HXML hXmlItem = hXmlParent << XCHILD(_T("note"));
+ hXmlItem << XATTR(_T("from"), me[i].GetFrom()) << XATTR(_T("tags"), me[i].GetTagsStr());
+ hXmlItem << XCHILD(_T("title"), me[i].GetTitle());
+ hXmlItem << XCHILD(_T("text"), me[i].GetText());
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Single note editor
+
+class CJabberDlgNoteItem : public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+ typedef void (CJabberProto::*TFnProcessNote)(CNoteItem *, bool ok);
+
+public:
+ CJabberDlgNoteItem(CJabberDlgBase *parent, CNoteItem *pNote);
+ CJabberDlgNoteItem(CJabberProto *proto, CNoteItem *pNote, TFnProcessNote fnProcess);
+
+protected:
+ void OnInitDialog();
+ int Resizer(UTILRESIZECONTROL *urc);
+
+private:
+ CNoteItem *m_pNote;
+ TFnProcessNote m_fnProcess;
+
+ CCtrlEdit m_txtTitle;
+ CCtrlEdit m_txtText;
+ CCtrlEdit m_txtTags;
+ CCtrlButton m_btnOk;
+
+ void btnOk_OnClick(CCtrlButton *)
+ {
+ TCHAR *szTitle = m_txtTitle.GetText();
+ TCHAR *szText = m_txtText.GetText();
+ TCHAR *szTags = m_txtTags.GetText();
+ TCHAR *szFrom = mir_tstrdup(m_pNote->GetFrom());
+ m_pNote->SetData(szTitle, szFrom, szText, szTags);
+ mir_free(szTitle);
+ mir_free(szText);
+ mir_free(szTags);
+ mir_free(szFrom);
+
+ m_autoClose = false;
+ if (m_fnProcess) (m_proto->*m_fnProcess)(m_pNote, true);
+ EndDialog(m_hwnd, TRUE);
+ }
+
+ void OnClose()
+ {
+ if (m_fnProcess) (m_proto->*m_fnProcess)(m_pNote, false);
+ CSuper::OnClose();
+ }
+};
+
+CJabberDlgNoteItem::CJabberDlgNoteItem(CJabberDlgBase *parent, CNoteItem *pNote):
+ CSuper(parent->GetProto(), IDD_NOTE_EDIT, parent->GetHwnd()),
+ m_pNote(pNote),
+ m_fnProcess(NULL),
+ m_txtTitle(this, IDC_TXT_TITLE),
+ m_txtText(this, IDC_TXT_TEXT),
+ m_txtTags(this, IDC_TXT_TAGS),
+ m_btnOk(this, IDOK)
+{
+ m_btnOk.OnClick = Callback(this, &CJabberDlgNoteItem::btnOk_OnClick);
+}
+
+CJabberDlgNoteItem::CJabberDlgNoteItem(CJabberProto *proto, CNoteItem *pNote, TFnProcessNote fnProcess):
+ CSuper(proto, IDD_NOTE_EDIT, NULL),
+ m_pNote(pNote),
+ m_fnProcess(fnProcess),
+ m_txtTitle(this, IDC_TXT_TITLE),
+ m_txtText(this, IDC_TXT_TEXT),
+ m_txtTags(this, IDC_TXT_TAGS),
+ m_btnOk(this, IDOK)
+{
+ m_btnOk.OnClick = Callback(this, &CJabberDlgNoteItem::btnOk_OnClick);
+}
+
+void CJabberDlgNoteItem::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+ WindowSetIcon( m_hwnd, m_proto, "notes" );
+
+ if (m_fnProcess)
+ {
+ TCHAR buf[256];
+ if (m_fnProcess == &CJabberProto::ProcessIncomingNote)
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("Incoming note from %s"), m_pNote->GetFrom());
+ else
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("Send note to %s"), m_pNote->GetFrom());
+
+ SetWindowText(m_hwnd, buf);
+ }
+
+ m_txtTitle.SetText(m_pNote->GetTitle());
+ m_txtText.SetText(m_pNote->GetText());
+ m_txtTags.SetText(m_pNote->GetTagsStr());
+}
+
+int CJabberDlgNoteItem::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId)
+ {
+ case IDC_TXT_TITLE:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_TOP;
+ case IDC_TXT_TEXT:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ case IDC_ST_TAGS:
+ case IDC_TXT_TAGS:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM;
+
+ case IDOK:
+ case IDCANCEL:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ }
+
+ return CSuper::Resizer(urc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Notebook window
+
+class CCtrlNotebookList: public CCtrlListBox
+{
+ typedef CCtrlListBox CSuper;
+ bool m_adding;
+ HFONT m_hfntNormal, m_hfntSmall, m_hfntBold;
+
+public:
+ CCtrlNotebookList( CDlgBase* dlg, int ctrlId ): CCtrlListBox( dlg, ctrlId ) {}
+ void SetFonts(HFONT hfntNormal, HFONT hfntSmall, HFONT hfntBold)
+ {
+ m_hfntNormal = hfntNormal;
+ m_hfntSmall = hfntSmall;
+ m_hfntBold = hfntBold;
+ }
+
+ int AddString(TCHAR *text, LPARAM data=0)
+ {
+ m_adding = true;
+ int idx = CCtrlListBox::AddString(text, data);
+ m_adding = false;
+ if (idx == LB_ERR) return idx;
+
+ MEASUREITEMSTRUCT mis = {0};
+ mis.CtlType = ODT_LISTBOX;
+ mis.CtlID = m_idCtrl;
+ mis.itemID = idx;
+ mis.itemData = data;
+ OnMeasureItem(&mis);
+ if (mis.itemHeight) SendMessage(m_hwnd, LB_SETITEMHEIGHT, idx, mis.itemHeight);
+ return idx;
+ }
+
+ void OnInit()
+ {
+ CSuper::OnInit();
+ Subclass();
+ }
+
+ LRESULT CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ if (msg == WM_SIZE)
+ {
+ SendMessage(m_hwnd, WM_SETREDRAW, FALSE, 0);
+ int cnt = GetCount();
+ for (int idx = 0; idx < cnt; ++idx)
+ {
+ MEASUREITEMSTRUCT mis = {0};
+ mis.CtlType = ODT_LISTBOX;
+ mis.CtlID = m_idCtrl;
+ mis.itemID = idx;
+ mis.itemData = GetItemData(idx);
+ OnMeasureItem(&mis);
+ if (mis.itemHeight) SendMessage(m_hwnd, LB_SETITEMHEIGHT, idx, mis.itemHeight);
+ }
+ SendMessage(m_hwnd, WM_SETREDRAW, TRUE, 0);
+ RedrawWindow(m_hwnd, NULL, NULL, RDW_INVALIDATE);
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+ }
+
+ BOOL OnDrawItem(DRAWITEMSTRUCT *lps)
+ {
+ if (m_adding) return FALSE;
+ if (lps->itemID == -1) return TRUE;
+ if (!lps->itemData) return TRUE;
+
+ HDC hdc = lps->hDC;
+ CNoteItem *pNote = (CNoteItem *)lps->itemData;
+
+ SetBkMode(hdc, TRANSPARENT);
+ if (lps->itemState & ODS_SELECTED)
+ {
+ FillRect(hdc, &lps->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ } else
+ {
+ FillRect(hdc, &lps->rcItem, GetSysColorBrush(COLOR_WINDOW));
+ SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
+ }
+
+ if (lps->itemID)
+ {
+ RECT rcTmp = lps->rcItem; rcTmp.bottom = rcTmp.top+1;
+ FillRect(hdc, &rcTmp, GetSysColorBrush(COLOR_BTNSHADOW));
+ }
+
+ RECT rc = lps->rcItem;
+ rc.left += 5;
+ rc.right -= 5;
+ rc.top += 2;
+
+ SelectObject(hdc, m_hfntBold);
+ rc.top += DrawText(hdc, pNote->GetTitle(), -1, &rc, DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS);
+ SelectObject(hdc, m_hfntNormal);
+ if (pNote->GetFrom())
+ {
+ TCHAR buf[256];
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("From: %s"), pNote->GetFrom());
+ rc.top += DrawText(hdc, buf, -1, &rc, DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS);
+ }
+ rc.top += DrawText(hdc, pNote->GetText(), -1, &rc, DT_NOPREFIX|DT_WORDBREAK|DT_EXPANDTABS|DT_END_ELLIPSIS);
+ SelectObject(hdc, m_hfntSmall);
+ rc.top += DrawText(hdc, pNote->GetTagsStr(), -1, &rc, DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS);
+ rc.top += 5;
+
+ int h = min(255, max(0, rc.bottom - rc.top));
+ if (SendMessage(m_hwnd, LB_GETITEMHEIGHT, lps->itemID, 0) != h)
+ SendMessage(m_hwnd, LB_SETITEMHEIGHT, lps->itemID, h);
+
+ return TRUE;
+ }
+
+ BOOL OnMeasureItem(MEASUREITEMSTRUCT *lps)
+ {
+ if (m_adding) return FALSE;
+ if (lps->itemID == -1) return TRUE;
+ if (!lps->itemData) return TRUE;
+
+ HDC hdc = GetDC(m_hwnd);
+ CNoteItem *pNote = (CNoteItem *)lps->itemData;
+
+ RECT rcTmp, rc;
+ GetClientRect(m_hwnd, &rc);
+ int maxHeight = rc.bottom - 10;
+ rc.bottom = 0;
+
+ SelectObject(hdc, m_hfntBold);
+ rcTmp = rc;
+ DrawText(hdc, pNote->GetTitle(), -1, &rcTmp, DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS|DT_CALCRECT);
+ lps->itemHeight += rcTmp.bottom;
+ SelectObject(hdc, m_hfntNormal);
+ if (pNote->GetFrom())
+ {
+ TCHAR buf[256];
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("From: %s"), pNote->GetFrom());
+ rcTmp = rc;
+ DrawText(hdc, buf, -1, &rcTmp, DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS|DT_CALCRECT);
+ lps->itemHeight += rcTmp.bottom;
+ }
+ rcTmp = rc;
+ DrawText(hdc, pNote->GetText(), -1, &rcTmp, DT_NOPREFIX|DT_WORDBREAK|DT_EXPANDTABS|DT_END_ELLIPSIS|DT_CALCRECT);
+ lps->itemHeight += rcTmp.bottom;
+ SelectObject(hdc, m_hfntSmall);
+ rcTmp = rc;
+ DrawText(hdc, pNote->GetTagsStr(), -1, &rcTmp, DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS|DT_CALCRECT);
+ lps->itemHeight += rcTmp.bottom;
+ lps->itemHeight += 5;
+
+ ReleaseDC(m_hwnd, hdc);
+
+ lps->itemWidth = rc.right;
+ lps->itemHeight = min(255, lps->itemHeight); // listbox can't make items taller then 255px
+ return TRUE;
+ }
+};
+
+class CJabberDlgNotes : public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+public:
+ CJabberDlgNotes(CJabberProto *proto);
+ void UpdateData();
+
+protected:
+ void OnInitDialog();
+ void OnClose();
+ void OnDestroy();
+ int Resizer(UTILRESIZECONTROL *urc);
+
+ void OnProtoCheckOnline(WPARAM wParam, LPARAM lParam);
+ void OnProtoRefresh(WPARAM wParam, LPARAM lParam);
+
+private:
+ CCtrlMButton m_btnAdd;
+ CCtrlMButton m_btnEdit;
+ CCtrlMButton m_btnRemove;
+ CCtrlNotebookList m_lstNotes;
+ CCtrlTreeView m_tvFilter;
+ CCtrlButton m_btnSave;
+
+ HFONT m_hfntNormal, m_hfntSmall, m_hfntBold;
+
+ void EnableControls()
+ {
+ m_btnSave.Enable(m_proto->m_bJabberOnline && m_proto->m_notes.IsModified());
+ m_btnEdit.Enable(m_lstNotes.GetCurSel() != LB_ERR);
+ m_btnRemove.Enable(m_lstNotes.GetCurSel() != LB_ERR);
+ }
+
+ void InsertTag(HTREEITEM htiRoot, const TCHAR *tag, bool bSelect)
+ {
+ TVINSERTSTRUCT tvi = {0};
+ tvi.hParent = htiRoot;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.itemex.mask = TVIF_TEXT|TVIF_PARAM;
+ tvi.itemex.pszText = (TCHAR *)tag;
+ tvi.itemex.lParam = (LPARAM)mir_tstrdup(tag);
+ HTREEITEM hti = m_tvFilter.InsertItem(&tvi);
+ if (bSelect) m_tvFilter.SelectItem(hti);
+ }
+
+ void PopulateTags(HTREEITEM htiRoot, TCHAR *szActiveTag)
+ {
+ LIST<TCHAR> tagSet(5, _tcscmp);
+ for (int i = 0; i < m_proto->m_notes.getCount(); ++i)
+ {
+ TCHAR *tags = m_proto->m_notes[i].GetTags();
+ for (TCHAR *tag = tags; tag && *tag; tag = tag + lstrlen(tag) + 1)
+ if (!tagSet.find(tag))
+ tagSet.insert(tag);
+ }
+
+ bool selected = false;
+ for (int j = 0; j < tagSet.getCount(); ++j)
+ {
+ bool select = !lstrcmp(szActiveTag, tagSet[j]);
+ selected |= select;
+ InsertTag(htiRoot, tagSet[j], select);
+ }
+
+ if (!selected)
+ m_tvFilter.SelectItem(htiRoot);
+
+ tagSet.destroy();
+ }
+
+ void RebuildTree()
+ {
+ TVITEMEX tvi = {0};
+ tvi.mask = TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem = m_tvFilter.GetSelection();
+ m_tvFilter.GetItem(&tvi);
+ TCHAR *szActiveTag = mir_tstrdup((TCHAR *)tvi.lParam);
+
+ m_tvFilter.DeleteAllItems();
+
+ TVINSERTSTRUCT tvis = {0};
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.itemex.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE;
+ tvis.itemex.stateMask =
+ tvis.itemex.state = TVIS_BOLD|TVIS_EXPANDED;
+ tvis.itemex.pszText = TranslateT("All tags");
+ tvis.itemex.lParam = NULL;
+
+
+ PopulateTags(m_tvFilter.InsertItem(&tvis), szActiveTag);
+ mir_free(szActiveTag);
+ }
+
+ void InsertItem(CNoteItem &item)
+ {
+ m_lstNotes.AddString((TCHAR *)item.GetTitle(), (LPARAM)&item);
+ EnableControls();
+ }
+
+ void ListItems(const TCHAR *tag)
+ {
+ m_lstNotes.ResetContent();
+ for (int i = 0; i < m_proto->m_notes.getCount(); ++i)
+ if (m_proto->m_notes[i].HasTag(tag))
+ InsertItem(m_proto->m_notes[i]);
+ EnableControls();
+ }
+
+ void btnAdd_OnClick(CCtrlFilterListView *)
+ {
+ CNoteItem *pNote = new CNoteItem();
+ CJabberDlgNoteItem dlg(this, pNote);
+ dlg.DoModal();
+
+ if (pNote->IsNotEmpty())
+ {
+ m_proto->m_notes.insert(pNote);
+ m_proto->m_notes.Modify();
+ UpdateData();
+ } else
+ {
+ delete pNote;
+ return;
+ }
+ EnableControls();
+ }
+
+ void btnEdit_OnClick(CCtrlFilterListView *)
+ {
+ int idx = m_lstNotes.GetCurSel();
+ if (idx != LB_ERR)
+ {
+ if (CNoteItem *pItem = (CNoteItem *)m_lstNotes.GetItemData(idx))
+ {
+ CJabberDlgNoteItem dlg(this, pItem);
+ if (dlg.DoModal())
+ {
+ m_proto->m_notes.Modify();
+ RebuildTree();
+ }
+ }
+ }
+ EnableControls();
+ }
+
+ void btnRemove_OnClick(CCtrlFilterListView *)
+ {
+ int idx = m_lstNotes.GetCurSel();
+ if (idx != LB_ERR)
+ {
+ if (CNoteItem *pItem = (CNoteItem *)m_lstNotes.GetItemData(idx))
+ {
+ m_lstNotes.DeleteString(idx);
+ m_proto->m_notes.remove(pItem);
+ }
+ RebuildTree();
+ }
+ EnableControls();
+ }
+
+ void lstNotes_OnSelChange(CCtrlListBox *)
+ {
+ EnableControls();
+ }
+
+ void tvFilter_OnDeleteItem(CCtrlTreeView::TEventInfo *e)
+ {
+ TCHAR *szText = (TCHAR *)e->nmtv->itemOld.lParam;
+ mir_free(szText);
+ EnableControls();
+ }
+
+ void tvFilter_OnSelChanged(CCtrlTreeView::TEventInfo *e)
+ {
+ TCHAR *szText = (TCHAR *)e->nmtv->itemNew.lParam;
+ ListItems(szText);
+ EnableControls();
+ }
+
+ void btnSave_OnClick(CCtrlButton *)
+ {
+ XmlNodeIq iq(_T("set"));
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVATE_STORAGE));
+ HXML storage = query << XCHILDNS(_T("storage"), _T(JABBER_FEAT_MIRANDA_NOTES));
+ m_proto->m_notes.SaveXml(storage);
+ m_proto->m_ThreadInfo->send(iq);
+ EnableControls();
+ }
+};
+
+CJabberDlgNotes::CJabberDlgNotes(CJabberProto *proto) :
+ CSuper(proto, IDD_NOTEBOOK, NULL),
+ m_btnAdd(this, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add")),
+ m_btnEdit(this, IDC_EDIT, SKINICON_OTHER_RENAME, LPGEN("Edit")),
+ m_btnRemove(this, IDC_REMOVE, SKINICON_OTHER_DELETE, LPGEN("Remove")),
+ m_lstNotes(this, IDC_LST_NOTES),
+ m_tvFilter(this, IDC_TV_FILTER),
+ m_btnSave(this, IDC_APPLY)
+{
+ m_btnAdd.OnClick = Callback(this, &CJabberDlgNotes::btnAdd_OnClick);
+ m_btnEdit.OnClick = Callback(this, &CJabberDlgNotes::btnEdit_OnClick);
+ m_btnRemove.OnClick = Callback(this, &CJabberDlgNotes::btnRemove_OnClick);
+ m_lstNotes.OnDblClick = Callback(this, &CJabberDlgNotes::btnEdit_OnClick);
+ m_lstNotes.OnSelChange = Callback(this, &CJabberDlgNotes::lstNotes_OnSelChange);
+ m_btnSave.OnClick = Callback(this, &CJabberDlgNotes::btnSave_OnClick);
+
+ m_tvFilter.OnSelChanged = Callback(this, &CJabberDlgNotes::tvFilter_OnSelChanged);
+ m_tvFilter.OnDeleteItem = Callback(this, &CJabberDlgNotes::tvFilter_OnDeleteItem);
+}
+
+void CJabberDlgNotes::UpdateData()
+{
+ RebuildTree();
+ //ListItems(NULL);
+ EnableControls();
+}
+
+void CJabberDlgNotes::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+ WindowSetIcon( m_hwnd, m_proto, "notes" );
+
+ LOGFONT lf, lfTmp;
+ m_hfntNormal = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ GetObject(m_hfntNormal, sizeof(lf), &lf);
+ lfTmp = lf; lfTmp.lfWeight = FW_BOLD;
+ m_hfntBold = CreateFontIndirect(&lfTmp);
+ lfTmp = lf; lfTmp.lfHeight *= 0.8;
+ m_hfntSmall = CreateFontIndirect(&lfTmp);
+ m_lstNotes.SetFonts(m_hfntNormal, m_hfntSmall, m_hfntBold);
+
+ Utils_RestoreWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "notesWnd_");
+}
+
+void CJabberDlgNotes::OnClose()
+{
+ if (m_proto->m_notes.IsModified())
+ {
+ if (IDYES != MessageBox(m_hwnd, TranslateT("Notes are not saved, close this window without uploading data to server?"), TranslateT("Are you sure?"), MB_ICONWARNING|MB_YESNO|MB_DEFBUTTON2))
+ {
+ m_lresult = TRUE;
+ return;
+ }
+ }
+
+ Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "notesWnd_");
+ DeleteObject(m_hfntSmall);
+ DeleteObject(m_hfntBold);
+ CSuper::OnClose();
+}
+
+void CJabberDlgNotes::OnDestroy()
+{
+ m_tvFilter.DeleteAllItems();
+ m_proto->m_pDlgNotes = NULL;
+ CSuper::OnDestroy();
+}
+
+void CJabberDlgNotes::OnProtoCheckOnline(WPARAM, LPARAM)
+{
+ EnableControls();
+}
+
+void CJabberDlgNotes::OnProtoRefresh(WPARAM, LPARAM)
+{
+}
+
+int CJabberDlgNotes::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch ( urc->wId ) {
+ case IDC_TV_FILTER:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_HEIGHT;
+ case IDC_LST_NOTES:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ case IDC_APPLY:
+ case IDCANCEL:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ case IDC_ADD:
+ case IDC_EDIT:
+ case IDC_REMOVE:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
+ }
+ return CSuper::Resizer(urc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Launches the incoming note window
+
+void CJabberProto::ProcessIncomingNote(CNoteItem *pNote, bool ok)
+{
+ if (ok && pNote->IsNotEmpty())
+ {
+ m_notes.insert(pNote);
+
+ XmlNodeIq iq(_T("set"));
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVATE_STORAGE));
+ HXML storage = query << XCHILDNS(_T("storage"), _T(JABBER_FEAT_MIRANDA_NOTES));
+ m_notes.SaveXml(storage);
+ m_ThreadInfo->send(iq);
+ } else
+ {
+ delete pNote;
+ }
+}
+
+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;
+}
+
+bool CJabberProto::OnIncomingNote(const TCHAR *szFrom, HXML hXml)
+{
+ if (!m_options.AcceptNotes)
+ return false;
+
+ if (!szFrom || !hXml) return true;
+ CNoteItem *pItem = new CNoteItem(hXml, (TCHAR *)szFrom);
+ if (!pItem->IsNotEmpty())
+ {
+ delete pItem;
+ return true;
+ }
+
+ if (m_options.AutosaveNotes && HContactFromJID(szFrom))
+ {
+ ProcessIncomingNote(pItem, true);
+ return false;
+ }
+
+ CLISTEVENT cle = {0};
+ char szService[256];
+ mir_snprintf( szService, sizeof(szService),"%s%s", m_szModuleName, JS_INCOMING_NOTE_EVENT );
+ cle.cbSize = sizeof(CLISTEVENT);
+ cle.hIcon = (HICON)LoadIconEx("notes");
+ cle.flags = CLEF_PROTOCOLGLOBAL|CLEF_TCHAR;
+ cle.hDbEvent = (HANDLE)("test");
+ cle.lParam = (LPARAM)pItem;
+ cle.pszService = szService;
+ cle.ptszTooltip = TranslateT("Incoming note");
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle);
+
+ return true;
+}
+
+INT_PTR __cdecl CJabberProto::OnIncomingNoteEvent(WPARAM, LPARAM lParam)
+{
+ CLISTEVENT *pCle = (CLISTEVENT *)lParam;
+ CNoteItem *pNote = (CNoteItem *)pCle->lParam;
+ if ( !pNote )
+ return 0;
+
+ CJabberDlgBase *pDlg = new CJabberDlgNoteItem(this, pNote, &CJabberProto::ProcessIncomingNote);
+ pDlg->Show();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Menu handling
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleNotes( WPARAM, LPARAM)
+{
+ UI_SAFE_OPEN_EX(CJabberDlgNotes, m_pDlgNotes, pDlg);
+ pDlg->UpdateData();
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuSendNote(WPARAM wParam, LPARAM)
+{
+ if (!wParam) return 0;
+
+ TCHAR szClientJid[ JABBER_MAX_JID_LEN ];
+ GetClientJID( JGetStringT( (HANDLE)wParam, "jid"), szClientJid, SIZEOF( szClientJid ));
+
+ CNoteItem *pItem = new CNoteItem( NULL, szClientJid );
+ CJabberDlgBase *pDlg = new CJabberDlgNoteItem(this, pItem, &CJabberProto::ProcessOutgoingNote);
+ pDlg->Show();
+
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber_notes.h b/protocols/JabberG/src/jabber_notes.h new file mode 100644 index 0000000000..de096943c2 --- /dev/null +++ b/protocols/JabberG/src/jabber_notes.h @@ -0,0 +1,82 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007-09 Maxim Mluhov
+Copyright ( C ) 2007-09 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jabber_notes_h__
+#define __jabber_notes_h__
+
+class CNoteItem
+{
+private:
+ TCHAR *m_szTitle;
+ TCHAR *m_szFrom;
+ TCHAR *m_szText;
+ TCHAR *m_szTags;
+ TCHAR *m_szTagsStr;
+
+public:
+ CNoteItem();
+ CNoteItem(HXML hXml, TCHAR *szFrom = 0);
+ ~CNoteItem();
+
+ void SetData(TCHAR *title, TCHAR *from, TCHAR *text, TCHAR *tags);
+
+ TCHAR *GetTitle() const { return m_szTitle; }
+ TCHAR *GetFrom() const { return m_szFrom; }
+ TCHAR *GetText() const { return m_szText; }
+ TCHAR *GetTags() const { return m_szTags; }
+ TCHAR *GetTagsStr() const { return m_szTagsStr; }
+
+ bool HasTag(const TCHAR *szTag);
+
+ bool IsNotEmpty()
+ {
+ return (m_szTitle && *m_szTitle) || (m_szText && *m_szText);
+ }
+
+ static int cmp(const CNoteItem *p1, const CNoteItem *p2);
+};
+
+class CNoteList: public OBJLIST<CNoteItem>
+{
+private:
+ bool m_bIsModified;
+
+public:
+ CNoteList(): OBJLIST<CNoteItem>(10, CNoteItem::cmp) {}
+
+ void remove(CNoteItem *p)
+ {
+ m_bIsModified = true;
+ OBJLIST<CNoteItem>::remove(p);
+ }
+
+ void AddNote(HXML hXml, TCHAR *szFrom = 0);
+ void LoadXml(HXML hXml);
+ void SaveXml(HXML hXmlParent);
+
+ bool IsModified() { return m_bIsModified; }
+ void Modify() { m_bIsModified = true; }
+};
+
+#endif // __jabber_notes_h__
diff --git a/protocols/JabberG/src/jabber_opt.cpp b/protocols/JabberG/src/jabber_opt.cpp new file mode 100644 index 0000000000..1f1270a8a1 --- /dev/null +++ b/protocols/JabberG/src/jabber_opt.cpp @@ -0,0 +1,2331 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_list.h"
+
+#include "jabber_caps.h"
+#include "jabber_opttree.h"
+#include "m_modernopt.h"
+
+static BOOL (WINAPI *pfnEnableThemeDialogTexture)(HANDLE, DWORD) = 0;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberRegisterDlgProc - the dialog proc for registering new account
+
+#define STR_FORMAT _T("%s %s@%S:%d?")
+
+struct { TCHAR *szCode; TCHAR *szDescription; } g_LanguageCodes[] = {
+ { _T("aa"), _T("Afar") },
+ { _T("ab"), _T("Abkhazian") },
+ { _T("af"), _T("Afrikaans") },
+ { _T("ak"), _T("Akan") },
+ { _T("sq"), _T("Albanian") },
+ { _T("am"), _T("Amharic") },
+ { _T("ar"), _T("Arabic") },
+ { _T("an"), _T("Aragonese") },
+ { _T("hy"), _T("Armenian") },
+ { _T("as"), _T("Assamese") },
+ { _T("av"), _T("Avaric") },
+ { _T("ae"), _T("Avestan") },
+ { _T("ay"), _T("Aymara") },
+ { _T("az"), _T("Azerbaijani") },
+ { _T("ba"), _T("Bashkir") },
+ { _T("bm"), _T("Bambara") },
+ { _T("eu"), _T("Basque") },
+ { _T("be"), _T("Belarusian") },
+ { _T("bn"), _T("Bengali") },
+ { _T("bh"), _T("Bihari") },
+ { _T("bi"), _T("Bislama") },
+ { _T("bs"), _T("Bosnian") },
+ { _T("br"), _T("Breton") },
+ { _T("bg"), _T("Bulgarian") },
+ { _T("my"), _T("Burmese") },
+ { _T("ca"), _T("Catalan; Valencian") },
+ { _T("ch"), _T("Chamorro") },
+ { _T("ce"), _T("Chechen") },
+ { _T("zh"), _T("Chinese") },
+ { _T("cu"), _T("Church Slavic; Old Slavonic") },
+ { _T("cv"), _T("Chuvash") },
+ { _T("kw"), _T("Cornish") },
+ { _T("co"), _T("Corsican") },
+ { _T("cr"), _T("Cree") },
+ { _T("cs"), _T("Czech") },
+ { _T("da"), _T("Danish") },
+ { _T("dv"), _T("Divehi; Dhivehi; Maldivian") },
+ { _T("nl"), _T("Dutch; Flemish") },
+ { _T("dz"), _T("Dzongkha") },
+ { _T("en"), _T("English") },
+ { _T("eo"), _T("Esperanto") },
+ { _T("et"), _T("Estonian") },
+ { _T("ee"), _T("Ewe") },
+ { _T("fo"), _T("Faroese") },
+ { _T("fj"), _T("Fijian") },
+ { _T("fi"), _T("Finnish") },
+ { _T("fr"), _T("French") },
+ { _T("fy"), _T("Western Frisian") },
+ { _T("ff"), _T("Fulah") },
+ { _T("ka"), _T("Georgian") },
+ { _T("de"), _T("German") },
+ { _T("gd"), _T("Gaelic; Scottish Gaelic") },
+ { _T("ga"), _T("Irish") },
+ { _T("gl"), _T("Galician") },
+ { _T("gv"), _T("Manx") },
+ { _T("el"), _T("Greek, Modern (1453-)") },
+ { _T("gn"), _T("Guarani") },
+ { _T("gu"), _T("Gujarati") },
+ { _T("ht"), _T("Haitian; Haitian Creole") },
+ { _T("ha"), _T("Hausa") },
+ { _T("he"), _T("Hebrew") },
+ { _T("hz"), _T("Herero") },
+ { _T("hi"), _T("Hindi") },
+ { _T("ho"), _T("Hiri Motu") },
+ { _T("hu"), _T("Hungarian") },
+ { _T("ig"), _T("Igbo") },
+ { _T("is"), _T("Icelandic") },
+ { _T("io"), _T("Ido") },
+ { _T("ii"), _T("Sichuan Yi") },
+ { _T("iu"), _T("Inuktitut") },
+ { _T("ie"), _T("Interlingue") },
+ { _T("ia"), _T("Interlingua (International Auxiliary Language Association)") },
+ { _T("id"), _T("Indonesian") },
+ { _T("ik"), _T("Inupiaq") },
+ { _T("it"), _T("Italian") },
+ { _T("jv"), _T("Javanese") },
+ { _T("ja"), _T("Japanese") },
+ { _T("kl"), _T("Kalaallisut; Greenlandic") },
+ { _T("kn"), _T("Kannada") },
+ { _T("ks"), _T("Kashmiri") },
+ { _T("kr"), _T("Kanuri") },
+ { _T("kk"), _T("Kazakh") },
+ { _T("km"), _T("Central Khmer") },
+ { _T("ki"), _T("Kikuyu; Gikuyu") },
+ { _T("rw"), _T("Kinyarwanda") },
+ { _T("ky"), _T("Kirghiz; Kyrgyz") },
+ { _T("kv"), _T("Komi") },
+ { _T("kg"), _T("Kongo") },
+ { _T("ko"), _T("Korean") },
+ { _T("kj"), _T("Kuanyama; Kwanyama") },
+ { _T("ku"), _T("Kurdish") },
+ { _T("lo"), _T("Lao") },
+ { _T("la"), _T("Latin") },
+ { _T("lv"), _T("Latvian") },
+ { _T("li"), _T("Limburgan; Limburger; Limburgish") },
+ { _T("ln"), _T("Lingala") },
+ { _T("lt"), _T("Lithuanian") },
+ { _T("lb"), _T("Luxembourgish; Letzeburgesch") },
+ { _T("lu"), _T("Luba-Katanga") },
+ { _T("lg"), _T("Ganda") },
+ { _T("mk"), _T("Macedonian") },
+ { _T("mh"), _T("Marshallese") },
+ { _T("ml"), _T("Malayalam") },
+ { _T("mi"), _T("Maori") },
+ { _T("mr"), _T("Marathi") },
+ { _T("ms"), _T("Malay") },
+ { _T("mg"), _T("Malagasy") },
+ { _T("mt"), _T("Maltese") },
+ { _T("mo"), _T("Moldavian") },
+ { _T("mn"), _T("Mongolian") },
+ { _T("na"), _T("Nauru") },
+ { _T("nv"), _T("Navajo; Navaho") },
+ { _T("nr"), _T("Ndebele, South; South Ndebele") },
+ { _T("nd"), _T("Ndebele, North; North Ndebele") },
+ { _T("ng"), _T("Ndonga") },
+ { _T("ne"), _T("Nepali") },
+ { _T("nn"), _T("Norwegian Nynorsk; Nynorsk, Norwegian") },
+ { _T("nb"), _T("Bokmaal, Norwegian; Norwegian Bokmaal") },
+ { _T("no"), _T("Norwegian") },
+ { _T("ny"), _T("Chichewa; Chewa; Nyanja") },
+ { _T("oc"), _T("Occitan (post 1500); Provencal") },
+ { _T("oj"), _T("Ojibwa") },
+ { _T("or"), _T("Oriya") },
+ { _T("om"), _T("Oromo") },
+ { _T("os"), _T("Ossetian; Ossetic") },
+ { _T("pa"), _T("Panjabi; Punjabi") },
+ { _T("fa"), _T("Persian") },
+ { _T("pi"), _T("Pali") },
+ { _T("pl"), _T("Polish") },
+ { _T("pt"), _T("Portuguese") },
+ { _T("ps"), _T("Pushto") },
+ { _T("qu"), _T("Quechua") },
+ { _T("rm"), _T("Romansh") },
+ { _T("ro"), _T("Romanian") },
+ { _T("rn"), _T("Rundi") },
+ { _T("ru"), _T("Russian") },
+ { _T("sg"), _T("Sango") },
+ { _T("sa"), _T("Sanskrit") },
+ { _T("sr"), _T("Serbian") },
+ { _T("hr"), _T("Croatian") },
+ { _T("si"), _T("Sinhala; Sinhalese") },
+ { _T("sk"), _T("Slovak") },
+ { _T("sl"), _T("Slovenian") },
+ { _T("se"), _T("Northern Sami") },
+ { _T("sm"), _T("Samoan") },
+ { _T("sn"), _T("Shona") },
+ { _T("sd"), _T("Sindhi") },
+ { _T("so"), _T("Somali") },
+ { _T("st"), _T("Sotho, Southern") },
+ { _T("es"), _T("Spanish; Castilian") },
+ { _T("sc"), _T("Sardinian") },
+ { _T("ss"), _T("Swati") },
+ { _T("su"), _T("Sundanese") },
+ { _T("sw"), _T("Swahili") },
+ { _T("sv"), _T("Swedish") },
+ { _T("ty"), _T("Tahitian") },
+ { _T("ta"), _T("Tamil") },
+ { _T("tt"), _T("Tatar") },
+ { _T("te"), _T("Telugu") },
+ { _T("tg"), _T("Tajik") },
+ { _T("tl"), _T("Tagalog") },
+ { _T("th"), _T("Thai") },
+ { _T("bo"), _T("Tibetan") },
+ { _T("ti"), _T("Tigrinya") },
+ { _T("to"), _T("Tonga (Tonga Islands)") },
+ { _T("tn"), _T("Tswana") },
+ { _T("ts"), _T("Tsonga") },
+ { _T("tk"), _T("Turkmen") },
+ { _T("tr"), _T("Turkish") },
+ { _T("tw"), _T("Twi") },
+ { _T("ug"), _T("Uighur; Uyghur") },
+ { _T("uk"), _T("Ukrainian") },
+ { _T("ur"), _T("Urdu") },
+ { _T("uz"), _T("Uzbek") },
+ { _T("ve"), _T("Venda") },
+ { _T("vi"), _T("Vietnamese") },
+ { _T("vo"), _T("Volapuk") },
+ { _T("cy"), _T("Welsh") },
+ { _T("wa"), _T("Walloon") },
+ { _T("wo"), _T("Wolof") },
+ { _T("xh"), _T("Xhosa") },
+ { _T("yi"), _T("Yiddish") },
+ { _T("yo"), _T("Yoruba") },
+ { _T("za"), _T("Zhuang; Chuang") },
+ { _T("zu"), _T("Zulu") },
+ { NULL, NULL }
+};
+
+class CJabberDlgRegister: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+public:
+ CJabberDlgRegister(CJabberProto *proto, HWND hwndParent, ThreadData *regInfo):
+ CJabberDlgBase(proto, IDD_OPT_REGISTER, hwndParent, false),
+ m_bProcessStarted(false),
+ m_regInfo(regInfo),
+ m_btnOk(this, IDOK)
+ {
+ m_autoClose = CLOSE_ON_CANCEL;
+ m_btnOk.OnClick = Callback(this, &CJabberDlgRegister::btnOk_OnClick);
+ }
+
+protected:
+ void OnInitDialog()
+ {
+ TCHAR text[256];
+ mir_sntprintf( text, SIZEOF(text), STR_FORMAT, TranslateT( "Register" ), m_regInfo->username, m_regInfo->server, m_regInfo->port );
+ SetDlgItemText( m_hwnd, IDC_REG_STATUS, text );
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ switch ( msg ) {
+ case WM_JABBER_REGDLG_UPDATE: // wParam=progress ( 0-100 ), lparam=status string
+ if (( TCHAR* )lParam == NULL )
+ SetDlgItemText( m_hwnd, IDC_REG_STATUS, TranslateT( "No message" ));
+ else
+ SetDlgItemText( m_hwnd, IDC_REG_STATUS, ( TCHAR* )lParam );
+
+ if ( wParam >= 0 )
+ SendMessage( GetDlgItem( m_hwnd, IDC_PROGRESS_REG ), PBM_SETPOS, wParam, 0 );
+ if ( wParam >= 100 )
+ m_btnOk.SetText(TranslateT("Close"));
+ else
+ SetFocus( GetDlgItem( m_hwnd, IDC_PROGRESS_REG ));
+
+ return TRUE;
+ }
+
+ return CSuper::DlgProc(msg, wParam, lParam);
+ }
+
+private:
+ bool m_bProcessStarted;
+ ThreadData *m_regInfo;
+
+ CCtrlButton m_btnOk;
+
+ void btnOk_OnClick(CCtrlButton *)
+ {
+ if ( m_bProcessStarted ) {
+ Close();
+ return;
+ }
+
+ ShowWindow(GetDlgItem(m_hwnd, IDC_PROGRESS_REG), SW_SHOW);
+
+ ThreadData *thread = new ThreadData( m_regInfo->proto, JABBER_SESSION_REGISTER );
+ _tcsncpy( thread->username, m_regInfo->username, SIZEOF( thread->username ));
+ _tcsncpy( thread->password, m_regInfo->password, SIZEOF( thread->password ));
+ strncpy( thread->server, m_regInfo->server, SIZEOF( thread->server ));
+ strncpy( thread->manualHost, m_regInfo->manualHost, SIZEOF( thread->manualHost ));
+ thread->port = m_regInfo->port;
+ thread->useSSL = m_regInfo->useSSL;
+ thread->reg_hwndDlg= m_hwnd;
+ m_proto->JForkThread(( JThreadFunc )&CJabberProto::ServerThread, thread );
+
+ m_btnOk.SetText(TranslateT("Cancel"));
+ m_bProcessStarted = true;
+
+ m_lresult = TRUE;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberOptDlgProc - main options dialog procedure
+
+class CCtrlEditJid: public CCtrlEdit
+{
+ typedef CCtrlEdit CSuper;
+
+public:
+ CCtrlEditJid( CDlgBase* dlg, int ctrlId );
+
+ void OnInit()
+ {
+ CCtrlEdit::OnInit();
+ Subclass();
+ }
+
+protected:
+ virtual LRESULT CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ if ( msg == WM_CHAR )
+ {
+ switch( wParam )
+ {
+ case '\"': case '&': case '\'': case '/':
+ case ':': case '<': case '>': case '@':
+ MessageBeep(MB_ICONASTERISK);
+ return 0;
+ }
+ }
+ return CCtrlEdit::CustomWndProc(msg, wParam, lParam);
+ }
+};
+
+CCtrlEditJid::CCtrlEditJid( CDlgBase* dlg, int ctrlId ):
+ CCtrlEdit( dlg, ctrlId )
+{
+}
+
+static void sttStoreJidFromUI(CJabberProto *ppro, CCtrlEdit &txtUsername, CCtrlCombo &cbServer)
+{
+ TCHAR *user = txtUsername.GetText();
+ TCHAR *server = cbServer.GetText();
+ int len = lstrlen(user) + lstrlen(server) + 2;
+ TCHAR *jid = (TCHAR *)mir_alloc(len * sizeof(TCHAR));
+ mir_sntprintf(jid, len, _T("%s@%s"), user, server);
+ ppro->JSetStringT(NULL, "jid", jid);
+ mir_free(jid);
+ mir_free(server);
+ mir_free(user);
+}
+
+class CDlgOptAccount: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+ CCtrlEditJid m_txtUsername;
+ CCtrlEdit m_txtPassword;
+ CCtrlEdit m_txtPriority;
+ CCtrlCheck m_chkSavePassword;
+ CCtrlCombo m_cbResource;
+ CCtrlCheck m_chkUseHostnameAsResource;
+ CCtrlCheck m_chkUseDomainLogin;
+ CCtrlCombo m_cbServer;
+ CCtrlEdit m_txtPort;
+ CCtrlCheck m_chkUseSsl;
+ CCtrlCheck m_chkUseTls;
+ CCtrlCheck m_chkManualHost;
+ CCtrlEdit m_txtManualHost;
+ CCtrlEdit m_txtManualPort;
+ CCtrlCheck m_chkKeepAlive;
+ CCtrlCheck m_chkAutoDeleteContacts;
+ CCtrlEdit m_txtUserDirectory;
+ CCtrlCombo m_cbLocale;
+ CCtrlButton m_btnRegister;
+ CCtrlButton m_btnUnregister;
+ CCtrlButton m_btnChangePassword;
+ CCtrlHyperlink m_lnkServers;
+
+public:
+ CDlgOptAccount(CJabberProto *proto):
+ CJabberDlgBase(proto, IDD_OPT_JABBER, NULL, false ),
+ m_txtUsername(this, IDC_EDIT_USERNAME),
+ m_txtPassword(this, IDC_EDIT_PASSWORD),
+ m_txtPriority(this, IDC_PRIORITY),
+ m_chkSavePassword(this, IDC_SAVEPASSWORD),
+ m_cbResource(this, IDC_COMBO_RESOURCE),
+ m_chkUseHostnameAsResource(this,IDC_HOSTNAME_AS_RESOURCE),
+ m_chkUseDomainLogin(this, IDC_USEDOMAINLOGIN),
+ m_cbServer(this, IDC_EDIT_LOGIN_SERVER),
+ m_txtPort(this, IDC_PORT),
+ m_chkUseSsl(this, IDC_USE_SSL),
+ m_chkUseTls(this, IDC_USE_TLS),
+ m_chkManualHost(this, IDC_MANUAL),
+ m_txtManualHost(this, IDC_HOST),
+ m_txtManualPort(this, IDC_HOSTPORT),
+ m_chkKeepAlive(this, IDC_KEEPALIVE),
+ m_chkAutoDeleteContacts(this, IDC_ROSTER_SYNC),
+ m_txtUserDirectory(this, IDC_JUD),
+ m_cbLocale(this, IDC_MSGLANG),
+ m_btnRegister(this, IDC_BUTTON_REGISTER),
+ m_btnUnregister(this, IDC_UNREGISTER),
+ m_btnChangePassword(this, IDC_BUTTON_CHANGE_PASSWORD),
+ m_lnkServers(this, IDC_LINK_PUBLIC_SERVER, "http://xmpp.org/services/")
+
+ {
+ CreateLink(m_txtUsername, "LoginName", _T(""));
+ CreateLink(m_txtPriority, "Priority", DBVT_WORD, 0, true);
+ CreateLink(m_chkSavePassword, proto->m_options.SavePassword);
+ CreateLink(m_cbResource, "Resource", _T("Miranda"));
+ CreateLink(m_chkUseHostnameAsResource, proto->m_options.HostNameAsResource);
+ CreateLink(m_chkUseDomainLogin, proto->m_options.UseDomainLogin);
+ CreateLink(m_cbServer, "LoginServer", _T("jabber.org"));
+ CreateLink(m_txtPort, "Port", DBVT_WORD, 5222);
+ CreateLink(m_chkUseSsl, proto->m_options.UseSSL);
+ CreateLink(m_chkUseTls, proto->m_options.UseTLS);
+ CreateLink(m_chkManualHost, proto->m_options.ManualConnect);
+ CreateLink(m_txtManualHost, "ManualHost", _T(""));
+ CreateLink(m_txtManualPort, "ManualPort", DBVT_WORD, 0);
+ CreateLink(m_chkKeepAlive, proto->m_options.KeepAlive);
+ CreateLink(m_chkAutoDeleteContacts, proto->m_options.RosterSync);
+ CreateLink(m_txtUserDirectory, "Jud", _T(""));
+
+ // Bind events
+ m_cbServer.OnDropdown = Callback(this, &CDlgOptAccount::cbServer_OnDropdown);
+ m_chkManualHost.OnChange = Callback(this, &CDlgOptAccount::chkManualHost_OnChange);
+ m_chkUseHostnameAsResource.OnChange = Callback(this, &CDlgOptAccount::chkUseHostnameAsResource_OnChange);
+ m_chkUseDomainLogin.OnChange = Callback(this, &CDlgOptAccount::chkUseDomainLogin_OnChange);
+ m_chkUseSsl.OnChange = Callback(this, &CDlgOptAccount::chkUseSsl_OnChange);
+ m_chkUseTls.OnChange = Callback(this, &CDlgOptAccount::chkUseTls_OnChange);
+
+ m_btnRegister.OnClick = Callback(this, &CDlgOptAccount::btnRegister_OnClick);
+ m_btnUnregister.OnClick = Callback(this, &CDlgOptAccount::btnUnregister_OnClick);
+ m_btnChangePassword.OnClick = Callback(this, &CDlgOptAccount::btnChangePassword_OnClick);
+ }
+
+ static CDlgBase *Create(void *param) { return new CDlgOptAccount((CJabberProto *)param); }
+
+protected:
+ void OnInitDialog()
+ {
+ CSuper::OnInitDialog();
+
+ int i;
+ DBVARIANT dbv;
+
+ m_gotservers = false;
+
+ SendDlgItemMessage(m_hwnd, IDC_PRIORITY_SPIN, UDM_SETRANGE, 0, (LPARAM)MAKELONG(127, -128));
+
+ TCHAR *passw = m_proto->JGetStringCrypt(NULL, "LoginPassword");
+ if (passw)
+ {
+ m_txtPassword.SetText(passw);
+ mir_free(passw);
+ }
+
+ m_cbServer.AddString(TranslateT("Loading..."));
+
+ // fill predefined resources
+ TCHAR* szResources[] = { _T("Home"), _T("Work"), _T("Office"), _T("Miranda") };
+ for (i = 0; i < SIZEOF(szResources); ++i)
+ m_cbResource.AddString(szResources[i]);
+
+ // append computer name to the resource list
+ TCHAR szCompName[ MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD dwCompNameLength = MAX_COMPUTERNAME_LENGTH;
+ if (GetComputerName(szCompName, &dwCompNameLength))
+ m_cbResource.AddString(szCompName);
+
+ if (!DBGetContactSettingTString(NULL, m_proto->m_szModuleName, "Resource", &dbv))
+ {
+ if (CB_ERR == m_cbResource.FindString(dbv.ptszVal, -1, true))
+ m_cbResource.AddString(dbv.ptszVal);
+
+ m_cbResource.SetText(dbv.ptszVal);
+ JFreeVariant(&dbv);
+ }
+ else m_cbResource.SetText(_T("Miranda"));
+
+ for (i = 0; g_LanguageCodes[i].szCode; ++i)
+ {
+ int iItem = m_cbLocale.AddString(TranslateTS(g_LanguageCodes[i].szDescription), (LPARAM)g_LanguageCodes[i].szCode);
+ if (!_tcscmp(m_proto->m_tszSelectedLang, g_LanguageCodes[i].szCode))
+ m_cbLocale.SetCurSel(iItem);
+ }
+
+ EnableWindow(GetDlgItem(m_hwnd, IDC_COMBO_RESOURCE ), m_chkUseHostnameAsResource.GetState() != BST_CHECKED);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_UNREGISTER), m_proto->m_bJabberOnline);
+
+ m_chkUseTls.Enable(!m_proto->m_options.Disable3920auth && (m_proto->m_options.UseSSL ? false : true));
+ if (m_proto->m_options.Disable3920auth) m_chkUseTls.SetState(BST_UNCHECKED);
+ m_chkUseSsl.Enable(m_proto->m_options.Disable3920auth || (m_proto->m_options.UseTLS ? false : true));
+
+ if (m_proto->m_options.ManualConnect)
+ {
+ m_txtManualHost.Enable();
+ m_txtManualPort.Enable();
+ m_txtPort.Disable();
+ }
+
+ if (m_proto->m_options.UseDomainLogin)
+ chkUseDomainLogin_OnChange(&m_chkUseDomainLogin);
+
+ CheckRegistration();
+
+ }
+
+ void OnApply()
+ {
+ // clear saved password
+ *m_proto->m_savedPassword = 0;
+
+ if (m_chkSavePassword.GetState() == BST_CHECKED)
+ {
+ TCHAR *text = m_txtPassword.GetText();
+ m_proto->JSetStringCrypt(NULL, "LoginPassword", text);
+ mir_free(text);
+ }
+ else m_proto->JDeleteSetting(NULL, "LoginPassword");
+
+ int index = m_cbLocale.GetCurSel();
+ if ( index >= 0 )
+ {
+ TCHAR *szLanguageCode = (TCHAR *)m_cbLocale.GetItemData(index);
+ if ( szLanguageCode ) {
+ m_proto->JSetStringT(NULL, "XmlLang", szLanguageCode);
+
+ mir_free( m_proto->m_tszSelectedLang );
+ m_proto->m_tszSelectedLang = mir_tstrdup( szLanguageCode );
+ } }
+
+ sttStoreJidFromUI(m_proto, m_txtUsername, m_cbServer);
+
+ if (m_proto->m_bJabberOnline)
+ {
+ if (m_txtUsername.IsChanged() || m_txtPassword.IsChanged() || m_cbResource.IsChanged() ||
+ m_cbServer.IsChanged() || m_chkUseHostnameAsResource.IsChanged() || m_txtPort.IsChanged() ||
+ m_txtManualHost.IsChanged() || m_txtManualPort.IsChanged() || m_cbLocale.IsChanged())
+ {
+ MessageBox(m_hwnd,
+ TranslateT("These changes will take effect the next time you connect to the Jabber network."),
+ TranslateT( "Jabber Protocol Option" ), MB_OK|MB_SETFOREGROUND );
+ }
+
+ m_proto->SendPresence(m_proto->m_iStatus, true);
+ }
+ }
+
+ void OnChange(CCtrlBase*)
+ {
+ if (m_initialized)
+ CheckRegistration();
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (msg)
+ {
+ case WM_ACTIVATE:
+ m_chkUseTls.Enable(!m_proto->m_options.Disable3920auth && (m_proto->m_options.UseSSL ? false : true));
+ if (m_proto->m_options.Disable3920auth) m_chkUseTls.SetState(BST_UNCHECKED);
+ break;
+
+ case WM_JABBER_REFRESH:
+ RefreshServers(( HXML )lParam );
+ break;
+ }
+ return CSuper::DlgProc(msg, wParam, lParam);
+ }
+
+private:
+ bool m_gotservers;
+
+ void btnRegister_OnClick(CCtrlButton *)
+ {
+ TCHAR buf[512] = _T(""), pass[512];
+ if (!m_proto->EnterString(buf, SIZEOF(buf), TranslateT("Confirm password"), JES_PASSWORD))
+ return;
+
+ m_txtPassword.GetText(pass, SIZEOF(pass));
+ if (lstrcmp(buf, pass))
+ {
+ MessageBox(m_hwnd, TranslateT("Passwords do not match."), _T("Miranda NG"), MB_ICONSTOP|MB_OK);
+ return;
+ }
+
+ ThreadData regInfo(m_proto, JABBER_SESSION_NORMAL);
+ m_txtUsername.GetText(regInfo.username, SIZEOF(regInfo.username));
+ m_txtPassword.GetText(regInfo.password, SIZEOF(regInfo.password));
+ m_cbServer.GetTextA(regInfo.server, SIZEOF(regInfo.server));
+ if (m_chkManualHost.GetState() == BST_CHECKED)
+ {
+ regInfo.port = (WORD)m_txtManualPort.GetInt();
+ m_txtManualHost.GetTextA(regInfo.manualHost, SIZEOF(regInfo.manualHost));
+ } else
+ {
+ regInfo.port = (WORD)m_txtPort.GetInt();
+ regInfo.manualHost[0] = '\0';
+ }
+
+ if (regInfo.username[0] && regInfo.password[0] && regInfo.server[0] && regInfo.port>0 && ( (m_chkManualHost.GetState() != BST_CHECKED) || regInfo.manualHost[0] ))
+ {
+ CJabberDlgRegister dlg(m_proto, m_hwnd, ®Info);
+ dlg.DoModal();
+// DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_OPT_REGISTER), m_hwnd, JabberRegisterDlgProc, (LPARAM)®Info);
+ }
+ }
+
+ void btnUnregister_OnClick(CCtrlButton *)
+ {
+ int res = MessageBox(NULL,
+ TranslateT("This operation will kill your account, roster and all another information stored at the server. Are you ready to do that?"),
+ TranslateT("Account removal warning"), MB_YESNOCANCEL);
+
+ if ( res == IDYES )
+ m_proto->m_ThreadInfo->send(
+ XmlNodeIq( _T("set"), m_proto->SerialNext(), m_proto->m_szJabberJID ) << XQUERY( _T(JABBER_FEAT_REGISTER))
+ << XCHILD( _T("remove")));
+ }
+
+ void btnChangePassword_OnClick(CCtrlButton *)
+ {
+ if ( !m_proto->m_bJabberOnline ) {
+ MessageBox( NULL,
+ TranslateT("You can change your password only when you are online"),
+ TranslateT("You must be online"), MB_OK | MB_ICONSTOP );
+ return;
+ }
+
+ m_proto->OnMenuHandleChangePassword(0, 0);
+ }
+
+ void cbServer_OnDropdown(CCtrlCombo*)
+ {
+ if ( !m_gotservers )
+ mir_forkthread(QueryServerListThread, this);
+ }
+
+ void chkManualHost_OnChange(CCtrlData *sender)
+ {
+ CCtrlCheck *chk = (CCtrlCheck *)sender;
+
+ if (chk->GetState() == BST_CHECKED)
+ {
+ m_txtManualHost.Enable();
+ m_txtManualPort.Enable();
+ m_txtPort.Disable();
+ } else
+ {
+ m_txtManualHost.Disable();
+ m_txtManualPort.Disable();
+ m_txtPort.Enable();
+ }
+ }
+
+ void chkUseHostnameAsResource_OnChange(CCtrlData *sender)
+ {
+ CCtrlCheck *chk = (CCtrlCheck *)sender;
+
+ m_cbResource.Enable(chk->GetState() != BST_CHECKED);
+ if (chk->GetState() == BST_CHECKED)
+ {
+ TCHAR szCompName[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD dwCompNameLength = MAX_COMPUTERNAME_LENGTH;
+ if (GetComputerName(szCompName, &dwCompNameLength))
+ m_cbResource.SetText(szCompName);
+ }
+ }
+
+ void chkUseDomainLogin_OnChange(CCtrlData *sender)
+ {
+ CCtrlCheck *chk = (CCtrlCheck *)sender;
+ BOOL checked = chk->GetState() == BST_CHECKED;
+
+ m_txtPassword.Enable(!checked);
+ m_txtUsername.Enable(!checked);
+ m_chkSavePassword.Enable(!checked);
+ if (checked) {
+ m_txtPassword.SetText(_T(""));
+ m_txtUsername.SetText(_T(""));
+ m_chkSavePassword.SetState(BST_CHECKED);
+ }
+ }
+
+ void chkUseSsl_OnChange(CCtrlData*)
+ {
+ BOOL bManualHost = m_chkManualHost.GetState() == BST_CHECKED;
+ if (m_chkUseSsl.GetState() == BST_CHECKED)
+ {
+ m_chkUseTls.Disable();
+ if (!bManualHost)
+ m_txtPort.SetInt(5223);
+ } else
+ {
+ if (!m_proto->m_options.Disable3920auth) m_chkUseTls.Enable();
+ if (!bManualHost)
+ m_txtPort.SetInt(5222);
+ }
+ }
+
+ void chkUseTls_OnChange(CCtrlData*)
+ {
+ if (m_chkUseTls.GetState() == BST_CHECKED)
+ m_chkUseSsl.Disable();
+ else
+ m_chkUseSsl.Enable();
+ }
+
+ void CheckRegistration()
+ {
+ ThreadData regInfo(m_proto, JABBER_SESSION_NORMAL);
+ m_txtUsername.GetText(regInfo.username, SIZEOF(regInfo.username));
+ m_txtPassword.GetText(regInfo.password, SIZEOF(regInfo.password));
+ m_cbServer.GetTextA(regInfo.server, SIZEOF(regInfo.server));
+ if (m_chkManualHost.GetState() == BST_CHECKED)
+ {
+ regInfo.port = (WORD)m_txtManualPort.GetInt();
+ m_txtManualHost.GetTextA(regInfo.manualHost, SIZEOF(regInfo.manualHost));
+ } else
+ {
+ regInfo.port = (WORD)m_txtPort.GetInt();
+ regInfo.manualHost[0] = '\0';
+ }
+
+ if (regInfo.username[0] && regInfo.password[0] && regInfo.server[0] && regInfo.port>0 && ( (m_chkManualHost.GetState() != BST_CHECKED) || regInfo.manualHost[0] ))
+ EnableWindow( GetDlgItem( m_hwnd, IDC_BUTTON_REGISTER ), TRUE );
+ else
+ EnableWindow( GetDlgItem( m_hwnd, IDC_BUTTON_REGISTER ), FALSE );
+ }
+
+ void RefreshServers( HXML node )
+ {
+ m_gotservers = node != NULL;
+
+ TCHAR *server = m_cbServer.GetText();
+ bool bDropdown = m_cbServer.GetDroppedState();
+ if (bDropdown) m_cbServer.ShowDropdown(false);
+
+ m_cbServer.ResetContent();
+ if ( node ) {
+ for (int i = 0; ; ++i) {
+ HXML n = xmlGetChild(node, i);
+ if ( !n )
+ break;
+
+ if ( !lstrcmp( xmlGetName( n ), _T("item")))
+ if ( const TCHAR *jid = xmlGetAttrValue( n, _T("jid")))
+ if ( m_cbServer.FindString(jid, -1, true) == CB_ERR)
+ m_cbServer.AddString(jid);
+ }
+ }
+
+ m_cbServer.SetText(server);
+
+ if (bDropdown) m_cbServer.ShowDropdown();
+ mir_free(server);
+ }
+
+ static void QueryServerListThread(void *arg)
+ {
+ CDlgOptAccount *wnd = (CDlgOptAccount *)arg;
+ HWND hwnd = wnd->GetHwnd();
+ bool bIsError = true;
+
+ if (!IsWindow(hwnd)) return;
+
+ NETLIBHTTPREQUEST request = {0};
+ request.cbSize = sizeof(request);
+ request.requestType = REQUEST_GET;
+ request.flags = NLHRF_REDIRECT | NLHRF_HTTP11;
+ request.szUrl = "http://xmpp.org/services/services.xml";
+
+ NETLIBHTTPREQUEST *result = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)wnd->GetProto()->m_hNetlibUser, (LPARAM)&request);
+ if ( result ) {
+ if ( result->resultCode == 200 && result->dataLength && result->pData ) {
+ TCHAR* buf = mir_a2t( result->pData );
+ XmlNode node( buf, NULL, NULL );
+ if ( node ) {
+ HXML queryNode = xmlGetChild( node, _T("query"));
+ SendMessage(hwnd, WM_JABBER_REFRESH, 0, (LPARAM)queryNode);
+ bIsError = false;
+ }
+ mir_free( buf );
+ }
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)result);
+ }
+
+ if ( bIsError )
+ SendMessage(hwnd, WM_JABBER_REFRESH, 0, (LPARAM)NULL);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberAdvOptDlgProc - advanced options dialog procedure
+
+class CDlgOptAdvanced: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+ CCtrlCheck m_chkDirect;
+ CCtrlCheck m_chkDirectManual;
+ CCtrlCheck m_chkProxy;
+ CCtrlEdit m_txtDirect;
+ CCtrlEdit m_txtProxy;
+ CCtrlTreeOpts m_otvOptions;
+
+public:
+ CDlgOptAdvanced(CJabberProto *proto):
+ CJabberDlgBase(proto, IDD_OPT_JABBER2, NULL, false),
+ m_chkDirect(this, IDC_DIRECT),
+ m_chkDirectManual(this, IDC_DIRECT_MANUAL),
+ m_chkProxy(this, IDC_PROXY_MANUAL),
+ m_txtDirect(this, IDC_DIRECT_ADDR),
+ m_txtProxy(this, IDC_PROXY_ADDR),
+ m_otvOptions(this, IDC_OPTTREE)
+ {
+ CreateLink(m_chkDirect, proto->m_options.BsDirect);
+ CreateLink(m_chkDirectManual, proto->m_options.BsDirectManual);
+ CreateLink(m_chkProxy, proto->m_options.BsProxyManual);
+ CreateLink(m_txtDirect, "BsDirectAddr", _T(""));
+ CreateLink(m_txtProxy, "BsProxyServer", _T(""));
+
+ m_chkDirect.OnChange =
+ m_chkDirectManual.OnChange = Callback(this, &CDlgOptAdvanced::chkDirect_OnChange);
+ m_chkProxy.OnChange = Callback(this, &CDlgOptAdvanced::chkProxy_OnChange);
+
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Send messages slower, but with full acknowledgement"), m_proto->m_options.MsgAck);
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Enable avatars"), m_proto->m_options.EnableAvatars);
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Log chat state changes"), m_proto->m_options.LogChatstates);
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Log presence subscription state changes"), m_proto->m_options.LogPresence);
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Log presence errors"), m_proto->m_options.LogPresenceErrors);
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Enable user moods receiving"), m_proto->m_options.EnableUserMood);
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Enable user tunes receiving"), m_proto->m_options.EnableUserTune);
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Enable user activity receiving"), m_proto->m_options.EnableUserActivity);
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Receive notes"), m_proto->m_options.AcceptNotes);
+ m_otvOptions.AddOption(LPGENT("Messaging") _T("/") LPGENT("Automatically save received notes"), m_proto->m_options.AutosaveNotes);
+
+ m_otvOptions.AddOption(LPGENT("Server options") _T("/") LPGENT("Disable SASL authentication (for old servers)"), m_proto->m_options.Disable3920auth);
+ m_otvOptions.AddOption(LPGENT("Server options") _T("/") LPGENT("Enable stream compression (if possible)"), m_proto->m_options.EnableZlib);
+
+ m_otvOptions.AddOption(LPGENT("Other") _T("/") LPGENT("Enable remote controlling (from another resource of same JID only)"), m_proto->m_options.EnableRemoteControl);
+ m_otvOptions.AddOption(LPGENT("Other") _T("/") LPGENT("Show transport agents on contact list"), m_proto->m_options.ShowTransport);
+ m_otvOptions.AddOption(LPGENT("Other") _T("/") LPGENT("Automatically add contact when accept authorization"), m_proto->m_options.AutoAdd);
+ m_otvOptions.AddOption(LPGENT("Other") _T("/") LPGENT("Automatically accept authorization requests"), m_proto->m_options.AutoAcceptAuthorization);
+ m_otvOptions.AddOption(LPGENT("Other") _T("/") LPGENT("Fix incorrect timestamps in incoming messages"), m_proto->m_options.FixIncorrectTimestamps);
+ m_otvOptions.AddOption(LPGENT("Other") _T("/") LPGENT("Disable frame"), m_proto->m_options.DisableFrame);
+ m_otvOptions.AddOption(LPGENT("Other") _T("/") LPGENT("Enable XMPP link processing (requires Association Manager)"), m_proto->m_options.ProcessXMPPLinks);
+ m_otvOptions.AddOption(LPGENT("Other") _T("/") LPGENT("Keep contacts assigned to local groups (ignore roster group)"), m_proto->m_options.IgnoreRosterGroups);
+
+ m_otvOptions.AddOption(LPGENT("Security") _T("/") LPGENT("Allow servers to request version (XEP-0092)"), m_proto->m_options.AllowVersionRequests);
+ m_otvOptions.AddOption(LPGENT("Security") _T("/") LPGENT("Show information about operating system in version replies"), m_proto->m_options.ShowOSVersion);
+ m_otvOptions.AddOption(LPGENT("Security") _T("/") LPGENT("Accept only in band incoming filetransfers (don't disclose own IP)"), m_proto->m_options.BsOnlyIBB);
+ m_otvOptions.AddOption(LPGENT("Security") _T("/") LPGENT("Accept HTTP Authentication requests (XEP-0070)"), m_proto->m_options.AcceptHttpAuth);
+ }
+
+ void OnInitDialog()
+ {
+ CSuper::OnInitDialog();
+
+ chkDirect_OnChange(&m_chkDirect);
+ chkProxy_OnChange(&m_chkProxy);
+ }
+
+ void OnApply()
+ {
+ BOOL bChecked = m_proto->m_options.ShowTransport;
+ LISTFOREACH(index, m_proto, LIST_ROSTER)
+ {
+ JABBER_LIST_ITEM* item = m_proto->ListGetItemPtrFromIndex( index );
+ if ( item != NULL ) {
+ if ( _tcschr( item->jid, '@' ) == NULL ) {
+ HANDLE hContact = m_proto->HContactFromJID( item->jid );
+ if ( hContact != NULL ) {
+ if ( bChecked ) {
+ if ( item->itemResource.status != m_proto->JGetWord( hContact, "Status", ID_STATUS_OFFLINE )) {
+ m_proto->JSetWord( hContact, "Status", ( WORD )item->itemResource.status );
+ } }
+ else if ( m_proto->JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != ID_STATUS_OFFLINE )
+ m_proto->JSetWord( hContact, "Status", ID_STATUS_OFFLINE );
+ } } }
+ }
+
+ m_proto->SendPresence( m_proto->m_iStatus, true );
+ }
+
+ void chkDirect_OnChange(CCtrlData *)
+ {
+ if (m_chkDirect.GetState() == BST_CHECKED)
+ {
+ if (m_chkDirectManual.GetState() == BST_CHECKED)
+ m_txtDirect.Enable();
+ else
+ m_txtDirect.Disable();
+
+ m_chkDirectManual.Enable();
+ } else
+ {
+ m_txtDirect.Disable();
+ m_chkDirectManual.Disable();
+ }
+ }
+
+ void chkProxy_OnChange(CCtrlData *)
+ {
+ if (m_chkProxy.GetState() == BST_CHECKED)
+ m_txtProxy.Enable();
+ else
+ m_txtProxy.Disable();
+ }
+
+ static CDlgBase *Create(void *param) { return new CDlgOptAdvanced((CJabberProto *)param); }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGcOptDlgProc - chat options dialog procedure
+
+class CDlgOptGc: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+ CCtrlEdit m_txtAltNick;
+ CCtrlEdit m_txtSlap;
+ CCtrlEdit m_txtQuit;
+ CCtrlTreeOpts m_otvOptions;
+
+public:
+ CDlgOptGc(CJabberProto *proto):
+ CJabberDlgBase(proto, IDD_OPT_JABBER4, NULL, false),
+ m_txtAltNick(this, IDC_TXT_ALTNICK),
+ m_txtSlap(this, IDC_TXT_SLAP),
+ m_txtQuit(this, IDC_TXT_QUIT),
+ m_otvOptions(this, IDC_OPTTREE)
+ {
+ CreateLink(m_txtAltNick, "GcAltNick", _T(""));
+ CreateLink(m_txtSlap, "GcMsgSlap", TranslateTS(JABBER_GC_MSG_SLAP));
+ CreateLink(m_txtQuit, "GcMsgQuit", TranslateTS(JABBER_GC_MSG_QUIT));
+
+ m_otvOptions.AddOption(LPGENT("General") _T("/") LPGENT("Autoaccept multiuser chat invitations"), m_proto->m_options.AutoAcceptMUC);
+ m_otvOptions.AddOption(LPGENT("General") _T("/") LPGENT("Automatically join bookmarks on login"), m_proto->m_options.AutoJoinBookmarks);
+ m_otvOptions.AddOption(LPGENT("General") _T("/") LPGENT("Automatically join conferences on login"), m_proto->m_options.AutoJoinConferences);
+ m_otvOptions.AddOption(LPGENT("General") _T("/") LPGENT("Hide conference windows at startup"), m_proto->m_options.AutoJoinHidden);
+ m_otvOptions.AddOption(LPGENT("General") _T("/") LPGENT("Do not show multiuser chat invitations"), m_proto->m_options.IgnoreMUCInvites);
+ m_otvOptions.AddOption(LPGENT("Log events") _T("/") LPGENT("Ban notifications"), m_proto->m_options.GcLogBans);
+ m_otvOptions.AddOption(LPGENT("Log events") _T("/") LPGENT("Room configuration changes"), m_proto->m_options.GcLogConfig);
+ m_otvOptions.AddOption(LPGENT("Log events") _T("/") LPGENT("Affiliation changes"), m_proto->m_options.GcLogAffiliations);
+ m_otvOptions.AddOption(LPGENT("Log events") _T("/") LPGENT("Role changes"), m_proto->m_options.GcLogRoles);
+ m_otvOptions.AddOption(LPGENT("Log events") _T("/") LPGENT("Status changes"), m_proto->m_options.GcLogStatuses);
+ m_otvOptions.AddOption(LPGENT("Log events") _T("/") LPGENT("Filter history messages"), m_proto->m_options.GcLogChatHistory);
+ }
+
+ static CDlgBase *Create(void *param) { return new CDlgOptGc((CJabberProto *)param); }
+};
+
+//////////////////////////////////////////////////////////////////////////
+// roster editor
+//
+
+#include <io.h>
+#define JM_STATUSCHANGED WM_USER+0x0001
+#define fopent(name, mode) _wfopen(name, mode)
+
+enum {
+ RRA_FILLLIST = 0,
+ RRA_SYNCROSTER,
+ RRA_SYNCDONE
+};
+
+typedef struct _tag_RosterhEditDat{
+ WNDPROC OldEditProc;
+ HWND hList;
+ int index;
+ int subindex;
+} ROSTEREDITDAT;
+
+static WNDPROC _RosterOldListProc=NULL;
+
+static int _RosterInsertListItem(HWND hList, const TCHAR * jid, const TCHAR * nick, const TCHAR * group, const TCHAR * subscr, BOOL bChecked)
+{
+ LVITEM item={0};
+ int index;
+ item.mask=LVIF_TEXT|LVIF_STATE;
+
+ item.iItem = ListView_GetItemCount(hList);
+ item.iSubItem = 0;
+ item.pszText = ( TCHAR* )jid;
+
+ index=ListView_InsertItem(hList, &item);
+
+ if ( index<0 )
+ return index;
+
+ ListView_SetCheckState(hList, index, bChecked);
+
+ ListView_SetItemText(hList, index, 0, ( TCHAR* )jid);
+ ListView_SetItemText(hList, index, 1, ( TCHAR* )nick);
+ ListView_SetItemText(hList, index, 2, ( TCHAR* )group);
+ ListView_SetItemText(hList, index, 3, TranslateTS( subscr ));
+
+ return index;
+}
+
+static void _RosterListClear(HWND hwndDlg)
+{
+ HWND hList=GetDlgItem(hwndDlg, IDC_ROSTER);
+ if (!hList) return;
+ ListView_DeleteAllItems(hList);
+ while ( ListView_DeleteColumn( hList, 0));
+
+ LV_COLUMN column={0};
+ column.mask=LVCF_TEXT;
+ column.cx=500;
+
+ column.pszText=TranslateT("JID");
+ ListView_InsertColumn(hList, 1, &column);
+
+ column.pszText=TranslateT("Nick Name");
+ ListView_InsertColumn(hList, 2, &column);
+
+ column.pszText=TranslateT("Group");
+ ListView_InsertColumn(hList, 3, &column);
+
+ column.pszText=TranslateT("Subscription");
+ ListView_InsertColumn(hList, 4, &column);
+
+ RECT rc;
+ GetClientRect(hList, &rc);
+ int width=rc.right-rc.left;
+
+ ListView_SetColumnWidth(hList,0,width*40/100);
+ ListView_SetColumnWidth(hList,1,width*25/100);
+ ListView_SetColumnWidth(hList,2,width*20/100);
+ ListView_SetColumnWidth(hList,3,width*10/100);
+}
+
+void CJabberProto::_RosterHandleGetRequest( HXML node )
+{
+ HWND hList=GetDlgItem(rrud.hwndDlg, IDC_ROSTER);
+ if (rrud.bRRAction==RRA_FILLLIST)
+ {
+ _RosterListClear(rrud.hwndDlg);
+ HXML query = xmlGetChild( node , "query");
+ if (!query) return;
+ int i = 1;
+ while (TRUE) {
+ HXML item = xmlGetNthChild( query, _T("item"), i++);
+ if (!item)
+ break;
+
+ const TCHAR *jid = xmlGetAttrValue( item, _T("jid"));
+ if (!jid)
+ continue;
+
+ const TCHAR *name = xmlGetAttrValue( item, _T("name"));
+ const TCHAR *subscription = xmlGetAttrValue( item, _T("subscription"));
+ const TCHAR *group = NULL;
+ HXML groupNode = xmlGetChild( item , "group" );
+ if ( groupNode )
+ group = xmlGetText( groupNode );
+ _RosterInsertListItem( hList, jid, name, group, subscription, TRUE );
+ }
+ // now it is require to process whole contact list to add not in roster contacts
+ {
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL )
+ {
+ char* str = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( str != NULL && !strcmp( str, m_szModuleName ))
+ {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv ))
+ {
+ LVFINDINFO lvfi={0};
+ lvfi.flags = LVFI_STRING;
+ lvfi.psz = dbv.ptszVal;
+ TCHAR *p = _tcschr(dbv.ptszVal,_T('@'));
+ if ( p ) {
+ p = _tcschr( dbv.ptszVal, _T('/'));
+ if ( p ) *p = _T('\0');
+ }
+ if ( ListView_FindItem(hList, -1, &lvfi) == -1) {
+ TCHAR *jid = mir_tstrdup( dbv.ptszVal );
+ TCHAR *name = NULL;
+ TCHAR *group = NULL;
+ DBVARIANT dbvtemp;
+ if ( !DBGetContactSettingTString( hContact, "CList", "MyHandle", &dbvtemp )) {
+ name = mir_tstrdup( dbvtemp.ptszVal );
+ DBFreeVariant( &dbvtemp );
+ }
+ if ( !DBGetContactSettingTString( hContact, "CList", "Group", &dbvtemp )) {
+ group = mir_tstrdup( dbvtemp.ptszVal );
+ DBFreeVariant( &dbvtemp );
+ }
+ _RosterInsertListItem( hList, jid, name, group, NULL, FALSE );
+ if ( jid ) mir_free( jid );
+ if ( name ) mir_free( name );
+ if ( group ) mir_free( group );
+ }
+ DBFreeVariant( &dbv );
+ }
+ }
+ hContact = db_find_next(hContact);
+ }
+ }
+ rrud.bReadyToDownload = FALSE;
+ rrud.bReadyToUpload = TRUE;
+ SetDlgItemText( rrud.hwndDlg, IDC_DOWNLOAD, TranslateT( "Download" ));
+ SetDlgItemText( rrud.hwndDlg, IDC_UPLOAD, TranslateT( "Upload" ));
+ SendMessage( rrud.hwndDlg, JM_STATUSCHANGED, 0, 0 );
+ return;
+ }
+ else if ( rrud.bRRAction == RRA_SYNCROSTER )
+ {
+ SetDlgItemText(rrud.hwndDlg, IDC_UPLOAD, TranslateT("Uploading..."));
+ HXML queryRoster = xmlGetChild( node , "query");
+ if (!queryRoster)
+ return;
+
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::_RosterHandleGetRequest );
+
+ XmlNode iq( _T("iq"));
+ xmlAddAttr( iq, _T("type"), _T("set"));
+ iq << XATTRID( iqId );
+
+ HXML query = iq << XCHILDNS( _T("query"), _T(JABBER_FEAT_IQ_ROSTER));
+
+ int itemCount=0;
+ int ListItemCount=ListView_GetItemCount(hList);
+ for (int index=0; index<ListItemCount; index++)
+ {
+ TCHAR jid[JABBER_MAX_JID_LEN]=_T("");
+ TCHAR name[260]=_T("");
+ TCHAR group[260]=_T("");
+ TCHAR subscr[260]=_T("");
+ ListView_GetItemText(hList, index, 0, jid, SIZEOF(jid));
+ ListView_GetItemText(hList, index, 1, name, SIZEOF(name));
+ ListView_GetItemText(hList, index, 2, group, SIZEOF(group));
+ ListView_GetItemText(hList, index, 3, subscr, SIZEOF(subscr));
+ HXML itemRoster = xmlGetChildByTag( queryRoster, "item", "jid", jid);
+ BOOL bRemove = !ListView_GetCheckState(hList,index);
+ if (itemRoster && bRemove)
+ {
+ //delete item
+ query << XCHILD( _T("item")) << XATTR( _T("jid"), jid ) << XATTR( _T("subscription") ,_T("remove"));
+ itemCount++;
+ }
+ else if ( !bRemove )
+ {
+ BOOL bPushed = itemRoster ? TRUE : FALSE;
+ if ( !bPushed ) {
+ const TCHAR *rosterName = xmlGetAttrValue( itemRoster, _T("name"));
+ if ( (rosterName!=NULL || name[0]!=_T('\0')) && lstrcmpi(rosterName,name))
+ bPushed=TRUE;
+ if ( !bPushed ) {
+ rosterName = xmlGetAttrValue( itemRoster, _T("subscription"));
+ if ((rosterName!=NULL || subscr[0]!=_T('\0')) && lstrcmpi(rosterName,subscr))
+ bPushed=TRUE;
+ }
+ if ( !bPushed ) {
+ HXML groupNode = xmlGetChild( itemRoster , "group" );
+ const TCHAR* rosterGroup=NULL;
+ if (groupNode != NULL)
+ rosterGroup = xmlGetText( groupNode );
+ if ((rosterGroup!=NULL || group[0]!=_T('\0')) && lstrcmpi(rosterGroup,group))
+ bPushed=TRUE;
+ }
+ }
+ if ( bPushed ) {
+ HXML item = query << XCHILD( _T("item"));
+ if ( group && _tcslen( group ))
+ item << XCHILD( _T("group"), group );
+ if ( name && _tcslen( name ))
+ item << XATTR( _T("name"), name );
+ item << XATTR( _T("jid"), jid ) << XATTR( _T("subscription"), subscr[0] ? subscr : _T("none"));
+ itemCount++;
+ }
+ }
+ }
+ rrud.bRRAction=RRA_SYNCDONE;
+ if (itemCount)
+ m_ThreadInfo->send( iq );
+ else
+ _RosterSendRequest(rrud.hwndDlg,RRA_FILLLIST);
+ }
+ else
+ {
+ SetDlgItemText(rrud.hwndDlg,IDC_UPLOAD,TranslateT("Upload"));
+ rrud.bReadyToUpload=FALSE;
+ rrud.bReadyToDownload=FALSE;
+ SendMessage(rrud.hwndDlg, JM_STATUSCHANGED,0,0);
+ SetDlgItemText(rrud.hwndDlg,IDC_DOWNLOAD,TranslateT("Downloading..."));
+ _RosterSendRequest(rrud.hwndDlg,RRA_FILLLIST);
+
+ }
+}
+
+void CJabberProto::_RosterSendRequest(HWND hwndDlg, BYTE rrAction)
+{
+ rrud.bRRAction=rrAction;
+ rrud.hwndDlg=hwndDlg;
+
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::_RosterHandleGetRequest );
+ m_ThreadInfo->send( XmlNode( _T("iq")) << XATTR( _T("type"), _T("get")) << XATTRID( iqId ) << XCHILDNS( _T("query"), _T(JABBER_FEAT_IQ_ROSTER )));
+}
+
+static void _RosterItemEditEnd( HWND hEditor, ROSTEREDITDAT * edat, BOOL bCancel )
+{
+ if (!bCancel)
+ {
+ int len = GetWindowTextLength(hEditor) + 1;
+ TCHAR *buff=(TCHAR*)mir_alloc(len*sizeof(TCHAR));
+ if ( buff ) {
+ GetWindowText(hEditor,buff,len);
+ ListView_SetItemText(edat->hList,edat->index, edat->subindex,buff);
+ }
+ mir_free(buff);
+ }
+ DestroyWindow(hEditor);
+}
+
+static BOOL CALLBACK _RosterItemNewEditProc( HWND hEditor, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ ROSTEREDITDAT * edat = (ROSTEREDITDAT *) GetWindowLongPtr(hEditor,GWLP_USERDATA);
+ if (!edat) return 0;
+ switch(msg)
+ {
+
+ case WM_KEYDOWN:
+ switch(wParam)
+ {
+ case VK_RETURN:
+ _RosterItemEditEnd(hEditor, edat, FALSE);
+ return 0;
+ case VK_ESCAPE:
+ _RosterItemEditEnd(hEditor, edat, TRUE);
+ return 0;
+ }
+ break;
+ case WM_GETDLGCODE:
+ if ( lParam ) {
+ MSG *msg2 = (MSG*)lParam;
+ if (msg2->message==WM_KEYDOWN && msg2->wParam==VK_TAB) return 0;
+ if (msg2->message==WM_CHAR && msg2->wParam=='\t') return 0;
+ }
+ return DLGC_WANTMESSAGE;
+ case WM_KILLFOCUS:
+ _RosterItemEditEnd(hEditor, edat, FALSE);
+ return 0;
+ }
+
+ if (msg==WM_DESTROY)
+ {
+ SetWindowLongPtr(hEditor, GWLP_WNDPROC, (LONG_PTR) edat->OldEditProc);
+ SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR) 0);
+ free(edat);
+ return 0;
+ }
+ else return CallWindowProc( edat->OldEditProc, hEditor, msg, wParam, lParam);
+}
+
+
+
+void CJabberProto::_RosterExportToFile(HWND hwndDlg)
+{
+ TCHAR filename[MAX_PATH]={0};
+
+ TCHAR filter[MAX_PATH];
+ mir_sntprintf(filter, SIZEOF(filter), _T("%s (*.xml)%c*.xml%c%c"), TranslateT("XML for MS Excel (UTF-8 encoded)"), 0, 0, 0);
+ OPENFILENAME ofn={0};
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ ofn.hInstance = NULL;
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = filename;
+ ofn.Flags = OFN_HIDEREADONLY;
+ ofn.nMaxFile = SIZEOF(filename);
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lpstrDefExt = _T("xml");
+ if (!GetSaveFileName(&ofn)) return;
+
+ FILE * fp = fopent(filename,_T("w"));
+ if (!fp) return;
+ HWND hList=GetDlgItem(hwndDlg, IDC_ROSTER);
+ int ListItemCount=ListView_GetItemCount(hList);
+
+ XmlNode root(_T("Workbook"));
+ root << XATTR(_T("xmlns"), _T("urn:schemas-microsoft-com:office:spreadsheet"))
+ << XATTR(_T("xmlns:o"), _T("urn:schemas-microsoft-com:office:office"))
+ << XATTR(_T("xmlns:x"), _T("urn:schemas-microsoft-com:office:excel"))
+ << XATTR(_T("xmlns:ss"), _T("urn:schemas-microsoft-com:office:spreadsheet"))
+ << XATTR(_T("xmlns:html"), _T("http://www.w3.org/TR/REC-html40"));
+ root << XCHILD(_T("ExcelWorkbook"))
+ << XATTR(_T("xmlns"), _T("urn:schemas-microsoft-com:office:excel"));
+ HXML table = root << XCHILD(_T("Worksheet")) << XATTR(_T("ss:Name"), _T("Exported roster"))
+ << XCHILD(_T("Table"));
+
+ for (int index=0; index<ListItemCount; index++)
+ {
+ TCHAR jid[JABBER_MAX_JID_LEN]=_T("");
+ TCHAR name[260]=_T("");
+ TCHAR group[260]=_T("");
+ TCHAR subscr[260]=_T("");
+ ListView_GetItemText(hList, index, 0, jid, SIZEOF(jid));
+ ListView_GetItemText(hList, index, 1, name, SIZEOF(name));
+ ListView_GetItemText(hList, index, 2, group, SIZEOF(group));
+ ListView_GetItemText(hList, index, 3, subscr, SIZEOF(subscr));
+
+ HXML node = table << XCHILD(_T("Row"));
+ node << XCHILD(_T("Cell")) << XCHILD(_T("Data"), _T("+")) << XATTR(_T("ss:Type"), _T("String"));
+ node << XCHILD(_T("Cell")) << XCHILD(_T("Data"), jid) << XATTR(_T("ss:Type"), _T("String"));
+ node << XCHILD(_T("Cell")) << XCHILD(_T("Data"), name) << XATTR(_T("ss:Type"), _T("String"));
+ node << XCHILD(_T("Cell")) << XCHILD(_T("Data"), group) << XATTR(_T("ss:Type"), _T("String"));
+ node << XCHILD(_T("Cell")) << XCHILD(_T("Data"), subscr) << XATTR(_T("ss:Type"), _T("String"));
+
+ }
+
+ char header[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?mso-application progid=\"Excel.Sheet\"?>\n";
+ fwrite(header, 1, sizeof(header) - 1 /* for zero terminator */, fp);
+
+ TCHAR *xtmp = xi.toString(root, NULL);
+ char *tmp = mir_utf8encodeT(xtmp);
+ xi.freeMem(xtmp);
+
+ fwrite(tmp, 1, strlen(tmp), fp);
+ mir_free(tmp);
+ fclose(fp);
+}
+
+void CJabberProto::_RosterImportFromFile(HWND hwndDlg)
+{
+ char filename[MAX_PATH]={0};
+ char *filter="XML for MS Excel (UTF-8 encoded)(*.xml)\0*.xml\0\0";
+ OPENFILENAMEA ofn={0};
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ ofn.hInstance = NULL;
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = filename;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+ ofn.nMaxFile = sizeof(filename);
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lpstrDefExt = "xml";
+ if ( !GetOpenFileNameA( &ofn ))
+ return;
+
+ FILE * fp=fopen(filename,"r");
+ if (!fp)
+ return;
+
+ DWORD bufsize = _filelength(_fileno(fp));
+ if (bufsize <= 0) {
+ fclose(fp);
+ return;
+ }
+
+ char* buffer=(char*)mir_calloc(bufsize+1); // zero-terminate it
+ fread(buffer,1,bufsize,fp);
+ fclose(fp);
+ _RosterListClear(hwndDlg);
+
+ TCHAR* newBuf = mir_utf8decodeT( buffer );
+ mir_free( buffer );
+
+ int nBytesProcessed = 0;
+ XmlNode node( newBuf, &nBytesProcessed, NULL );
+ if ( node ) {
+ HXML Workbook = xmlGetChild( node, _T("Workbook"));
+ if ( Workbook ) {
+ HXML Worksheet = xmlGetChild( Workbook , "Worksheet");
+ if ( Worksheet ) {
+ HXML Table = xmlGetChild( Worksheet , "Table" );
+ if ( Table ) {
+ int index=1;
+ HWND hList=GetDlgItem(hwndDlg, IDC_ROSTER);
+ while (TRUE)
+ {
+ HXML Row = xmlGetNthChild( Table, _T("Row"), index++ );
+ if (!Row)
+ break;
+
+ BOOL bAdd=FALSE;
+ const TCHAR* jid=NULL;
+ const TCHAR* name=NULL;
+ const TCHAR* group=NULL;
+ const TCHAR* subscr=NULL;
+ HXML Cell = xmlGetNthChild( Row, _T("Cell"), 1 );
+ HXML Data = (Cell) ? xmlGetChild( Cell , "Data") : XmlNode();
+ if ( Data )
+ {
+ if (!lstrcmpi(xmlGetText( Data ),_T("+"))) bAdd=TRUE;
+ else if (lstrcmpi(xmlGetText( Data ),_T("-"))) continue;
+
+ Cell = xmlGetNthChild( Row, _T("Cell"),2);
+ if (Cell) Data=xmlGetChild( Cell , "Data");
+ else Data = NULL;
+ if (Data)
+ {
+ jid=xmlGetText( Data );
+ if (!jid || lstrlen(jid)==0) continue;
+ }
+
+ Cell=xmlGetNthChild( Row,_T("Cell"),3);
+ if (Cell) Data=xmlGetChild( Cell , "Data");
+ else Data = NULL;
+ if (Data) name=xmlGetText( Data );
+
+ Cell=xmlGetNthChild( Row,_T("Cell"),4);
+ if (Cell) Data=xmlGetChild( Cell , "Data");
+ else Data = NULL;
+ if (Data) group=xmlGetText( Data );
+
+ Cell=xmlGetNthChild( Row,_T("Cell"),5);
+ if (Cell) Data=xmlGetChild( Cell , "Data");
+ else Data = NULL;
+ if (Data) subscr=xmlGetText( Data );
+ }
+ _RosterInsertListItem(hList,jid,name,group,subscr,bAdd);
+ } } } } }
+
+ mir_free( newBuf );
+ SendMessage(hwndDlg, JM_STATUSCHANGED, 0, 0);
+}
+
+static BOOL CALLBACK _RosterNewListProc( HWND hList, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ if (msg==WM_MOUSEWHEEL || msg==WM_NCLBUTTONDOWN || msg==WM_NCRBUTTONDOWN)
+ {
+ SetFocus(hList);
+ }
+
+ if (msg==WM_LBUTTONDOWN)
+ {
+ POINT pt;
+ GetCursorPos(&pt);
+ ScreenToClient(hList, &pt);
+
+ LVHITTESTINFO lvhti={0};
+ lvhti.pt=pt;
+ ListView_SubItemHitTest(hList,&lvhti);
+ if (lvhti.flags&LVHT_ONITEM && lvhti.iSubItem !=0)
+ {
+ RECT rc;
+ TCHAR buff[260];
+ ListView_GetSubItemRect(hList, lvhti.iItem, lvhti.iSubItem, LVIR_BOUNDS,&rc);
+ ListView_GetItemText(hList, lvhti.iItem, lvhti.iSubItem, buff, SIZEOF(buff));
+ HWND hEditor=CreateWindow(TEXT("EDIT"),buff,WS_CHILD|ES_AUTOHSCROLL,rc.left+3, rc.top+2, rc.right-rc.left-3, rc.bottom - rc.top-3,hList, NULL, hInst, NULL);
+ SendMessage(hEditor,WM_SETFONT,(WPARAM)SendMessage(hList,WM_GETFONT,0,0),0);
+ ShowWindow(hEditor,SW_SHOW);
+ SetWindowText(hEditor, buff);
+ ClientToScreen(hList, &pt);
+ ScreenToClient(hEditor, &pt);
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+
+ ROSTEREDITDAT * edat=(ROSTEREDITDAT *)malloc(sizeof(ROSTEREDITDAT));
+ edat->OldEditProc=(WNDPROC)GetWindowLongPtr(hEditor, GWLP_WNDPROC);
+ SetWindowLongPtr(hEditor,GWLP_WNDPROC,(LONG_PTR)_RosterItemNewEditProc);
+ edat->hList=hList;
+ edat->index=lvhti.iItem;
+ edat->subindex=lvhti.iSubItem;
+ SetWindowLongPtr(hEditor,GWLP_USERDATA,(LONG_PTR)edat);
+ }
+ }
+ return CallWindowProc(_RosterOldListProc, hList, msg, wParam, lParam );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberRosterOptDlgProc - advanced options dialog procedure
+
+static int sttRosterEditorResizer(HWND /*hwndDlg*/, LPARAM, UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId)
+ {
+ case IDC_HEADERBAR:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORX_WIDTH;
+ case IDC_ROSTER:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORY_HEIGHT|RD_ANCHORX_WIDTH;
+ case IDC_DOWNLOAD:
+ case IDC_UPLOAD:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
+ case IDC_EXPORT:
+ case IDC_IMPORT:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+// case IDC_STATUSBAR:
+// return RD_ANCHORX_LEFT|RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP;
+}
+
+static INT_PTR CALLBACK JabberRosterOptDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = ( CJabberProto* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case JM_STATUSCHANGED:
+ {
+ int count = ListView_GetItemCount(GetDlgItem(hwndDlg,IDC_ROSTER));
+ EnableWindow( GetDlgItem( hwndDlg, IDC_DOWNLOAD ), ppro->m_bJabberOnline );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_UPLOAD ), count && ppro->m_bJabberOnline );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_EXPORT ), count > 0);
+ break;
+ }
+ case WM_CLOSE:
+ {
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ case WM_DESTROY:
+ {
+ Utils_SaveWindowPosition(hwndDlg, NULL, ppro->m_szModuleName, "rosterCtrlWnd_");
+ ppro->rrud.hwndDlg = NULL;
+ WindowFreeIcon(hwndDlg);
+ break;
+ }
+ case WM_INITDIALOG:
+ {
+ ppro = ( CJabberProto* )lParam;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+
+ TranslateDialogDefault( hwndDlg );
+ WindowSetIcon( hwndDlg, ppro, "Agents" );
+
+ Utils_RestoreWindowPosition(hwndDlg, NULL, ppro->m_szModuleName, "rosterCtrlWnd_");
+
+ ListView_SetExtendedListViewStyle(GetDlgItem(hwndDlg,IDC_ROSTER), LVS_EX_CHECKBOXES | LVS_EX_BORDERSELECT /*| LVS_EX_FULLROWSELECT*/ | LVS_EX_GRIDLINES /*| LVS_EX_HEADERDRAGDROP*/ );
+ _RosterOldListProc=(WNDPROC) GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_ROSTER), GWLP_WNDPROC);
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_ROSTER), GWLP_WNDPROC, (LONG_PTR) _RosterNewListProc);
+ _RosterListClear(hwndDlg);
+ ppro->rrud.hwndDlg = hwndDlg;
+ ppro->rrud.bReadyToDownload = TRUE;
+ ppro->rrud.bReadyToUpload = FALSE;
+ SendMessage( hwndDlg, JM_STATUSCHANGED, 0, 0 );
+
+ return TRUE;
+ }
+ case WM_GETMINMAXINFO:
+ {
+ LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
+ lpmmi->ptMinTrackSize.x = 550;
+ lpmmi->ptMinTrackSize.y = 390;
+ return 0;
+ }
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd = {0};
+ urd.cbSize = sizeof(urd);
+ urd.hInstance = hInst;
+ urd.hwndDlg = hwndDlg;
+ urd.lpTemplate = MAKEINTRESOURCEA(IDD_OPT_JABBER3);
+ urd.pfnResizer = sttRosterEditorResizer;
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd);
+ break;
+ }
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDC_DOWNLOAD:
+ ppro->rrud.bReadyToUpload = FALSE;
+ ppro->rrud.bReadyToDownload = FALSE;
+ SendMessage( ppro->rrud.hwndDlg, JM_STATUSCHANGED,0,0);
+ SetDlgItemText( ppro->rrud.hwndDlg, IDC_DOWNLOAD, TranslateT("Downloading..."));
+ ppro->_RosterSendRequest(hwndDlg, RRA_FILLLIST);
+ break;
+
+ case IDC_UPLOAD:
+ ppro->rrud.bReadyToUpload = FALSE;
+ SendMessage( ppro->rrud.hwndDlg, JM_STATUSCHANGED, 0, 0 );
+ SetDlgItemText( ppro->rrud.hwndDlg, IDC_UPLOAD, TranslateT("Connecting..."));
+ ppro->_RosterSendRequest( hwndDlg, RRA_SYNCROSTER );
+ break;
+
+ case IDC_EXPORT:
+ ppro->_RosterExportToFile( hwndDlg );
+ break;
+
+ case IDC_IMPORT:
+ ppro->_RosterImportFromFile( hwndDlg );
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleRosterControl( WPARAM, LPARAM )
+{
+ if ( rrud.hwndDlg && IsWindow( rrud.hwndDlg ))
+ SetForegroundWindow( rrud.hwndDlg );
+ else
+ CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_OPT_JABBER3 ), NULL, JabberRosterOptDlgProc, ( LPARAM )this );
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberOptInit - initializes all options dialogs
+
+int CJabberProto::OnOptionsInit( WPARAM wParam, LPARAM )
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof( odp );
+ odp.hInstance = hInst;
+ odp.ptszGroup = LPGENT("Network");
+ odp.ptszTitle = m_tszUserName;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR | ODPF_DONTTRANSLATE;
+
+ odp.ptszTab = LPGENT("Account");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_JABBER);
+ odp.pfnDlgProc = CDlgBase::DynamicDlgProc;
+ odp.dwInitParam = (LPARAM)&OptCreateAccount;
+ OptCreateAccount.create = CDlgOptAccount::Create;
+ OptCreateAccount.param = this;
+ Options_AddPage(wParam, &odp);
+
+ odp.ptszTab = LPGENT("Conferences");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_JABBER4);
+ odp.pfnDlgProc = CDlgBase::DynamicDlgProc;
+ odp.dwInitParam = (LPARAM)&OptCreateGc;
+ OptCreateGc.create = CDlgOptGc::Create;
+ OptCreateGc.param = this;
+ Options_AddPage(wParam, &odp);
+
+ odp.flags |= ODPF_EXPERTONLY;
+
+ odp.ptszTab = LPGENT("Advanced");
+ odp.pszTemplate = MAKEINTRESOURCEA( IDD_OPT_JABBER2 );
+ odp.pfnDlgProc = CDlgBase::DynamicDlgProc;
+ odp.dwInitParam = (LPARAM)&OptCreateAdvanced;
+ OptCreateAdvanced.create = CDlgOptAdvanced::Create;
+ OptCreateAdvanced.param = this;
+ Options_AddPage(wParam, &odp);
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Account manager UI
+
+class CJabberDlgAccMgrUI: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+ CCtrlCombo m_cbType;
+ CCtrlEditJid m_txtUsername;
+ CCtrlCombo m_cbServer;
+ CCtrlEdit m_txtPassword;
+ CCtrlCheck m_chkSavePassword;
+ CCtrlCheck m_chkUseDomainLogin;
+ CCtrlCombo m_cbResource;
+ CCtrlCheck m_chkManualHost;
+ CCtrlEdit m_txtManualHost;
+ CCtrlEdit m_txtPort;
+ CCtrlButton m_btnRegister;
+
+public:
+ CJabberDlgAccMgrUI(CJabberProto *proto, HWND hwndParent):
+ CJabberDlgBase(proto, IDD_ACCMGRUI, hwndParent, false),
+ m_cbType(this, IDC_CB_TYPE),
+ m_txtUsername(this, IDC_EDIT_USERNAME),
+ m_txtPassword(this, IDC_EDIT_PASSWORD),
+ m_chkUseDomainLogin(this, IDC_USEDOMAINLOGIN),
+ m_chkSavePassword(this, IDC_SAVEPASSWORD),
+ m_cbResource(this, IDC_COMBO_RESOURCE),
+ m_cbServer(this, IDC_EDIT_LOGIN_SERVER),
+ m_txtPort(this, IDC_PORT),
+ m_chkManualHost(this, IDC_MANUAL),
+ m_txtManualHost(this, IDC_HOST),
+ m_btnRegister(this, IDC_BUTTON_REGISTER)
+ {
+ CreateLink(m_txtUsername, "LoginName", _T(""));
+ CreateLink(m_chkSavePassword, proto->m_options.SavePassword);
+ CreateLink(m_cbResource, "Resource", _T("Miranda"));
+ CreateLink(m_cbServer, "LoginServer", _T("jabber.org"));
+ CreateLink(m_txtPort, "Port", DBVT_WORD, 5222);
+ CreateLink(m_chkUseDomainLogin, proto->m_options.UseDomainLogin);
+
+ // Bind events
+ m_cbType.OnChange = Callback(this, &CJabberDlgAccMgrUI::cbType_OnChange);
+ m_cbServer.OnDropdown = Callback(this, &CJabberDlgAccMgrUI::cbServer_OnDropdown);
+ m_chkManualHost.OnChange = Callback(this, &CJabberDlgAccMgrUI::chkManualHost_OnChange);
+ m_chkUseDomainLogin.OnChange = Callback(this, &CJabberDlgAccMgrUI::chkUseDomainLogin_OnChange);
+
+ m_btnRegister.OnClick = Callback(this, &CJabberDlgAccMgrUI::btnRegister_OnClick);
+ }
+
+protected:
+ enum { ACC_PUBLIC, ACC_TLS, ACC_SSL, ACC_GTALK, ACC_LJTALK, ACC_FBOOK, ACC_VK, ACC_SMS };
+
+ void OnInitDialog()
+ {
+ CSuper::OnInitDialog();
+
+ int i;
+ DBVARIANT dbv;
+ char server[256], manualServer[256]={0};
+
+ m_gotservers = false;
+
+ TCHAR *passw = m_proto->JGetStringCrypt(NULL, "LoginPassword");
+ if (passw)
+ {
+ m_txtPassword.SetText(passw);
+ mir_free(passw);
+ }
+
+ m_cbServer.AddString(TranslateT("Loading..."));
+
+ // fill predefined resources
+ TCHAR* szResources[] = { _T("Home"), _T("Work"), _T("Office"), _T("Miranda") };
+ for (i = 0; i < SIZEOF(szResources); ++i)
+ m_cbResource.AddString(szResources[i]);
+
+ // append computer name to the resource list
+ TCHAR szCompName[ MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD dwCompNameLength = MAX_COMPUTERNAME_LENGTH;
+ if (GetComputerName(szCompName, &dwCompNameLength))
+ m_cbResource.AddString(szCompName);
+
+ if (!DBGetContactSettingTString(NULL, m_proto->m_szModuleName, "Resource", &dbv))
+ {
+ if(CB_ERR == m_cbResource.FindString(dbv.ptszVal, -1, true))
+ m_cbResource.AddString(dbv.ptszVal);
+
+ m_cbResource.SetText(dbv.ptszVal);
+ JFreeVariant(&dbv);
+ } else
+ {
+ m_cbResource.SetText(_T("Miranda"));
+ }
+
+ m_cbType.AddString(TranslateT("Public XMPP Network"), ACC_PUBLIC);
+ m_cbType.AddString(TranslateT("Secure XMPP Network"), ACC_TLS);
+ m_cbType.AddString(TranslateT("Secure XMPP Network (old style)"), ACC_SSL);
+ m_cbType.AddString(TranslateT("Google Talk!"), ACC_GTALK);
+ m_cbType.AddString(TranslateT("LiveJournal Talk"), ACC_LJTALK);
+ m_cbType.AddString(TranslateT("Facebook Chat"), ACC_FBOOK);
+ m_cbType.AddString(TranslateT("Vkontakte"), ACC_VK);
+ m_cbType.AddString(TranslateT("S.ms"), ACC_SMS);
+
+ m_cbServer.GetTextA(server, SIZEOF(server));
+ if (!DBGetContactSettingString(NULL, m_proto->m_szModuleName, "ManualHost", &dbv))
+ {
+ lstrcpynA(manualServer, dbv.pszVal, SIZEOF(manualServer));
+ JFreeVariant(&dbv);
+ }
+
+ m_canregister = true;
+ if (!lstrcmpA(manualServer, "talk.google.com"))
+ {
+ m_cbType.SetCurSel(ACC_GTALK);
+ m_canregister = false;
+ }
+ else if (!lstrcmpA(server, "livejournal.com"))
+ {
+ m_cbType.SetCurSel(ACC_LJTALK);
+ m_canregister = false;
+ }
+ else if (!lstrcmpA(server, "chat.facebook.com"))
+ {
+ m_cbType.SetCurSel(ACC_FBOOK);
+ m_canregister = false;
+ }
+ else if (!lstrcmpA(server, "vk.com"))
+ {
+ m_cbType.SetCurSel(ACC_VK);
+ m_canregister = false;
+ }
+ else if (!lstrcmpA(server, "S.ms"))
+ {
+ m_cbType.SetCurSel(ACC_SMS);
+ m_canregister = false;
+ }
+ else if (m_proto->m_options.UseSSL)
+ m_cbType.SetCurSel(ACC_SSL);
+ else if (m_proto->m_options.UseTLS) {
+ m_cbType.SetCurSel(ACC_TLS);
+ m_txtPort.SetInt(5222);
+ }
+ else
+ m_cbType.SetCurSel(ACC_PUBLIC);
+ //cbType_OnChange(&m_cbType);
+
+ if (m_chkManualHost.Enabled())
+ {
+ if (m_proto->m_options.ManualConnect)
+ {
+ m_chkManualHost.SetState(BST_CHECKED);
+ m_txtManualHost.Enable();
+ m_txtPort.Enable();
+
+ if (!DBGetContactSettingTString(NULL, m_proto->m_szModuleName, "ManualHost", &dbv))
+ {
+ m_txtManualHost.SetText(dbv.ptszVal);
+ JFreeVariant(&dbv);
+ }
+
+ m_txtPort.SetInt(m_proto->JGetWord(NULL, "ManualPort", m_txtPort.GetInt()));
+ } else
+ {
+ int defPort = m_txtPort.GetInt();
+ int port = m_proto->JGetWord(NULL, "Port", defPort);
+
+ if (port != defPort)
+ {
+ m_chkManualHost.SetState(BST_CHECKED);
+ m_txtManualHost.Enable();
+ m_txtPort.Enable();
+
+ m_txtManualHost.SetTextA(server);
+ m_txtPort.SetInt(port);
+ } else
+ {
+ m_chkManualHost.SetState(BST_UNCHECKED);
+ m_txtManualHost.Disable();
+ m_txtPort.Disable();
+ }
+ }
+ }
+
+ if (m_proto->m_options.UseDomainLogin)
+ chkUseDomainLogin_OnChange(&m_chkUseDomainLogin);
+
+ CheckRegistration();
+ }
+
+ void OnApply()
+ {
+ // clear saved password
+ *m_proto->m_savedPassword = 0;
+
+ BOOL bUseHostnameAsResource = FALSE;
+ TCHAR szCompName[MAX_COMPUTERNAME_LENGTH + 1], szResource[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD dwCompNameLength = MAX_COMPUTERNAME_LENGTH;
+ if (GetComputerName(szCompName, &dwCompNameLength))
+ {
+ m_cbResource.GetText(szResource, SIZEOF(szResource));
+ if (!lstrcmp(szCompName, szResource))
+ bUseHostnameAsResource = TRUE;
+ }
+ m_proto->m_options.HostNameAsResource = bUseHostnameAsResource;
+
+ if (m_chkSavePassword.GetState() == BST_CHECKED)
+ {
+ TCHAR *text = m_txtPassword.GetText();
+ m_proto->JSetStringCrypt(NULL, "LoginPassword", text);
+ mir_free(text);
+ } else
+ {
+ m_proto->JDeleteSetting(NULL, "LoginPassword");
+ }
+
+ switch (m_cbType.GetItemData(m_cbType.GetCurSel()))
+ {
+ case ACC_FBOOK:
+ m_proto->m_options.IgnoreRosterGroups = TRUE;
+
+ case ACC_VK:
+ case ACC_PUBLIC:
+ m_proto->m_options.UseSSL = m_proto->m_options.UseTLS = FALSE;
+ break;
+
+ case ACC_GTALK:
+ m_proto->JSetWord(NULL, "Priority", 24);
+ {
+ int port = m_txtPort.GetInt();
+ if (port == 443 || port == 5223)
+ {
+ m_proto->m_options.UseSSL = TRUE;
+ m_proto->m_options.UseTLS = FALSE;
+ }
+ else if (port == 5222)
+ {
+ m_proto->m_options.UseSSL = FALSE;
+ m_proto->m_options.UseTLS = TRUE;
+ }
+ }
+ break;
+
+ case ACC_TLS:
+ case ACC_LJTALK:
+ case ACC_SMS:
+ m_proto->m_options.UseSSL = FALSE;
+ m_proto->m_options.UseTLS = TRUE;
+ break;
+
+ case ACC_SSL:
+ m_proto->m_options.UseSSL = TRUE;
+ m_proto->m_options.UseTLS = FALSE;
+ break;
+ }
+
+ char server[256];
+ char manualServer[256];
+
+ m_cbServer.GetTextA(server, SIZEOF(server));
+ m_txtManualHost.GetTextA(manualServer, SIZEOF(manualServer));
+
+ if ((m_chkManualHost.GetState() == BST_CHECKED) && lstrcmpA(server, manualServer))
+ {
+ m_proto->m_options.ManualConnect = TRUE;
+ m_proto->JSetString(NULL, "ManualHost", manualServer);
+ m_proto->JSetWord(NULL, "ManualPort", m_txtPort.GetInt());
+ m_proto->JSetWord(NULL, "Port", m_txtPort.GetInt());
+ } else
+ {
+ m_proto->m_options.ManualConnect = FALSE;
+ m_proto->JDeleteSetting(NULL, "ManualHost");
+ m_proto->JDeleteSetting(NULL, "ManualPort");
+ m_proto->JSetWord(NULL, "Port", m_txtPort.GetInt());
+ }
+
+ sttStoreJidFromUI(m_proto, m_txtUsername, m_cbServer);
+
+ if (m_proto->m_bJabberOnline)
+ {
+ if (m_cbType.IsChanged() || m_txtPassword.IsChanged() || m_cbResource.IsChanged() ||
+ m_cbServer.IsChanged() || m_txtPort.IsChanged() || m_txtManualHost.IsChanged())
+ {
+ MessageBox(m_hwnd,
+ TranslateT("Some changes will take effect the next time you connect to the Jabber network."),
+ TranslateT("Jabber Protocol Option"), MB_OK|MB_SETFOREGROUND);
+ }
+
+ m_proto->SendPresence(m_proto->m_iStatus, true);
+ }
+ }
+
+ void OnChange(CCtrlBase*)
+ {
+ if (m_initialized)
+ CheckRegistration();
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (msg) {
+ case WM_JABBER_REFRESH:
+ RefreshServers(( HXML )lParam);
+ break;
+ }
+ return CSuper::DlgProc(msg, wParam, lParam);
+ }
+
+private:
+ bool m_gotservers;
+ bool m_canregister;
+
+ void btnRegister_OnClick(CCtrlButton *)
+ {
+ TCHAR buf[512] = _T(""), pass[512];
+ if (!m_proto->EnterString(buf, SIZEOF(buf), TranslateT("Confirm password"), JES_PASSWORD))
+ return;
+
+ m_txtPassword.GetText(pass, SIZEOF(pass));
+ if (lstrcmp(buf, pass))
+ {
+ MessageBox(m_hwnd, TranslateT("Passwords do not match."), _T("Miranda NG"), MB_ICONSTOP|MB_OK);
+ return;
+ }
+
+ ThreadData regInfo(m_proto, JABBER_SESSION_NORMAL);
+ m_txtUsername.GetText(regInfo.username, SIZEOF(regInfo.username));
+ m_txtPassword.GetText(regInfo.password, SIZEOF(regInfo.password));
+ m_cbServer.GetTextA(regInfo.server, SIZEOF(regInfo.server));
+ regInfo.port = (WORD)m_txtPort.GetInt();
+ if (m_chkManualHost.GetState() == BST_CHECKED)
+ {
+ m_txtManualHost.GetTextA(regInfo.manualHost, SIZEOF(regInfo.manualHost));
+ } else
+ {
+ regInfo.manualHost[0] = '\0';
+ }
+
+ if (regInfo.username[0] && regInfo.password[0] && regInfo.server[0] && regInfo.port>0 && ( (m_chkManualHost.GetState() != BST_CHECKED) || regInfo.manualHost[0] ))
+ {
+ CJabberDlgRegister dlg(m_proto, m_hwnd, ®Info);
+ dlg.DoModal();
+// DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_OPT_REGISTER), m_hwnd, JabberRegisterDlgProc, (LPARAM)®Info);
+ }
+ }
+
+ void cbServer_OnDropdown(CCtrlCombo* )
+ {
+ if ( !m_gotservers )
+ mir_forkthread(QueryServerListThread, this);
+ }
+
+ void cbType_OnChange(CCtrlData *sender)
+ {
+ CCtrlCombo *chk = (CCtrlCombo *)sender;
+ setupConnection(chk->GetItemData(chk->GetCurSel()));
+ CheckRegistration();
+ }
+
+ void chkUseDomainLogin_OnChange(CCtrlData *sender)
+ {
+ CCtrlCheck *chk = (CCtrlCheck *)sender;
+ BOOL checked = chk->GetState() == BST_CHECKED;
+
+ m_txtPassword.Enable(!checked);
+ m_txtUsername.Enable(!checked);
+ m_chkSavePassword.Enable(!checked);
+ if (checked) {
+ m_txtPassword.SetText(_T(""));
+ m_txtUsername.SetText(_T(""));
+ m_chkSavePassword.SetState(BST_CHECKED);
+ }
+ }
+
+ void chkManualHost_OnChange(CCtrlData *sender)
+ {
+ CCtrlCheck *chk = (CCtrlCheck *)sender;
+
+ if (chk->GetState() == BST_CHECKED)
+ {
+ char buf[256];
+ m_cbServer.GetTextA(buf, SIZEOF(buf));
+ m_txtManualHost.SetTextA(buf);
+ m_txtPort.SetInt(5222);
+
+ m_txtManualHost.Enable();
+ m_txtPort.Enable();
+ } else
+ {
+ m_txtManualHost.Disable();
+ m_txtPort.Disable();
+ }
+ }
+
+ void CheckRegistration();
+ void setupConnection(int type);
+ void setupPublic();
+ void setupSecure();
+ void setupSecureSSL();
+ void setupGoogle();
+ void setupLJ();
+ void setupFB();
+ void setupVK();
+ void setupSMS();
+ void RefreshServers( HXML node);
+ static void QueryServerListThread(void *arg);
+};
+
+void CJabberDlgAccMgrUI::CheckRegistration()
+{
+ if ( !m_canregister ) {
+ m_btnRegister.Disable();
+ return;
+ }
+
+ ThreadData regInfo(m_proto, JABBER_SESSION_NORMAL);
+ m_txtUsername.GetText(regInfo.username, SIZEOF(regInfo.username));
+ m_txtPassword.GetText(regInfo.password, SIZEOF(regInfo.password));
+ m_cbServer.GetTextA(regInfo.server, SIZEOF(regInfo.server));
+ regInfo.port = (WORD)m_txtPort.GetInt();
+ if (m_chkManualHost.GetState() == BST_CHECKED)
+ m_txtManualHost.GetTextA(regInfo.manualHost, SIZEOF(regInfo.manualHost));
+ else
+ regInfo.manualHost[0] = '\0';
+
+ if (regInfo.username[0] && regInfo.password[0] && regInfo.server[0] && regInfo.port > 0 && ( (m_chkManualHost.GetState() != BST_CHECKED) || regInfo.manualHost[0] ))
+ m_btnRegister.Enable();
+ else
+ m_btnRegister.Disable();
+}
+
+void CJabberDlgAccMgrUI::setupConnection(int type)
+{
+ switch (type) {
+ case ACC_PUBLIC: setupPublic(); break;
+ case ACC_TLS: setupSecure(); break;
+ case ACC_SSL: setupSecureSSL(); break;
+ case ACC_GTALK: setupGoogle(); break;
+ case ACC_LJTALK: setupLJ(); break;
+ case ACC_FBOOK: setupFB(); break;
+ case ACC_VK: setupVK(); break;
+ case ACC_SMS: setupSMS(); break;
+ }
+}
+
+void CJabberDlgAccMgrUI::setupPublic()
+{
+ m_canregister = true;
+ m_gotservers = false;
+ m_chkManualHost.SetState(BST_UNCHECKED);
+ m_txtManualHost.SetTextA("");
+ m_txtPort.SetInt(5222);
+
+ m_cbServer.Enable();
+ m_chkManualHost.Enable();
+ m_txtManualHost.Disable();
+ m_txtPort.Disable();
+ m_btnRegister.Enable();
+}
+
+void CJabberDlgAccMgrUI::setupSecure()
+{
+ m_canregister = true;
+ m_gotservers = false;
+ m_chkManualHost.SetState(BST_UNCHECKED);
+ m_txtManualHost.SetTextA("");
+ m_txtPort.SetInt(5222);
+
+ m_cbServer.Enable();
+ m_chkManualHost.Enable();
+ m_txtManualHost.Disable();
+ m_txtPort.Disable();
+ m_btnRegister.Enable();
+}
+
+void CJabberDlgAccMgrUI::setupSecureSSL()
+{
+ m_canregister = true;
+ m_gotservers = false;
+ m_chkManualHost.SetState(BST_UNCHECKED);
+ m_txtManualHost.SetTextA("");
+ m_txtPort.SetInt(5223);
+
+ m_cbServer.Enable();
+ m_chkManualHost.Enable();
+ m_txtManualHost.Disable();
+ m_txtPort.Disable();
+ m_btnRegister.Enable();
+}
+
+void CJabberDlgAccMgrUI::setupGoogle()
+{
+ m_canregister = false;
+ m_gotservers = true;
+ m_cbServer.ResetContent();
+ m_cbServer.AddStringA("gmail.com");
+ m_cbServer.AddStringA("googlemail.com");
+ m_cbServer.SetTextA("gmail.com");
+ m_chkManualHost.SetState(BST_CHECKED);
+ m_txtManualHost.SetTextA("talk.google.com");
+ m_txtPort.SetInt(443);
+
+ m_cbServer.Enable();
+ m_chkManualHost.Disable();
+ m_txtManualHost.Disable();
+ //m_txtPort.Disable();
+ m_btnRegister.Disable();
+}
+
+void CJabberDlgAccMgrUI::setupLJ()
+{
+ m_canregister = false;
+ m_gotservers = true;
+ m_cbServer.ResetContent();
+ m_cbServer.SetTextA("livejournal.com");
+ m_cbServer.AddStringA("livejournal.com");
+ m_chkManualHost.SetState(BST_UNCHECKED);
+ m_txtManualHost.SetTextA("");
+ m_txtPort.SetInt(5222);
+
+ m_cbServer.Disable();
+ m_chkManualHost.Disable();
+ m_txtManualHost.Disable();
+ m_txtPort.Disable();
+ m_btnRegister.Disable();
+}
+
+void CJabberDlgAccMgrUI::setupFB()
+{
+ m_canregister = false;
+ m_gotservers = true;
+ m_cbServer.ResetContent();
+ m_cbServer.SetTextA("chat.facebook.com");
+ m_cbServer.AddStringA("chat.facebook.com");
+ m_chkManualHost.SetState(BST_UNCHECKED);
+ m_txtManualHost.SetTextA("");
+ m_txtPort.SetInt(443);
+
+ m_cbServer.Disable();
+ m_chkManualHost.Disable();
+ m_txtManualHost.Disable();
+ m_txtPort.Disable();
+ m_btnRegister.Disable();
+}
+
+void CJabberDlgAccMgrUI::setupVK()
+{
+ m_canregister = false;
+ m_gotservers = true;
+ m_cbServer.ResetContent();
+ m_cbServer.SetTextA("VK.com");
+ m_cbServer.AddStringA("VK.com");
+ m_chkManualHost.SetState(BST_UNCHECKED);
+ m_txtManualHost.SetTextA("");
+ m_txtPort.SetInt(5222);
+
+ m_cbServer.Disable();
+ m_chkManualHost.Disable();
+ m_txtManualHost.Disable();
+ m_txtPort.Disable();
+ m_btnRegister.Disable();
+}
+
+void CJabberDlgAccMgrUI::setupSMS()
+{
+ m_canregister = false;
+ m_gotservers = true;
+ m_cbServer.ResetContent();
+ m_cbServer.SetTextA("S.ms");
+ m_cbServer.AddStringA("S.ms");
+ m_chkManualHost.SetState(BST_UNCHECKED);
+ m_txtManualHost.SetTextA("");
+ m_txtPort.SetInt(5222);
+
+ m_cbServer.Disable();
+ m_chkManualHost.Disable();
+ m_txtManualHost.Disable();
+ m_txtPort.Disable();
+ m_btnRegister.Disable();
+ // m_cbResource.Disable();
+}
+
+void CJabberDlgAccMgrUI::RefreshServers( HXML node )
+{
+ m_gotservers = node != NULL;
+
+ TCHAR *server = m_cbServer.GetText();
+ bool bDropdown = m_cbServer.GetDroppedState();
+ if (bDropdown) m_cbServer.ShowDropdown(false);
+
+ m_cbServer.ResetContent();
+ if ( node )
+ {
+ for (int i = 0; ; ++i) {
+ HXML n = xmlGetChild(node, i);
+ if ( !n )
+ break;
+
+ if ( !lstrcmp( xmlGetName( n ), _T("item")))
+ if ( const TCHAR *jid = xmlGetAttrValue( n, _T("jid")))
+ if (m_cbServer.FindString(jid, -1, true) == CB_ERR)
+ m_cbServer.AddString(jid);
+ }
+ }
+
+ m_cbServer.SetText(server);
+
+ if (bDropdown) m_cbServer.ShowDropdown();
+ mir_free(server);
+}
+
+void CJabberDlgAccMgrUI::QueryServerListThread(void *arg)
+{
+ CDlgOptAccount *wnd = (CDlgOptAccount *)arg;
+ HWND hwnd = wnd->GetHwnd();
+ bool bIsError = true;
+
+ NETLIBHTTPREQUEST request = {0};
+ request.cbSize = sizeof(request);
+ request.requestType = REQUEST_GET;
+ request.flags = NLHRF_GENERATEHOST|NLHRF_SMARTREMOVEHOST|NLHRF_SMARTAUTHHEADER|NLHRF_HTTP11;
+ request.szUrl = "http://xmpp.org/services/services.xml";
+
+ NETLIBHTTPREQUEST *result = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)wnd->GetProto()->m_hNetlibUser, (LPARAM)&request);
+ if ( result && IsWindow( hwnd )) {
+ if ((result->resultCode == 200) && result->dataLength && result->pData) {
+ TCHAR* ptszText = mir_a2t( result->pData );
+ XmlNode node( ptszText, NULL, NULL );
+ if ( node ) {
+ HXML queryNode = xmlGetChild( node, _T("query"));
+ if ( queryNode && IsWindow(hwnd)) {
+ SendMessage(hwnd, WM_JABBER_REFRESH, 0, (LPARAM)queryNode);
+ bIsError = false;
+ } }
+ mir_free( ptszText );
+ } }
+
+ if ( result )
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)result);
+ if ( bIsError )
+ SendMessage(hwnd, WM_JABBER_REFRESH, 0, (LPARAM)NULL);
+}
+
+INT_PTR CJabberProto::SvcCreateAccMgrUI(WPARAM, LPARAM lParam)
+{
+ CJabberDlgAccMgrUI *dlg = new CJabberDlgAccMgrUI(this, (HWND)lParam);
+ dlg->Show();
+ return (INT_PTR)dlg->GetHwnd();
+}
+
+void CJabberProto::JabberUpdateDialogs( BOOL )
+{
+ if ( rrud.hwndDlg )
+ SendMessage(rrud.hwndDlg, JM_STATUSCHANGED, 0,0);
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuOptions( WPARAM, LPARAM )
+{
+ OPENOPTIONSDIALOG ood = {0};
+ ood.cbSize = sizeof(ood);
+ ood.pszGroup = "Network";
+ ood.pszPage = mir_t2a(m_tszUserName);
+ ood.pszTab = "Account";
+ Options_Open(&ood);
+
+ mir_free((void *)ood.pszPage);
+ return 0;
+}
+
+int CJabberProto::OnModernOptInit( WPARAM, LPARAM )
+{/*
+ static int iBoldControls[] =
+ {
+ IDC_TITLE1, MODERNOPT_CTRL_LAST
+ };
+
+ MODERNOPTOBJECT obj = {0};
+ obj.cbSize = sizeof(obj);
+ obj.dwFlags = MODEROPT_FLG_TCHAR;
+ obj.hIcon = LoadIconEx("main");
+ obj.hInstance = hInst;
+ obj.iSection = MODERNOPT_PAGE_ACCOUNTS;
+ obj.iType = MODERNOPT_TYPE_SUBSECTIONPAGE;
+ obj.lptzSubsection = mir_a2t(m_szModuleName); // title!!!!!!!!!!!
+ obj.lpzTemplate = MAKEINTRESOURCEA(IDD_MODERNOPT);
+ obj.iBoldControls = iBoldControls;
+ obj.pfnDlgProc = JabberWizardDlgProc;
+ obj.lpszClassicGroup = "Network";
+ obj.lpszClassicPage = m_szModuleName; // title!!!!!!!!!!!
+ obj.lpszHelpUrl = "http://forums.miranda-im.org/showthread.php?t=14294";
+ CallService(MS_MODERNOPT_ADDOBJECT, wParam, (LPARAM)&obj);
+ mir_free(obj.lptzSubsection); */
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber_opttree.cpp b/protocols/JabberG/src/jabber_opttree.cpp new file mode 100644 index 0000000000..bd43b55a08 --- /dev/null +++ b/protocols/JabberG/src/jabber_opttree.cpp @@ -0,0 +1,263 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2007 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_opttree.h"
+
+enum { IMG_GROUP, IMG_CHECK, IMG_NOCHECK, IMG_RCHECK, IMG_NORCHECK, IMG_GRPOPEN, IMG_GRPCLOSED };
+
+CCtrlTreeOpts::CCtrlTreeOpts(CDlgBase* dlg, int ctrlId):
+ CCtrlTreeView(dlg, ctrlId),
+ m_options(5)
+{
+}
+
+CCtrlTreeOpts::~CCtrlTreeOpts()
+{
+ for (int i = 0; i < m_options.getCount(); ++i)
+ delete m_options[i];
+ m_options.destroy();
+}
+
+void CCtrlTreeOpts::AddOption(TCHAR *szOption, CMOption<BYTE> &option)
+{
+ m_options.insert(new COptionsItem(szOption, option), m_options.getCount());
+}
+
+BOOL CCtrlTreeOpts::OnNotify(int idCtrl, NMHDR *pnmh)
+{
+ switch (pnmh->code)
+ {
+ case TVN_KEYDOWN:
+ {
+ LPNMTVKEYDOWN lpnmtvkd = (LPNMTVKEYDOWN)pnmh;
+ HTREEITEM hti;
+ if ((lpnmtvkd->wVKey == VK_SPACE) && (hti = GetSelection()))
+ ProcessItemClick(hti);
+ break;
+ }
+
+ case NM_CLICK:
+ {
+ TVHITTESTINFO hti;
+ hti.pt.x=(short)LOWORD(GetMessagePos());
+ hti.pt.y=(short)HIWORD(GetMessagePos());
+ ScreenToClient(pnmh->hwndFrom,&hti.pt);
+ if(HitTest(&hti))
+ if(hti.flags&TVHT_ONITEMICON)
+ ProcessItemClick(hti.hItem);
+ break;
+ }
+
+ case TVN_ITEMEXPANDEDW:
+ {
+ LPNMTREEVIEWW lpnmtv = (LPNMTREEVIEWW)pnmh;
+ TVITEM tvi;
+ tvi.mask=TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvi.hItem = lpnmtv->itemNew.hItem;
+ tvi.iImage = tvi.iSelectedImage =
+ (lpnmtv->itemNew.state & TVIS_EXPANDED) ? IMG_GRPOPEN : IMG_GRPCLOSED;
+ SendMessageW(pnmh->hwndFrom, TVM_SETITEMW, 0, (LPARAM)&tvi);
+ break;
+ }
+
+ case TVN_ITEMEXPANDEDA:
+ {
+ LPNMTREEVIEWA lpnmtv = (LPNMTREEVIEWA)pnmh;
+ TVITEM tvi;
+ tvi.mask=TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvi.hItem = lpnmtv->itemNew.hItem;
+ tvi.iImage = tvi.iSelectedImage =
+ (lpnmtv->itemNew.state & TVIS_EXPANDED) ? IMG_GRPOPEN : IMG_GRPCLOSED;
+ SendMessageA(pnmh->hwndFrom, TVM_SETITEMA, 0, (LPARAM)&tvi);
+ break;
+ }
+ }
+
+ return CCtrlTreeView::OnNotify(idCtrl, pnmh);
+}
+
+void CCtrlTreeOpts::OnInit()
+{
+ CCtrlTreeView::OnInit();
+
+ TCHAR itemName[1024];
+ HIMAGELIST hImgLst;
+
+ SelectItem(NULL);
+ DeleteAllItems();
+
+ hImgLst = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR|ILC_COLOR32|ILC_MASK, 5, 1);
+ ImageList_AddIcon_Icolib(hImgLst, LoadSkinnedIcon(SKINICON_OTHER_MIRANDA));
+ ImageList_AddIcon_Icolib(hImgLst, LoadSkinnedIcon(SKINICON_OTHER_TICK)); // check on
+ ImageList_AddIcon_Icolib(hImgLst, LoadSkinnedIcon(SKINICON_OTHER_NOTICK)); // check off
+ ImageList_AddIcon_Icolib(hImgLst, LoadSkinnedIcon(SKINICON_OTHER_TICK)); // radio on
+ ImageList_AddIcon_Icolib(hImgLst, LoadSkinnedIcon(SKINICON_OTHER_NOTICK)); // radio on
+ ImageList_AddIcon_Icolib(hImgLst, LoadSkinnedIcon(SKINICON_OTHER_GROUPOPEN));
+ ImageList_AddIcon_Icolib(hImgLst, LoadSkinnedIcon(SKINICON_OTHER_GROUPSHUT));
+ SetImageList(hImgLst, TVSIL_NORMAL);
+
+ /* build options tree. based on code from IcoLib */
+ for (int i = 0; i < m_options.getCount(); i++)
+ {
+ TCHAR* sectionName;
+ int sectionLevel = 0;
+
+ HTREEITEM hSection = NULL;
+ lstrcpy(itemName, m_options[i]->m_szOptionName);
+ sectionName = itemName;
+
+ while (sectionName)
+ {
+ // allow multi-level tree
+ TCHAR* pItemName = sectionName;
+ HTREEITEM hItem;
+
+ if (sectionName = _tcschr(sectionName, '/'))
+ {
+ // one level deeper
+ *sectionName = 0;
+ sectionName++;
+ }
+
+ hItem = FindNamedItem(hSection, pItemName);
+ if (!sectionName || !hItem)
+ {
+ if (!hItem)
+ {
+ TVINSERTSTRUCT tvis = {0};
+
+ tvis.hParent = hSection;
+ tvis.hInsertAfter = TVI_LAST;//TVI_SORT;
+ tvis.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = pItemName;
+ tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED;
+ if (sectionName)
+ {
+ tvis.item.lParam = -1;
+ tvis.item.state |= TVIS_BOLD;
+ tvis.item.stateMask |= TVIS_BOLD;
+ tvis.item.iImage = tvis.item.iSelectedImage = IMG_GRPOPEN;
+ } else
+ {
+ tvis.item.lParam = i;
+
+ BYTE val = *m_options[i]->m_option;
+
+ if (m_options[i]->m_groupId == OPTTREE_CHECK)
+ {
+ tvis.item.iImage = tvis.item.iSelectedImage = val ? IMG_CHECK : IMG_NOCHECK;
+ } else
+ {
+ tvis.item.iImage = tvis.item.iSelectedImage = val ? IMG_RCHECK : IMG_NORCHECK;
+ }
+ }
+ hItem = InsertItem(&tvis);
+ if (!sectionName)
+ m_options[i]->m_hItem = hItem;
+ }
+ }
+ sectionLevel++;
+ hSection = hItem;
+ }
+ }
+
+ TranslateTree();
+ ShowWindow(m_hwnd, SW_SHOW);
+ SelectItem(FindNamedItem(0, NULL));
+}
+
+void CCtrlTreeOpts::OnDestroy()
+{
+ ImageList_Destroy(GetImageList(TVSIL_NORMAL));
+}
+
+void CCtrlTreeOpts::OnApply()
+{
+ CCtrlTreeView::OnApply();
+
+ for (int i = 0; i < m_options.getCount(); ++i)
+ {
+ TVITEMEX tvi;
+ GetItem(m_options[i]->m_hItem, &tvi);
+ *m_options[i]->m_option = ((tvi.iImage == IMG_CHECK) || (tvi.iImage == IMG_RCHECK)) ? 1 : 0;
+ }
+}
+
+void CCtrlTreeOpts::ProcessItemClick(HTREEITEM hti)
+{
+ TVITEMEX tvi;
+ GetItem(hti, &tvi);
+ switch (tvi.iImage)
+ {
+ case IMG_GRPOPEN:
+ tvi.iImage = tvi.iSelectedImage = IMG_GRPCLOSED;
+ Expand(tvi.hItem, TVE_COLLAPSE);
+ break;
+ case IMG_GRPCLOSED:
+ tvi.iImage = tvi.iSelectedImage = IMG_GRPOPEN;
+ Expand(tvi.hItem, TVE_EXPAND);
+ break;
+
+ case IMG_CHECK:
+ tvi.iImage = tvi.iSelectedImage = IMG_NOCHECK;
+ SendMessage(::GetParent(::GetParent(m_hwnd)), PSM_CHANGED, 0, 0);
+ break;
+ case IMG_NOCHECK:
+ tvi.iImage = tvi.iSelectedImage = IMG_CHECK;
+ SendMessage(::GetParent(::GetParent(m_hwnd)), PSM_CHANGED, 0, 0);
+ break;
+ case IMG_NORCHECK:
+ {
+ int i;
+ for (i = 0; i < m_options.getCount(); ++i)
+ {
+ if (m_options[i]->m_groupId == m_options[tvi.lParam]->m_groupId)
+ {
+ TVITEMEX tvi_tmp;
+ tvi_tmp.mask = TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvi_tmp.hItem = m_options[i]->m_hItem;
+ tvi_tmp.iImage = tvi_tmp.iSelectedImage = IMG_NORCHECK;
+ SetItem(&tvi_tmp);
+ }
+ }
+ tvi.iImage = tvi.iSelectedImage = IMG_RCHECK;
+ SendMessage(::GetParent(::GetParent(m_hwnd)), PSM_CHANGED, 0, 0);
+ break;
+ }
+ }
+
+ SetItem(&tvi);
+}
+
+CCtrlTreeOpts::COptionsItem::COptionsItem(TCHAR *szOption, CMOption<BYTE> &option):
+ m_option(&option), m_groupId(OPTTREE_CHECK), m_hItem(NULL)
+{
+ m_szOptionName = mir_tstrdup(szOption);
+}
+
+CCtrlTreeOpts::COptionsItem::~COptionsItem()
+{
+ mir_free(m_szOptionName);
+}
diff --git a/protocols/JabberG/src/jabber_opttree.h b/protocols/JabberG/src/jabber_opttree.h new file mode 100644 index 0000000000..3246ad74a3 --- /dev/null +++ b/protocols/JabberG/src/jabber_opttree.h @@ -0,0 +1,64 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2007 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jabber_opttree_h__
+#define __jabber_opttree_h__
+
+#define OPTTREE_CHECK 0
+
+class CCtrlTreeOpts : public CCtrlTreeView
+{
+ typedef CCtrlTreeView CSuper;
+
+public:
+ CCtrlTreeOpts( CDlgBase* dlg, int ctrlId );
+ ~CCtrlTreeOpts();
+
+ void AddOption(TCHAR *szOption, CMOption<BYTE> &option);
+
+ BOOL OnNotify(int idCtrl, NMHDR *pnmh);
+ void OnDestroy();
+ void OnInit();
+ void OnApply();
+
+protected:
+ struct COptionsItem
+ {
+ TCHAR *m_szOptionName;
+ int m_groupId;
+
+ CMOption<BYTE> *m_option;
+
+ HTREEITEM m_hItem;
+
+ COptionsItem(TCHAR *szOption, CMOption<BYTE> &option);
+ ~COptionsItem();
+ };
+
+ LIST<COptionsItem> m_options;
+
+ void ProcessItemClick(HTREEITEM hti);
+};
+
+#endif // __opttree_h__
diff --git a/protocols/JabberG/src/jabber_password.cpp b/protocols/JabberG/src/jabber_password.cpp new file mode 100644 index 0000000000..2ce5128b78 --- /dev/null +++ b/protocols/JabberG/src/jabber_password.cpp @@ -0,0 +1,99 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+static INT_PTR CALLBACK JabberChangePasswordDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam );
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleChangePassword( WPARAM, LPARAM )
+{
+ if ( IsWindow( m_hwndJabberChangePassword ))
+ SetForegroundWindow( m_hwndJabberChangePassword );
+ else
+ m_hwndJabberChangePassword = CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_CHANGEPASSWORD ), NULL, JabberChangePasswordDlgProc, ( LPARAM )this );
+
+ return 0;
+}
+
+static INT_PTR CALLBACK JabberChangePasswordDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ CJabberProto* ppro = (CJabberProto*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ ppro = (CJabberProto*)lParam;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, ( LONG_PTR )lParam );
+
+ WindowSetIcon( hwndDlg, ppro, "key" );
+ TranslateDialogDefault( hwndDlg );
+ if ( ppro->m_bJabberOnline && ppro->m_ThreadInfo!=NULL ) {
+ TCHAR text[1024];
+ mir_sntprintf( text, SIZEOF( text ), _T("%s %s@") _T(TCHAR_STR_PARAM), TranslateT( "Set New Password for" ), ppro->m_ThreadInfo->username, ppro->m_ThreadInfo->server );
+ SetWindowText( hwndDlg, text );
+ }
+ return TRUE;
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDOK:
+ if ( ppro->m_bJabberOnline && ppro->m_ThreadInfo!=NULL ) {
+ TCHAR newPasswd[512], text[512];
+ GetDlgItemText( hwndDlg, IDC_NEWPASSWD, newPasswd, SIZEOF( newPasswd ));
+ GetDlgItemText( hwndDlg, IDC_NEWPASSWD2, text, SIZEOF( text ));
+ if ( _tcscmp( newPasswd, text )) {
+ MessageBox( hwndDlg, TranslateT( "New password does not match." ), TranslateT( "Change Password" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND );
+ break;
+ }
+ GetDlgItemText( hwndDlg, IDC_OLDPASSWD, text, SIZEOF( text ));
+ if ( _tcscmp( text, ppro->m_ThreadInfo->password )) {
+ MessageBox( hwndDlg, TranslateT( "Current password is incorrect." ), TranslateT( "Change Password" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND );
+ break;
+ }
+ _tcsncpy( ppro->m_ThreadInfo->newPassword, newPasswd, SIZEOF( ppro->m_ThreadInfo->newPassword ));
+
+ int iqId = ppro->SerialNext();
+ ppro->IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultSetPassword );
+
+ XmlNodeIq iq( _T("set"), iqId, _A2T(ppro->m_ThreadInfo->server));
+ HXML q = iq << XQUERY( _T(JABBER_FEAT_REGISTER));
+ q << XCHILD( _T("username"), ppro->m_ThreadInfo->username );
+ q << XCHILD( _T("password"), newPasswd );
+ ppro->m_ThreadInfo->send( iq );
+ }
+ DestroyWindow( hwndDlg );
+ break;
+ case IDCANCEL:
+ DestroyWindow( hwndDlg );
+ break;
+ }
+ break;
+ case WM_CLOSE:
+ DestroyWindow( hwndDlg );
+ break;
+ case WM_DESTROY:
+ ppro->m_hwndJabberChangePassword = NULL;
+ WindowFreeIcon( hwndDlg );
+ break;
+ }
+
+ return FALSE;
+}
diff --git a/protocols/JabberG/src/jabber_presence_manager.cpp b/protocols/JabberG/src/jabber_presence_manager.cpp new file mode 100644 index 0000000000..8a8ece811d --- /dev/null +++ b/protocols/JabberG/src/jabber_presence_manager.cpp @@ -0,0 +1,51 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-08 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2008-09 Dmitriy Chervov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_presence_manager.h"
+
+BOOL CJabberPresenceManager::FillPermanentHandlers()
+{
+ return TRUE;
+}
+
+BOOL CJabberPresenceManager::HandlePresencePermanent(HXML node, ThreadData *pThreadData)
+{
+ BOOL bStopHandling = FALSE;
+ Lock();
+ CJabberPresencePermanentInfo *pInfo = m_pPermanentHandlers;
+ while ( pInfo && !bStopHandling ) {
+ CJabberPresenceInfo presenceInfo;
+ presenceInfo.m_pUserData = pInfo->m_pUserData;
+
+ if ((ppro->*(pInfo->m_pHandler))(node, pThreadData, &presenceInfo)) {
+ bStopHandling = TRUE;
+ break;
+ }
+ pInfo = pInfo->m_pNext;
+ }
+ Unlock();
+
+ return bStopHandling;
+}
diff --git a/protocols/JabberG/src/jabber_presence_manager.h b/protocols/JabberG/src/jabber_presence_manager.h new file mode 100644 index 0000000000..be138d3b93 --- /dev/null +++ b/protocols/JabberG/src/jabber_presence_manager.h @@ -0,0 +1,195 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-08 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2008-09 Dmitriy Chervov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_PRESENCE_MANAGER_H_
+#define _JABBER_PRESENCE_MANAGER_H_
+
+#include "jabber_xml.h"
+
+struct CJabberProto;
+typedef void ( CJabberProto::*JABBER_PRESENCE_PFUNC )( HXML node, void *usedata );
+typedef void ( *PRESENCE_USER_DATA_FREE_FUNC )( void *pUserData );
+
+class CJabberPresenceInfo;
+
+typedef BOOL ( CJabberProto::*JABBER_PRESENCE_HANDLER )( HXML node, ThreadData *pThreadData, CJabberPresenceInfo* pInfo );
+
+class CJabberPresenceInfo
+{
+protected:
+ friend class CJabberPresenceManager;
+ JABBER_PRESENCE_HANDLER m_pHandler;
+ CJabberPresenceInfo* m_pNext;
+
+public:
+ void *m_pUserData;
+
+ CJabberPresenceInfo()
+ {
+ ZeroMemory(this, sizeof(*this));
+ }
+ ~CJabberPresenceInfo()
+ {
+ }
+ void* GetUserData()
+ {
+ return m_pUserData;
+ }
+};
+
+class CJabberPresencePermanentInfo
+{
+ friend class CJabberPresenceManager;
+
+ CJabberPresencePermanentInfo* m_pNext;
+
+ JABBER_PRESENCE_HANDLER m_pHandler;
+ void *m_pUserData;
+ PRESENCE_USER_DATA_FREE_FUNC m_pUserDataFree;
+ int m_iPriority;
+public:
+ CJabberPresencePermanentInfo()
+ {
+ ZeroMemory(this, sizeof(CJabberPresencePermanentInfo));
+ }
+ ~CJabberPresencePermanentInfo()
+ {
+ if ( m_pUserDataFree )
+ m_pUserDataFree(m_pUserData);
+ }
+};
+
+class CJabberPresenceManager
+{
+protected:
+ CJabberProto* ppro;
+ CRITICAL_SECTION m_cs;
+ CJabberPresencePermanentInfo* m_pPermanentHandlers;
+
+public:
+ CJabberPresenceManager( CJabberProto* proto )
+ {
+ InitializeCriticalSection(&m_cs);
+ m_pPermanentHandlers = NULL;
+ ppro = proto;
+ }
+ ~CJabberPresenceManager()
+ {
+ Lock();
+ CJabberPresencePermanentInfo *pInfo = m_pPermanentHandlers;
+ while ( pInfo )
+ {
+ CJabberPresencePermanentInfo *pTmp = pInfo->m_pNext;
+ delete pInfo;
+ pInfo = pTmp;
+ }
+ m_pPermanentHandlers = NULL;
+ Unlock();
+ DeleteCriticalSection(&m_cs);
+ }
+ BOOL Start()
+ {
+ return TRUE;
+ }
+ BOOL Shutdown()
+ {
+ return TRUE;
+ }
+ void Lock()
+ {
+ EnterCriticalSection(&m_cs);
+ }
+ void Unlock()
+ {
+ LeaveCriticalSection(&m_cs);
+ }
+ CJabberPresencePermanentInfo* AddPermanentHandler(JABBER_PRESENCE_HANDLER pHandler, void *pUserData = NULL, PRESENCE_USER_DATA_FREE_FUNC pUserDataFree = NULL, int iPriority = JH_PRIORITY_DEFAULT)
+ {
+ CJabberPresencePermanentInfo* pInfo = new CJabberPresencePermanentInfo();
+ if (!pInfo)
+ return NULL;
+
+ pInfo->m_pHandler = pHandler;
+ pInfo->m_pUserData = pUserData;
+ pInfo->m_pUserDataFree = pUserDataFree;
+ pInfo->m_iPriority = iPriority;
+
+ Lock();
+ if (!m_pPermanentHandlers)
+ m_pPermanentHandlers = pInfo;
+ else
+ {
+ if (m_pPermanentHandlers->m_iPriority > pInfo->m_iPriority) {
+ pInfo->m_pNext = m_pPermanentHandlers;
+ m_pPermanentHandlers = pInfo;
+ } else
+ {
+ CJabberPresencePermanentInfo* pTmp = m_pPermanentHandlers;
+ while (pTmp->m_pNext && pTmp->m_pNext->m_iPriority <= pInfo->m_iPriority)
+ pTmp = pTmp->m_pNext;
+ pInfo->m_pNext = pTmp->m_pNext;
+ pTmp->m_pNext = pInfo;
+ }
+ }
+ Unlock();
+
+ return pInfo;
+ }
+ BOOL DeletePermanentHandler(CJabberPresencePermanentInfo *pInfo)
+ { // returns TRUE when pInfo found, or FALSE otherwise
+ Lock();
+ if (!m_pPermanentHandlers)
+ {
+ Unlock();
+ return FALSE;
+ }
+ if (m_pPermanentHandlers == pInfo) // check first item
+ {
+ m_pPermanentHandlers = m_pPermanentHandlers->m_pNext;
+ delete pInfo;
+ Unlock();
+ return TRUE;
+ } else
+ {
+ CJabberPresencePermanentInfo* pTmp = m_pPermanentHandlers;
+ while (pTmp->m_pNext)
+ {
+ if (pTmp->m_pNext == pInfo)
+ {
+ pTmp->m_pNext = pTmp->m_pNext->m_pNext;
+ delete pInfo;
+ Unlock();
+ return TRUE;
+ }
+ pTmp = pTmp->m_pNext;
+ }
+ }
+ Unlock();
+ return FALSE;
+ }
+ BOOL HandlePresencePermanent(HXML node, ThreadData *pThreadData);
+ BOOL FillPermanentHandlers();
+};
+
+#endif
diff --git a/protocols/JabberG/src/jabber_privacy.cpp b/protocols/JabberG/src/jabber_privacy.cpp new file mode 100644 index 0000000000..636cf50bf6 --- /dev/null +++ b/protocols/JabberG/src/jabber_privacy.cpp @@ -0,0 +1,2328 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007-09 Maxim Mluhov
+Copyright ( C ) 2007-09 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_privacy.h"
+
+#include <m_icolib.h>
+#include <m_genmenu.h>
+#include <m_clistint.h>
+
+#define JABBER_PL_BUSY_MSG "Sending request, please wait..."
+
+BOOL CJabberProto::OnIqRequestPrivacyLists( HXML, CJabberIqInfo* pInfo )
+{
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_SET ) {
+ if ( !m_pDlgPrivacyLists )
+ {
+ m_privacyListManager.RemoveAllLists();
+ QueryPrivacyLists();
+ }
+ else m_pDlgPrivacyLists->SetStatusText(TranslateT("Warning: privacy lists were changed on server."));
+
+ XmlNodeIq iq( _T("result"), pInfo );
+ m_ThreadInfo->send( iq );
+ }
+ return TRUE;
+}
+
+void CJabberProto::OnIqResultPrivacyListModify( HXML, CJabberIqInfo* pInfo )
+{
+ if ( !pInfo->m_pUserData )
+ return;
+
+ CPrivacyListModifyUserParam *pParam = ( CPrivacyListModifyUserParam * )pInfo->m_pUserData;
+
+ if ( pInfo->m_nIqType != JABBER_IQ_TYPE_RESULT )
+ pParam->m_bAllOk = FALSE;
+
+ InterlockedDecrement( &pParam->m_dwCount );
+ if ( !pParam->m_dwCount ) {
+ TCHAR szText[ 512 ];
+ if ( !pParam->m_bAllOk )
+ mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Error occurred while applying changes"));
+ else
+ mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Privacy lists successfully saved"));
+ if (m_pDlgPrivacyLists)
+ m_pDlgPrivacyLists->SetStatusText( szText );
+ // FIXME: enable apply button
+ delete pParam;
+ }
+}
+
+void CJabberProto::OnIqResultPrivacyList( HXML iqNode )
+{
+ if ( !iqNode )
+ return;
+
+ const TCHAR *type = xmlGetAttrValue( iqNode, _T("type"));
+ if ( !type )
+ return;
+
+ if ( !_tcscmp( type, _T("result"))) {
+ HXML query = xmlGetChild( iqNode , "query" );
+ if ( !query )
+ return;
+ HXML list = xmlGetChild( query , "list" );
+ if ( !list )
+ return;
+ TCHAR *szListName = ( TCHAR* )xmlGetAttrValue( list, _T("name"));
+ if ( !szListName )
+ return;
+ m_privacyListManager.Lock();
+ CPrivacyList* pList = m_privacyListManager.FindList( szListName );
+ if ( !pList ) {
+ m_privacyListManager.AddList( szListName );
+ pList = m_privacyListManager.FindList( szListName );
+ if ( !pList ) {
+ m_privacyListManager.Unlock();
+ return;
+ } }
+
+ HXML item;
+ for ( int i = 1; ( item = xmlGetNthChild( list, _T("item"), i )) != NULL; i++ ) {
+ const TCHAR *itemType = xmlGetAttrValue( item, _T("type"));
+ PrivacyListRuleType nItemType = Else;
+ if ( itemType ) {
+ if ( !_tcsicmp( itemType, _T( "jid" )))
+ nItemType = Jid;
+ else if ( !_tcsicmp( itemType, _T( "group" )))
+ nItemType = Group;
+ else if ( !_tcsicmp( itemType, _T( "subscription" )))
+ nItemType = Subscription;
+ }
+
+ const TCHAR *itemValue = xmlGetAttrValue( item, _T("value"));
+
+ const TCHAR *itemAction = xmlGetAttrValue( item, _T("action"));
+ BOOL bAllow = TRUE;
+ if ( itemAction && !_tcsicmp( itemAction, _T( "deny" )))
+ bAllow = FALSE;
+
+ const TCHAR *itemOrder = xmlGetAttrValue( item, _T("order"));
+ DWORD dwOrder = 0;
+ if ( itemOrder )
+ dwOrder = _ttoi( itemOrder );
+
+ DWORD dwPackets = 0;
+ if ( xmlGetChild( item , "message" ))
+ dwPackets |= JABBER_PL_RULE_TYPE_MESSAGE;
+ if ( xmlGetChild( item , "presence-in" ))
+ dwPackets |= JABBER_PL_RULE_TYPE_PRESENCE_IN;
+ if ( xmlGetChild( item , "presence-out" ))
+ dwPackets |= JABBER_PL_RULE_TYPE_PRESENCE_OUT;
+ if ( xmlGetChild( item , "iq" ))
+ dwPackets |= JABBER_PL_RULE_TYPE_IQ;
+
+ pList->AddRule( nItemType, itemValue, bAllow, dwOrder, dwPackets );
+ }
+ pList->Reorder();
+ pList->SetLoaded();
+ pList->SetModified(FALSE);
+ m_privacyListManager.Unlock();
+
+ UI_SAFE_NOTIFY(m_pDlgPrivacyLists, WM_JABBER_REFRESH);
+} }
+
+CPrivacyList* GetSelectedList(HWND hDlg)
+{
+ LRESULT nCurSel = SendDlgItemMessage( hDlg, IDC_LB_LISTS, LB_GETCURSEL, 0, 0 );
+ if ( nCurSel == LB_ERR )
+ return NULL;
+
+ LRESULT nItemData = SendDlgItemMessage( hDlg, IDC_LB_LISTS, LB_GETITEMDATA, nCurSel, 0 );
+ if ( nItemData == LB_ERR || nItemData == 0 )
+ return NULL;
+
+ return ( CPrivacyList* )nItemData;
+}
+
+CPrivacyListRule* GetSelectedRule(HWND hDlg)
+{
+ LRESULT nCurSel = SendDlgItemMessage( hDlg, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );
+ if ( nCurSel == LB_ERR)
+ return NULL;
+
+ LRESULT nItemData = SendDlgItemMessage( hDlg, IDC_PL_RULES_LIST, LB_GETITEMDATA, nCurSel, 0 );
+ if ( nItemData == LB_ERR || nItemData == 0 )
+ return NULL;
+
+ return (CPrivacyListRule* )nItemData;
+}
+
+void CJabberProto::OnIqResultPrivacyListActive( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ CPrivacyList *pList = (CPrivacyList *)pInfo->GetUserData();
+
+ if ( m_pDlgPrivacyLists )
+ EnableWindow( GetDlgItem( m_pDlgPrivacyLists->GetHwnd(), IDC_ACTIVATE ), TRUE );
+
+ if ( !iqNode )
+ return;
+
+ const TCHAR *type = xmlGetAttrValue( iqNode, _T("type"));
+ if ( !type )
+ return;
+
+ TCHAR szText[ 512 ];
+ szText[0] = _T('\0');
+ m_privacyListManager.Lock();
+ if ( !_tcscmp( type, _T("result"))) {
+ if ( pList ) {
+ m_privacyListManager.SetActiveListName( pList->GetListName());
+ mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Privacy list %s set as active"), pList->GetListName());
+ }
+ else {
+ m_privacyListManager.SetActiveListName( NULL );
+ mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Active privacy list successfully declined"));
+ }
+ }
+ else mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Error occurred while setting active list"));
+
+ m_privacyListManager.Unlock();
+
+ if ( m_pDlgPrivacyLists )
+ {
+ m_pDlgPrivacyLists->SetStatusText( szText );
+ RedrawWindow(GetDlgItem(m_pDlgPrivacyLists->GetHwnd(), IDC_LB_LISTS), NULL, NULL, RDW_INVALIDATE);
+ }
+
+ BuildPrivacyListsMenu( true );
+}
+
+void CJabberProto::OnIqResultPrivacyListDefault( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ CPrivacyList *pList = (CPrivacyList *)pInfo->GetUserData();
+
+ if ( m_pDlgPrivacyLists )
+ EnableWindow( GetDlgItem( m_pDlgPrivacyLists->GetHwnd(), IDC_SET_DEFAULT ), TRUE );
+
+ if ( !iqNode )
+ return;
+
+ const TCHAR *type = xmlGetAttrValue( iqNode, _T("type"));
+ if ( !type )
+ return;
+
+ TCHAR szText[ 512 ];
+ szText[0] = _T('\0');
+ m_privacyListManager.Lock();
+ if ( !_tcscmp( type, _T("result"))) {
+ if ( pList ) {
+ m_privacyListManager.SetDefaultListName( pList->GetListName());
+ mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Privacy list %s set as default"), pList->GetListName());
+ }
+ else {
+ m_privacyListManager.SetDefaultListName( NULL );
+ mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Default privacy list successfully declined"));
+ }
+ }
+ else {
+ mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Error occurred while setting default list"));
+ }
+ m_privacyListManager.Unlock();
+
+ if ( m_pDlgPrivacyLists )
+ {
+ m_pDlgPrivacyLists->SetStatusText( szText );
+ RedrawWindow(GetDlgItem(m_pDlgPrivacyLists->GetHwnd(), IDC_LB_LISTS), NULL, NULL, RDW_INVALIDATE);
+ }
+}
+
+void CJabberProto::OnIqResultPrivacyLists( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if ( pInfo->m_nIqType != JABBER_IQ_TYPE_RESULT )
+ return;
+
+ HXML query = xmlGetChild( iqNode, "query" );
+ if ( !query )
+ return;
+
+ if ( m_ThreadInfo )
+ m_ThreadInfo->jabberServerCaps |= JABBER_CAPS_PRIVACY_LISTS;
+
+ m_privacyListManager.Lock();
+ m_privacyListManager.RemoveAllLists();
+
+ for ( int i = 1; ; i++ ) {
+ HXML list = xmlGetNthChild( query, _T("list"), i );
+ if ( !list )
+ break;
+
+ const TCHAR *listName = xmlGetAttrValue( list, _T("name"));
+ if ( listName ) {
+ m_privacyListManager.AddList(( TCHAR* )listName);
+
+ // Query contents only if list editior is visible!
+ if ( m_pDlgPrivacyLists ) {
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultPrivacyList);
+ m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId ) << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS)) << XCHILD( _T("list")) << XATTR( _T("name"), listName ));
+ } } }
+
+ const TCHAR *szName = NULL;
+ HXML node = xmlGetChild( query , "active" );
+ if ( node )
+ szName = xmlGetAttrValue( node, _T("name"));
+ m_privacyListManager.SetActiveListName( szName );
+
+ szName = NULL;
+ node = xmlGetChild( query , "default" );
+ if ( node )
+ szName = xmlGetAttrValue( node, _T("name"));
+ m_privacyListManager.SetDefaultListName( szName );
+
+ m_privacyListManager.Unlock();
+
+ UI_SAFE_NOTIFY(m_pDlgPrivacyLists, WM_JABBER_REFRESH);
+
+ BuildPrivacyListsMenu( true );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Add privacy list box
+class CJabberDlgPrivacyAddList: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+public:
+ TCHAR szLine[512];
+
+ CJabberDlgPrivacyAddList(CJabberProto *proto, HWND hwndParent):
+ CJabberDlgBase(proto, IDD_PRIVACY_ADD_LIST, hwndParent, false),
+ m_txtName(this, IDC_EDIT_NAME),
+ m_btnOk(this, IDOK),
+ m_btnCancel(this, IDCANCEL)
+ {
+ m_btnOk.OnClick = Callback( this, &CJabberDlgPrivacyAddList::btnOk_OnClick );
+ m_btnCancel.OnClick = Callback( this, &CJabberDlgPrivacyAddList::btnCancel_OnClick);
+ }
+
+ void btnOk_OnClick(CCtrlButton*)
+ {
+ GetDlgItemText(m_hwnd, IDC_EDIT_NAME, szLine, SIZEOF(szLine));
+ EndDialog(m_hwnd, 1);
+ }
+ void btnCancel_OnClick(CCtrlButton*)
+ {
+ *szLine = 0;
+ EndDialog(m_hwnd, 0);
+ }
+
+private:
+ CCtrlEdit m_txtName;
+ CCtrlButton m_btnOk;
+ CCtrlButton m_btnCancel;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Privacy rule editor
+class CJabberDlgPrivacyRule: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+ CCtrlButton m_btnOk;
+ CCtrlButton m_btnCancel;
+ CCtrlCombo m_cbType;
+
+public:
+ CPrivacyListRule *m_pRule;
+
+ CJabberDlgPrivacyRule(CJabberProto *proto, HWND hwndParent, CPrivacyListRule *pRule):
+ CJabberDlgBase(proto, IDD_PRIVACY_RULE, hwndParent, false),
+ m_btnOk(this, IDOK),
+ m_btnCancel(this, IDCANCEL),
+ m_cbType(this, IDC_COMBO_TYPE)
+ {
+ m_pRule = pRule;
+ m_cbType.OnChange = Callback(this, &CJabberDlgPrivacyRule::cbType_OnChange);
+ m_btnOk.OnClick = Callback(this, &CJabberDlgPrivacyRule::btnOk_OnClick);
+ m_btnCancel.OnClick = Callback(this, &CJabberDlgPrivacyRule::btnCancel_OnClick);
+ }
+
+ virtual void OnInitDialog()
+ {
+ CSuper::OnInitDialog();
+
+ m_proto->m_hwndPrivacyRule = m_hwnd;
+
+ SendDlgItemMessage(m_hwnd, IDC_ICO_MESSAGE, STM_SETICON, (WPARAM)m_proto->LoadIconEx("pl_msg_allow"), 0);
+ SendDlgItemMessage(m_hwnd, IDC_ICO_QUERY, STM_SETICON, (WPARAM)m_proto->LoadIconEx("pl_iq_allow"), 0);
+ SendDlgItemMessage(m_hwnd, IDC_ICO_PRESENCEIN, STM_SETICON, (WPARAM)m_proto->LoadIconEx("pl_prin_allow"), 0);
+ SendDlgItemMessage(m_hwnd, IDC_ICO_PRESENCEOUT, STM_SETICON, (WPARAM)m_proto->LoadIconEx("pl_prout_allow"), 0);
+
+ TCHAR* szTypes[] = { _T("JID"), _T("Group"), _T("Subscription"), _T("Any") };
+ int i, nTypes[] = { Jid, Group, Subscription, Else };
+ for ( i = 0; i < SIZEOF(szTypes); i++ )
+ {
+ LRESULT nItem = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_ADDSTRING, 0, (LPARAM)TranslateTS( szTypes[i] ));
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_SETITEMDATA, nItem, nTypes[i] );
+ if ( m_pRule->GetType() == nTypes[i] )
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_SETCURSEL, nItem, 0 );
+ }
+
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_RESETCONTENT, 0, 0 );
+ TCHAR* szSubscriptions[] = { _T("none"), _T("from"), _T("to"), _T("both") };
+ for ( i = 0; i < SIZEOF(szSubscriptions); i++ )
+ {
+ LRESULT nItem = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_ADDSTRING, 0, (LPARAM)TranslateTS( szSubscriptions[i] ));
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_SETITEMDATA, nItem, (LPARAM)szSubscriptions[i] );
+ }
+
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_COMBO_TYPE, CBN_SELCHANGE ), 0 );
+
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_ACTION, CB_ADDSTRING, 0, (LPARAM)TranslateTS( _T("Deny" )));
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_ACTION, CB_ADDSTRING, 0, (LPARAM)TranslateTS( _T("Allow" )));
+
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_ACTION, CB_SETCURSEL, m_pRule->GetAction() ? 1 : 0, 0 );
+
+ DWORD dwPackets = m_pRule->GetPackets();
+ if ( !dwPackets )
+ dwPackets = JABBER_PL_RULE_TYPE_ALL;
+ if ( dwPackets & JABBER_PL_RULE_TYPE_IQ )
+ SendDlgItemMessage( m_hwnd, IDC_CHECK_QUERIES, BM_SETCHECK, BST_CHECKED, 0 );
+ if ( dwPackets & JABBER_PL_RULE_TYPE_MESSAGE )
+ SendDlgItemMessage( m_hwnd, IDC_CHECK_MESSAGES, BM_SETCHECK, BST_CHECKED, 0 );
+ if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_IN )
+ SendDlgItemMessage( m_hwnd, IDC_CHECK_PRESENCE_IN, BM_SETCHECK, BST_CHECKED, 0 );
+ if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_OUT )
+ SendDlgItemMessage( m_hwnd, IDC_CHECK_PRESENCE_OUT, BM_SETCHECK, BST_CHECKED, 0 );
+
+ if ( m_pRule->GetValue() && ( m_pRule->GetType() == Jid || m_pRule->GetType() == Group ))
+ SetDlgItemText( m_hwnd, IDC_EDIT_VALUE, m_pRule->GetValue());
+ }
+
+ void cbType_OnChange(CCtrlData*)
+ {
+ if ( !m_pRule ) return;
+
+ LRESULT nCurSel = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_GETCURSEL, 0, 0 );
+ if ( nCurSel == CB_ERR )
+ return;
+
+ LRESULT nItemData = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_GETITEMDATA, nCurSel, 0 );
+ switch (nItemData)
+ {
+ case Jid:
+ {
+ ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUES ), SW_SHOW );
+ ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUE ), SW_HIDE );
+
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_RESETCONTENT, 0, 0 );
+
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL )
+ {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto != NULL && !strcmp( szProto, m_proto->m_szModuleName ))
+ {
+ DBVARIANT dbv;
+ if ( !m_proto->JGetStringT( hContact, "jid", &dbv ))
+ {
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_ADDSTRING, 0, (LPARAM)dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ }
+ hContact = db_find_next(hContact);
+ }
+
+ // append known chatroom jids from bookmarks
+ LISTFOREACH(i, m_proto, LIST_BOOKMARK)
+ {
+ JABBER_LIST_ITEM *item = 0;
+ if ( item = m_proto->ListGetItemPtrFromIndex( i ))
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_ADDSTRING, 0, (LPARAM)item->jid );
+ }
+
+ // FIXME: ugly code :)
+ if ( m_pRule->GetValue())
+ {
+ SetDlgItemText( m_hwnd, IDC_COMBO_VALUES, m_pRule->GetValue());
+ LRESULT nSelPos = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_FINDSTRINGEXACT , -1, (LPARAM)m_pRule->GetValue());
+ if ( nSelPos != CB_ERR )
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_SETCURSEL, nSelPos, 0 );
+ }
+ break;
+ }
+
+ case Group:
+ {
+ ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUES ), SW_SHOW );
+ ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUE ), SW_HIDE );
+
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_RESETCONTENT, 0, 0 );
+
+ char buf[ 20 ];
+ DBVARIANT dbv;
+ for ( int i = 0; ; i++ )
+ {
+ mir_snprintf(buf, 20, "%d", i);
+ if ( DBGetContactSettingTString(NULL, "CListGroups", buf, &dbv))
+ break;
+
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_ADDSTRING, 0, (LPARAM)&dbv.ptszVal[1] );
+ DBFreeVariant(&dbv);
+ }
+
+ // FIXME: ugly code :)
+ if ( m_pRule->GetValue())
+ {
+ SetDlgItemText( m_hwnd, IDC_COMBO_VALUES, m_pRule->GetValue());
+ LRESULT nSelPos = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_FINDSTRINGEXACT , -1, (LPARAM)m_pRule->GetValue());
+ if ( nSelPos != CB_ERR )
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_SETCURSEL, nSelPos, 0 );
+ }
+ break;
+ }
+
+ case Subscription:
+ {
+ ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUES ), SW_HIDE );
+ ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUE ), SW_SHOW );
+
+ if ( m_pRule->GetValue())
+ {
+ LRESULT nSelected = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_SELECTSTRING, -1, (LPARAM)TranslateTS(m_pRule->GetValue()));
+ if ( nSelected == CB_ERR )
+ SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_SETCURSEL, 0, 0 );
+ }
+ else SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_SETCURSEL, 0, 0 );
+ break;
+ }
+
+ case Else:
+ {
+ ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUES ), SW_HIDE );
+ ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUE ), SW_HIDE );
+ break;
+ }
+ }
+
+ return;
+ }
+
+ void btnOk_OnClick(CCtrlButton *)
+ {
+ LRESULT nItemData = -1;
+ LRESULT nCurSel = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_GETCURSEL, 0, 0 );
+ if ( nCurSel != CB_ERR )
+ nItemData = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_GETITEMDATA, nCurSel, 0 );
+
+ switch ( nItemData )
+ {
+ case Jid:
+ case Group:
+ {
+ TCHAR szText[ 512 ];
+ GetDlgItemText( m_hwnd, IDC_COMBO_VALUES, szText, SIZEOF(szText));
+ m_pRule->SetValue( szText );
+ break;
+ }
+ case Subscription:
+ {
+ nCurSel = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_GETCURSEL, 0, 0 );
+ if ( nCurSel != CB_ERR )
+ m_pRule->SetValue(( TCHAR* )SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_GETITEMDATA, nCurSel, 0 ));
+ else
+ m_pRule->SetValue( _T( "none" ));
+ break;
+ }
+
+ default:
+ m_pRule->SetValue( NULL );
+ break;
+ }
+
+ m_pRule->SetType( ( PrivacyListRuleType )nItemData );
+ nCurSel = SendDlgItemMessage( m_hwnd, IDC_COMBO_ACTION, CB_GETCURSEL, 0, 0 );
+ if ( nCurSel == CB_ERR )
+ nCurSel = 1;
+ m_pRule->SetAction( nCurSel ? TRUE : FALSE );
+
+ DWORD dwPackets = 0;
+ if ( BST_CHECKED == SendDlgItemMessage( m_hwnd, IDC_CHECK_MESSAGES, BM_GETCHECK, 0, 0 ))
+ dwPackets |= JABBER_PL_RULE_TYPE_MESSAGE;
+ if ( BST_CHECKED == SendDlgItemMessage( m_hwnd, IDC_CHECK_PRESENCE_IN, BM_GETCHECK, 0, 0 ))
+ dwPackets |= JABBER_PL_RULE_TYPE_PRESENCE_IN;
+ if ( BST_CHECKED == SendDlgItemMessage( m_hwnd, IDC_CHECK_PRESENCE_OUT, BM_GETCHECK, 0, 0 ))
+ dwPackets |= JABBER_PL_RULE_TYPE_PRESENCE_OUT;
+ if ( BST_CHECKED == SendDlgItemMessage( m_hwnd, IDC_CHECK_QUERIES, BM_GETCHECK, 0, 0 ))
+ dwPackets |= JABBER_PL_RULE_TYPE_IQ;
+ if ( !dwPackets )
+ dwPackets = JABBER_PL_RULE_TYPE_ALL;
+
+ m_pRule->SetPackets( dwPackets );
+
+ EndDialog( m_hwnd, 1 );
+ }
+
+ void btnCancel_OnClick(CCtrlButton *)
+ {
+ EndDialog(m_hwnd, 0);
+ }
+
+ void OnDestroy()
+ {
+ g_ReleaseIcon(( HICON )SendDlgItemMessage(m_hwnd, IDC_ICO_MESSAGE, STM_SETICON, 0, 0));
+ g_ReleaseIcon(( HICON )SendDlgItemMessage(m_hwnd, IDC_ICO_QUERY, STM_SETICON, 0, 0));
+ g_ReleaseIcon(( HICON )SendDlgItemMessage(m_hwnd, IDC_ICO_PRESENCEIN, STM_SETICON, 0, 0));
+ g_ReleaseIcon(( HICON )SendDlgItemMessage(m_hwnd, IDC_ICO_PRESENCEOUT, STM_SETICON, 0, 0));
+ m_proto->m_hwndPrivacyRule = NULL;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Main privacy list dialog
+class CJabberDlgPrivacyLists: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+public:
+ CJabberDlgPrivacyLists(CJabberProto *proto);
+
+protected:
+ static int idSimpleControls[];
+ static int idAdvancedControls[];
+
+ void OnInitDialog();
+ void OnClose();
+ void OnDestroy();
+ void OnProtoRefresh(WPARAM, LPARAM);
+ int Resizer(UTILRESIZECONTROL *urc);
+
+ UI_MESSAGE_MAP(CJabberDlgPrivacyLists, CSuper);
+ UI_MESSAGE(WM_MEASUREITEM, OnWmMeasureItem);
+ UI_MESSAGE(WM_DRAWITEM, OnWmDrawItem);
+ UI_MESSAGE(WM_GETMINMAXINFO, OnWmGetMinMaxInfo);
+ UI_MESSAGE_MAP_END();
+
+ BOOL OnWmMeasureItem(UINT msg, WPARAM wParam, LPARAM lParam);
+ BOOL OnWmDrawItem(UINT msg, WPARAM wParam, LPARAM lParam);
+ BOOL OnWmGetMinMaxInfo(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ void btnSimple_OnClick(CCtrlButton *);
+ void btnAdvanced_OnClick(CCtrlButton *);
+ void btnAddJid_OnClick(CCtrlButton *);
+ void btnActivate_OnClick(CCtrlButton *);
+ void btnSetDefault_OnClick(CCtrlButton *);
+ void lbLists_OnSelChange(CCtrlListBox *);
+ void lbLists_OnDblClick(CCtrlListBox *);
+ void lbRules_OnSelChange(CCtrlListBox *);
+ void lbRules_OnDblClick(CCtrlListBox *);
+ void btnEditRule_OnClick(CCtrlButton *);
+ void btnAddRule_OnClick(CCtrlButton *);
+ void btnRemoveRule_OnClick(CCtrlButton *);
+ void btnUpRule_OnClick(CCtrlButton *);
+ void btnDownRule_OnClick(CCtrlButton *);
+ void btnAddList_OnClick(CCtrlButton *);
+ void btnRemoveList_OnClick(CCtrlButton *);
+ void btnApply_OnClick(CCtrlButton *);
+ void clcClist_OnUpdate(CCtrlClc::TEventInfo *evt);
+ void clcClist_OnOptionsChanged(CCtrlClc::TEventInfo *evt);
+ void clcClist_OnClick(CCtrlClc::TEventInfo *evt);
+
+ void OnCommand_Close(HWND hwndCtrl, WORD idCtrl, WORD idCode);
+
+ void ShowAdvancedList(CPrivacyList *pList);
+ void DrawNextRulePart(HDC hdc, COLORREF color, const TCHAR *text, RECT *rc);
+ void DrawRuleAction(HDC hdc, COLORREF clLine1, COLORREF clLine2, CPrivacyListRule *pRule, RECT *rc);
+ void DrawRulesList(LPDRAWITEMSTRUCT lpdis);
+ void DrawLists(LPDRAWITEMSTRUCT lpdis);
+
+ void CListResetOptions(HWND hwndList);
+ void CListFilter(HWND hwndList);
+ bool CListIsGroup(HANDLE hGroup);
+ HANDLE CListFindGroupByName(TCHAR *name);
+ void CListResetIcons(HWND hwndList, HANDLE hItem, bool hide=false);
+ void CListSetupIcons(HWND hwndList, HANDLE hItem, int iSlot, DWORD dwProcess, BOOL bAction);
+ HANDLE CListAddContact(HWND hwndList, TCHAR *jid);
+ void CListApplyList(HWND hwndList, CPrivacyList *pList = NULL);
+ DWORD CListGetPackets(HWND hwndList, HANDLE hItem, bool bAction);
+ void CListBuildList(HWND hwndList, CPrivacyList *pList);
+
+ void EnableEditorControls();
+ BOOL CanExit();
+
+ static LRESULT CALLBACK LstListsSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ static LRESULT CALLBACK LstRulesSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ struct TCLCInfo
+ {
+ struct TJidData
+ {
+ HANDLE hItem;
+ TCHAR *jid;
+
+ static int cmp(const TJidData *p1, const TJidData *p2) { return lstrcmp(p1->jid, p2->jid); }
+ };
+
+ HANDLE hItemDefault;
+ HANDLE hItemSubNone;
+ HANDLE hItemSubTo;
+ HANDLE hItemSubFrom;
+ HANDLE hItemSubBoth;
+
+ LIST<TJidData> newJids;
+
+ bool bChanged;
+
+ CPrivacyList *pList;
+
+ TCLCInfo(): newJids(1, TJidData::cmp), bChanged(false), pList(0) {}
+ ~TCLCInfo()
+ {
+ for (int i = 0; i < newJids.getCount(); ++i)
+ {
+ mir_free(newJids[i]->jid);
+ mir_free(newJids[i]);
+ }
+ newJids.destroy();
+ }
+
+ void addJid(HANDLE hItem, TCHAR *jid)
+ {
+ TJidData *data = (TJidData *)mir_alloc(sizeof(TJidData));
+ data->hItem = hItem;
+ data->jid = mir_tstrdup(jid);
+ newJids.insert(data);
+ }
+
+ HANDLE findJid(TCHAR *jid)
+ {
+ TJidData data = {0};
+ data.jid = jid;
+ TJidData *found = newJids.find(&data);
+ return found ? found->hItem : 0;
+ }
+ };
+
+ TCLCInfo clc_info;
+
+private:
+ CCtrlMButton m_btnSimple;
+ CCtrlMButton m_btnAdvanced;
+ CCtrlMButton m_btnAddJid;
+ CCtrlMButton m_btnActivate;
+ CCtrlMButton m_btnSetDefault;
+ CCtrlMButton m_btnEditRule;
+ CCtrlMButton m_btnAddRule;
+ CCtrlMButton m_btnRemoveRule;
+ CCtrlMButton m_btnUpRule;
+ CCtrlMButton m_btnDownRule;
+ CCtrlMButton m_btnAddList;
+ CCtrlMButton m_btnRemoveList;
+ CCtrlButton m_btnApply;
+ CCtrlListBox m_lbLists;
+ CCtrlListBox m_lbRules;
+ CCtrlClc m_clcClist;
+};
+
+int CJabberDlgPrivacyLists::idSimpleControls[] =
+{
+ IDC_CLIST, IDC_CANVAS,
+ IDC_TXT_OTHERJID, IDC_NEWJID, IDC_ADDJID,
+ IDC_ICO_MESSAGE, IDC_ICO_QUERY, IDC_ICO_INPRESENCE, IDC_ICO_OUTPRESENCE,
+ IDC_TXT_MESSAGE, IDC_TXT_QUERY, IDC_TXT_INPRESENCE, IDC_TXT_OUTPRESENCE,
+ 0
+};
+
+int CJabberDlgPrivacyLists::idAdvancedControls[] =
+{
+ IDC_PL_RULES_LIST,
+ IDC_ADD_RULE, IDC_EDIT_RULE, IDC_REMOVE_RULE,
+ IDC_UP_RULE, IDC_DOWN_RULE,
+ 0
+};
+
+CJabberDlgPrivacyLists::CJabberDlgPrivacyLists(CJabberProto *proto):
+ CSuper(proto, IDD_PRIVACY_LISTS, NULL),
+ m_btnSimple(this, IDC_BTN_SIMPLE, proto->LoadIconEx("group"), LPGEN("Simple mode")),
+ m_btnAdvanced(this, IDC_BTN_ADVANCED, proto->LoadIconEx("sd_view_list"), LPGEN("Advanced mode")),
+ m_btnAddJid(this, IDC_ADDJID, proto->LoadIconEx("addroster"), LPGEN("Add JID")),
+ m_btnActivate(this, IDC_ACTIVATE, proto->LoadIconEx("pl_list_active"), LPGEN("Activate")),
+ m_btnSetDefault(this, IDC_SET_DEFAULT, proto->LoadIconEx("pl_list_default"), LPGEN("Set default")),
+ m_btnEditRule(this, IDC_EDIT_RULE, SKINICON_OTHER_RENAME, LPGEN("Edit rule")),
+ m_btnAddRule(this, IDC_ADD_RULE, SKINICON_OTHER_ADDCONTACT, LPGEN("Add rule")),
+ m_btnRemoveRule(this, IDC_REMOVE_RULE, SKINICON_OTHER_DELETE, LPGEN("Delete rule")),
+ m_btnUpRule(this, IDC_UP_RULE, proto->LoadIconEx("arrow_up"), LPGEN("Move rule up")),
+ m_btnDownRule(this, IDC_DOWN_RULE, proto->LoadIconEx("arrow_down"), LPGEN("Move rule down")),
+ m_btnAddList(this, IDC_ADD_LIST, SKINICON_OTHER_ADDCONTACT, LPGEN("Add list...")),
+ m_btnRemoveList(this, IDC_REMOVE_LIST, SKINICON_OTHER_DELETE, LPGEN("Remove list")),
+ m_btnApply(this, IDC_APPLY),
+ m_lbLists(this, IDC_LB_LISTS),
+ m_lbRules(this, IDC_PL_RULES_LIST),
+ m_clcClist(this, IDC_CLIST)
+{
+ m_btnSimple.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnSimple_OnClick);
+ m_btnAdvanced.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnAdvanced_OnClick);
+ m_btnAddJid.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnAddJid_OnClick);
+ m_btnActivate.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnActivate_OnClick);
+ m_btnSetDefault.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnSetDefault_OnClick);
+ m_btnEditRule.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnEditRule_OnClick);
+ m_btnAddRule.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnAddRule_OnClick);
+ m_btnRemoveRule.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnRemoveRule_OnClick);
+ m_btnUpRule.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnUpRule_OnClick);
+ m_btnDownRule.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnDownRule_OnClick);
+ m_btnAddList.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnAddList_OnClick);
+ m_btnRemoveList.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnRemoveList_OnClick);
+ m_btnApply.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnApply_OnClick);
+
+ m_lbLists.OnSelChange = Callback(this, &CJabberDlgPrivacyLists::lbLists_OnSelChange);
+ m_lbLists.OnDblClick = Callback(this, &CJabberDlgPrivacyLists::lbLists_OnDblClick);
+ m_lbRules.OnSelChange = Callback(this, &CJabberDlgPrivacyLists::lbRules_OnSelChange);
+ m_lbRules.OnDblClick = Callback(this, &CJabberDlgPrivacyLists::lbRules_OnDblClick);
+
+ m_clcClist.OnNewContact =
+ m_clcClist.OnListRebuilt = Callback(this, &CJabberDlgPrivacyLists::clcClist_OnUpdate);
+ m_clcClist.OnOptionsChanged = Callback(this, &CJabberDlgPrivacyLists::clcClist_OnOptionsChanged);
+ m_clcClist.OnClick = Callback(this, &CJabberDlgPrivacyLists::clcClist_OnClick);
+}
+
+void CJabberDlgPrivacyLists::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+
+ WindowSetIcon( m_hwnd, m_proto, "privacylists" );
+
+ EnableWindow( GetDlgItem( m_hwnd, IDC_ADD_RULE ), FALSE );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_EDIT_RULE ), FALSE );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_REMOVE_RULE ), FALSE );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_UP_RULE ), FALSE );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_DOWN_RULE ), FALSE );
+
+ m_proto->QueryPrivacyLists();
+
+ LOGFONT lf;
+ GetObject((HFONT)SendDlgItemMessage(m_hwnd, IDC_TXT_LISTS, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ HFONT hfnt = CreateFontIndirect(&lf);
+ SendDlgItemMessage(m_hwnd, IDC_TXT_LISTS, WM_SETFONT, (WPARAM)hfnt, TRUE);
+ SendDlgItemMessage(m_hwnd, IDC_TXT_RULES, WM_SETFONT, (WPARAM)hfnt, TRUE);
+
+ SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_CLIST), GWL_STYLE,
+ GetWindowLongPtr(GetDlgItem(m_hwnd, IDC_CLIST), GWL_STYLE)|CLS_HIDEEMPTYGROUPS|CLS_USEGROUPS|CLS_GREYALTERNATE);
+ m_clcClist.SetExStyle(CLS_EX_DISABLEDRAGDROP|CLS_EX_TRACKSELECT);
+
+ HIMAGELIST hIml = ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),(IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK,9,9);
+ ImageList_AddIcon_Icolib(hIml, LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT));
+ ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_msg_allow"));
+ ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_msg_deny"));
+ ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_prin_allow"));
+ ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_prin_deny"));
+ ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_prout_allow"));
+ ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_prout_deny"));
+ ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_iq_allow"));
+ ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_iq_deny"));
+ m_clcClist.SetExtraImageList(hIml);
+ m_clcClist.SetExtraColumns(4);
+
+ m_btnSimple.MakePush();
+ m_btnAdvanced.MakePush();
+
+ CLCINFOITEM cii = {0};
+ cii.cbSize = sizeof(cii);
+
+ cii.flags = CLCIIF_GROUPFONT;
+ cii.pszText = TranslateT("** Default **");
+ clc_info.hItemDefault = m_clcClist.AddInfoItem(&cii);
+ cii.pszText = TranslateT("** Subsription: both **");
+ clc_info.hItemSubBoth = m_clcClist.AddInfoItem(&cii);
+ cii.pszText = TranslateT("** Subsription: to **");
+ clc_info.hItemSubTo = m_clcClist.AddInfoItem(&cii);
+ cii.pszText = TranslateT("** Subsription: from **");
+ clc_info.hItemSubFrom = m_clcClist.AddInfoItem(&cii);
+ cii.pszText = TranslateT("** Subsription: none **");
+ clc_info.hItemSubNone = m_clcClist.AddInfoItem(&cii);
+
+ CListResetOptions(GetDlgItem(m_hwnd, IDC_CLIST));
+ CListFilter(GetDlgItem(m_hwnd, IDC_CLIST));
+ CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST));
+
+ if ( DBGetContactSettingByte(NULL, m_proto->m_szModuleName, "plistsWnd_simpleMode", 1))
+ {
+ UIShowControls(m_hwnd, idSimpleControls, SW_SHOW);
+ UIShowControls(m_hwnd, idAdvancedControls, SW_HIDE);
+ CheckDlgButton(m_hwnd, IDC_BTN_SIMPLE, TRUE);
+ } else
+ {
+ UIShowControls(m_hwnd, idSimpleControls, SW_HIDE);
+ UIShowControls(m_hwnd, idAdvancedControls, SW_SHOW);
+ CheckDlgButton(m_hwnd, IDC_BTN_ADVANCED, TRUE);
+ }
+
+ SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_LB_LISTS), GWLP_USERDATA,
+ SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_LB_LISTS), GWLP_WNDPROC, (LONG_PTR)LstListsSubclassProc));
+ SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_PL_RULES_LIST), GWLP_USERDATA,
+ SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_PL_RULES_LIST), GWLP_WNDPROC, (LONG_PTR)LstRulesSubclassProc));
+
+ SetStatusText(TranslateT("Loading..."));
+
+ Utils_RestoreWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "plistsWnd_sz");
+}
+
+void CJabberDlgPrivacyLists::OnClose()
+{
+ if (CanExit()) {
+ DestroyWindow(m_hwnd);
+ CSuper::OnClose();
+ }
+ else
+ m_lresult = TRUE;
+}
+
+void CJabberDlgPrivacyLists::OnDestroy()
+{
+ m_proto->m_pDlgPrivacyLists = NULL;
+
+ // Wipe all data and query lists without contents
+ m_proto->m_privacyListManager.RemoveAllLists();
+ m_proto->QueryPrivacyLists();
+ m_proto->m_privacyListManager.SetModified( FALSE );
+
+ // Delete custom bold font
+ DeleteObject((HFONT)SendDlgItemMessage(m_hwnd, IDC_TXT_LISTS, WM_GETFONT, 0, 0));
+
+ DBWriteContactSettingByte(NULL, m_proto->m_szModuleName, "plistsWnd_simpleMode", IsDlgButtonChecked(m_hwnd, IDC_BTN_SIMPLE));
+
+ Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "plistsWnd_sz");
+
+ CSuper::OnDestroy();
+}
+
+void CJabberDlgPrivacyLists::OnProtoRefresh(WPARAM, LPARAM)
+{
+ LRESULT sel = SendDlgItemMessage(m_hwnd, IDC_LB_LISTS, LB_GETCURSEL, 0, 0);
+ TCHAR *szCurrentSelectedList = NULL;
+ if ( sel != LB_ERR ) {
+ LRESULT len = SendDlgItemMessage(m_hwnd, IDC_LB_LISTS, LB_GETTEXTLEN, sel, 0) + 1;
+ szCurrentSelectedList = (TCHAR *)mir_alloc(len * sizeof(TCHAR));
+ SendDlgItemMessage(m_hwnd, IDC_LB_LISTS, LB_GETTEXT, sel, (LPARAM)szCurrentSelectedList);
+ }
+
+ SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_RESETCONTENT, 0, 0 );
+
+ LRESULT nItemId = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_ADDSTRING, 0, (LPARAM)TranslateT( "<none>" ));
+ SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETITEMDATA, nItemId, (LPARAM)NULL );
+
+ m_proto->m_privacyListManager.Lock();
+ CPrivacyList* pList = m_proto->m_privacyListManager.GetFirstList();
+ while ( pList ) {
+ if ( !pList->IsDeleted()) {
+ nItemId = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_ADDSTRING, 0, (LPARAM)pList->GetListName());
+ SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETITEMDATA, nItemId, (LPARAM)pList );
+ }
+ pList = pList->GetNext();
+ }
+
+ if ( !szCurrentSelectedList || ( SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SELECTSTRING, -1, (LPARAM)szCurrentSelectedList ) == LB_ERR ))
+ SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETCURSEL, 0, 0 );
+ if ( szCurrentSelectedList )
+ mir_free( szCurrentSelectedList );
+
+ m_proto->m_privacyListManager.Unlock();
+
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_LB_LISTS, LBN_SELCHANGE ), 0 );
+ EnableEditorControls();
+}
+
+BOOL CJabberDlgPrivacyLists::OnWmMeasureItem(UINT, WPARAM, LPARAM lParam)
+{
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+ if ((lpmis->CtlID != IDC_PL_RULES_LIST) && (lpmis->CtlID != IDC_LB_LISTS))
+ return FALSE;
+
+ TEXTMETRIC tm = {0};
+ HDC hdc = GetDC(GetDlgItem(m_hwnd, lpmis->CtlID));
+ GetTextMetrics(hdc, &tm);
+ ReleaseDC(GetDlgItem(m_hwnd, lpmis->CtlID), hdc);
+
+ if (lpmis->CtlID == IDC_PL_RULES_LIST)
+ lpmis->itemHeight = tm.tmHeight * 2;
+ else if (lpmis->CtlID == IDC_LB_LISTS)
+ lpmis->itemHeight = tm.tmHeight;
+
+ if (lpmis->itemHeight < 18) lpmis->itemHeight = 18;
+
+ return TRUE;
+}
+
+BOOL CJabberDlgPrivacyLists::OnWmDrawItem(UINT, WPARAM, LPARAM lParam)
+{
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+
+ if (lpdis->CtlID == IDC_PL_RULES_LIST)
+ DrawRulesList(lpdis);
+ else if (lpdis->CtlID == IDC_LB_LISTS)
+ DrawLists(lpdis);
+ else if (lpdis->CtlID == IDC_CANVAS)
+ {
+ static struct
+ {
+ TCHAR *textEng;
+ char *icon;
+ TCHAR *text;
+ } items[] =
+ {
+ { _T("Message"), "pl_msg_allow" },
+ { _T("Presence (in)"), "pl_prin_allow" },
+ { _T("Presence (out)"), "pl_prout_allow" },
+ { _T("Query"), "pl_iq_allow" },
+ };
+
+ int i, totalWidth = -5; // spacing for last item
+ for (i = 0; i < SIZEOF(items); ++i)
+ {
+ SIZE sz = {0};
+ if (!items[i].text) items[i].text = TranslateTS(items[i].textEng);
+ GetTextExtentPoint32(lpdis->hDC, items[i].text, lstrlen(items[i].text), &sz);
+ totalWidth += sz.cx + 18 + 5; // 18 pixels for icon, 5 pixel spacing
+ }
+
+ COLORREF clText = GetSysColor(COLOR_BTNTEXT);
+ RECT rc = lpdis->rcItem;
+ rc.left = (rc.left + rc.right - totalWidth)/2;
+
+ for (i = 0; i < SIZEOF(items); ++i)
+ {
+ DrawIconEx(lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, m_proto->LoadIconEx(items[i].icon),
+ 16, 16, 0, NULL, DI_NORMAL);
+ rc.left += 18;
+ DrawNextRulePart(lpdis->hDC, clText, items[i].text, &rc);
+ rc.left += 5;
+ }
+ }
+ else return FALSE;
+
+ return TRUE;
+}
+
+BOOL CJabberDlgPrivacyLists::OnWmGetMinMaxInfo(UINT, WPARAM, LPARAM lParam)
+{
+ LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
+ lpmmi->ptMinTrackSize.x = 550;
+ lpmmi->ptMinTrackSize.y = 390;
+ return 0;
+}
+
+void CJabberDlgPrivacyLists::ShowAdvancedList(CPrivacyList *pList)
+{
+ int nLbSel = SendDlgItemMessage(m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0);
+ SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_RESETCONTENT, 0, 0 );
+
+ BOOL bListEmpty = TRUE;
+
+ CPrivacyListRule* pRule = pList->GetFirstRule();
+
+ while ( pRule ) {
+ bListEmpty = FALSE;
+ TCHAR szTypeValue[ 512 ];
+ switch ( pRule->GetType()) {
+ case Jid:
+ mir_sntprintf( szTypeValue, SIZEOF( szTypeValue ), _T( "If jabber id is '%s' then" ), pRule->GetValue());
+ break;
+ case Group:
+ mir_sntprintf( szTypeValue, SIZEOF( szTypeValue ), _T( "If group is '%s' then" ), pRule->GetValue());
+ break;
+ case Subscription:
+ mir_sntprintf( szTypeValue, SIZEOF( szTypeValue ), _T( "If subscription is '%s' then" ), pRule->GetValue());
+ break;
+ case Else:
+ mir_sntprintf( szTypeValue, SIZEOF( szTypeValue ), _T( "Else"));
+ break;
+ }
+
+ TCHAR szPackets[ 512 ];
+ szPackets[ 0 ] = '\0';
+
+ DWORD dwPackets = pRule->GetPackets();
+ if ( !dwPackets )
+ dwPackets = JABBER_PL_RULE_TYPE_ALL;
+ if ( dwPackets == JABBER_PL_RULE_TYPE_ALL )
+ _tcscpy( szPackets, _T("all"));
+ else {
+ if ( dwPackets & JABBER_PL_RULE_TYPE_MESSAGE )
+ _tcscat( szPackets, _T("messages"));
+ if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_IN ) {
+ if ( _tcslen( szPackets ))
+ _tcscat( szPackets, _T(", "));
+ _tcscat( szPackets, _T("presence-in"));
+ }
+ if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_OUT ) {
+ if ( _tcslen( szPackets ))
+ _tcscat( szPackets, _T(", "));
+ _tcscat( szPackets, _T("presence-out"));
+ }
+ if ( dwPackets & JABBER_PL_RULE_TYPE_IQ ) {
+ if ( _tcslen( szPackets ))
+ _tcscat( szPackets, _T(", "));
+ _tcscat( szPackets, _T("queries"));
+ } }
+
+ TCHAR szListItem[ 512 ];
+ mir_sntprintf( szListItem, SIZEOF( szListItem ), _T("%s %s %s"), szTypeValue, pRule->GetAction() ? _T("allow") : _T("deny"), szPackets );
+
+ LRESULT nItemId = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_ADDSTRING, 0, (LPARAM)szListItem );
+ SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETITEMDATA, nItemId, (LPARAM)pRule );
+
+ pRule = pRule->GetNext();
+ }
+
+ EnableWindow( GetDlgItem( m_hwnd, IDC_PL_RULES_LIST ), !bListEmpty );
+ if ( bListEmpty )
+ SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_ADDSTRING, 0, (LPARAM)TranslateTS(_T("List has no rules, empty lists will be deleted then changes applied")));
+ else
+ SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETCURSEL, nLbSel, 0 );
+
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_PL_RULES_LIST, LBN_SELCHANGE ), 0 );
+}
+
+void CJabberDlgPrivacyLists::DrawNextRulePart(HDC hdc, COLORREF color, const TCHAR *text, RECT *rc)
+{
+ SetTextColor(hdc, color);
+ DrawText(hdc, text, lstrlen(text), rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS);
+
+ SIZE sz;
+ GetTextExtentPoint32(hdc, text, lstrlen(text), &sz);
+ rc->left += sz.cx;
+}
+
+void CJabberDlgPrivacyLists::DrawRuleAction(HDC hdc, COLORREF clLine1, COLORREF, CPrivacyListRule *pRule, RECT *rc)
+{
+ DrawNextRulePart(hdc, clLine1, pRule->GetAction() ? TranslateT("allow ") : TranslateT("deny "), rc);
+ if (!pRule->GetPackets() || (pRule->GetPackets() == JABBER_PL_RULE_TYPE_ALL))
+ {
+ DrawNextRulePart(hdc, clLine1, TranslateT("all."), rc);
+ } else
+ {
+ bool needComma = false;
+ int itemCount =
+ ((pRule->GetPackets() & JABBER_PL_RULE_TYPE_MESSAGE) ? 1 : 0) +
+ ((pRule->GetPackets() & JABBER_PL_RULE_TYPE_PRESENCE_IN) ? 1 : 0) +
+ ((pRule->GetPackets() & JABBER_PL_RULE_TYPE_PRESENCE_OUT) ? 1 : 0) +
+ ((pRule->GetPackets() & JABBER_PL_RULE_TYPE_IQ) ? 1 : 0);
+
+ if (pRule->GetPackets() & JABBER_PL_RULE_TYPE_MESSAGE)
+ {
+ --itemCount;
+ needComma = true;
+ DrawNextRulePart(hdc, clLine1, TranslateT("messages"), rc);
+ }
+ if (pRule->GetPackets() & JABBER_PL_RULE_TYPE_PRESENCE_IN)
+ {
+ --itemCount;
+ if (needComma)
+ DrawNextRulePart(hdc, clLine1, itemCount ? _T(", ") : TranslateT(" and "), rc);
+ needComma = true;
+ DrawNextRulePart(hdc, clLine1, TranslateT("incoming presences"), rc);
+ }
+ if (pRule->GetPackets() & JABBER_PL_RULE_TYPE_PRESENCE_OUT)
+ {
+ --itemCount;
+ if (needComma)
+ DrawNextRulePart(hdc, clLine1, itemCount ? _T(", ") : TranslateT(" and "), rc);
+ needComma = true;
+ DrawNextRulePart(hdc, clLine1, TranslateT("outgoing presences"), rc);
+ }
+ if (pRule->GetPackets() & JABBER_PL_RULE_TYPE_IQ)
+ {
+ --itemCount;
+ if (needComma)
+ DrawNextRulePart(hdc, clLine1, itemCount ? _T(", ") : TranslateT(" and "), rc);
+ needComma = true;
+ DrawNextRulePart(hdc, clLine1, TranslateT("queries"), rc);
+ }
+ DrawNextRulePart(hdc, clLine1, _T("."), rc);
+ }
+}
+
+void CJabberDlgPrivacyLists::DrawRulesList(LPDRAWITEMSTRUCT lpdis)
+{
+ if (lpdis->itemID == -1)
+ return;
+
+ CPrivacyListRule *pRule = (CPrivacyListRule *)lpdis->itemData;
+
+ COLORREF clLine1, clLine2, clBack;
+ if (lpdis->itemState & ODS_SELECTED)
+ {
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ clBack = GetSysColor(COLOR_HIGHLIGHT);
+ clLine1 = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ } else
+ {
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_WINDOW));
+ clBack = GetSysColor(COLOR_WINDOW);
+ clLine1 = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ clLine2 = RGB(
+ GetRValue(clLine1) * 0.66 + GetRValue(clBack) * 0.34,
+ GetGValue(clLine1) * 0.66 + GetGValue(clBack) * 0.34,
+ GetBValue(clLine1) * 0.66 + GetBValue(clBack) * 0.34
+ );
+
+ SetBkMode(lpdis->hDC, TRANSPARENT);
+
+ RECT rc;
+
+ if ( !pRule ) {
+ rc = lpdis->rcItem;
+ rc.left += 25;
+
+ int len = SendDlgItemMessage(m_hwnd, lpdis->CtlID, LB_GETTEXTLEN, lpdis->itemID, 0) + 1;
+ TCHAR *str = (TCHAR *)_alloca(len * sizeof(TCHAR));
+ SendDlgItemMessage(m_hwnd, lpdis->CtlID, LB_GETTEXT, lpdis->itemID, (LPARAM)str);
+ DrawNextRulePart(lpdis->hDC, clLine1, str, &rc);
+ }
+ else if (pRule->GetType() == Else) {
+ rc = lpdis->rcItem;
+ rc.left += 25;
+
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("Else "), &rc);
+ DrawRuleAction(lpdis->hDC, clLine1, clLine2, pRule, &rc);
+ }
+ else {
+ rc = lpdis->rcItem;
+ rc.bottom -= (rc.bottom - rc.top) / 2;
+ rc.left += 25;
+
+ switch ( pRule->GetType())
+ {
+ case Jid:
+ {
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("If jabber id is '"), &rc);
+ DrawNextRulePart(lpdis->hDC, clLine1, pRule->GetValue(), &rc);
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("'"), &rc);
+
+ if (HANDLE hContact = m_proto->HContactFromJID(pRule->GetValue())) {
+ TCHAR *szName = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR);
+ if ( szName ) {
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(" (nickname: "), &rc);
+ DrawNextRulePart(lpdis->hDC, clLine1, szName, &rc);
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(")"), &rc);
+ } }
+ break;
+ }
+
+ case Group:
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("If group is '"), &rc);
+ DrawNextRulePart(lpdis->hDC, clLine1, pRule->GetValue(), &rc);
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("'"), &rc);
+ break;
+ case Subscription:
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("If subscription is '"), &rc);
+ DrawNextRulePart(lpdis->hDC, clLine1, pRule->GetValue(), &rc);
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("'"), &rc);
+ break;
+ }
+
+ rc = lpdis->rcItem;
+ rc.top += (rc.bottom - rc.top) / 2;
+ rc.left += 25;
+
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("then "), &rc);
+ DrawRuleAction(lpdis->hDC, clLine1, clLine2, pRule, &rc);
+ }
+
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.left+4, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2,
+ m_proto->LoadIconEx("main"), 16, 16, 0, NULL, DI_NORMAL);
+
+ if (pRule)
+ {
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.left+4, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2,
+ m_proto->LoadIconEx(pRule->GetAction() ? "disco_ok" : "disco_fail"),
+ 16, 16, 0, NULL, DI_NORMAL);
+ }
+
+ if (lpdis->itemState & ODS_FOCUS)
+ {
+ LRESULT sel = SendDlgItemMessage(m_hwnd, lpdis->CtlID, LB_GETCURSEL, 0, 0);
+ if ((sel == LB_ERR) || (sel == (LRESULT)lpdis->itemID))
+ DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
+ }
+}
+
+void CJabberDlgPrivacyLists::DrawLists(LPDRAWITEMSTRUCT lpdis)
+{
+ if (lpdis->itemID == -1)
+ return;
+
+ CPrivacyList *pList = (CPrivacyList *)lpdis->itemData;
+
+ COLORREF clLine1, clLine2, clBack;
+ if (lpdis->itemState & ODS_SELECTED)
+ {
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ clBack = GetSysColor(COLOR_HIGHLIGHT);
+ clLine1 = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ } else
+ {
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_WINDOW));
+ clBack = GetSysColor(COLOR_WINDOW);
+ clLine1 = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ clLine2 = RGB(
+ GetRValue(clLine1) * 0.66 + GetRValue(clBack) * 0.34,
+ GetGValue(clLine1) * 0.66 + GetGValue(clBack) * 0.34,
+ GetBValue(clLine1) * 0.66 + GetBValue(clBack) * 0.34
+ );
+
+ SetBkMode(lpdis->hDC, TRANSPARENT);
+
+ RECT rc;
+ m_proto->m_privacyListManager.Lock();
+ TCHAR *szDefault = NEWTSTR_ALLOCA(m_proto->m_privacyListManager.GetDefaultListName());
+ TCHAR *szActive = NEWTSTR_ALLOCA(m_proto->m_privacyListManager.GetActiveListName());
+ m_proto->m_privacyListManager.Unlock();
+
+ rc = lpdis->rcItem;
+ rc.left +=3;
+
+ bool bActive = false;
+ bool bDefault = false;
+ TCHAR *szName;
+
+ if (!pList)
+ {
+ if (!szActive) bActive = true;
+ if (!szDefault) bDefault = true;
+ szName = TranslateT("<none>");
+ } else
+ {
+ if (!lstrcmp(pList->GetListName(), szActive)) bActive = true;
+ if (!lstrcmp(pList->GetListName(), szDefault)) bDefault = true;
+ szName = pList->GetListName();
+ }
+
+ HFONT hfnt = NULL;
+ if (bActive)
+ {
+ LOGFONT lf;
+ GetObject(GetCurrentObject(lpdis->hDC, OBJ_FONT), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hfnt = (HFONT)SelectObject(lpdis->hDC, CreateFontIndirect(&lf));
+ }
+
+ DrawNextRulePart(lpdis->hDC, clLine1, szName, &rc);
+
+ if (bActive && bDefault)
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(" (act., def.)"), &rc);
+ else if (bActive)
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(" (active)"), &rc);
+ else if (bDefault)
+ DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(" (default)"), &rc);
+
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.right-16-4, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2,
+ m_proto->LoadIconEx(bActive ? "pl_list_active" : "pl_list_any"),
+ 16, 16, 0, NULL, DI_NORMAL);
+
+ if (bDefault)
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.right-16-4, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2,
+ m_proto->LoadIconEx("disco_ok"),
+ 16, 16, 0, NULL, DI_NORMAL);
+
+ if (hfnt)
+ DeleteObject(SelectObject(lpdis->hDC, hfnt));
+
+ if (lpdis->itemState & ODS_FOCUS)
+ {
+ int sel = SendDlgItemMessage(m_hwnd, lpdis->CtlID, LB_GETCURSEL, 0, 0);
+ if ((sel == LB_ERR) || (sel == (int)lpdis->itemID))
+ DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
+ }
+}
+
+void CJabberDlgPrivacyLists::CListResetOptions(HWND)
+{
+ m_clcClist.SetBkBitmap(0, NULL);
+ m_clcClist.SetBkColor(GetSysColor(COLOR_WINDOW));
+ m_clcClist.SetGreyoutFlags(0);
+ m_clcClist.SetLeftMargin(4);
+ m_clcClist.SetIndent(10);
+ m_clcClist.SetHideEmptyGroups(false);
+ m_clcClist.SetHideOfflineRoot(false);
+ for (int i = 0; i <= FONTID_MAX; i++)
+ m_clcClist.SetTextColor(i, GetSysColor(COLOR_WINDOWTEXT));
+}
+
+void CJabberDlgPrivacyLists::CListFilter(HWND)
+{
+ for (HANDLE hContact = db_find_first();
+ hContact;
+ hContact = db_find_next(hContact))
+ {
+ char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (!proto || lstrcmpA(proto, m_proto->m_szModuleName))
+ if (HANDLE hItem = m_clcClist.FindContact(hContact))
+ m_clcClist.DeleteItem(hItem);
+ }
+}
+
+bool CJabberDlgPrivacyLists::CListIsGroup(HANDLE hGroup)
+{
+ char idstr[33];
+ _i64toa((INT_PTR)hGroup-1, idstr, 10);
+
+ DBVARIANT dbv;
+ bool result = DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv) == 0;
+ if ( result )
+ DBFreeVariant(&dbv);
+
+ return result;
+}
+
+HANDLE CJabberDlgPrivacyLists::CListFindGroupByName(TCHAR *name)
+{
+ char idstr[33];
+ DBVARIANT dbv;
+
+ HANDLE hGroup = 0;
+
+ for (int i= 0; !hGroup; ++i)
+ {
+ _itoa(i, idstr, 10);
+
+ if (DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv))
+ break;
+
+ if (!_tcscmp(dbv.ptszVal + 1, name))
+ hGroup = (HANDLE)(i+1);
+
+ DBFreeVariant(&dbv);
+ }
+
+ return hGroup;
+}
+
+void CJabberDlgPrivacyLists::CListResetIcons(HWND, HANDLE hItem, bool hide)
+{
+ for (int i = 0; i < 4; ++i)
+ m_clcClist.SetExtraImage(hItem, i, hide ? 0xFF : 0);
+}
+
+void CJabberDlgPrivacyLists::CListSetupIcons(HWND, HANDLE hItem, int iSlot, DWORD dwProcess, BOOL bAction)
+{
+ if (dwProcess && !m_clcClist.GetExtraImage(hItem, iSlot))
+ m_clcClist.SetExtraImage(hItem, iSlot, iSlot*2 + (bAction?1:2));
+}
+
+HANDLE CJabberDlgPrivacyLists::CListAddContact(HWND hwndList, TCHAR *jid)
+{
+ HANDLE hItem = 0;
+
+ HANDLE hContact = m_proto->HContactFromJID(jid);
+ if ( !hContact ) {
+ hItem = clc_info.findJid(jid);
+ if (!hItem)
+ {
+ CLCINFOITEM cii = {0};
+ cii.cbSize = sizeof(cii);
+ cii.pszText = jid;
+ hItem = m_clcClist.AddInfoItem(&cii);
+ CListResetIcons(hwndList, hItem);
+ clc_info.addJid(hItem, jid);
+ }
+ } else
+ {
+ hItem = m_clcClist.FindContact(hContact);
+ }
+
+ return hItem;
+}
+
+void CJabberDlgPrivacyLists::CListApplyList(HWND hwndList, CPrivacyList *pList)
+{
+ clc_info.pList = pList;
+
+ bool bHideIcons = pList ? false : true;
+
+ CListResetIcons(hwndList, clc_info.hItemDefault, bHideIcons);
+ CListResetIcons(hwndList, clc_info.hItemSubBoth, bHideIcons);
+ CListResetIcons(hwndList, clc_info.hItemSubFrom, bHideIcons);
+ CListResetIcons(hwndList, clc_info.hItemSubNone, bHideIcons);
+ CListResetIcons(hwndList, clc_info.hItemSubTo, bHideIcons);
+
+ // group handles start with 1 (0 is "root")
+ for (int iGroup = 1; CListIsGroup((HANDLE)iGroup); ++iGroup)
+ {
+ HANDLE hItem = m_clcClist.FindGroup((HANDLE)iGroup);
+ if (!hItem) continue;
+ CListResetIcons(hwndList, hItem, bHideIcons);
+ }
+
+ for (HANDLE hContact=db_find_first(); hContact;
+ hContact=db_find_next(hContact))
+ {
+ HANDLE hItem = m_clcClist.FindContact(hContact);
+ if (!hItem) continue;
+ CListResetIcons(hwndList, hItem, bHideIcons);
+ }
+
+ for (int iJid = 0; iJid < clc_info.newJids.getCount(); ++iJid)
+ CListResetIcons(hwndList, clc_info.newJids[iJid]->hItem, bHideIcons);
+
+ if (!pList)
+ goto lbl_return;
+
+ CPrivacyListRule *pRule;
+ for (pRule = pList->GetFirstRule(); pRule; pRule = pRule->GetNext())
+ {
+ HANDLE hItem = 0;
+ switch (pRule->GetType())
+ {
+ case Jid:
+ {
+ hItem = CListAddContact(hwndList, pRule->GetValue());
+ break;
+ }
+ case Group:
+ {
+ HANDLE hGroup = CListFindGroupByName(pRule->GetValue());
+ hItem = m_clcClist.FindGroup(hGroup);
+ break;
+ }
+ case Subscription:
+ {
+ if (!lstrcmp(pRule->GetValue(), _T("none"))) hItem = clc_info.hItemSubNone;
+ else if (!lstrcmp(pRule->GetValue(), _T("from"))) hItem = clc_info.hItemSubFrom;
+ else if (!lstrcmp(pRule->GetValue(), _T("to"))) hItem = clc_info.hItemSubTo;
+ else if (!lstrcmp(pRule->GetValue(), _T("both"))) hItem = clc_info.hItemSubBoth;
+ break;
+ }
+ case Else:
+ {
+ hItem = clc_info.hItemDefault;
+ break;
+ }
+ }
+
+ if (!hItem) continue;
+
+ DWORD dwPackets = pRule->GetPackets();
+ if (!dwPackets) dwPackets = JABBER_PL_RULE_TYPE_ALL;
+ CListSetupIcons(hwndList, hItem, 0, dwPackets & JABBER_PL_RULE_TYPE_MESSAGE, pRule->GetAction());
+ CListSetupIcons(hwndList, hItem, 1, dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_IN, pRule->GetAction());
+ CListSetupIcons(hwndList, hItem, 2, dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_OUT, pRule->GetAction());
+ CListSetupIcons(hwndList, hItem, 3, dwPackets & JABBER_PL_RULE_TYPE_IQ, pRule->GetAction());
+ }
+
+lbl_return:
+ clc_info.bChanged = false;
+}
+
+DWORD CJabberDlgPrivacyLists::CListGetPackets(HWND, HANDLE hItem, bool bAction)
+{
+ DWORD result = 0;
+
+ int iIcon = 0;
+
+ iIcon = m_clcClist.GetExtraImage(hItem, 0);
+ if ( bAction && (iIcon == 1)) result |= JABBER_PL_RULE_TYPE_MESSAGE;
+ else if (!bAction && (iIcon == 2)) result |= JABBER_PL_RULE_TYPE_MESSAGE;
+
+ iIcon = m_clcClist.GetExtraImage(hItem, 1);
+ if ( bAction && (iIcon == 3)) result |= JABBER_PL_RULE_TYPE_PRESENCE_IN;
+ else if (!bAction && (iIcon == 4)) result |= JABBER_PL_RULE_TYPE_PRESENCE_IN;
+
+ iIcon = m_clcClist.GetExtraImage(hItem, 2);
+ if ( bAction && (iIcon == 5)) result |= JABBER_PL_RULE_TYPE_PRESENCE_OUT;
+ else if (!bAction && (iIcon == 6)) result |= JABBER_PL_RULE_TYPE_PRESENCE_OUT;
+
+ iIcon = m_clcClist.GetExtraImage(hItem, 3);
+ if ( bAction && (iIcon == 7)) result |= JABBER_PL_RULE_TYPE_IQ;
+ else if (!bAction && (iIcon == 8)) result |= JABBER_PL_RULE_TYPE_IQ;
+
+ return result;
+}
+
+void CJabberDlgPrivacyLists::CListBuildList(HWND hwndList, CPrivacyList *pList)
+{
+ if (!pList) return;
+
+ if (!clc_info.bChanged) return;
+
+ clc_info.bChanged = false;
+
+ DWORD dwOrder = 0;
+ DWORD dwPackets = 0;
+
+ HANDLE hItem;
+ TCHAR *szJid;
+
+ pList->RemoveAllRules();
+
+ for (int iJid = 0; iJid < clc_info.newJids.getCount(); ++iJid)
+ {
+ hItem = clc_info.newJids[iJid]->hItem;
+ szJid = clc_info.newJids[iJid]->jid;
+
+ if (dwPackets = CListGetPackets(hwndList, hItem, true))
+ pList->AddRule(Jid, szJid, TRUE, dwOrder++, dwPackets);
+ if (dwPackets = CListGetPackets(hwndList, hItem, false))
+ pList->AddRule(Jid, szJid, FALSE, dwOrder++, dwPackets);
+ }
+
+ for (HANDLE hContact=db_find_first(); hContact;
+ hContact=db_find_next(hContact))
+ {
+ hItem = m_clcClist.FindContact(hContact);
+
+ DBVARIANT dbv = {0};
+ if ( m_proto->JGetStringT(hContact, "jid", &dbv)) {
+ if ( m_proto->JGetStringT(hContact, "ChatRoomID", &dbv))
+ continue;
+ }
+
+ szJid = dbv.ptszVal;
+
+ if (dwPackets = CListGetPackets(hwndList, hItem, true))
+ pList->AddRule(Jid, szJid, TRUE, dwOrder++, dwPackets);
+ if (dwPackets = CListGetPackets(hwndList, hItem, false))
+ pList->AddRule(Jid, szJid, FALSE, dwOrder++, dwPackets);
+
+ JFreeVariant(&dbv);
+ }
+
+ // group handles start with 1 (0 is "root")
+ for (int iGroup = 1; ; ++iGroup)
+ {
+ char idstr[33];
+ _itoa(iGroup-1, idstr, 10);
+ DBVARIANT dbv = {0};
+ if (DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv))
+ break;
+
+ hItem = m_clcClist.FindGroup((HANDLE)iGroup);
+ szJid = dbv.ptszVal+1;
+
+ if (dwPackets = CListGetPackets(hwndList, hItem, true))
+ pList->AddRule(Group, szJid, TRUE, dwOrder++, dwPackets);
+ if (dwPackets = CListGetPackets(hwndList, hItem, false))
+ pList->AddRule(Group, szJid, FALSE, dwOrder++, dwPackets);
+
+ DBFreeVariant(&dbv);
+ }
+
+ hItem = clc_info.hItemSubBoth;
+ szJid = _T("both");
+ if (dwPackets = CListGetPackets(hwndList, hItem, true))
+ pList->AddRule(Subscription, szJid, TRUE, dwOrder++, dwPackets);
+ if (dwPackets = CListGetPackets(hwndList, hItem, false))
+ pList->AddRule(Subscription, szJid, FALSE, dwOrder++, dwPackets);
+
+ hItem = clc_info.hItemSubFrom;
+ szJid = _T("from");
+ if (dwPackets = CListGetPackets(hwndList, hItem, true))
+ pList->AddRule(Subscription, szJid, TRUE, dwOrder++, dwPackets);
+ if (dwPackets = CListGetPackets(hwndList, hItem, false))
+ pList->AddRule(Subscription, szJid, FALSE, dwOrder++, dwPackets);
+
+ hItem = clc_info.hItemSubNone;
+ szJid = _T("none");
+ if (dwPackets = CListGetPackets(hwndList, hItem, true))
+ pList->AddRule(Subscription, szJid, TRUE, dwOrder++, dwPackets);
+ if (dwPackets = CListGetPackets(hwndList, hItem, false))
+ pList->AddRule(Subscription, szJid, FALSE, dwOrder++, dwPackets);
+
+ hItem = clc_info.hItemSubTo;
+ szJid = _T("to");
+ if (dwPackets = CListGetPackets(hwndList, hItem, true))
+ pList->AddRule(Subscription, szJid, TRUE, dwOrder++, dwPackets);
+ if (dwPackets = CListGetPackets(hwndList, hItem, false))
+ pList->AddRule(Subscription, szJid, FALSE, dwOrder++, dwPackets);
+
+ hItem = clc_info.hItemDefault;
+ szJid = NULL;
+ if (dwPackets = CListGetPackets(hwndList, hItem, true))
+ pList->AddRule(Else, szJid, TRUE, dwOrder++, dwPackets);
+ if (dwPackets = CListGetPackets(hwndList, hItem, false))
+ pList->AddRule(Else, szJid, FALSE, dwOrder++, dwPackets);
+
+ pList->Reorder();
+ pList->SetModified();
+}
+
+void CJabberDlgPrivacyLists::EnableEditorControls()
+{
+ m_proto->m_privacyListManager.Lock();
+ BOOL bListsLoaded = m_proto->m_privacyListManager.IsAllListsLoaded();
+ BOOL bListsModified = m_proto->m_privacyListManager.IsModified() || clc_info.bChanged;
+ m_proto->m_privacyListManager.Unlock();
+
+ int nCurSel = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );
+ int nItemCount = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCOUNT, 0, 0 );
+ BOOL bSelected = nCurSel != CB_ERR;
+ BOOL bListSelected = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETCOUNT, 0, 0);
+ bListSelected = bListSelected && (SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETCURSEL, 0, 0) != LB_ERR);
+ bListSelected = bListSelected && SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETITEMDATA, SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETCURSEL, 0, 0), 0);
+
+ EnableWindow( GetDlgItem( m_hwnd, IDC_TXT_OTHERJID ), bListsLoaded && bListSelected );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_NEWJID ), bListsLoaded && bListSelected );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_ADDJID ), bListsLoaded && bListSelected );
+
+ EnableWindow( GetDlgItem( m_hwnd, IDC_ADD_RULE ), bListsLoaded && bListSelected );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_EDIT_RULE ), bListsLoaded && bSelected );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_REMOVE_RULE ), bListsLoaded && bSelected );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_UP_RULE ), bListsLoaded && bSelected && nCurSel != 0 );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_DOWN_RULE ), bListsLoaded && bSelected && nCurSel != ( nItemCount - 1 ));
+ EnableWindow( GetDlgItem( m_hwnd, IDC_REMOVE_LIST ), bListsLoaded && bListSelected );
+ EnableWindow( GetDlgItem( m_hwnd, IDC_APPLY ), bListsLoaded && bListsModified );
+
+ if (bListsLoaded)
+ SetStatusText( TranslateT("Ready."));
+}
+
+LRESULT CALLBACK CJabberDlgPrivacyLists::LstListsSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC sttOldWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ switch (msg)
+ {
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ {
+ if (wParam == VK_INSERT)
+ return UIEmulateBtnClick(GetParent(hwnd), IDC_ADD_LIST);
+ if (wParam == VK_DELETE)
+ return UIEmulateBtnClick(GetParent(hwnd), IDC_REMOVE_LIST);
+ if (wParam == VK_SPACE)
+ {
+ if (GetAsyncKeyState(VK_CONTROL))
+ return UIEmulateBtnClick(GetParent(hwnd), IDC_SET_DEFAULT);
+ return UIEmulateBtnClick(GetParent(hwnd), IDC_ACTIVATE);
+ }
+
+ break;
+ }
+ }
+ return CallWindowProc(sttOldWndProc, hwnd, msg, wParam, lParam);
+}
+
+LRESULT CALLBACK CJabberDlgPrivacyLists::LstRulesSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC sttOldWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ switch (msg)
+ {
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ {
+ if (wParam == VK_INSERT)
+ return UIEmulateBtnClick(GetParent(hwnd), IDC_ADD_RULE);
+ if (wParam == VK_DELETE)
+ return UIEmulateBtnClick(GetParent(hwnd), IDC_REMOVE_RULE);
+ if ((wParam == VK_UP) && (lParam & (1UL << 29)))
+ return UIEmulateBtnClick(GetParent(hwnd), IDC_UP_RULE);
+ if ((wParam == VK_DOWN) && (lParam & (1UL << 29)))
+ return UIEmulateBtnClick(GetParent(hwnd), IDC_DOWN_RULE);
+ if (wParam == VK_F2)
+ return UIEmulateBtnClick(GetParent(hwnd), IDC_EDIT_RULE);
+
+ break;
+ }
+ }
+ return CallWindowProc(sttOldWndProc, hwnd, msg, wParam, lParam);
+}
+
+BOOL CJabberDlgPrivacyLists::CanExit()
+{
+ m_proto->m_privacyListManager.Lock();
+ BOOL bModified = m_proto->m_privacyListManager.IsModified();
+ m_proto->m_privacyListManager.Unlock();
+
+ if (clc_info.bChanged)
+ bModified = TRUE;
+
+ if ( !bModified )
+ return TRUE;
+
+ if ( IDYES == MessageBox( m_hwnd, TranslateT("Privacy lists are not saved, discard any changes and exit?"), TranslateT("Are you sure?"), MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2 ))
+ return TRUE;
+
+ return FALSE;
+}
+
+void CJabberDlgPrivacyLists::btnSimple_OnClick(CCtrlButton *)
+{
+ CheckDlgButton(m_hwnd, IDC_BTN_SIMPLE, TRUE);
+ CheckDlgButton(m_hwnd, IDC_BTN_ADVANCED, FALSE);
+ UIShowControls(m_hwnd, idSimpleControls, SW_SHOW);
+ UIShowControls(m_hwnd, idAdvancedControls, SW_HIDE);
+ CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), GetSelectedList(m_hwnd));
+}
+
+void CJabberDlgPrivacyLists::btnAdvanced_OnClick(CCtrlButton *)
+{
+ CheckDlgButton(m_hwnd, IDC_BTN_SIMPLE, FALSE);
+ CheckDlgButton(m_hwnd, IDC_BTN_ADVANCED, TRUE);
+ UIShowControls(m_hwnd, idSimpleControls, SW_HIDE);
+ UIShowControls(m_hwnd, idAdvancedControls, SW_SHOW);
+ CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), GetSelectedList(m_hwnd));
+ PostMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_LB_LISTS, LBN_SELCHANGE), 0);
+}
+
+void CJabberDlgPrivacyLists::btnAddJid_OnClick(CCtrlButton *)
+{
+ int len = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_NEWJID))+1;
+ TCHAR *buf = (TCHAR *)_alloca(sizeof(TCHAR) * len);
+ GetWindowText(GetDlgItem(m_hwnd, IDC_NEWJID), buf, len);
+ SetWindowText(GetDlgItem(m_hwnd, IDC_NEWJID), _T(""));
+ CListAddContact(GetDlgItem(m_hwnd, IDC_CLIST), buf);
+}
+
+void CJabberDlgPrivacyLists::btnActivate_OnClick(CCtrlButton *)
+{
+ if ( m_proto->m_bJabberOnline )
+ {
+ m_proto->m_privacyListManager.Lock();
+ CPrivacyList* pList = GetSelectedList(m_hwnd);
+ if ( pList && pList->IsModified()) {
+ m_proto->m_privacyListManager.Unlock();
+ MessageBox( m_hwnd, TranslateT("Please save list before activating"), TranslateT("First, save the list"), MB_OK | MB_ICONSTOP );
+ return;
+ }
+ EnableWindow( GetDlgItem( m_hwnd, IDC_ACTIVATE ), FALSE );
+ SetWindowLongPtr( GetDlgItem( m_hwnd, IDC_ACTIVATE ), GWLP_USERDATA, (LONG_PTR)pList );
+ XmlNodeIq iq( m_proto->m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyListActive, JABBER_IQ_TYPE_SET, NULL, 0, -1, pList ));
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS));
+ HXML active = query << XCHILD( _T("active"));
+ if ( pList )
+ active << XATTR( _T("name"), pList->GetListName());
+ m_proto->m_privacyListManager.Unlock();
+
+ SetStatusText(TranslateT( JABBER_PL_BUSY_MSG ));
+
+ m_proto->m_ThreadInfo->send( iq );
+ }
+}
+
+void CJabberDlgPrivacyLists::btnSetDefault_OnClick(CCtrlButton *)
+{
+ if ( m_proto->m_bJabberOnline )
+ {
+ m_proto->m_privacyListManager.Lock();
+ CPrivacyList* pList = GetSelectedList(m_hwnd);
+ if ( pList && pList->IsModified()) {
+ m_proto->m_privacyListManager.Unlock();
+ MessageBox( m_hwnd, TranslateT("Please save list before you make it the default list"), TranslateT("First, save the list"), MB_OK | MB_ICONSTOP );
+ return;
+ }
+ EnableWindow( GetDlgItem( m_hwnd, IDC_SET_DEFAULT ), FALSE );
+ SetWindowLongPtr( GetDlgItem( m_hwnd, IDC_SET_DEFAULT ), GWLP_USERDATA, (LONG_PTR)pList );
+
+ XmlNodeIq iq( m_proto->m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyListDefault, JABBER_IQ_TYPE_SET, NULL, 0, -1, pList ));
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS ));
+ HXML defaultTag = query << XCHILD( _T("default"));
+ if ( pList )
+ xmlAddAttr( defaultTag, _T("name"), pList->GetListName());
+ m_proto->m_privacyListManager.Unlock();
+
+ SetStatusText(TranslateT( JABBER_PL_BUSY_MSG ));
+
+ m_proto->m_ThreadInfo->send( iq );
+ }
+}
+
+void CJabberDlgPrivacyLists::lbLists_OnSelChange(CCtrlListBox *)
+{
+ LRESULT nCurSel = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETCURSEL, 0, 0 );
+ if ( nCurSel == LB_ERR )
+ return;
+
+ LRESULT nErr = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETITEMDATA, nCurSel, 0 );
+ if ( nErr == LB_ERR )
+ return;
+ if ( nErr == 0 )
+ {
+ if (IsWindowVisible(GetDlgItem(m_hwnd, IDC_CLIST)))
+ {
+ CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), clc_info.pList);
+ CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), NULL);
+ } else
+ {
+ EnableWindow( GetDlgItem( m_hwnd, IDC_PL_RULES_LIST ), FALSE );
+ SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_RESETCONTENT, 0, 0 );
+ SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_ADDSTRING, 0, (LPARAM)TranslateTS(_T("No list selected")));
+ }
+ EnableEditorControls();
+ return;
+ }
+
+ m_proto->m_privacyListManager.Lock();
+ if (IsWindowVisible(GetDlgItem(m_hwnd, IDC_CLIST)))
+ {
+ CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), clc_info.pList);
+ CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), (CPrivacyList* )nErr);
+ }
+ else ShowAdvancedList((CPrivacyList* )nErr);
+
+ m_proto->m_privacyListManager.Unlock();
+
+ EnableEditorControls();
+}
+
+void CJabberDlgPrivacyLists::lbLists_OnDblClick(CCtrlListBox *)
+{
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_ACTIVATE, 0 ), 0 );
+}
+
+void CJabberDlgPrivacyLists::lbRules_OnSelChange(CCtrlListBox *)
+{
+ EnableEditorControls();
+}
+
+void CJabberDlgPrivacyLists::lbRules_OnDblClick(CCtrlListBox *)
+{
+ PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_EDIT_RULE, 0 ), 0 );
+}
+
+void CJabberDlgPrivacyLists::btnEditRule_OnClick(CCtrlButton *)
+{
+ // FIXME: potential deadlock due to PLM lock while editing rule
+ m_proto->m_privacyListManager.Lock();
+ {
+ CPrivacyListRule* pRule = GetSelectedRule( m_hwnd );
+ CPrivacyList* pList = GetSelectedList(m_hwnd);
+ if ( pList && pRule ) {
+ CJabberDlgPrivacyRule dlgPrivacyRule(m_proto, m_hwnd, pRule);
+ int nResult = dlgPrivacyRule.DoModal();
+ if ( nResult ) {
+ pList->SetModified();
+ PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
+ } } }
+ m_proto->m_privacyListManager.Unlock();
+}
+
+void CJabberDlgPrivacyLists::btnAddRule_OnClick(CCtrlButton *)
+{
+ // FIXME: potential deadlock due to PLM lock while editing rule
+ m_proto->m_privacyListManager.Lock();
+ {
+ CPrivacyList* pList = GetSelectedList(m_hwnd);
+ if ( pList ) {
+ CPrivacyListRule* pRule = new CPrivacyListRule( m_proto, Jid, _T(""), FALSE );
+ CJabberDlgPrivacyRule dlgPrivacyRule(m_proto, m_hwnd, pRule);
+ int nResult = dlgPrivacyRule.DoModal();
+ if ( nResult ) {
+ pList->AddRule(pRule);
+ pList->Reorder();
+ pList->SetModified();
+ PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
+ }
+ else delete pRule;
+ } }
+ m_proto->m_privacyListManager.Unlock();
+}
+
+void CJabberDlgPrivacyLists::btnRemoveRule_OnClick(CCtrlButton *)
+{
+ m_proto->m_privacyListManager.Lock();
+ {
+ CPrivacyList* pList = GetSelectedList(m_hwnd);
+ CPrivacyListRule* pRule = GetSelectedRule( m_hwnd );
+
+ if ( pList && pRule ) {
+ pList->RemoveRule( pRule );
+ int nCurSel = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );
+ int nItemCount = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCOUNT, 0, 0 );
+ SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETCURSEL, nCurSel != nItemCount - 1 ? nCurSel : nCurSel - 1, 0 );
+ pList->Reorder();
+ pList->SetModified();
+ PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
+ } }
+
+ m_proto->m_privacyListManager.Unlock();
+}
+
+void CJabberDlgPrivacyLists::btnUpRule_OnClick(CCtrlButton *)
+{
+ m_proto->m_privacyListManager.Lock();
+ {
+ CPrivacyList* pList = GetSelectedList(m_hwnd);
+ CPrivacyListRule* pRule = GetSelectedRule( m_hwnd );
+ int nCurSel = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );
+
+ if ( pList && pRule && nCurSel ) {
+ pRule->SetOrder( pRule->GetOrder() - 11 );
+ SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETCURSEL, nCurSel - 1, 0 );
+ pList->Reorder();
+ pList->SetModified();
+ PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
+ } }
+
+ m_proto->m_privacyListManager.Unlock();
+}
+
+void CJabberDlgPrivacyLists::btnDownRule_OnClick(CCtrlButton *)
+{
+ m_proto->m_privacyListManager.Lock();
+ {
+ CPrivacyList* pList = GetSelectedList(m_hwnd);
+ CPrivacyListRule* pRule = GetSelectedRule( m_hwnd );
+ int nCurSel = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );
+ int nItemCount = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCOUNT, 0, 0 );
+
+ if ( pList && pRule && ( nCurSel != ( nItemCount - 1 ))) {
+ pRule->SetOrder( pRule->GetOrder() + 11 );
+ SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETCURSEL, nCurSel + 1, 0 );
+ pList->Reorder();
+ pList->SetModified();
+ PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
+ } }
+
+ m_proto->m_privacyListManager.Unlock();
+}
+
+void CJabberDlgPrivacyLists::btnAddList_OnClick(CCtrlButton *)
+{
+ // FIXME: line length is hard coded in dialog procedure
+ CJabberDlgPrivacyAddList dlgPrivacyAddList(m_proto, m_hwnd);
+ int nRetVal = dlgPrivacyAddList.DoModal();
+ if ( nRetVal && _tcslen( dlgPrivacyAddList.szLine )) {
+ m_proto->m_privacyListManager.Lock();
+ CPrivacyList* pList = m_proto->m_privacyListManager.FindList( dlgPrivacyAddList.szLine );
+ if ( !pList ) {
+ m_proto->m_privacyListManager.AddList( dlgPrivacyAddList.szLine );
+ pList = m_proto->m_privacyListManager.FindList( dlgPrivacyAddList.szLine );
+ if ( pList ) {
+ pList->SetModified( TRUE );
+ pList->SetLoaded( TRUE );
+ }
+ }
+ if ( pList )
+ pList->SetDeleted( FALSE );
+ int nSelected = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SELECTSTRING, -1, (LPARAM)dlgPrivacyAddList.szLine );
+ if ( nSelected == CB_ERR ) {
+ nSelected = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_ADDSTRING, 0, (LPARAM)dlgPrivacyAddList.szLine );
+ SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETITEMDATA, nSelected, (LPARAM)pList );
+ SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETCURSEL, nSelected, 0 );
+ }
+ m_proto->m_privacyListManager.Unlock();
+ PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
+ }
+}
+
+void CJabberDlgPrivacyLists::btnRemoveList_OnClick(CCtrlButton *)
+{
+ m_proto->m_privacyListManager.Lock();
+ {
+ CPrivacyList* pList = GetSelectedList(m_hwnd);
+ if ( pList ) {
+ TCHAR *szListName = pList->GetListName();
+ if ( ( m_proto->m_privacyListManager.GetActiveListName() && !_tcscmp( szListName, m_proto->m_privacyListManager.GetActiveListName()))
+ || ( m_proto->m_privacyListManager.GetDefaultListName() && !_tcscmp( szListName, m_proto->m_privacyListManager.GetDefaultListName()))) {
+ m_proto->m_privacyListManager.Unlock();
+ MessageBox( m_hwnd, TranslateTS(_T("Can't remove active or default list")), TranslateTS(_T("Sorry")), MB_OK | MB_ICONSTOP );
+ return;
+ }
+ pList->SetDeleted();
+ pList->SetModified();
+ } }
+
+ m_proto->m_privacyListManager.Unlock();
+ PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
+}
+
+void CJabberDlgPrivacyLists::btnApply_OnClick(CCtrlButton *)
+{
+ if ( !m_proto->m_bJabberOnline ) {
+ SetStatusText(TranslateT("Unable to save list because you are currently offline."));
+ return;
+ }
+
+ m_proto->m_privacyListManager.Lock();
+ {
+ if (IsWindowVisible(GetDlgItem(m_hwnd, IDC_CLIST)))
+ CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), clc_info.pList);
+
+ CPrivacyListModifyUserParam *pUserData = NULL;
+ CPrivacyList* pList = m_proto->m_privacyListManager.GetFirstList();
+ while ( pList ) {
+ if ( pList->IsModified()) {
+ CPrivacyListRule* pRule = pList->GetFirstRule();
+ if ( !pRule )
+ pList->SetDeleted();
+ if ( pList->IsDeleted()) {
+ pList->RemoveAllRules();
+ pRule = NULL;
+ }
+ pList->SetModified( FALSE );
+
+ if ( !pUserData )
+ pUserData = new CPrivacyListModifyUserParam();
+
+ pUserData->m_dwCount++;
+
+ XmlNodeIq iq( m_proto->m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyListModify, JABBER_IQ_TYPE_SET, NULL, 0, -1, pUserData ));
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS ));
+ HXML listTag = query << XCHILD( _T("list")) << XATTR( _T("name"), pList->GetListName());
+
+ while ( pRule ) {
+ HXML itemTag = listTag << XCHILD( _T("item"));
+ switch ( pRule->GetType()) {
+ case Jid:
+ itemTag << XATTR( _T("type"), _T("jid"));
+ break;
+ case Group:
+ itemTag << XATTR( _T("type"), _T("group"));
+ break;
+ case Subscription:
+ itemTag << XATTR( _T("type"), _T("subscription"));
+ break;
+ }
+ if ( pRule->GetType() != Else )
+ itemTag << XATTR( _T("value"), pRule->GetValue());
+ if ( pRule->GetAction())
+ itemTag << XATTR( _T("action"), _T("allow"));
+ else
+ itemTag << XATTR( _T("action"), _T("deny"));
+ itemTag << XATTRI( _T("order"), pRule->GetOrder());
+ DWORD dwPackets = pRule->GetPackets();
+ if ( dwPackets != JABBER_PL_RULE_TYPE_ALL ) {
+ if ( dwPackets & JABBER_PL_RULE_TYPE_IQ )
+ itemTag << XCHILD( _T("iq"));
+ if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_IN )
+ itemTag << XCHILD( _T("presence-in"));
+ if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_OUT )
+ itemTag << XCHILD( _T("presence-out"));
+ if ( dwPackets & JABBER_PL_RULE_TYPE_MESSAGE )
+ itemTag << XCHILD( _T("message"));
+ }
+ pRule = pRule->GetNext();
+ }
+
+ m_proto->m_ThreadInfo->send( iq );
+ }
+ pList = pList->GetNext();
+ } }
+ m_proto->m_privacyListManager.Unlock();
+ SetStatusText(TranslateT( JABBER_PL_BUSY_MSG ));
+ PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
+}
+
+void CJabberDlgPrivacyLists::OnCommand_Close(HWND /*hwndCtrl*/, WORD /*idCtrl*/, WORD /*idCode*/)
+{
+ if (IsWindowVisible(GetDlgItem(m_hwnd, IDC_CLIST)))
+ CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), clc_info.pList);
+
+ if (CanExit())
+ DestroyWindow(m_hwnd);
+}
+
+void CJabberDlgPrivacyLists::clcClist_OnUpdate(CCtrlClc::TEventInfo*)
+{
+ CListFilter(GetDlgItem(m_hwnd, IDC_CLIST));
+ CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), GetSelectedList(m_hwnd));
+}
+
+void CJabberDlgPrivacyLists::clcClist_OnOptionsChanged(CCtrlClc::TEventInfo*)
+{
+ CListResetOptions(GetDlgItem(m_hwnd, IDC_CLIST));
+ CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), GetSelectedList(m_hwnd));
+}
+
+void CJabberDlgPrivacyLists::clcClist_OnClick(CCtrlClc::TEventInfo *evt)
+{
+ HANDLE hItem;
+ DWORD hitFlags;
+ int iImage;
+
+ if(evt->info->iColumn==-1) return;
+ hItem = m_clcClist.HitTest(evt->info->pt.x, evt->info->pt.y, &hitFlags);
+ if(hItem==NULL) return;
+ if (!(hitFlags&CLCHT_ONITEMEXTRA)) return;
+
+ iImage = m_clcClist.GetExtraImage(hItem, evt->info->iColumn);
+ if (iImage != 0xFF)
+ {
+ if (iImage == 0)
+ iImage = evt->info->iColumn * 2 + 2;
+ else if (iImage == evt->info->iColumn * 2 + 2)
+ iImage = evt->info->iColumn * 2 + 1;
+ else
+ iImage = 0;
+
+ m_clcClist.SetExtraImage(hItem, evt->info->iColumn, iImage);
+
+ clc_info.bChanged = true;
+
+ EnableEditorControls();
+ }
+}
+
+int CJabberDlgPrivacyLists::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId) {
+ case IDC_HEADERBAR:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORX_WIDTH;
+ case IDC_BTN_SIMPLE:
+ case IDC_BTN_ADVANCED:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_TOP;
+ case IDC_LB_LISTS:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORY_HEIGHT;
+ case IDC_PL_RULES_LIST:
+ case IDC_CLIST:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORY_HEIGHT|RD_ANCHORX_WIDTH;
+ case IDC_NEWJID:
+ case IDC_CANVAS:
+ return RD_ANCHORX_LEFT|RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM;
+ case IDC_ADD_LIST:
+ case IDC_ACTIVATE:
+ case IDC_REMOVE_LIST:
+ case IDC_SET_DEFAULT:
+ case IDC_TXT_OTHERJID:
+ case IDC_ADD_RULE:
+ case IDC_UP_RULE:
+ case IDC_EDIT_RULE:
+ case IDC_DOWN_RULE:
+ case IDC_REMOVE_RULE:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
+ case IDC_ADDJID:
+ case IDC_APPLY:
+ case IDCANCEL:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ }
+ return CSuper::Resizer(urc);
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuHandlePrivacyLists( WPARAM, LPARAM )
+{
+ UI_SAFE_OPEN(CJabberDlgPrivacyLists, m_pDlgPrivacyLists);
+ return 0;
+}
+
+void CJabberProto::QueryPrivacyLists( ThreadData *pThreadInfo )
+{
+ XmlNodeIq iq( m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyLists ));
+ iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS));
+ if ( pThreadInfo )
+ pThreadInfo->send( iq );
+ else if ( m_ThreadInfo )
+ m_ThreadInfo->send( iq );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// builds privacy menu
+
+INT_PTR __cdecl CJabberProto::menuSetPrivacyList( WPARAM, LPARAM, LPARAM iList )
+{
+ m_privacyListManager.Lock();
+ CPrivacyList *pList = NULL;
+
+ if (iList)
+ {
+ pList = m_privacyListManager.GetFirstList();
+ for (int i = 1; pList && (i < iList); ++i)
+ pList = pList->GetNext();
+ }
+
+ XmlNodeIq iq( m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyListActive, JABBER_IQ_TYPE_SET, NULL, 0, -1, pList ));
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS));
+ HXML active = query << XCHILD( _T("active"));
+ if ( pList )
+ active << XATTR( _T("name"), pList->GetListName());
+ m_privacyListManager.Unlock();
+
+ m_ThreadInfo->send( iq );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// init privacy menu
+
+void CJabberProto::BuildPrivacyMenu()
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.position = 200005;
+ mi.pszContactOwner = m_szModuleName;
+ mi.icolibItem = GetIconHandle(IDI_AGENTS);
+ mi.flags = CMIF_ROOTPOPUP | CMIF_CHILDPOPUP | CMIF_ICONFROMICOLIB | CMIF_HIDDEN;
+ mi.pszName = LPGEN("Privacy Lists");
+ mi.hParentMenu = MO_GetProtoRootMenu( m_szModuleName );
+ m_hPrivacyMenuRoot = Menu_AddProtoMenuItem(&mi);
+
+ JCreateService( "/PrivacyLists", &CJabberProto::OnMenuHandlePrivacyLists );
+ char srvFce[MAX_PATH + 64];
+ mir_snprintf( srvFce, SIZEOF(srvFce), "%s/PrivacyLists", m_szModuleName );
+ mi.pszService = srvFce;
+ mi.position = 3000040000;
+ mi.flags = CMIF_CHILDPOPUP | CMIF_TCHAR | CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetIconHandle(IDI_PRIVACY_LISTS);
+ mi.ptszName = LPGENT("List Editor...");
+ mi.hParentMenu = m_hPrivacyMenuRoot;
+ Menu_AddProtoMenuItem(&mi);
+}
+
+void CJabberProto::BuildPrivacyListsMenu( bool bDeleteOld )
+{
+ int i;
+ if ( bDeleteOld )
+ for ( i=0; i < m_hPrivacyMenuItems.getCount(); i++ )
+ CallService( MO_REMOVEMENUITEM, (WPARAM)m_hPrivacyMenuItems[i], 0 );
+ m_hPrivacyMenuItems.destroy();
+
+ m_privacyListManager.Lock();
+
+ i = 0;
+ char srvFce[MAX_PATH + 64], *svcName = srvFce+strlen( m_szModuleName );
+
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.position = 2000040000;
+ mi.flags = CMIF_CHILDPOPUP | CMIF_ICONFROMICOLIB | CMIF_TCHAR;
+ mi.hParentMenu = m_hPrivacyMenuRoot;
+ mi.pszService = srvFce;
+
+ mir_snprintf( srvFce, SIZEOF(srvFce), "%s/menuPrivacy%d", m_szModuleName, i );
+ if ( i > m_privacyMenuServiceAllocated ) {
+ JCreateServiceParam( svcName, &CJabberProto::menuSetPrivacyList, i );
+ m_privacyMenuServiceAllocated = i;
+ }
+ mi.position++;
+ mi.icolibItem = LoadSkinnedIconHandle(
+ m_privacyListManager.GetActiveListName() ?
+ SKINICON_OTHER_SMALLDOT :
+ SKINICON_OTHER_EMPTYBLOB);
+ mi.ptszName = LPGENT("<none>");
+ m_hPrivacyMenuItems.insert( Menu_AddProtoMenuItem(&mi));
+
+ for ( CPrivacyList *pList = m_privacyListManager.GetFirstList(); pList; pList = pList->GetNext()) {
+ ++i;
+ mir_snprintf( srvFce, SIZEOF(srvFce), "%s/menuPrivacy%d", m_szModuleName, i );
+
+ if ( i > m_privacyMenuServiceAllocated ) {
+ JCreateServiceParam( svcName, &CJabberProto::menuSetPrivacyList, i );
+ m_privacyMenuServiceAllocated = i;
+ }
+
+ mi.position++;
+ mi.icolibItem = LoadSkinnedIconHandle(
+ lstrcmp( m_privacyListManager.GetActiveListName(), pList->GetListName()) ?
+ SKINICON_OTHER_SMALLDOT :
+ SKINICON_OTHER_EMPTYBLOB);
+ mi.ptszName = pList->GetListName();
+ m_hPrivacyMenuItems.insert( Menu_AddProtoMenuItem(&mi));
+ }
+
+ m_privacyListManager.Unlock();
+}
diff --git a/protocols/JabberG/src/jabber_privacy.h b/protocols/JabberG/src/jabber_privacy.h new file mode 100644 index 0000000000..4a2cfd8f2f --- /dev/null +++ b/protocols/JabberG/src/jabber_privacy.h @@ -0,0 +1,435 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_PRIVACY_H_
+#define _JABBER_PRIVACY_H_
+
+#define JABBER_PL_RULE_TYPE_MESSAGE 1
+#define JABBER_PL_RULE_TYPE_PRESENCE_IN 2
+#define JABBER_PL_RULE_TYPE_PRESENCE_OUT 4
+#define JABBER_PL_RULE_TYPE_IQ 8
+#define JABBER_PL_RULE_TYPE_ALL 0x0F
+
+enum PrivacyListRuleType
+{
+ Jid,
+ Group,
+ Subscription,
+ Else
+};
+
+struct CPrivacyListModifyUserParam
+{
+ BOOL m_bAllOk;
+ volatile LONG m_dwCount;
+ CPrivacyListModifyUserParam() :
+ m_bAllOk( TRUE ),
+ m_dwCount( 0 )
+ {
+ }
+};
+
+class CPrivacyList;
+
+class CPrivacyListRule
+{
+protected:
+ friend class CPrivacyList;
+public:
+ CPrivacyListRule( CJabberProto* ppro, PrivacyListRuleType type = Else, const TCHAR *szValue = _T(""), BOOL bAction = TRUE, DWORD dwOrder = 90, DWORD dwPackets = 0)
+ {
+ m_proto = ppro;
+ m_szValue = mir_tstrdup(szValue);
+ m_nType = type;
+ m_bAction = bAction;
+ m_dwOrder = dwOrder;
+ m_dwPackets = dwPackets;
+ m_pNext = NULL;
+ };
+ ~CPrivacyListRule()
+ {
+ if (m_szValue)
+ mir_free(m_szValue);
+
+ if (m_pNext)
+ delete m_pNext;
+ };
+ __inline CPrivacyListRule* GetNext()
+ {
+ return m_pNext;
+ }
+ CPrivacyListRule* SetNext(CPrivacyListRule *pNext)
+ {
+ CPrivacyListRule *pRetVal = m_pNext;
+ m_pNext = pNext;
+ return pRetVal;
+ }
+ __inline DWORD GetOrder()
+ {
+ return m_dwOrder;
+ }
+ DWORD SetOrder(DWORD dwOrder)
+ {
+ DWORD dwRetVal = m_dwOrder;
+ m_dwOrder = dwOrder;
+ return dwRetVal;
+ }
+ __inline PrivacyListRuleType GetType()
+ {
+ return m_nType;
+ }
+ __inline BOOL SetType( PrivacyListRuleType type )
+ {
+ m_nType = type;
+ return TRUE;
+ }
+ __inline TCHAR* GetValue()
+ {
+ return m_szValue;
+ }
+ __inline BOOL SetValue( TCHAR *szValue )
+ {
+ replaceStrT( m_szValue, szValue );
+ return TRUE;
+ }
+ __inline DWORD GetPackets()
+ {
+ return m_dwPackets;
+ }
+ __inline BOOL SetPackets( DWORD dwPackets )
+ {
+ m_dwPackets = dwPackets;
+ return TRUE;
+ }
+ __inline BOOL GetAction()
+ {
+ return m_bAction;
+ }
+ __inline BOOL SetAction( BOOL bAction )
+ {
+ m_bAction = bAction;
+ return TRUE;
+ }
+ CJabberProto* m_proto;
+protected:
+ PrivacyListRuleType m_nType;
+ TCHAR *m_szValue;
+ BOOL m_bAction;
+ DWORD m_dwOrder;
+ DWORD m_dwPackets;
+ CPrivacyListRule *m_pNext;
+};
+
+class CPrivacyList;
+class CPrivacyList
+{
+protected:
+ CPrivacyListRule *m_pRules;
+ TCHAR *m_szListName;
+ CPrivacyList *m_pNext;
+ BOOL m_bLoaded;
+ BOOL m_bModified;
+ BOOL m_bDeleted;
+public:
+ CJabberProto* m_proto;
+
+ CPrivacyList(CJabberProto* ppro, TCHAR *szListName)
+ {
+ m_proto = ppro;
+ m_szListName = mir_tstrdup(szListName);
+ m_pRules = NULL;
+ m_pNext = NULL;
+ m_bLoaded = FALSE;
+ m_bModified = FALSE;
+ m_bDeleted = FALSE;
+ };
+ ~CPrivacyList()
+ {
+ if (m_szListName)
+ mir_free(m_szListName);
+ RemoveAllRules();
+ if (m_pNext)
+ delete m_pNext;
+ };
+ BOOL RemoveAllRules()
+ {
+ if (m_pRules)
+ delete m_pRules;
+ m_pRules = NULL;
+ return TRUE;
+ }
+ __inline TCHAR* GetListName()
+ {
+ return m_szListName;
+ }
+ __inline CPrivacyListRule* GetFirstRule()
+ {
+ return m_pRules;
+ }
+ __inline CPrivacyList* GetNext()
+ {
+ return m_pNext;
+ }
+ CPrivacyList* SetNext(CPrivacyList *pNext)
+ {
+ CPrivacyList *pRetVal = m_pNext;
+ m_pNext = pNext;
+ return pRetVal;
+ }
+ BOOL AddRule(PrivacyListRuleType type, const TCHAR *szValue, BOOL bAction, DWORD dwOrder, DWORD dwPackets)
+ {
+ CPrivacyListRule *pRule = new CPrivacyListRule( m_proto, type, szValue, bAction, dwOrder, dwPackets );
+ if ( !pRule )
+ return FALSE;
+ pRule->SetNext( m_pRules );
+ m_pRules = pRule;
+ return TRUE;
+ }
+ BOOL AddRule(CPrivacyListRule *pRule)
+ {
+ pRule->SetNext( m_pRules );
+ m_pRules = pRule;
+ return TRUE;
+ }
+ BOOL RemoveRule(CPrivacyListRule *pRuleToRemove)
+ {
+ if ( !m_pRules )
+ return FALSE;
+
+ if ( m_pRules == pRuleToRemove ) {
+ m_pRules = m_pRules->GetNext();
+ pRuleToRemove->SetNext( NULL );
+ delete pRuleToRemove;
+ return TRUE;
+ }
+
+ CPrivacyListRule *pRule = m_pRules;
+ while ( pRule->GetNext()) {
+ if ( pRule->GetNext() == pRuleToRemove ) {
+ pRule->SetNext( pRule->GetNext()->GetNext());
+ pRuleToRemove->SetNext( NULL );
+ delete pRuleToRemove;
+ return TRUE;
+ }
+ pRule = pRule->GetNext();
+ }
+ return FALSE;
+ }
+ BOOL Reorder()
+ {
+ // 0 or 1 rules?
+ if ( !m_pRules )
+ return TRUE;
+ if ( !m_pRules->GetNext()) {
+ m_pRules->SetOrder( 100 );
+ return TRUE;
+ }
+
+ // get rules count
+ DWORD dwCount = 0;
+ CPrivacyListRule *pRule = m_pRules;
+ while ( pRule ) {
+ dwCount++;
+ pRule = pRule->GetNext();
+ }
+
+ // create pointer array for sort procedure
+ CPrivacyListRule **pRules = ( CPrivacyListRule ** )mir_alloc( dwCount * sizeof( CPrivacyListRule * ));
+ if ( !pRules )
+ return FALSE;
+ DWORD dwPos = 0;
+ pRule = m_pRules;
+ while ( pRule ) {
+ pRules[dwPos++] = pRule;
+ pRule = pRule->GetNext();
+ }
+
+ // sort array of pointers, slow, but working :)
+ DWORD i, j;
+ CPrivacyListRule *pTmp;
+ for ( i = 0; i < dwCount; i++ ) {
+ for ( j = dwCount - 1; j > i; j-- ) {
+ if ( pRules[j - 1]->GetOrder() > pRules[j]->GetOrder()) {
+ pTmp = pRules[j - 1];
+ pRules[j - 1] = pRules[j];
+ pRules[j] = pTmp;
+ }
+ }
+ }
+
+ // reorder linked list
+ DWORD dwOrder = 100;
+ CPrivacyListRule **ppPtr = &m_pRules;
+ for ( i = 0; i < dwCount; i++ ) {
+ *ppPtr = pRules[ i ];
+ ppPtr = &pRules[ i ]->m_pNext;
+ pRules[ i ]->SetOrder( dwOrder );
+ dwOrder += 10;
+ }
+ *ppPtr = NULL;
+ mir_free( pRules );
+
+ return TRUE;
+ }
+ __inline void SetLoaded(BOOL bLoaded = TRUE)
+ {
+ m_bLoaded = bLoaded;
+ }
+ __inline BOOL IsLoaded()
+ {
+ return m_bLoaded;
+ }
+ __inline void SetModified(BOOL bModified = TRUE)
+ {
+ m_bModified = bModified;
+ }
+ __inline BOOL IsModified()
+ {
+ return m_bModified;
+ }
+ __inline void SetDeleted(BOOL bDeleted = TRUE)
+ {
+ m_bDeleted = bDeleted;
+ }
+ __inline BOOL IsDeleted()
+ {
+ return m_bDeleted;
+ }
+};
+
+class CPrivacyListManager
+{
+protected:
+ TCHAR *m_szActiveListName;
+ TCHAR *m_szDefaultListName;
+ CPrivacyList *m_pLists;
+ CRITICAL_SECTION m_cs;
+ BOOL m_bModified;
+
+public:
+ CJabberProto* m_proto;
+
+ CPrivacyListManager( CJabberProto* ppro )
+ {
+ m_proto = ppro;
+ m_szActiveListName = NULL;
+ m_szDefaultListName = NULL;
+ m_pLists = NULL;
+ InitializeCriticalSection(&m_cs);
+ m_bModified = FALSE;
+ };
+ ~CPrivacyListManager()
+ {
+ if (m_szActiveListName)
+ mir_free(m_szActiveListName);
+ if (m_szDefaultListName)
+ mir_free(m_szDefaultListName);
+ RemoveAllLists();
+ DeleteCriticalSection(&m_cs);
+ };
+ BOOL Lock()
+ {
+ EnterCriticalSection(&m_cs);
+ return TRUE;
+ }
+ BOOL Unlock()
+ {
+ LeaveCriticalSection(&m_cs);
+ return TRUE;
+ }
+ void SetActiveListName(const TCHAR *szListName)
+ {
+ replaceStrT(m_szActiveListName, szListName);
+ }
+ void SetDefaultListName(const TCHAR *szListName)
+ {
+ replaceStrT(m_szDefaultListName, szListName);
+ }
+ TCHAR* GetDefaultListName()
+ {
+ return m_szDefaultListName;
+ }
+ TCHAR* GetActiveListName()
+ {
+ return m_szActiveListName;
+ }
+ BOOL RemoveAllLists()
+ {
+ if (m_pLists)
+ delete m_pLists;
+ m_pLists = NULL;
+ return TRUE;
+ }
+ CPrivacyList* FindList( const TCHAR *szListName )
+ {
+ CPrivacyList *pList = m_pLists;
+ while ( pList ) {
+ if ( !_tcscmp(pList->GetListName(), szListName ))
+ return pList;
+ pList = pList->GetNext();
+ }
+ return NULL;
+ }
+ CPrivacyList* GetFirstList()
+ {
+ return m_pLists;
+ }
+ BOOL AddList( TCHAR *szListName )
+ {
+ if (FindList(szListName))
+ return FALSE;
+ CPrivacyList *pList = new CPrivacyList( m_proto, szListName );
+ pList->SetNext( m_pLists );
+ m_pLists = pList;
+ return TRUE;
+ }
+ BOOL SetModified(BOOL bModified = TRUE)
+ {
+ m_bModified = bModified;
+ return TRUE;
+ }
+ BOOL IsModified()
+ {
+ if ( m_bModified )
+ return TRUE;
+ CPrivacyList *pList = m_pLists;
+ while ( pList ) {
+ if ( pList->IsModified())
+ return TRUE;
+ pList = pList->GetNext();
+ }
+ return FALSE;
+ }
+ BOOL IsAllListsLoaded()
+ {
+ CPrivacyList *pList = m_pLists;
+ while ( pList ) {
+ if ( !pList->IsLoaded())
+ return FALSE;
+ pList = pList->GetNext();
+ }
+ return TRUE;
+ }
+};
+
+#endif //_JABBER_PRIVACY_H_
diff --git a/protocols/JabberG/src/jabber_proto.cpp b/protocols/JabberG/src/jabber_proto.cpp new file mode 100644 index 0000000000..6444482ee6 --- /dev/null +++ b/protocols/JabberG/src/jabber_proto.cpp @@ -0,0 +1,1659 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <m_addcontact.h>
+#include <m_file.h>
+#include <m_genmenu.h>
+#include <m_icolib.h>
+
+#include "jabber_list.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+#include "jabber_disco.h"
+
+#include "m_proto_listeningto.h"
+#include "m_modernopt.h"
+
+#pragma warning(disable:4355)
+
+static int compareTransports( const TCHAR* p1, const TCHAR* p2 )
+{ return _tcsicmp( p1, p2 );
+}
+
+static int compareListItems( const JABBER_LIST_ITEM* p1, const JABBER_LIST_ITEM* p2 )
+{
+ if ( p1->list != p2->list )
+ return p1->list - p2->list;
+
+ // for bookmarks, temporary contacts & groupchat members
+ // resource must be used in the comparison
+ if (( p1->list == LIST_ROSTER && ( p1->bUseResource == TRUE || p2->bUseResource == TRUE ))
+ || ( p1->list == LIST_BOOKMARK ) || ( p1->list == LIST_VCARD_TEMP ))
+ return lstrcmpi( p1->jid, p2->jid );
+
+ TCHAR szp1[ JABBER_MAX_JID_LEN ], szp2[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( p1->jid, szp1, SIZEOF( szp1 ));
+ JabberStripJid( p2->jid, szp2, SIZEOF( szp2 ));
+ return lstrcmpi( szp1, szp2 );
+}
+
+CJabberProto::CJabberProto( const char* aProtoName, const TCHAR* aUserName ) :
+ m_options( this ),
+ m_lstTransports( 50, compareTransports ),
+ m_lstRoster( 50, compareListItems ),
+ m_iqManager( this ),
+ m_messageManager( this ),
+ m_presenceManager( this ),
+ m_sendManager( this ),
+ m_adhocManager( this ),
+ m_clientCapsManager( this ),
+ m_privacyListManager( this ),
+ m_privacyMenuServiceAllocated( -1 ),
+ m_priorityMenuVal( 0 ),
+ m_priorityMenuValSet( false ),
+ m_hPrivacyMenuRoot( 0 ),
+ m_hPrivacyMenuItems( 10 ),
+ m_pLastResourceList( NULL ),
+ m_lstJabberFeatCapPairsDynamic( 2 ),
+ m_uEnabledFeatCapsDynamic( 0 )
+{
+ InitializeCriticalSection( &m_csModeMsgMutex );
+ InitializeCriticalSection( &m_csLists );
+ InitializeCriticalSection( &m_csLastResourceMap );
+
+ m_szXmlStreamToBeInitialized = NULL;
+
+ m_iVersion = 2;
+ m_tszUserName = mir_tstrdup( aUserName );
+ m_szModuleName = mir_strdup( aProtoName );
+ m_szProtoName = mir_strdup( aProtoName );
+ _strlwr( m_szProtoName );
+ m_szProtoName[0] = toupper( m_szProtoName[0] );
+ Log( "Setting protocol/module name to '%s/%s'", m_szProtoName, m_szModuleName );
+
+ // Initialize Jabber API
+ m_JabberApi.m_psProto = this;
+ m_JabberSysApi.m_psProto = this;
+ m_JabberNetApi.m_psProto = this;
+
+ // Jabber dialog list
+ m_windowList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0);
+
+ // Protocol services and events...
+ m_hEventNudge = JCreateHookableEvent( JE_NUDGE );
+ m_hEventXStatusIconChanged = JCreateHookableEvent( JE_CUSTOMSTATUS_EXTRAICON_CHANGED );
+ m_hEventXStatusChanged = JCreateHookableEvent( JE_CUSTOMSTATUS_CHANGED );
+
+ JCreateService( PS_CREATEACCMGRUI, &CJabberProto::SvcCreateAccMgrUI );
+
+ JCreateService( PS_GETAVATARINFOT, &CJabberProto::JabberGetAvatarInfo );
+ JCreateService( PS_GETMYAWAYMSG, &CJabberProto::GetMyAwayMsg );
+ JCreateService( PS_SET_LISTENINGTO, &CJabberProto::OnSetListeningTo );
+
+ JCreateService( PS_JOINCHAT, &CJabberProto::OnJoinChat );
+ JCreateService( PS_LEAVECHAT, &CJabberProto::OnLeaveChat );
+
+ JCreateService( JS_GETCUSTOMSTATUSICON, &CJabberProto::OnGetXStatusIcon );
+ JCreateService( JS_GETXSTATUS, &CJabberProto::OnGetXStatus );
+ JCreateService( JS_SETXSTATUS, &CJabberProto::OnSetXStatus );
+ JCreateService( JS_SETXSTATUSEX, &CJabberProto::OnSetXStatusEx );
+
+ // not needed anymore and therefore commented out
+ // JCreateService( JS_GETXSTATUSEX, &CJabberProto::OnGetXStatusEx );
+
+ JCreateService( JS_HTTP_AUTH, &CJabberProto::OnHttpAuthRequest );
+ JCreateService( JS_INCOMING_NOTE_EVENT, &CJabberProto::OnIncomingNoteEvent );
+
+ JCreateService( JS_SENDXML, &CJabberProto::ServiceSendXML );
+ JCreateService( PS_GETMYAVATART, &CJabberProto::JabberGetAvatar );
+ JCreateService( PS_GETAVATARCAPS, &CJabberProto::JabberGetAvatarCaps );
+ JCreateService( PS_SETMYAVATART, &CJabberProto::JabberSetAvatar );
+ JCreateService( PS_SETMYNICKNAME, &CJabberProto::JabberSetNickname );
+
+ JCreateService( JS_GETADVANCEDSTATUSICON, &CJabberProto::JGetAdvancedStatusIcon );
+ JCreateService( JS_DB_GETEVENTTEXT_CHATSTATES, &CJabberProto::OnGetEventTextChatStates );
+ JCreateService( JS_DB_GETEVENTTEXT_PRESENCE, &CJabberProto::OnGetEventTextPresence );
+
+ JCreateService( JS_GETJABBERAPI, &CJabberProto::JabberGetApi );
+
+ // XEP-0224 support (Attention/Nudge)
+ JCreateService( JS_SEND_NUDGE, &CJabberProto::JabberSendNudge );
+
+ // service to get from protocol chat buddy info
+ JCreateService( MS_GC_PROTO_GETTOOLTIPTEXT, &CJabberProto::JabberGCGetToolTipText );
+
+ // XMPP URI parser service for "File Association Manager" plugin
+ JCreateService( JS_PARSE_XMPP_URI, &CJabberProto::JabberServiceParseXmppURI );
+
+ JHookEvent( ME_MODERNOPT_INITIALIZE, &CJabberProto::OnModernOptInit );
+ JHookEvent( ME_OPT_INITIALISE, &CJabberProto::OnOptionsInit );
+ JHookEvent( ME_SKIN2_ICONSCHANGED, &CJabberProto::OnReloadIcons );
+
+ m_iqManager.FillPermanentHandlers();
+ m_iqManager.Start();
+ m_messageManager.FillPermanentHandlers();
+ m_messageManager.Start();
+ m_presenceManager.FillPermanentHandlers();
+ m_presenceManager.Start();
+ m_sendManager.Start();
+ m_adhocManager.FillDefaultNodes();
+ m_clientCapsManager.AddDefaultCaps();
+
+ IconsInit();
+ InitPopups();
+ GlobalMenuInit();
+ WsInit();
+ IqInit();
+ SerialInit();
+ ConsoleInit();
+ InitCustomFolders();
+
+ m_pepServices.insert(new CPepMood(this));
+ m_pepServices.insert(new CPepActivity(this));
+
+ *m_savedPassword = 0;
+
+ char text[ MAX_PATH ];
+ mir_snprintf( text, sizeof( text ), "%s/Status", m_szModuleName );
+ CallService( MS_DB_SETSETTINGRESIDENT, TRUE, ( LPARAM )text );
+ mir_snprintf( text, sizeof( text ), "%s/%s", m_szModuleName, DBSETTING_DISPLAY_UID );
+ CallService( MS_DB_SETSETTINGRESIDENT, TRUE, ( LPARAM )text );
+
+ mir_snprintf( text, sizeof( text ), "%s/SubscriptionText", m_szModuleName );
+ CallService( MS_DB_SETSETTINGRESIDENT, TRUE, ( LPARAM )text );
+ mir_snprintf( text, sizeof( text ), "%s/Subscription", m_szModuleName );
+ CallService( MS_DB_SETSETTINGRESIDENT, TRUE, ( LPARAM )text );
+ mir_snprintf( text, sizeof( text ), "%s/Auth", m_szModuleName );
+ CallService( MS_DB_SETSETTINGRESIDENT, TRUE, ( LPARAM )text );
+ mir_snprintf( text, sizeof( text ), "%s/Grant", m_szModuleName );
+ CallService( MS_DB_SETSETTINGRESIDENT, TRUE, ( LPARAM )text );
+
+ DBVARIANT dbv;
+ if ( !JGetStringT( NULL, "XmlLang", &dbv )) {
+ m_tszSelectedLang = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else m_tszSelectedLang = mir_tstrdup( _T( "en" ));
+
+ if (!DBGetContactSettingString(NULL, m_szModuleName, "Password", &dbv)) {
+ CallService(MS_DB_CRYPT_DECODESTRING, lstrlenA(dbv.pszVal) + 1, (LPARAM)dbv.pszVal);
+ TCHAR *pssw = mir_a2t(dbv.pszVal);
+ JSetStringCrypt(NULL, "LoginPassword", pssw);
+ mir_free(pssw);
+ JFreeVariant(&dbv);
+ JDeleteSetting(NULL, "Password");
+ }
+
+ CleanLastResourceMap();
+}
+
+CJabberProto::~CJabberProto()
+{
+ WsUninit();
+ IqUninit();
+ XStatusUninit();
+ SerialUninit();
+ ConsoleUninit();
+ GlobalMenuUninit();
+
+ delete m_pInfoFrame;
+
+ DestroyHookableEvent( m_hEventNudge );
+ DestroyHookableEvent( m_hEventXStatusIconChanged );
+ DestroyHookableEvent( m_hEventXStatusChanged );
+ if ( m_hInitChat )
+ DestroyHookableEvent( m_hInitChat );
+
+ CleanLastResourceMap();
+
+ ListWipe();
+ DeleteCriticalSection( &m_csLists );
+
+ mir_free( m_tszSelectedLang );
+ mir_free( m_phIconLibItems );
+ mir_free( m_AuthMechs.m_gssapiHostName );
+
+ DeleteCriticalSection( &m_filterInfo.csPatternLock );
+ DeleteCriticalSection( &m_csModeMsgMutex );
+ DeleteCriticalSection( &m_csLastResourceMap );
+
+ mir_free( m_modeMsgs.szOnline );
+ mir_free( m_modeMsgs.szAway );
+ mir_free( m_modeMsgs.szNa );
+ mir_free( m_modeMsgs.szDnd );
+ mir_free( m_modeMsgs.szFreechat );
+
+ mir_free( m_transportProtoTableStartIndex );
+
+ mir_free( m_szStreamId );
+ mir_free( m_szProtoName );
+ mir_free( m_szModuleName );
+ mir_free( m_tszUserName );
+
+ int i;
+ for ( i=0; i < m_lstTransports.getCount(); i++ )
+ mir_free( m_lstTransports[i] );
+ m_lstTransports.destroy();
+
+ for ( i=0; i < m_lstJabberFeatCapPairsDynamic.getCount(); i++ ) {
+ mir_free( m_lstJabberFeatCapPairsDynamic[i]->szExt );
+ mir_free( m_lstJabberFeatCapPairsDynamic[i]->szFeature );
+ if ( m_lstJabberFeatCapPairsDynamic[i]->szDescription )
+ mir_free( m_lstJabberFeatCapPairsDynamic[i]->szDescription );
+ delete m_lstJabberFeatCapPairsDynamic[i];
+ }
+ m_lstJabberFeatCapPairsDynamic.destroy();
+ m_hPrivacyMenuItems.destroy();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// OnModulesLoadedEx - performs hook registration
+
+static COLORREF crCols[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+
+int CJabberProto::OnModulesLoadedEx( WPARAM, LPARAM )
+{
+ JHookEvent( ME_USERINFO_INITIALISE, &CJabberProto::OnUserInfoInit );
+ XStatusInit();
+ m_pepServices.InitGui();
+
+ m_pInfoFrame = new CJabberInfoFrame(this);
+
+ if ( ServiceExists( MS_GC_REGISTER )) {
+ jabberChatDllPresent = true;
+
+ GCREGISTER gcr = {0};
+ gcr.cbSize = sizeof( GCREGISTER );
+ gcr.dwFlags = GC_TYPNOTIF | GC_CHANMGR | GC_TCHAR;
+ gcr.iMaxText = 0;
+ gcr.nColors = 16;
+ gcr.pColors = &crCols[0];
+ gcr.ptszModuleDispName = m_tszUserName;
+ gcr.pszModule = m_szModuleName;
+ CallServiceSync( MS_GC_REGISTER, NULL, ( LPARAM )&gcr );
+
+ JHookEvent( ME_GC_EVENT, &CJabberProto::JabberGcEventHook );
+ JHookEvent( ME_GC_BUILDMENU, &CJabberProto::JabberGcMenuHook );
+
+ char szEvent[ 200 ];
+ mir_snprintf( szEvent, sizeof szEvent, "%s\\ChatInit", m_szModuleName );
+ m_hInitChat = CreateHookableEvent( szEvent );
+ JHookEvent( szEvent, &CJabberProto::JabberGcInit );
+ }
+
+ if ( ServiceExists( MS_MSG_ADDICON )) {
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = m_szModuleName;
+ sid.hIcon = LoadIconEx("main");
+ sid.hIconDisabled = LoadIconEx("main");
+ sid.flags = MBF_HIDDEN;
+ sid.szTooltip = Translate("Jabber Resource");
+ CallService(MS_MSG_ADDICON, 0, (LPARAM) &sid);
+ JHookEvent( ME_MSG_ICONPRESSED, &CJabberProto::OnProcessSrmmIconClick );
+ JHookEvent( ME_MSG_WINDOWEVENT, &CJabberProto::OnProcessSrmmEvent );
+
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL ) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto != NULL && !strcmp( szProto, m_szModuleName ))
+ MenuHideSrmmIcon(hContact);
+ hContact = db_find_next(hContact);
+ } }
+
+ DBEVENTTYPEDESCR dbEventType = {0};
+ dbEventType.cbSize = sizeof(DBEVENTTYPEDESCR);
+ dbEventType.eventType = JABBER_DB_EVENT_TYPE_CHATSTATES;
+ dbEventType.module = m_szModuleName;
+ dbEventType.descr = "Chat state notifications";
+ CallService( MS_DB_EVENT_REGISTERTYPE, 0, (LPARAM)&dbEventType );
+
+ dbEventType.eventType = JABBER_DB_EVENT_TYPE_PRESENCE;
+ dbEventType.module = m_szModuleName;
+ dbEventType.descr = "Presence notifications";
+ CallService( MS_DB_EVENT_REGISTERTYPE, 0, (LPARAM)&dbEventType );
+
+ JHookEvent( ME_IDLE_CHANGED, &CJabberProto::OnIdleChanged );
+
+ CheckAllContactsAreTransported();
+
+ // Set all contacts to offline
+ HANDLE hContact = db_find_first();
+ while ( hContact != NULL ) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto != NULL && !strcmp( szProto, m_szModuleName )) {
+ SetContactOfflineStatus( hContact );
+
+ if ( JGetByte( hContact, "IsTransport", 0 )) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ TCHAR* domain = NEWTSTR_ALLOCA(dbv.ptszVal);
+ TCHAR* resourcepos = _tcschr( domain, '/' );
+ if ( resourcepos != NULL )
+ *resourcepos = '\0';
+ m_lstTransports.insert( mir_tstrdup( domain ));
+ JFreeVariant( &dbv );
+ } } }
+
+ hContact = db_find_next(hContact);
+ }
+
+ CleanLastResourceMap();
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberAddToList - adds a contact to the contact list
+
+HANDLE CJabberProto::AddToListByJID( const TCHAR* newJid, DWORD flags )
+{
+ HANDLE hContact;
+ TCHAR* jid, *nick;
+
+ Log( "AddToListByJID jid = " TCHAR_STR_PARAM, newJid );
+
+ if (( hContact=HContactFromJID( newJid )) == NULL ) {
+ // not already there: add
+ jid = mir_tstrdup( newJid );
+ Log( "Add new jid to contact jid = " TCHAR_STR_PARAM, jid );
+ hContact = ( HANDLE ) CallService( MS_DB_CONTACT_ADD, 0, 0 );
+ CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM ) hContact, ( LPARAM )m_szModuleName );
+ JSetStringT( hContact, "jid", jid );
+ if (( nick=JabberNickFromJID( newJid )) == NULL )
+ nick = mir_tstrdup( newJid );
+// JSetStringT( hContact, "Nick", nick );
+ mir_free( nick );
+ mir_free( jid );
+
+ // Note that by removing or disable the "NotOnList" will trigger
+ // the plugin to add a particular contact to the roster list.
+ // See DBSettingChanged hook at the bottom part of this source file.
+ // But the add module will delete "NotOnList". So we will not do it here.
+ // Also because we need "MyHandle" and "Group" info, which are set after
+ // PS_ADDTOLIST is called but before the add dialog issue deletion of
+ // "NotOnList".
+ // If temporary add, "NotOnList" won't be deleted, and that's expected.
+ DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 );
+ if ( flags & PALF_TEMPORARY )
+ DBWriteContactSettingByte( hContact, "CList", "Hidden", 1 );
+ }
+ else {
+ // already exist
+ // Set up a dummy "NotOnList" when adding permanently only
+ if ( !( flags & PALF_TEMPORARY ))
+ DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 );
+ }
+
+ if (hContact && newJid)
+ DBCheckIsTransportedContact( newJid, hContact );
+ return hContact;
+}
+
+HANDLE CJabberProto::AddToList( int flags, PROTOSEARCHRESULT* psr )
+{
+ if ( psr->cbSize != sizeof( JABBER_SEARCH_RESULT ) && psr->id == NULL )
+ return NULL;
+
+ JABBER_SEARCH_RESULT* jsr = ( JABBER_SEARCH_RESULT* )psr;
+ TCHAR *jid = psr->id ? psr->id : jsr->jid;
+ return AddToListByJID( jid, flags );
+}
+
+HANDLE __cdecl CJabberProto::AddToListByEvent( int flags, int /*iContact*/, HANDLE hDbEvent )
+{
+ DBEVENTINFO dbei;
+ HANDLE hContact;
+ char* nick, *firstName, *lastName, *jid;
+
+ Log( "AddToListByEvent" );
+ ZeroMemory( &dbei, sizeof( dbei ));
+ dbei.cbSize = sizeof( dbei );
+ if (( dbei.cbBlob = CallService( MS_DB_EVENT_GETBLOBSIZE, ( WPARAM )hDbEvent, 0 )) == ( DWORD )( -1 ))
+ return NULL;
+ if (( dbei.pBlob=( PBYTE ) alloca( dbei.cbBlob )) == NULL )
+ return NULL;
+ if ( CallService( MS_DB_EVENT_GET, ( WPARAM )hDbEvent, ( LPARAM )&dbei ))
+ return NULL;
+ if ( strcmp( dbei.szModule, m_szModuleName ))
+ return NULL;
+
+/*
+ // EVENTTYPE_CONTACTS is when adding from when we receive contact list ( not used in Jabber )
+ // EVENTTYPE_ADDED is when adding from when we receive "You are added" ( also not used in Jabber )
+ // Jabber will only handle the case of EVENTTYPE_AUTHREQUEST
+ // EVENTTYPE_AUTHREQUEST is when adding from the authorization request dialog
+*/
+
+ if ( dbei.eventType != EVENTTYPE_AUTHREQUEST )
+ return NULL;
+
+ nick = ( char* )( dbei.pBlob + sizeof( DWORD )*2);
+ firstName = nick + strlen( nick ) + 1;
+ lastName = firstName + strlen( firstName ) + 1;
+ jid = lastName + strlen( lastName ) + 1;
+
+ TCHAR* newJid = dbei.flags & DBEF_UTF ? mir_utf8decodeT( jid ) : mir_a2t( jid );
+ hContact = ( HANDLE ) AddToListByJID( newJid, flags );
+ mir_free( newJid );
+ return hContact;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberAuthAllow - processes the successful authorization
+
+int CJabberProto::Authorize( HANDLE hDbEvent )
+{
+ DBEVENTINFO dbei;
+ char* nick, *firstName, *lastName, *jid;
+
+ if ( !m_bJabberOnline )
+ return 1;
+
+ memset( &dbei, 0, sizeof( dbei ));
+ dbei.cbSize = sizeof( dbei );
+ if (( dbei.cbBlob=CallService( MS_DB_EVENT_GETBLOBSIZE, ( WPARAM )hDbEvent, 0 )) == ( DWORD )( -1 ))
+ return 1;
+ if (( dbei.pBlob=( PBYTE )alloca( dbei.cbBlob )) == NULL )
+ return 1;
+ if ( CallService( MS_DB_EVENT_GET, ( WPARAM )hDbEvent, ( LPARAM )&dbei ))
+ return 1;
+ if ( dbei.eventType != EVENTTYPE_AUTHREQUEST )
+ return 1;
+ if ( strcmp( dbei.szModule, m_szModuleName ))
+ return 1;
+
+ nick = ( char* )(dbei.pBlob + sizeof(DWORD)*2);
+ firstName = nick + strlen( nick ) + 1;
+ lastName = firstName + strlen( firstName ) + 1;
+ jid = lastName + strlen( lastName ) + 1;
+
+ Log( "Send 'authorization allowed' to " TCHAR_STR_PARAM, jid );
+
+ TCHAR* newJid = dbei.flags & DBEF_UTF ? mir_utf8decodeT( jid ) : mir_a2t( jid );
+
+ m_ThreadInfo->send( XmlNode( _T("presence")) << XATTR( _T("to"), newJid) << XATTR( _T("type"), _T("subscribed")));
+
+ // Automatically add this user to my roster if option is enabled
+ if ( m_options.AutoAdd == TRUE ) {
+ HANDLE hContact;
+ JABBER_LIST_ITEM *item;
+
+ if (( item = ListGetItemPtr( LIST_ROSTER, newJid )) == NULL || ( item->subscription != SUB_BOTH && item->subscription != SUB_TO )) {
+ Log( "Try adding contact automatically jid = " TCHAR_STR_PARAM, jid );
+ if (( hContact=AddToListByJID( newJid, 0 )) != NULL ) {
+ // Trigger actual add by removing the "NotOnList" added by AddToListByJID()
+ // See AddToListByJID() and JabberDbSettingChanged().
+ DBDeleteContactSetting( hContact, "CList", "NotOnList" );
+ } } }
+
+ mir_free( newJid );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberAuthDeny - handles the unsuccessful authorization
+
+int CJabberProto::AuthDeny( HANDLE hDbEvent, const TCHAR* /*szReason*/ )
+{
+ DBEVENTINFO dbei;
+ char* nick, *firstName, *lastName, *jid;
+
+ if ( !m_bJabberOnline )
+ return 1;
+
+ Log( "Entering AuthDeny" );
+ memset( &dbei, 0, sizeof( dbei ));
+ dbei.cbSize = sizeof( dbei );
+ if (( dbei.cbBlob=CallService( MS_DB_EVENT_GETBLOBSIZE, ( WPARAM )hDbEvent, 0 )) == ( DWORD )( -1 ))
+ return 1;
+ if (( dbei.pBlob=( PBYTE ) mir_alloc( dbei.cbBlob )) == NULL )
+ return 1;
+ if ( CallService( MS_DB_EVENT_GET, ( WPARAM )hDbEvent, ( LPARAM )&dbei )) {
+ mir_free( dbei.pBlob );
+ return 1;
+ }
+ if ( dbei.eventType != EVENTTYPE_AUTHREQUEST ) {
+ mir_free( dbei.pBlob );
+ return 1;
+ }
+ if ( strcmp( dbei.szModule, m_szModuleName )) {
+ mir_free( dbei.pBlob );
+ return 1;
+ }
+
+ nick = ( char* )( dbei.pBlob + sizeof(DWORD)*2);
+ firstName = nick + strlen( nick ) + 1;
+ lastName = firstName + strlen( firstName ) + 1;
+ jid = lastName + strlen( lastName ) + 1;
+
+ Log( "Send 'authorization denied' to %s" , jid );
+
+ TCHAR* newJid = dbei.flags & DBEF_UTF ? mir_utf8decodeT( jid ) : mir_a2t( jid );
+
+ m_ThreadInfo->send( XmlNode( _T("presence")) << XATTR( _T("to"), newJid) << XATTR( _T("type"), _T("unsubscribed")));
+
+ mir_free( newJid );
+ mir_free( dbei.pBlob );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSR_AUTH
+
+int __cdecl CJabberProto::AuthRecv( HANDLE, PROTORECVEVENT* )
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSS_AUTHREQUEST
+
+int __cdecl CJabberProto::AuthRequest( HANDLE, const TCHAR* )
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// ChangeInfo
+
+HANDLE __cdecl CJabberProto::ChangeInfo( int /*iInfoType*/, void* )
+{
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberFileAllow - starts a file transfer
+
+HANDLE __cdecl CJabberProto::FileAllow( HANDLE /*hContact*/, HANDLE hTransfer, const TCHAR* szPath )
+{
+ if ( !m_bJabberOnline )
+ return 0;
+
+ filetransfer* ft = ( filetransfer* )hTransfer;
+ ft->std.tszWorkingDir = mir_tstrdup( szPath );
+ size_t len = _tcslen( ft->std.tszWorkingDir )-1;
+ if ( ft->std.tszWorkingDir[len] == '/' || ft->std.tszWorkingDir[len] == '\\' )
+ ft->std.tszWorkingDir[len] = 0;
+
+ switch ( ft->type ) {
+ case FT_OOB:
+ JForkThread(( JThreadFunc )&CJabberProto::FileReceiveThread, ft );
+ break;
+ case FT_BYTESTREAM:
+ FtAcceptSiRequest( ft );
+ break;
+ case FT_IBB:
+ FtAcceptIbbRequest( ft );
+ break;
+ }
+ return hTransfer;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberFileCancel - cancels a file transfer
+
+int __cdecl CJabberProto::FileCancel( HANDLE /*hContact*/, HANDLE hTransfer )
+{
+ filetransfer* ft = ( filetransfer* )hTransfer;
+ HANDLE hEvent;
+
+ Log( "Invoking FileCancel()" );
+ if ( ft->type == FT_OOB ) {
+ if ( ft->s ) {
+ Log( "FT canceled" );
+ Log( "Closing ft->s = %d", ft->s );
+ ft->state = FT_ERROR;
+ Netlib_CloseHandle( ft->s );
+ ft->s = NULL;
+ if ( ft->hFileEvent != NULL ) {
+ hEvent = ft->hFileEvent;
+ ft->hFileEvent = NULL;
+ SetEvent( hEvent );
+ }
+ Log( "ft->s is now NULL, ft->state is now FT_ERROR" );
+ }
+ }
+ else FtCancel( ft );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberFileDeny - denies a file transfer
+
+int __cdecl CJabberProto::FileDeny( HANDLE /*hContact*/, HANDLE hTransfer, const TCHAR* /*reason*/ )
+{
+ if ( !m_bJabberOnline )
+ return 1;
+
+ filetransfer* ft = ( filetransfer* )hTransfer;
+
+ switch ( ft->type ) {
+ case FT_OOB:
+ m_ThreadInfo->send( XmlNodeIq( _T("error"), ft->iqId, ft->jid ) << XCHILD( _T("error"), _T("File transfer refused")) << XATTRI( _T("code"), 406 ));
+ break;
+
+ case FT_BYTESTREAM:
+ case FT_IBB:
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("error"), ft->iqId, ft->jid )
+ << XCHILD( _T("error"), _T("File transfer refused")) << XATTRI( _T("code"), 403 ) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("forbidden"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"))
+ << XCHILD( _T("text"), _T("File transfer refused")) << XATTR( _T("xmlns"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+ break;
+ }
+ delete ft;
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberFileResume - processes file renaming etc
+
+int __cdecl CJabberProto::FileResume( HANDLE hTransfer, int* action, const TCHAR** szFilename )
+{
+ filetransfer* ft = ( filetransfer* )hTransfer;
+ if ( !m_bJabberOnline || ft == NULL )
+ return 1;
+
+ if ( *action == FILERESUME_RENAME )
+ replaceStrT( ft->std.tszCurrentFile, *szFilename );
+
+ SetEvent( ft->hWaitEvent );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GetCaps - return protocol capabilities bits
+
+DWORD_PTR __cdecl CJabberProto::GetCaps( int type, HANDLE hContact )
+{
+ switch( type ) {
+ case PFLAGNUM_1:
+ return PF1_IM | PF1_AUTHREQ | PF1_CHAT | PF1_SERVERCLIST | PF1_MODEMSG | PF1_BASICSEARCH | PF1_EXTSEARCH | PF1_FILE | PF1_CONTACT;
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_HEAVYDND | PF2_FREECHAT;
+ case PFLAGNUM_3:
+ return PF2_ONLINE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_HEAVYDND | PF2_FREECHAT;
+ case PFLAGNUM_4:
+ return PF4_FORCEAUTH | PF4_NOCUSTOMAUTH | PF4_NOAUTHDENYREASON | PF4_SUPPORTTYPING | PF4_AVATARS | PF4_IMSENDUTF | PF4_FORCEADDED;
+ case PFLAG_UNIQUEIDTEXT:
+ return ( DWORD_PTR ) JTranslate( "JID" );
+ case PFLAG_UNIQUEIDSETTING:
+ return ( DWORD_PTR ) "jid";
+ case PFLAG_MAXCONTACTSPERPACKET:
+ {
+ DBVARIANT dbv;
+ if(JGetStringT( hContact, "jid", &dbv ))
+ return 0;
+ TCHAR szClientJid[ JABBER_MAX_JID_LEN ];
+ GetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
+ JFreeVariant( &dbv );
+ JabberCapsBits jcb = GetResourceCapabilites( szClientJid, TRUE );
+ return (( ~jcb & JABBER_CAPS_ROSTER_EXCHANGE ) ? 0 : 50);
+ }
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GetIcon - loads an icon for the contact list
+
+HICON __cdecl CJabberProto::GetIcon( int iconIndex )
+{
+ if (LOWORD(iconIndex) == PLI_PROTOCOL)
+ {
+ if (iconIndex & PLIF_ICOLIBHANDLE)
+ return (HICON)GetIconHandle(IDI_JABBER);
+
+ bool big = (iconIndex & PLIF_SMALL) == 0;
+ HICON hIcon = LoadIconEx("main", big);
+
+ if (iconIndex & PLIF_ICOLIB)
+ return hIcon;
+
+ HICON hIcon2 = CopyIcon(hIcon);
+ g_ReleaseIcon(hIcon);
+ return hIcon2;
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GetInfo - retrieves a contact info
+
+int __cdecl CJabberProto::GetInfo( HANDLE hContact, int /*infoType*/ )
+{
+ if ( !m_bJabberOnline )
+ return 1;
+
+ int result = 1;
+ DBVARIANT dbv;
+ if ( JGetByte( hContact, "ChatRoom" , 0))
+ return 1;
+
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ if ( m_ThreadInfo ) {
+ TCHAR jid[ JABBER_MAX_JID_LEN ];
+ GetClientJID( dbv.ptszVal, jid, SIZEOF( jid ));
+
+ m_ThreadInfo->send(
+ XmlNodeIq( m_iqManager.AddHandler( &CJabberProto::OnIqResultEntityTime, JABBER_IQ_TYPE_GET, jid, JABBER_IQ_PARSE_HCONTACT ))
+ << XCHILDNS( _T("time"), _T(JABBER_FEAT_ENTITY_TIME)));
+
+ // XEP-0012, last logoff time
+ XmlNodeIq iq2( m_iqManager.AddHandler( &CJabberProto::OnIqResultLastActivity, JABBER_IQ_TYPE_GET, dbv.ptszVal, JABBER_IQ_PARSE_FROM ));
+ iq2 << XQUERY( _T(JABBER_FEAT_LAST_ACTIVITY));
+ m_ThreadInfo->send( iq2 );
+
+ JABBER_LIST_ITEM *item = NULL;
+
+ if (( item = ListGetItemPtr( LIST_VCARD_TEMP, dbv.ptszVal )) == NULL)
+ item = ListGetItemPtr( LIST_ROSTER, dbv.ptszVal );
+
+ if ( !item ) {
+ TCHAR szBareJid[ JABBER_MAX_JID_LEN ];
+ _tcsncpy( szBareJid, dbv.ptszVal, SIZEOF( szBareJid ));
+ TCHAR* pDelimiter = _tcschr( szBareJid, _T('/'));
+ if ( pDelimiter ) {
+ *pDelimiter = 0;
+ pDelimiter++;
+ if ( !*pDelimiter )
+ pDelimiter = NULL;
+ }
+ JABBER_LIST_ITEM *tmpItem = NULL;
+ if ( pDelimiter && ( tmpItem = ListGetItemPtr( LIST_CHATROOM, szBareJid ))) {
+ JABBER_RESOURCE_STATUS *him = NULL;
+ for ( int i=0; i < tmpItem->resourceCount; i++ ) {
+ JABBER_RESOURCE_STATUS& p = tmpItem->resource[i];
+ if ( !lstrcmp( p.resourceName, pDelimiter )) him = &p;
+ }
+ if ( him ) {
+ item = ListAdd( LIST_VCARD_TEMP, dbv.ptszVal );
+ ListAddResource( LIST_VCARD_TEMP, dbv.ptszVal, him->status, him->statusMessage, him->priority );
+ }
+ }
+ else
+ item = ListAdd( LIST_VCARD_TEMP, dbv.ptszVal );
+ }
+
+ if ( item ) {
+ if ( item->resource ) {
+ for ( int i = 0; i < item->resourceCount; i++ ) {
+ TCHAR szp1[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( dbv.ptszVal, szp1, SIZEOF( szp1 ));
+ mir_sntprintf( jid, 256, _T("%s/%s"), szp1, item->resource[i].resourceName );
+
+ XmlNodeIq iq3( m_iqManager.AddHandler( &CJabberProto::OnIqResultLastActivity, JABBER_IQ_TYPE_GET, jid, JABBER_IQ_PARSE_FROM ));
+ iq3 << XQUERY( _T(JABBER_FEAT_LAST_ACTIVITY));
+ m_ThreadInfo->send( iq3 );
+
+ if ( !item->resource[i].dwVersionRequestTime ) {
+ XmlNodeIq iq4( m_iqManager.AddHandler( &CJabberProto::OnIqResultVersion, JABBER_IQ_TYPE_GET, jid, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_HCONTACT | JABBER_IQ_PARSE_CHILD_TAG_NODE ));
+ iq4 << XQUERY( _T(JABBER_FEAT_VERSION));
+ m_ThreadInfo->send( iq4 );
+ }
+
+ if ( !item->resource[i].pSoftwareInfo ) {
+ XmlNodeIq iq5( m_iqManager.AddHandler( &CJabberProto::OnIqResultCapsDiscoInfoSI, JABBER_IQ_TYPE_GET, jid, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_CHILD_TAG_NODE | JABBER_IQ_PARSE_HCONTACT ));
+ iq5 << XQUERY( _T(JABBER_FEAT_DISCO_INFO ));
+ m_ThreadInfo->send( iq5 );
+ }
+ }
+ }
+ else if ( !item->itemResource.dwVersionRequestTime ) {
+ XmlNodeIq iq4( m_iqManager.AddHandler( &CJabberProto::OnIqResultVersion, JABBER_IQ_TYPE_GET, item->jid, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_HCONTACT | JABBER_IQ_PARSE_CHILD_TAG_NODE ));
+ iq4 << XQUERY( _T(JABBER_FEAT_VERSION));
+ m_ThreadInfo->send( iq4 );
+ } } }
+
+ SendGetVcard( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ result = 0;
+ }
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SearchBasic - searches the contact by JID
+
+struct JABBER_SEARCH_BASIC
+{ int hSearch;
+ TCHAR jid[128];
+};
+
+void __cdecl CJabberProto::BasicSearchThread( JABBER_SEARCH_BASIC *jsb )
+{
+ Sleep( 100 );
+
+ JABBER_SEARCH_RESULT jsr = { 0 };
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+ jsr.hdr.flags = PSR_TCHAR;
+ jsr.hdr.nick = jsb->jid;
+ jsr.hdr.firstName = _T("");
+ jsr.hdr.lastName = _T("");
+ jsr.hdr.id = jsb->jid;
+
+ _tcsncpy( jsr.jid, jsb->jid, SIZEOF( jsr.jid ));
+
+ jsr.jid[SIZEOF( jsr.jid )-1] = '\0';
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, ( HANDLE ) jsb->hSearch, ( LPARAM )&jsr );
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) jsb->hSearch, 0 );
+ mir_free( jsb );
+}
+
+HANDLE __cdecl CJabberProto::SearchBasic( const TCHAR* szJid )
+{
+ Log( "JabberBasicSearch called with lParam = '%s'", szJid );
+
+ JABBER_SEARCH_BASIC *jsb;
+ if ( !m_bJabberOnline || ( jsb=( JABBER_SEARCH_BASIC * ) mir_alloc( sizeof( JABBER_SEARCH_BASIC )))==NULL )
+ return 0;
+
+ if ( _tcschr( szJid, '@' ) == NULL ) {
+ TCHAR *szServer = mir_a2t( m_ThreadInfo->server );
+ const TCHAR* p = _tcsstr( szJid, szServer );
+ if ( !p )
+ {
+ bool numericjid = true;
+ for (const TCHAR * i = szJid; *i && numericjid; ++i)
+ numericjid = (*i >= '0') && (*i <= '9');
+
+ mir_free( szServer );
+ szServer = JGetStringT( NULL, "LoginServer" );
+ if ( !szServer )
+ {
+ szServer = mir_tstrdup( _T( "jabber.org" ));
+ } else if (numericjid && !_tcsicmp(szServer, _T("S.ms")))
+ {
+ mir_free(szServer);
+ szServer = mir_tstrdup(_T("sms"));
+ }
+ mir_sntprintf( jsb->jid, SIZEOF(jsb->jid), _T("%s@%s"), szJid, szServer );
+ }
+ else _tcsncpy( jsb->jid, szJid, SIZEOF(jsb->jid));
+ mir_free( szServer );
+ }
+ else _tcsncpy( jsb->jid, szJid, SIZEOF(jsb->jid));
+
+ Log( "Adding '%s' without validation", jsb->jid );
+ jsb->hSearch = SerialNext();
+ JForkThread(( JThreadFunc )&CJabberProto::BasicSearchThread, jsb );
+ return ( HANDLE )jsb->hSearch;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SearchByEmail - searches the contact by its e-mail
+
+HANDLE __cdecl CJabberProto::SearchByEmail( const TCHAR* email )
+{
+ if ( !m_bJabberOnline ) return 0;
+ if ( email == NULL ) return 0;
+
+ char szServerName[100];
+ if ( JGetStaticString( "Jud", NULL, szServerName, sizeof szServerName ))
+ strcpy( szServerName, "users.jabber.org" );
+
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_GETSEARCH, &CJabberProto::OnIqResultSetSearch );
+ m_ThreadInfo->send( XmlNodeIq( _T("set"), iqId, _A2T(szServerName)) << XQUERY( _T("jabber:iq:search"))
+ << XCHILD( _T("email"), email));
+ return ( HANDLE )iqId;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSearchByName - searches the contact by its first or last name, or by a nickname
+
+HANDLE __cdecl CJabberProto::SearchByName( const TCHAR* nick, const TCHAR* firstName, const TCHAR* lastName )
+{
+ if ( !m_bJabberOnline )
+ return NULL;
+
+ BOOL bIsExtFormat = m_options.ExtendedSearch;
+
+ char szServerName[100];
+ if ( JGetStaticString( "Jud", NULL, szServerName, sizeof szServerName ))
+ strcpy( szServerName, "users.jabber.org" );
+
+ int iqId = SerialNext();
+ XmlNodeIq iq( _T("set"), iqId, _A2T(szServerName));
+ HXML query = iq << XQUERY( _T("jabber:iq:search"));
+
+ if ( bIsExtFormat ) {
+ IqAdd( iqId, IQ_PROC_GETSEARCH, &CJabberProto::OnIqResultExtSearch );
+
+ if ( m_tszSelectedLang )
+ iq << XATTR( _T("xml:lang"), m_tszSelectedLang );
+
+ HXML x = query << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("submit"));
+ if ( nick[0] != '\0' )
+ x << XCHILD( _T("field")) << XATTR( _T("var"), _T("user")) << XATTR( _T("value"), nick);
+
+ if ( firstName[0] != '\0' )
+ x << XCHILD( _T("field")) << XATTR( _T("var"), _T("fn")) << XATTR( _T("value"), firstName);
+
+ if ( lastName[0] != '\0' )
+ x << XCHILD( _T("field")) << XATTR( _T("var"), _T("given")) << XATTR( _T("value"), lastName);
+ }
+ else {
+ IqAdd( iqId, IQ_PROC_GETSEARCH, &CJabberProto::OnIqResultSetSearch );
+ if ( nick[0] != '\0' )
+ query << XCHILD( _T("nick"), nick);
+
+ if ( firstName[0] != '\0' )
+ query << XCHILD( _T("first"), firstName);
+
+ if ( lastName[0] != '\0' )
+ query << XCHILD( _T("last"), lastName);
+ }
+
+ m_ThreadInfo->send( iq );
+ return ( HANDLE )iqId;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RecvContacts
+
+int __cdecl CJabberProto::RecvContacts( HANDLE /*hContact*/, PROTORECVEVENT* )
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RecvFile
+
+int __cdecl CJabberProto::RecvFile( HANDLE hContact, PROTORECVFILET* evt )
+{
+ return Proto_RecvFile(hContact, evt);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RecvMsg
+
+int __cdecl CJabberProto::RecvMsg( HANDLE hContact, PROTORECVEVENT* evt )
+{
+ INT_PTR nDbEvent = Proto_RecvMessage(hContact, evt);
+
+ EnterCriticalSection( &m_csLastResourceMap );
+ if (IsLastResourceExists( (void *)evt->lParam)) {
+ m_ulpResourceToDbEventMap[ m_dwResourceMapPointer++ ] = nDbEvent;
+ m_ulpResourceToDbEventMap[ m_dwResourceMapPointer++ ] = evt->lParam;
+ if ( m_dwResourceMapPointer >= SIZEOF( m_ulpResourceToDbEventMap ))
+ m_dwResourceMapPointer = 0;
+ }
+ LeaveCriticalSection( &m_csLastResourceMap );
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RecvUrl
+
+int __cdecl CJabberProto::RecvUrl( HANDLE /*hContact*/, PROTORECVEVENT* )
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SendContacts
+
+int __cdecl CJabberProto::SendContacts( HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList )
+{
+ DBVARIANT dbv;
+ if ( !m_bJabberOnline || JGetStringT( hContact, "jid", &dbv )) {
+// JSendBroadcast( hContact, ACKTYPE_CONTACTS, ACKRESULT_FAILED, ( HANDLE ) 1, 0 );
+ return 0;
+ }
+
+ TCHAR szClientJid[ JABBER_MAX_JID_LEN ];
+ GetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
+ JFreeVariant( &dbv );
+
+ JabberCapsBits jcb = GetResourceCapabilites( szClientJid, TRUE );
+ if ( ~jcb & JABBER_CAPS_ROSTER_EXCHANGE )
+ return 0;
+
+ XmlNode m( _T("message"));
+// m << XCHILD( _T("body"), msg );
+ HXML x = m << XCHILDNS( _T("x"), _T(JABBER_FEAT_ROSTER_EXCHANGE));
+
+ for ( int i = 0; i < nContacts; ++i ) {
+ if (!JGetStringT( hContactsList[i], "jid", &dbv )) {
+ x << XCHILD( _T("item")) << XATTR( _T("action"), _T("add")) <<
+ XATTR( _T("jid"), dbv.ptszVal);
+ JFreeVariant( &dbv );
+ }
+ }
+
+ int id = SerialNext();
+ m << XATTR( _T("to"), szClientJid ) << XATTRID( id );
+
+ m_ThreadInfo->send( m );
+// mir_free( msg );
+
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SendFile - sends a file
+
+HANDLE __cdecl CJabberProto::SendFile( HANDLE hContact, const TCHAR* szDescription, TCHAR** ppszFiles )
+{
+ if ( !m_bJabberOnline ) return 0;
+
+ if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) == ID_STATUS_OFFLINE )
+ return 0;
+
+ DBVARIANT dbv;
+ if ( JGetStringT( hContact, "jid", &dbv ))
+ return 0;
+
+ int i, j;
+ struct _stati64 statbuf;
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_ROSTER, dbv.ptszVal );
+ if ( item == NULL ) {
+ JFreeVariant( &dbv );
+ return 0;
+ }
+
+ // Check if another file transfer session request is pending ( waiting for disco result )
+ if ( item->ft != NULL ) {
+ JFreeVariant( &dbv );
+ return 0;
+ }
+
+ JabberCapsBits jcb = GetResourceCapabilites( item->jid, TRUE );
+ if ( jcb == JABBER_RESOURCE_CAPS_IN_PROGRESS ) {
+ Sleep(600);
+ jcb = GetResourceCapabilites( item->jid, TRUE );
+ }
+
+ // fix for very smart clients, like gajim
+ if ( !m_options.BsDirect && !m_options.BsProxyManual ) {
+ // disable bytestreams
+ jcb &= ~JABBER_CAPS_BYTESTREAMS;
+ }
+
+ // if only JABBER_CAPS_SI_FT feature set (without BS or IBB), disable JABBER_CAPS_SI_FT
+ if (( jcb & (JABBER_CAPS_SI_FT | JABBER_CAPS_IBB | JABBER_CAPS_BYTESTREAMS)) == JABBER_CAPS_SI_FT)
+ jcb &= ~JABBER_CAPS_SI_FT;
+
+ if (
+ // can't get caps
+ ( jcb & JABBER_RESOURCE_CAPS_ERROR )
+ // caps not already received
+ || ( jcb == JABBER_RESOURCE_CAPS_NONE )
+ // XEP-0096 and OOB not supported?
+ || !(jcb & ( JABBER_CAPS_SI_FT | JABBER_CAPS_OOB ))
+ ) {
+ JFreeVariant( &dbv );
+ MsgPopup( hContact, TranslateT("No compatible file transfer machanism exist"), item->jid );
+ return 0;
+ }
+
+ filetransfer* ft = new filetransfer(this);
+ ft->std.hContact = hContact;
+ while( ppszFiles[ ft->std.totalFiles ] != NULL )
+ ft->std.totalFiles++;
+
+ ft->std.ptszFiles = ( TCHAR** ) mir_calloc( sizeof( TCHAR* )* ft->std.totalFiles );
+ ft->fileSize = ( unsigned __int64* ) mir_calloc( sizeof( unsigned __int64 ) * ft->std.totalFiles );
+ for ( i=j=0; i < ft->std.totalFiles; i++ ) {
+ if ( _tstati64( ppszFiles[i], &statbuf ))
+ Log( "'%s' is an invalid filename", ppszFiles[i] );
+ else {
+ ft->std.ptszFiles[j] = mir_tstrdup( ppszFiles[i] );
+ ft->fileSize[j] = statbuf.st_size;
+ j++;
+ ft->std.totalBytes += statbuf.st_size;
+ } }
+ if ( j == 0 ) {
+ delete ft;
+ JFreeVariant( &dbv );
+ return NULL;
+ }
+
+ ft->std.tszCurrentFile = mir_tstrdup( ppszFiles[0] );
+ ft->szDescription = mir_tstrdup( szDescription );
+ ft->jid = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+
+ if ( jcb & JABBER_CAPS_SI_FT )
+ FtInitiate( item->jid, ft );
+ else if ( jcb & JABBER_CAPS_OOB )
+ JForkThread(( JThreadFunc )&CJabberProto::FileServerThread, ft );
+
+ return ft;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSendMessage - sends a message
+
+struct TFakeAckParams
+{
+ inline TFakeAckParams( HANDLE p1, const char* p2 )
+ : hContact( p1 ), msg( p2 ) {}
+
+ HANDLE hContact;
+ const char* msg;
+};
+
+void __cdecl CJabberProto::SendMessageAckThread( void* param )
+{
+ TFakeAckParams *par = ( TFakeAckParams* )param;
+ Sleep( 100 );
+ Log( "Broadcast ACK" );
+ JSendBroadcast( par->hContact, ACKTYPE_MESSAGE,
+ par->msg ? ACKRESULT_FAILED : ACKRESULT_SUCCESS,
+ ( HANDLE ) 1, ( LPARAM ) par->msg );
+ Log( "Returning from thread" );
+ delete par;
+}
+
+static char PGP_PROLOG[] = "-----BEGIN PGP MESSAGE-----\r\n\r\n";
+static char PGP_EPILOG[] = "\r\n-----END PGP MESSAGE-----\r\n";
+
+int __cdecl CJabberProto::SendMsg( HANDLE hContact, int flags, const char* pszSrc )
+{
+ int id;
+
+ DBVARIANT dbv;
+ if ( !m_bJabberOnline || JGetStringT( hContact, "jid", &dbv )) {
+ TFakeAckParams *param = new TFakeAckParams( hContact, Translate( "Protocol is offline or no jid" ));
+ JForkThread( &CJabberProto::SendMessageAckThread, param );
+ return 1;
+ }
+
+ TCHAR *msg;
+ int isEncrypted;
+
+ if ( !strncmp( pszSrc, PGP_PROLOG, strlen( PGP_PROLOG ))) {
+ const char* szEnd = strstr( pszSrc, PGP_EPILOG );
+ char* tempstring = ( char* )alloca( strlen( pszSrc ) + 1 );
+ size_t nStrippedLength = strlen(pszSrc) - strlen(PGP_PROLOG) - (szEnd ? strlen(szEnd) : 0);
+ strncpy( tempstring, pszSrc + strlen(PGP_PROLOG), nStrippedLength );
+ tempstring[ nStrippedLength ] = 0;
+ pszSrc = tempstring;
+ isEncrypted = 1;
+ flags &= ~PREF_UNICODE;
+ }
+ else isEncrypted = 0;
+
+ if ( flags & PREF_UTF ) {
+
+ mir_utf8decode( NEWSTR_ALLOCA( pszSrc ), &msg );
+
+ }
+ else if ( flags & PREF_UNICODE )
+ msg = mir_u2t(( wchar_t* )&pszSrc[ strlen( pszSrc )+1 ] );
+ else
+ msg = mir_a2t( pszSrc );
+
+ int nSentMsgId = 0;
+
+ if ( msg != NULL ) {
+ TCHAR* msgType;
+ if ( ListExist( LIST_CHATROOM, dbv.ptszVal ) && _tcschr( dbv.ptszVal, '/' )==NULL )
+ msgType = _T("groupchat");
+ else
+ msgType = _T("chat");
+
+ XmlNode m( _T("message" )); xmlAddAttr( m, _T("type"), msgType );
+ if ( !isEncrypted )
+ m << XCHILD( _T("body"), msg );
+ else {
+ m << XCHILD( _T("body"), _T("[This message is encrypted.]" ));
+ m << XCHILD( _T("x"), msg) << XATTR(_T("xmlns"), _T("jabber:x:encrypted"));
+ }
+ mir_free( msg );
+
+ TCHAR szClientJid[ JABBER_MAX_JID_LEN ];
+ GetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
+
+ JABBER_RESOURCE_STATUS *r = ResourceInfoFromJID( szClientJid );
+ if ( r )
+ r->bMessageSessionActive = TRUE;
+
+ JabberCapsBits jcb = GetResourceCapabilites( szClientJid, TRUE );
+
+ if ( jcb & JABBER_RESOURCE_CAPS_ERROR )
+ jcb = JABBER_RESOURCE_CAPS_NONE;
+
+ if ( jcb & JABBER_CAPS_CHATSTATES )
+ m << XCHILDNS( _T("active"), _T(JABBER_FEAT_CHATSTATES));
+
+ if (
+ // if message delivery check disabled by entity caps manager
+ ( jcb & JABBER_CAPS_MESSAGE_EVENTS_NO_DELIVERY ) ||
+ // if client knows nothing about delivery
+ !( jcb & ( JABBER_CAPS_MESSAGE_EVENTS | JABBER_CAPS_MESSAGE_RECEIPTS )) ||
+ // if message sent to groupchat
+ !lstrcmp( msgType, _T("groupchat")) ||
+ // if message delivery check disabled in settings
+ !m_options.MsgAck || !JGetByte( hContact, "MsgAck", TRUE )) {
+ if ( !lstrcmp( msgType, _T("groupchat")))
+ xmlAddAttr( m, _T("to"), dbv.ptszVal );
+ else {
+ id = SerialNext();
+ xmlAddAttr( m, _T("to"), szClientJid ); xmlAddAttrID( m, id );
+ }
+ m_ThreadInfo->send( m );
+
+ JForkThread( &CJabberProto::SendMessageAckThread, new TFakeAckParams( hContact, 0 ));
+
+ nSentMsgId = 1;
+ }
+ else {
+ id = SerialNext();
+ xmlAddAttr( m, _T("to"), szClientJid ); xmlAddAttrID( m, id );
+
+ // 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
+ id = 1;
+
+ m_ThreadInfo->send( m );
+ nSentMsgId = id;
+ } }
+
+ JFreeVariant( &dbv );
+ return nSentMsgId;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SendUrl
+
+int __cdecl CJabberProto::SendUrl( HANDLE /*hContact*/, int /*flags*/, const char* /*url*/ )
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSetApparentMode - sets the visibility status
+
+int __cdecl CJabberProto::SetApparentMode( HANDLE hContact, int mode )
+{
+ if ( mode != 0 && mode != ID_STATUS_ONLINE && mode != ID_STATUS_OFFLINE )
+ return 1;
+
+ int oldMode = JGetWord( hContact, "ApparentMode", 0 );
+ if ( mode == oldMode )
+ return 1;
+
+ JSetWord( hContact, "ApparentMode", ( WORD )mode );
+ if ( !m_bJabberOnline )
+ return 0;
+
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ TCHAR* jid = dbv.ptszVal;
+ switch ( mode ) {
+ case ID_STATUS_ONLINE:
+ if ( m_iStatus == ID_STATUS_INVISIBLE || oldMode == ID_STATUS_OFFLINE )
+ m_ThreadInfo->send( XmlNode( _T("presence")) << XATTR( _T("to"), jid ));
+ break;
+ case ID_STATUS_OFFLINE:
+ if ( m_iStatus != ID_STATUS_INVISIBLE || oldMode == ID_STATUS_ONLINE )
+ SendPresenceTo( ID_STATUS_INVISIBLE, jid, NULL );
+ break;
+ case 0:
+ if ( oldMode == ID_STATUS_ONLINE && m_iStatus == ID_STATUS_INVISIBLE )
+ SendPresenceTo( ID_STATUS_INVISIBLE, jid, NULL );
+ else if ( oldMode == ID_STATUS_OFFLINE && m_iStatus != ID_STATUS_INVISIBLE )
+ SendPresenceTo( m_iStatus, jid, NULL );
+ break;
+ }
+ JFreeVariant( &dbv );
+ }
+
+ // TODO: update the zebra list ( jabber:iq:privacy )
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSetStatus - sets the protocol status
+
+int __cdecl CJabberProto::SetStatus( int iNewStatus )
+{
+ if (m_iDesiredStatus == iNewStatus)
+ return 0;
+
+ int oldStatus = m_iStatus;
+
+ Log( "PS_SETSTATUS( %d )", iNewStatus );
+ m_iDesiredStatus = iNewStatus;
+
+ if ( iNewStatus == ID_STATUS_OFFLINE ) {
+ if ( m_ThreadInfo ) {
+ if ( m_bJabberOnline ) {
+ // Quit all chatrooms (will send quit message)
+ LISTFOREACH(i, this, LIST_CHATROOM)
+ if (JABBER_LIST_ITEM *item = ListGetItemPtrFromIndex(i))
+ GcQuit(item, 0, NULL);
+ }
+
+ m_ThreadInfo->send( "</stream:stream>" );
+ m_ThreadInfo->shutdown();
+
+ if ( m_bJabberConnected ) {
+ m_bJabberConnected = m_bJabberOnline = FALSE;
+ RebuildInfoFrame();
+ }
+ }
+
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
+ JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, m_iStatus );
+ }
+ else if ( !m_bJabberConnected && !m_ThreadInfo && !( m_iStatus >= ID_STATUS_CONNECTING && m_iStatus < ID_STATUS_CONNECTING + MAX_CONNECT_RETRIES )) {
+ m_iStatus = ID_STATUS_CONNECTING;
+ ThreadData* thread = new ThreadData( this, JABBER_SESSION_NORMAL );
+ JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, m_iStatus );
+ thread->hThread = JForkThreadEx(( JThreadFunc )&CJabberProto::ServerThread, thread );
+
+ RebuildInfoFrame();
+ }
+ else if ( m_bJabberOnline )
+ SetServerStatus( iNewStatus );
+ else
+ JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, m_iStatus );
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetAwayMsg - returns a contact's away message
+
+void __cdecl CJabberProto::GetAwayMsgThread( void* hContact )
+{
+ DBVARIANT dbv;
+ JABBER_LIST_ITEM *item;
+ JABBER_RESOURCE_STATUS *r;
+ int i, msgCount;
+ size_t len;
+
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ if (( item = ListGetItemPtr( LIST_ROSTER, dbv.ptszVal )) != NULL ) {
+ JFreeVariant( &dbv );
+ if ( item->resourceCount > 0 ) {
+ Log( "resourceCount > 0" );
+ r = item->resource;
+ len = msgCount = 0;
+ for ( i=0; i<item->resourceCount; i++ )
+ if ( r[i].statusMessage ) {
+ msgCount++;
+ len += ( _tcslen( r[i].resourceName ) + _tcslen( r[i].statusMessage ) + 8 );
+ }
+
+ TCHAR* str = ( TCHAR* )alloca( sizeof( TCHAR )*( len+1 ));
+ str[0] = str[len] = '\0';
+ for ( i=0; i < item->resourceCount; i++ )
+ if ( r[i].statusMessage ) {
+ if ( str[0] != '\0' ) _tcscat( str, _T("\r\n" ));
+ if ( msgCount > 1 ) {
+ _tcscat( str, _T("( "));
+ _tcscat( str, r[i].resourceName );
+ _tcscat( str, _T(" ): "));
+ }
+ _tcscat( str, r[i].statusMessage );
+ }
+
+ JSendBroadcast( hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)str);
+ return;
+ }
+
+ if ( item->itemResource.statusMessage != NULL ) {
+ JSendBroadcast( hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)item->itemResource.statusMessage);
+ return;
+ }
+ }
+ else JFreeVariant( &dbv );
+ }
+
+ JSendBroadcast( hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, ( HANDLE ) 1, ( LPARAM )0 );
+}
+
+HANDLE __cdecl CJabberProto::GetAwayMsg( HANDLE hContact )
+{
+ Log( "GetAwayMsg called, hContact=%08X", hContact );
+
+ JForkThread( &CJabberProto::GetAwayMsgThread, hContact );
+ return (HANDLE)1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSR_AWAYMSG
+
+int __cdecl CJabberProto::RecvAwayMsg( HANDLE /*hContact*/, int /*statusMode*/, PROTORECVEVENT* )
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSS_AWAYMSG
+
+int __cdecl CJabberProto::SendAwayMsg( HANDLE /*hContact*/, HANDLE /*hProcess*/, const char* )
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSetAwayMsg - sets the away status message
+
+int __cdecl CJabberProto::SetAwayMsg( int status, const TCHAR* msg )
+{
+ Log( "SetAwayMsg called, wParam=%d lParam=" TCHAR_STR_PARAM, status, msg );
+
+ EnterCriticalSection( &m_csModeMsgMutex );
+
+ TCHAR **szMsg;
+
+ switch ( status ) {
+ case ID_STATUS_ONLINE:
+ szMsg = &m_modeMsgs.szOnline;
+ break;
+ case ID_STATUS_AWAY:
+ case ID_STATUS_ONTHEPHONE:
+ case ID_STATUS_OUTTOLUNCH:
+ szMsg = &m_modeMsgs.szAway;
+ status = ID_STATUS_AWAY;
+ break;
+ case ID_STATUS_NA:
+ szMsg = &m_modeMsgs.szNa;
+ break;
+ case ID_STATUS_DND:
+ case ID_STATUS_OCCUPIED:
+ szMsg = &m_modeMsgs.szDnd;
+ status = ID_STATUS_DND;
+ break;
+ case ID_STATUS_FREECHAT:
+ szMsg = &m_modeMsgs.szFreechat;
+ break;
+ default:
+ LeaveCriticalSection( &m_csModeMsgMutex );
+ return 1;
+ }
+
+ TCHAR* newModeMsg = mir_tstrdup( msg );
+
+ if (( *szMsg == NULL && newModeMsg == NULL ) ||
+ ( *szMsg != NULL && newModeMsg != NULL && !lstrcmp( *szMsg, newModeMsg ))) {
+ // Message is the same, no update needed
+ mir_free( newModeMsg );
+ LeaveCriticalSection( &m_csModeMsgMutex );
+ }
+ else {
+ // Update with the new mode message
+ if ( *szMsg != NULL )
+ mir_free( *szMsg );
+ *szMsg = newModeMsg;
+ // Send a presence update if needed
+ LeaveCriticalSection( &m_csModeMsgMutex );
+ if ( status == m_iStatus ) {
+ SendPresence( m_iStatus, true );
+ } }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserIsTyping - sends a UTN notification
+
+int __cdecl CJabberProto::UserIsTyping( HANDLE hContact, int type )
+{
+ if ( !m_bJabberOnline ) return 0;
+
+ DBVARIANT dbv;
+ if ( JGetStringT( hContact, "jid", &dbv )) return 0;
+
+ JABBER_LIST_ITEM *item;
+ if (( item = ListGetItemPtr( LIST_ROSTER, dbv.ptszVal )) != NULL ) {
+ TCHAR szClientJid[ JABBER_MAX_JID_LEN ];
+ GetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
+
+ JabberCapsBits jcb = GetResourceCapabilites( szClientJid, TRUE );
+
+ if ( jcb & JABBER_RESOURCE_CAPS_ERROR )
+ jcb = JABBER_RESOURCE_CAPS_NONE;
+
+ XmlNode m( _T("message")); xmlAddAttr( m, _T("to"), szClientJid );
+
+ if ( jcb & JABBER_CAPS_CHATSTATES ) {
+ m << XATTR( _T("type"), _T("chat")) << XATTRID( SerialNext());
+ switch ( type ) {
+ case PROTOTYPE_SELFTYPING_OFF:
+ m << XCHILDNS( _T("paused"), _T(JABBER_FEAT_CHATSTATES));
+ m_ThreadInfo->send( m );
+ break;
+ case PROTOTYPE_SELFTYPING_ON:
+ m << XCHILDNS( _T("composing"), _T(JABBER_FEAT_CHATSTATES));
+ m_ThreadInfo->send( m );
+ break;
+ }
+ }
+ else if ( jcb & JABBER_CAPS_MESSAGE_EVENTS ) {
+ HXML x = m << XCHILDNS( _T("x"), _T(JABBER_FEAT_MESSAGE_EVENTS));
+ if ( item->messageEventIdStr != NULL )
+ x << XCHILD( _T("id"), item->messageEventIdStr );
+
+ switch ( type ) {
+ case PROTOTYPE_SELFTYPING_OFF:
+ m_ThreadInfo->send( m );
+ break;
+ case PROTOTYPE_SELFTYPING_ON:
+ x << XCHILD( _T("composing"));
+ m_ThreadInfo->send( m );
+ break;
+ } } }
+
+ JFreeVariant( &dbv );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Notify dialogs
+
+void CJabberProto::WindowSubscribe(HWND hwnd)
+{
+ WindowList_Add(m_windowList, hwnd, NULL);
+}
+
+void CJabberProto::WindowUnsubscribe(HWND hwnd)
+{
+ WindowList_Remove(m_windowList, hwnd);
+}
+
+void CJabberProto::WindowNotify(UINT msg, bool async)
+{
+ if (async)
+ WindowList_BroadcastAsync(m_windowList, msg, 0, 0);
+ else
+ WindowList_Broadcast(m_windowList, msg, 0, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// InfoFrame events
+
+void CJabberProto::InfoFrame_OnSetup(CJabberInfoFrame_Event*)
+{
+ OnMenuOptions(0,0);
+}
+
+void CJabberProto::InfoFrame_OnTransport(CJabberInfoFrame_Event *evt)
+{
+ if (evt->m_event == CJabberInfoFrame_Event::CLICK)
+ {
+ HANDLE hContact = (HANDLE)evt->m_pUserData;
+ POINT pt;
+
+ HMENU hContactMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)hContact, 0);
+ GetCursorPos(&pt);
+ int res = TrackPopupMenu(hContactMenu, TPM_RETURNCMD, pt.x, pt.y, 0, (HWND)CallService(MS_CLUI_GETHWND, 0, 0), NULL);
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(res, MPCF_CONTACTMENU), (LPARAM)hContact);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// OnEvent - maintain protocol events
+
+int __cdecl CJabberProto::OnEvent( PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam )
+{
+ switch( eventType ) {
+ case EV_PROTO_ONLOAD: return OnModulesLoadedEx( 0, 0 );
+ case EV_PROTO_ONEXIT: return OnPreShutdown( 0, 0 );
+ case EV_PROTO_ONOPTIONS: return OnOptionsInit( wParam, lParam );
+
+ case EV_PROTO_ONMENU:
+ MenuInit();
+ break;
+
+ case EV_PROTO_ONRENAME:
+ if ( m_hMenuRoot )
+ {
+ CLISTMENUITEM clmi = { 0 };
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_NAME | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ clmi.ptszName = m_tszUserName;
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuRoot, ( LPARAM )&clmi );
+ }
+ break;
+
+ case EV_PROTO_ONCONTACTDELETED:
+ return OnContactDeleted(wParam, lParam);
+
+ case EV_PROTO_DBSETTINGSCHANGED:
+ return OnDbSettingChanged(wParam, lParam);
+ }
+ return 1;
+}
diff --git a/protocols/JabberG/src/jabber_proto.h b/protocols/JabberG/src/jabber_proto.h new file mode 100644 index 0000000000..eff59bb002 --- /dev/null +++ b/protocols/JabberG/src/jabber_proto.h @@ -0,0 +1,1008 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_PROTO_H_
+#define _JABBER_PROTO_H_
+
+#include "jabber_disco.h"
+#include "jabber_rc.h"
+#include "jabber_privacy.h"
+#include "jabber_search.h"
+#include "jabber_iq.h"
+#include "jabber_icolib.h"
+#include "jabber_xstatus.h"
+#include "jabber_notes.h"
+#include "jabber_message_manager.h"
+#include "jabber_presence_manager.h"
+#include "jabber_send_manager.h"
+
+struct CJabberProto;
+typedef void ( __cdecl CJabberProto::*JThreadFunc )( void* );
+typedef int ( __cdecl CJabberProto::*JEventFunc )( WPARAM, LPARAM );
+typedef INT_PTR ( __cdecl CJabberProto::*JServiceFunc )( WPARAM, LPARAM );
+typedef INT_PTR ( __cdecl CJabberProto::*JServiceFuncParam )( WPARAM, LPARAM, LPARAM );
+
+enum TJabberGcLogInfoType { INFO_BAN, INFO_STATUS, INFO_CONFIG, INFO_AFFILIATION, INFO_ROLE };
+
+// for JabberEnterString
+enum { JES_MULTINE, JES_COMBO, JES_RICHEDIT, JES_PASSWORD };
+
+typedef UNIQUE_MAP<TCHAR,TCharKeyCmp> U_TCHAR_MAP;
+
+#define JABBER_DEFAULT_RECENT_COUNT 10
+
+struct JABBER_IQ_FUNC
+{
+ int iqId; // id to match IQ get/set with IQ result
+ JABBER_IQ_PROCID procId; // must be unique in the list, except for IQ_PROC_NONE which can have multiple entries
+ JABBER_IQ_PFUNC func; // callback function
+ time_t requestTime; // time the request was sent, used to remove relinquent entries
+};
+
+struct JABBER_GROUPCHAT_INVITE_INFO
+{
+ TCHAR* roomJid;
+ TCHAR* from;
+ TCHAR* reason;
+ TCHAR* password;
+};
+
+struct ROSTERREQUSERDATA
+{
+ HWND hwndDlg;
+ BYTE bRRAction;
+ BOOL bReadyToDownload;
+ BOOL bReadyToUpload;
+};
+
+struct TFilterInfo
+{
+ enum Type { T_JID, T_XMLNS, T_ANY, T_OFF };
+
+ volatile BOOL msg, presence, iq;
+ volatile Type type;
+
+ CRITICAL_SECTION csPatternLock;
+ TCHAR pattern[256];
+};
+
+struct CJabberSysInterface: public IJabberSysInterface
+{
+ int STDMETHODCALLTYPE GetVersion() const; // Returns version of IJabberSysInterface.
+ int STDMETHODCALLTYPE CompareJIDs(LPCTSTR jid1, LPCTSTR jid2); // Strips resource names from given JIDs and returns result of comparison for these JIDs.
+ HANDLE STDMETHODCALLTYPE ContactFromJID(LPCTSTR jid); // Returns contact handle for given JID.
+ LPTSTR STDMETHODCALLTYPE ContactToJID(HANDLE hContact); // Returns JID of hContact. You must free the result using mir_free().
+ LPTSTR STDMETHODCALLTYPE GetBestResourceName(LPCTSTR jid); // Returns best resource name for given JID. You must free the result using mir_free().
+ LPTSTR STDMETHODCALLTYPE GetResourceList(LPCTSTR jid); // Returns all resource names for a given JID in format "resource1\0resource2\0resource3\0\0" (all resources are separated by \0 character and the whole string is terminated with two \0 characters). You must free the string using mir_free().
+ char* STDMETHODCALLTYPE GetModuleName() const; // Returns Jabber module name.
+
+ CJabberProto *m_psProto;
+};
+
+struct CJabberNetInterface: public IJabberNetInterface
+{
+ int STDMETHODCALLTYPE GetVersion() const; // Returns version of IJabberNetInterface.
+ unsigned int STDMETHODCALLTYPE SerialNext(); // Returns id that can be used for next message sent through SendXmlNode().
+ int STDMETHODCALLTYPE SendXmlNode(HXML node); // Sends XML node.
+
+ // In all incoming stanza handlers, return TRUE to continue processing of the stanza (Jabber plugin will then call other handlers). Return FALSE only when you're sure noone else will need to process this stanza.
+ // Registers incoming <presence/> handler. Returns handler handle on success or NULL on error.
+ HJHANDLER STDMETHODCALLTYPE AddPresenceHandler(JABBER_HANDLER_FUNC Func, void *pUserData, int iPriority);
+ // Registers incoming <message/> handler for messages of types specified by iMsgTypes. iMsgTypes is a combination of JABBER_MESSAGE_TYPE_* flags. Returns handler handle on success or NULL on error.
+ HJHANDLER STDMETHODCALLTYPE AddMessageHandler(JABBER_HANDLER_FUNC Func, int iMsgTypes, LPCTSTR szXmlns, LPCTSTR szTag, void *pUserData, int iPriority);
+ // Registers incoming <iq/> handler. iIqTypes is a combination of JABBER_IQ_TYPE_* flags. Returns handler handle on success or NULL on error.
+ HJHANDLER STDMETHODCALLTYPE AddIqHandler(JABBER_HANDLER_FUNC Func, int iIqTypes, LPCTSTR szXmlns, LPCTSTR szTag, void *pUserData, int iPriority);
+ // Registers temporary handler for incoming <iq/> stanza of type iIqType with id iIqId. iIqTypes is a combination of JABBER_IQ_TYPE_* flags. Returns handler handle on success or NULL on error. You must free pUserData in the handler by yourself.
+ HJHANDLER STDMETHODCALLTYPE AddTemporaryIqHandler(JABBER_HANDLER_FUNC Func, int iIqTypes, int iIqId, void *pUserData, DWORD dwTimeout, int iPriority);
+
+ // Registers handler for outgoing nodes. The handler may modify the node if it's necessary. Return TRUE in the handler to continue, or FALSE to abort sending.
+ HJHANDLER STDMETHODCALLTYPE AddSendHandler(JABBER_HANDLER_FUNC Func, void *pUserData, int iPriority);
+
+ // Unregisters handler by its handle.
+ int STDMETHODCALLTYPE RemoveHandler(HJHANDLER hHandler);
+
+ int STDMETHODCALLTYPE RegisterFeature(LPCTSTR szFeature, LPCTSTR szDescription); // Registers feature so that it's displayed with proper description in other users' details. Call this function in your ME_SYSTEM_MODULESLOADED handler. Returns TRUE on success or FALSE on error.
+ int STDMETHODCALLTYPE AddFeatures(LPCTSTR szFeatures); // Adds features to the list of features returned by the client.
+ int STDMETHODCALLTYPE RemoveFeatures(LPCTSTR szFeatures); // Removes features from the list of features returned by the client.
+ LPTSTR STDMETHODCALLTYPE GetResourceFeatures(LPCTSTR jid); // Returns all features supported by JID in format "feature1\0feature2\0...\0featureN\0\0". You must free returned string using mir_free().
+
+ CJabberProto *m_psProto;
+
+private:
+ JabberFeatCapPairDynamic *FindFeature(LPCTSTR szFeature);
+};
+
+struct CJabberInterface: public IJabberInterface
+{
+ DWORD STDMETHODCALLTYPE GetFlags() const; // Set of JIF_* flags.
+ int STDMETHODCALLTYPE GetVersion() const; // Returns version of IJabberInterface.
+ DWORD STDMETHODCALLTYPE GetJabberVersion() const; // Returns Jabber plugin version.
+
+ IJabberSysInterface* STDMETHODCALLTYPE Sys() const; // Jabber system utilities.
+ IJabberNetInterface* STDMETHODCALLTYPE Net() const; // Jabber network interface.
+
+ CJabberProto *m_psProto;
+};
+
+struct CJabberProto : public PROTO_INTERFACE, public MZeroedObject
+{
+ typedef PROTO_INTERFACE CSuper;
+
+ CJabberProto( const char*, const TCHAR* );
+ ~CJabberProto();
+
+ //====================================================================================
+ // PROTO_INTERFACE
+ //====================================================================================
+
+ virtual HANDLE __cdecl AddToList( int flags, PROTOSEARCHRESULT* psr );
+ virtual HANDLE __cdecl AddToListByEvent( int flags, int iContact, HANDLE hDbEvent );
+
+ virtual int __cdecl Authorize( HANDLE hDbEvent );
+ virtual int __cdecl AuthDeny( HANDLE hDbEvent, const TCHAR* szReason );
+ virtual int __cdecl AuthRecv( HANDLE hContact, PROTORECVEVENT* );
+ virtual int __cdecl AuthRequest( HANDLE hContact, const TCHAR* szMessage );
+
+ virtual HANDLE __cdecl ChangeInfo( int iInfoType, void* pInfoData );
+
+ virtual HANDLE __cdecl FileAllow( HANDLE hContact, HANDLE hTransfer, const TCHAR* szPath );
+ virtual int __cdecl FileCancel( HANDLE hContact, HANDLE hTransfer );
+ virtual int __cdecl FileDeny( HANDLE hContact, HANDLE hTransfer, const TCHAR* szReason );
+ virtual int __cdecl FileResume( HANDLE hTransfer, int* action, const TCHAR** szFilename );
+
+ virtual DWORD_PTR __cdecl GetCaps( int type, HANDLE hContact = NULL );
+ virtual HICON __cdecl GetIcon( int iconIndex );
+ virtual int __cdecl GetInfo( HANDLE hContact, int infoType );
+
+ virtual HANDLE __cdecl SearchBasic( const TCHAR* id );
+ virtual HANDLE __cdecl SearchByEmail( const TCHAR* email );
+ virtual HANDLE __cdecl SearchByName( const TCHAR* nick, const TCHAR* firstName, const TCHAR* lastName );
+ virtual HWND __cdecl SearchAdvanced( HWND owner );
+ virtual HWND __cdecl CreateExtendedSearchUI( HWND owner );
+
+ virtual int __cdecl RecvContacts( HANDLE hContact, PROTORECVEVENT* );
+ virtual int __cdecl RecvFile( HANDLE hContact, PROTORECVFILET* );
+ virtual int __cdecl RecvMsg( HANDLE hContact, PROTORECVEVENT* );
+ virtual int __cdecl RecvUrl( HANDLE hContact, PROTORECVEVENT* );
+
+ virtual int __cdecl SendContacts( HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList );
+ virtual HANDLE __cdecl SendFile( HANDLE hContact, const TCHAR* szDescription, TCHAR** ppszFiles );
+ virtual int __cdecl SendMsg( HANDLE hContact, int flags, const char* msg );
+ virtual int __cdecl SendUrl( HANDLE hContact, int flags, const char* url );
+
+ virtual int __cdecl SetApparentMode( HANDLE hContact, int mode );
+ virtual int __cdecl SetStatus( int iNewStatus );
+
+ virtual HANDLE __cdecl GetAwayMsg( HANDLE hContact );
+ virtual int __cdecl RecvAwayMsg( HANDLE hContact, int mode, PROTORECVEVENT* evt );
+ virtual int __cdecl SendAwayMsg( HANDLE hContact, HANDLE hProcess, const char* msg );
+ virtual int __cdecl SetAwayMsg( int m_iStatus, const TCHAR* msg );
+
+ virtual int __cdecl UserIsTyping( HANDLE hContact, int type );
+
+ virtual int __cdecl OnEvent( PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam );
+
+ //====| Services |====================================================================
+ INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM wParam, LPARAM lParam);
+ INT_PTR __cdecl GetMyAwayMsg(WPARAM wParam, LPARAM lParam);
+
+ //====| Events |======================================================================
+ void __cdecl OnAddContactForever( DBCONTACTWRITESETTING* cws, HANDLE hContact );
+ int __cdecl OnContactDeleted( WPARAM, LPARAM );
+ int __cdecl OnDbSettingChanged( WPARAM, LPARAM );
+ int __cdecl OnIdleChanged( WPARAM, LPARAM );
+ int __cdecl OnModernOptInit( WPARAM, LPARAM );
+ int __cdecl OnModulesLoadedEx( WPARAM, LPARAM );
+ int __cdecl OnOptionsInit( WPARAM, LPARAM );
+ int __cdecl OnPreShutdown( WPARAM, LPARAM );
+ int __cdecl OnPrebuildContactMenu( WPARAM, LPARAM );
+ int __cdecl OnMsgUserTyping( WPARAM, LPARAM );
+ int __cdecl OnProcessSrmmIconClick( WPARAM, LPARAM );
+ int __cdecl OnProcessSrmmEvent( WPARAM, LPARAM );
+ int __cdecl OnReloadIcons( WPARAM, LPARAM );
+ void __cdecl OnRenameContact( DBCONTACTWRITESETTING* cws, HANDLE hContact );
+ void __cdecl OnRenameGroup( DBCONTACTWRITESETTING* cws, HANDLE hContact );
+ int __cdecl OnUserInfoInit( WPARAM, LPARAM );
+
+ int __cdecl JabberGcEventHook( WPARAM, LPARAM );
+ int __cdecl JabberGcMenuHook( WPARAM, LPARAM );
+ int __cdecl JabberGcInit( WPARAM, LPARAM );
+
+ int __cdecl CListMW_ExtraIconsApply( WPARAM, LPARAM );
+
+ // Google Shared Status
+ BOOL m_bGoogleSharedStatus;
+ BOOL m_bGoogleSharedStatusLock;
+ void OnIqResultGoogleSharedStatus(HXML iqNode, CJabberIqInfo* pInfo);
+ BOOL OnIqSetGoogleSharedStatus(HXML iqNode, CJabberIqInfo* pInfo);
+ void SendIqGoogleSharedStatus(int status, const TCHAR *msg);
+
+ //====| Data |========================================================================
+
+ ThreadData* m_ThreadInfo;
+ CJabberOptions m_options;
+
+ HANDLE m_hNetlibUser;
+ PVOID m_sslCtx;
+
+ HANDLE m_hThreadHandle;
+
+ TCHAR* m_szJabberJID;
+ char* m_szStreamId;
+ BOOL m_bJabberConnected; // TCP connection to jabber server established
+ BOOL m_bJabberOnline; // XMPP connection initialized and we can send XMPP packets
+ int m_nJabberSearchID;
+ time_t m_tmJabberLoggedInTime;
+ time_t m_tmJabberIdleStartTime;
+ UINT m_nJabberCodePage;
+ TCHAR* m_tszSelectedLang;
+
+ CMString m_szCurrentEntityCapsHash;
+
+ CRITICAL_SECTION m_csModeMsgMutex;
+ JABBER_MODEMSGS m_modeMsgs;
+ BOOL m_bModeMsgStatusChangePending;
+
+ HANDLE m_hHookExtraIconsRebuild;
+ HANDLE m_hHookExtraIconsApply;
+
+ BOOL m_bChangeStatusMessageOnly;
+ BOOL m_bSendKeepAlive;
+ BOOL m_bPepSupported;
+ BOOL m_bGoogleTalk;
+
+ HWND m_hwndAgentRegInput;
+ HWND m_hwndRegProgress;
+ HWND m_hwndJabberChangePassword;
+ HWND m_hwndMucVoiceList;
+ HWND m_hwndMucMemberList;
+ HWND m_hwndMucModeratorList;
+ HWND m_hwndMucBanList;
+ HWND m_hwndMucAdminList;
+ HWND m_hwndMucOwnerList;
+ HWND m_hwndJabberAddBookmark;
+ HWND m_hwndPrivacyRule;
+
+ CJabberDlgBase *m_pDlgPrivacyLists;
+ CJabberDlgBase *m_pDlgBookmarks;
+ CJabberDlgBase *m_pDlgServiceDiscovery;
+ CJabberDlgBase *m_pDlgJabberJoinGroupchat;
+ CJabberDlgBase *m_pDlgNotes;
+
+ HANDLE m_windowList;
+
+ // Service and event handles
+ HANDLE m_hEventNudge;
+ HANDLE m_hEventXStatusIconChanged;
+ HANDLE m_hEventXStatusChanged;
+
+ // Transports list
+ LIST<TCHAR> m_lstTransports;
+
+ CJabberIqManager m_iqManager;
+ CJabberMessageManager m_messageManager;
+ CJabberPresenceManager m_presenceManager; // manager of <presence> stanzas and their handlers
+ CJabberSendManager m_sendManager; // manager of outgoing stanza handlers
+ CJabberAdhocManager m_adhocManager;
+ CJabberClientCapsManager m_clientCapsManager;
+ CPrivacyListManager m_privacyListManager;
+ CJabberSDManager m_SDManager;
+
+ //HWND m_hwndConsole;
+ CJabberDlgBase *m_pDlgConsole;
+ HANDLE m_hThreadConsole;
+ UINT m_dwConsoleThreadId;
+
+ // proto frame
+ CJabberInfoFrame *m_pInfoFrame;
+
+ LIST<JABBER_LIST_ITEM> m_lstRoster;
+ CRITICAL_SECTION m_csLists;
+ BOOL m_bListInitialised;
+
+ LIST<JabberFeatCapPairDynamic> m_lstJabberFeatCapPairsDynamic; // list of features registered through IJabberNetInterface::RegisterFeature()
+ JabberCapsBits m_uEnabledFeatCapsDynamic;
+
+ CRITICAL_SECTION m_csIqList;
+ JABBER_IQ_FUNC *m_ppIqList;
+ int m_nIqCount;
+ int m_nIqAlloced;
+
+ HGENMENU m_hMenuRoot;
+ HGENMENU m_hMenuChangePassword;
+ HGENMENU m_hMenuGroupchat;
+ HGENMENU m_hMenuBookmarks;
+ HGENMENU m_hMenuNotes;
+
+ HGENMENU m_hMenuPrivacyLists;
+ HGENMENU m_hMenuRosterControl;
+ HGENMENU m_hMenuServiceDiscovery;
+ HGENMENU m_hMenuSDMyTransports;
+ HGENMENU m_hMenuSDTransports;
+ HGENMENU m_hMenuSDConferences;
+
+ HWND m_hwndCommandWindow;
+
+ int m_nIqIdRegGetReg;
+ int m_nIqIdRegSetReg;
+
+ int m_nSDBrowseMode;
+ DWORD m_dwSDLastRefresh;
+ DWORD m_dwSDLastAutoDisco;
+
+ HANDLE m_hChooseMenuItem;
+ int m_privacyMenuServiceAllocated;
+
+ TFilterInfo m_filterInfo;
+
+ CNoteList m_notes;
+
+ CRITICAL_SECTION m_csLastResourceMap;
+ void *m_pLastResourceList;
+ ULONG_PTR m_ulpResourceToDbEventMap[256]; // last 128 messages (128+128)
+ DWORD m_dwResourceMapPointer;
+
+ CJabberInterface m_JabberApi;
+ CJabberSysInterface m_JabberSysApi;
+ CJabberNetInterface m_JabberNetApi;
+
+ /*******************************************************************
+ * Function declarations
+ *******************************************************************/
+
+ void JabberUpdateDialogs( BOOL bEnable );
+
+ void CleanLastResourceMap();
+ BOOL IsLastResourceExists(void *pResource);
+ void* AddToLastResourceMap( LPCTSTR szFullJid );
+ TCHAR* FindLastResourceByDbEvent( HANDLE hDbEvent );
+
+ //---- jabber_adhoc.cpp --------------------------------------------------------------
+
+ int __cdecl ContactMenuRunCommands(WPARAM wParam, LPARAM lParam);
+
+ HWND GetWindowFromIq( HXML iqNode );
+ BOOL HandleAdhocCommandRequest( HXML iqNode, CJabberIqInfo* pInfo );
+ BOOL IsRcRequestAllowedByACL( CJabberIqInfo* pInfo );
+
+ int AdhocSetStatusHandler( HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession );
+ int AdhocOptionsHandler( HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession );
+ int AdhocForwardHandler( HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession );
+ int AdhocLockWSHandler( HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession );
+ int AdhocQuitMirandaHandler( HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession );
+ int AdhocLeaveGroupchatsHandler( HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession );
+
+ void OnIqResult_ListOfCommands( HXML iqNode );
+ void OnIqResult_CommandExecution( HXML iqNode );
+ int AdHoc_RequestListOfCommands( TCHAR * szResponder, HWND hwndDlg );
+ int AdHoc_ExecuteCommand( HWND hwndDlg, TCHAR * jid, struct JabberAdHocData* dat );
+ int AdHoc_SubmitCommandForm(HWND hwndDlg, JabberAdHocData * dat, TCHAR* action);
+ int AdHoc_AddCommandRadio(HWND hFrame, TCHAR * labelStr, int id, int ypos, int value);
+ int AdHoc_OnJAHMCommandListResult( HWND hwndDlg, HXML iqNode, JabberAdHocData* dat );
+ int AdHoc_OnJAHMProcessResult( HWND hwndDlg, HXML workNode, JabberAdHocData* dat );
+
+ void ContactMenuAdhocCommands( struct CJabberAdhocStartupParams* param );
+
+ //---- jabber_bookmarks.c ------------------------------------------------------------
+
+ INT_PTR __cdecl OnMenuHandleBookmarks( WPARAM wParam, LPARAM lParam );
+
+ int AddEditBookmark( JABBER_LIST_ITEM* item );
+
+ //---- jabber_notes.c -----------------------------------------------------------------
+
+ void CJabberProto::ProcessIncomingNote(CNoteItem *pNote, bool ok);
+ void CJabberProto::ProcessOutgoingNote(CNoteItem *pNote, bool ok);
+
+ bool CJabberProto::OnIncomingNote(const TCHAR *szFrom, HXML hXml);
+
+ INT_PTR __cdecl CJabberProto::OnMenuSendNote(WPARAM, LPARAM);
+ INT_PTR __cdecl CJabberProto::OnMenuHandleNotes(WPARAM, LPARAM);
+ INT_PTR __cdecl CJabberProto::OnIncomingNoteEvent(WPARAM, LPARAM);
+
+ //---- jabber_byte.c -----------------------------------------------------------------
+
+ void __cdecl ByteSendThread( JABBER_BYTE_TRANSFER *jbt );
+ void __cdecl ByteReceiveThread( JABBER_BYTE_TRANSFER *jbt );
+
+ void IqResultProxyDiscovery( HXML iqNode, CJabberIqInfo* pInfo );
+ void ByteInitiateResult( HXML iqNode, CJabberIqInfo* pInfo );
+ void ByteSendViaProxy( JABBER_BYTE_TRANSFER *jbt );
+ int ByteSendParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen );
+ void IqResultStreamActivate( HXML iqNode );
+ int ByteReceiveParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen );
+ int ByteSendProxyParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen );
+
+ //---- jabber_caps.cpp ---------------------------------------------------------------
+
+ JabberCapsBits GetTotalJidCapabilites( const TCHAR *jid );
+ JabberCapsBits GetResourceCapabilites( const TCHAR *jid, BOOL appendBestResource );
+
+ //---- jabber_captcha.cpp ------------------------------------------------------------
+
+ void GetCaptchaImage ( HXML node, char *ImageBuf, const TCHAR *PicType, TCHAR*& CaptchaPath);
+ void sendCaptchaResult(TCHAR* buf, ThreadData* info, LPCTSTR from, LPCTSTR challenge, LPCTSTR fromjid, LPCTSTR sid);
+ void sendCaptchaError(ThreadData* info, LPCTSTR from, LPCTSTR to, LPCTSTR challenge);
+
+ //---- jabber_chat.cpp ---------------------------------------------------------------
+
+ void GcLogCreate( JABBER_LIST_ITEM* item );
+ void GcLogUpdateMemberStatus( JABBER_LIST_ITEM* item, const TCHAR* resource, const TCHAR* nick, const TCHAR* jid, int action, HXML reason, int nStatusCode = -1 );
+ void GcLogShowInformation( JABBER_LIST_ITEM *item, JABBER_RESOURCE_STATUS *user, TJabberGcLogInfoType type );
+ void GcQuit( JABBER_LIST_ITEM* jid, int code, HXML reason );
+
+ void FilterList(HWND hwndList);
+ void ResetListOptions(HWND hwndList);
+ void InviteUser(TCHAR *room, TCHAR *pUser, TCHAR *text);
+
+ void AdminSet( const TCHAR* to, const TCHAR* ns, const TCHAR* szItem, const TCHAR* itemVal, const TCHAR* var, const TCHAR* varVal );
+ void AdminGet( const TCHAR* to, const TCHAR* ns, const TCHAR* var, const TCHAR* varVal, JABBER_IQ_PFUNC foo );
+ void AdminSetReason( const TCHAR* to, const TCHAR* ns, const TCHAR* szItem, const TCHAR* itemVal, const TCHAR* var, const TCHAR* varVal, const TCHAR* rsn );
+ void AddMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* str );
+ void AddMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* str , TCHAR* rsn);
+ void DeleteMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* jid );
+
+ //---- jabber_console.cpp ------------------------------------------------------------
+
+ INT_PTR __cdecl OnMenuHandleConsole( WPARAM wParam, LPARAM lParam );
+ void __cdecl ConsoleThread( void* );
+
+ void ConsoleInit( void );
+ void ConsoleUninit( void );
+
+ bool FilterXml(HXML node, DWORD flags);
+ bool RecursiveCheckFilter(HXML node, DWORD flags);
+
+ //---- jabber_disco.cpp --------------------------------------------------------------
+
+ void LaunchServiceDiscovery(TCHAR *jid);
+ INT_PTR __cdecl OnMenuHandleServiceDiscovery( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuHandleServiceDiscoveryMyTransports( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuHandleServiceDiscoveryTransports( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuHandleServiceDiscoveryConferences( WPARAM wParam, LPARAM lParam );
+
+ void OnIqResultServiceDiscoveryInfo( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultServiceDiscoveryItems( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultServiceDiscoveryRootInfo( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultServiceDiscoveryRoot( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultServiceDiscoveryRootItems( HXML iqNode, CJabberIqInfo* pInfo );
+ BOOL SendInfoRequest(CJabberSDNode* pNode, HXML parent);
+ BOOL SendBothRequests(CJabberSDNode* pNode, HXML parent);
+ void PerformBrowse(HWND hwndDlg);
+ BOOL IsNodeRegistered(CJabberSDNode *pNode);
+ void ApplyNodeIcon(HTREELISTITEM hItem, CJabberSDNode *pNode);
+ BOOL SyncTree(HTREELISTITEM hIndex, CJabberSDNode* pNode);
+ void ServiceDiscoveryShowMenu(CJabberSDNode *node, HTREELISTITEM hItem, POINT pt);
+
+ int SetupServiceDiscoveryDlg( TCHAR* jid );
+
+ void OnIqResultCapsDiscoInfo( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultCapsDiscoInfoSI( HXML iqNode, CJabberIqInfo* pInfo );
+
+ void RegisterAgent( HWND hwndDlg, TCHAR* jid );
+
+ //---- jabber_file.cpp ---------------------------------------------------------------
+
+ int FileReceiveParse( filetransfer* ft, char* buffer, int datalen );
+ int FileSendParse( JABBER_SOCKET s, filetransfer* ft, char* buffer, int datalen );
+
+ void UpdateChatUserStatus( wchar_t* chat_jid, wchar_t* jid, wchar_t* nick, int role, int affil, int status, BOOL update_nick );
+
+ void GroupchatJoinRoomByJid(HWND hwndParent, TCHAR *jid);
+
+ void RenameParticipantNick( JABBER_LIST_ITEM* item, const TCHAR* oldNick, HXML itemNode );
+ void AcceptGroupchatInvite( const TCHAR* roomJid, const TCHAR* reason, const TCHAR* password );
+
+ //---- jabber_form.c -----------------------------------------------------------------
+
+ void FormCreateDialog( HXML xNode, TCHAR* defTitle, JABBER_FORM_SUBMIT_FUNC pfnSubmit, void *userdata );
+
+ //---- jabber_ft.c -------------------------------------------------------------------
+
+ void __cdecl FileReceiveThread( filetransfer* ft );
+ void __cdecl FileServerThread( filetransfer* ft );
+
+ void FtCancel( filetransfer* ft );
+ void FtInitiate( TCHAR* jid, filetransfer* ft );
+ void FtHandleSiRequest( HXML iqNode );
+ void FtAcceptSiRequest( filetransfer* ft );
+ void FtAcceptIbbRequest( filetransfer* ft );
+ BOOL FtHandleBytestreamRequest( HXML iqNode, CJabberIqInfo* pInfo );
+ BOOL FtHandleIbbRequest( HXML iqNode, BOOL bOpen );
+
+ //---- jabber_groupchat.c ------------------------------------------------------------
+
+ INT_PTR __cdecl OnMenuHandleJoinGroupchat( WPARAM wParam, LPARAM lParam );
+ void __cdecl GroupchatInviteAcceptThread( JABBER_GROUPCHAT_INVITE_INFO *inviteInfo );
+
+ INT_PTR __cdecl OnJoinChat( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnLeaveChat( WPARAM wParam, LPARAM lParam );
+
+ JABBER_RESOURCE_STATUS* GcFindResource(JABBER_LIST_ITEM *item, const TCHAR *resource);
+ void GroupchatJoinRoom( LPCTSTR server, LPCTSTR room, LPCTSTR nick, LPCTSTR password, bool autojoin = false );
+ void GroupchatProcessPresence( HXML node );
+ void GroupchatProcessMessage( HXML node );
+ void GroupchatProcessInvite( LPCTSTR roomJid, LPCTSTR from, LPCTSTR reason, LPCTSTR password );
+ void GroupchatJoinDlg( TCHAR* roomJid );
+ void OnIqResultDiscovery(HXML iqNode, CJabberIqInfo *pInfo);
+
+ //---- jabber_icolib.cpp -------------------------------------------------------------
+
+ int* m_transportProtoTableStartIndex;
+
+ void IconsInit( void );
+ HANDLE GetIconHandle( int iconId );
+ HICON LoadIconEx( const char* name, bool big = false );
+ int LoadAdvancedIcons(int iID);
+ int GetTransportProtoID( TCHAR* TransportDomain );
+ int GetTransportStatusIconIndex(int iID, int Status);
+ BOOL DBCheckIsTransportedContact(const TCHAR* jid, HANDLE hContact);
+ void CheckAllContactsAreTransported( void );
+ INT_PTR __cdecl JGetAdvancedStatusIcon(WPARAM wParam, LPARAM lParam );
+
+ //---- jabber_iq.c -------------------------------------------------------------------
+
+ JABBER_IQ_PFUNC JabberIqFetchFunc( int iqId );
+
+ void __cdecl ExpirerThread( void* );
+
+ void IqInit();
+ void IqUninit();
+ void IqAdd( unsigned int iqId, JABBER_IQ_PROCID procId, JABBER_IQ_PFUNC func );
+ void IqRemove( int index );
+ void IqExpire();
+
+ void OnIqResultBind( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultDiscoBookmarks( HXML iqNode );
+ void OnIqResultEntityTime( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultExtSearch( HXML iqNode );
+ void OnIqResultGetAuth( HXML iqNode );
+ void OnIqResultGetVCardAvatar( HXML iqNode );
+ void OnIqResultGetClientAvatar( HXML iqNode );
+ void OnIqResultGetServerAvatar( HXML iqNode );
+ void OnIqResultGotAvatar( HANDLE hContact, HXML n, const TCHAR* mimeType );
+ void OnIqResultGetMuc( HXML iqNode );
+ void OnIqResultGetRegister( HXML iqNode );
+ void OnIqResultGetRoster( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultGetVcard( HXML iqNode );
+ void OnIqResultLastActivity( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultMucGetAdminList( HXML iqNode );
+ void OnIqResultMucGetBanList( HXML iqNode );
+ void OnIqResultMucGetMemberList( HXML iqNode );
+ void OnIqResultMucGetModeratorList( HXML iqNode );
+ void OnIqResultMucGetOwnerList( HXML iqNode );
+ void OnIqResultMucGetVoiceList( HXML iqNode );
+ void OnIqResultNestedRosterGroups( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultNotes( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultSession( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultSetAuth( HXML iqNode );
+ void OnIqResultSetBookmarks( HXML iqNode );
+ void OnIqResultSetPassword( HXML iqNode );
+ void OnIqResultSetRegister( HXML iqNode );
+ void OnIqResultSetSearch( HXML iqNode );
+ void OnIqResultSetVcard( HXML iqNode );
+ void OnIqResultVersion( HXML node, CJabberIqInfo *pInfo );
+ void OnProcessLoginRq( ThreadData* info, DWORD rq );
+ void OnLoggedIn( void );
+
+ //---- jabber_iq_handlers.cpp --------------------------------------------------------
+
+ BOOL OnIqRequestVersion( HXML node, CJabberIqInfo* pInfo );
+ BOOL OnIqRequestLastActivity( HXML node, CJabberIqInfo *pInfo );
+ BOOL OnIqRequestPing( HXML node, CJabberIqInfo *pInfo );
+ BOOL OnIqRequestTime( HXML node, CJabberIqInfo *pInfo );
+ BOOL OnIqProcessIqOldTime( HXML node, CJabberIqInfo *pInfo );
+ BOOL OnIqRequestAvatar( HXML node, CJabberIqInfo *pInfo );
+ BOOL OnSiRequest( HXML node, CJabberIqInfo *pInfo );
+ BOOL OnRosterPushRequest( HXML node, CJabberIqInfo *pInfo );
+ BOOL OnIqRequestOOB( HXML node, CJabberIqInfo *pInfo );
+ BOOL OnIqHttpAuth( HXML node, CJabberIqInfo* pInfo );
+ BOOL AddClistHttpAuthEvent( CJabberHttpAuthParams *pParams );
+
+ void __cdecl IbbSendThread( JABBER_IBB_TRANSFER *jibb );
+ void __cdecl IbbReceiveThread( JABBER_IBB_TRANSFER *jibb );
+
+ void OnIbbInitiateResult( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIbbCloseResult( HXML iqNode, CJabberIqInfo* pInfo );
+ BOOL OnFtHandleIbbIq( HXML iqNode, CJabberIqInfo* pInfo );
+ BOOL OnIbbRecvdData( const TCHAR *data, const TCHAR *sid, const TCHAR *seq );
+
+ void OnFtSiResult( HXML iqNode, CJabberIqInfo* pInfo );
+ BOOL FtIbbSend( int blocksize, filetransfer* ft );
+ BOOL FtSend( HANDLE hConn, filetransfer* ft );
+ void FtSendFinal( BOOL success, filetransfer* ft );
+ int FtReceive( HANDLE hConn, filetransfer* ft, char* buffer, int datalen );
+ void FtReceiveFinal( BOOL success, filetransfer* ft );
+
+ //---- jabber_message_handlers.cpp --------------------------------------------------------
+
+ BOOL OnMessageError( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo );
+ BOOL OnMessageIbb( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo );
+ BOOL OnMessagePubsubEvent( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo );
+ BOOL OnMessageGroupchat( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo );
+
+ //---- jabber_list.cpp ---------------------------------------------------------------
+
+ JABBER_LIST_ITEM *ListAdd( JABBER_LIST list, const TCHAR* jid );
+ JABBER_LIST_ITEM *ListGetItemPtr( JABBER_LIST list, const TCHAR* jid );
+ JABBER_LIST_ITEM *ListGetItemPtrFromIndex( int index );
+
+ void ListWipe( void );
+ int ListExist( JABBER_LIST list, const TCHAR* jid );
+
+ BOOL ListLock();
+ BOOL ListUnlock();
+
+ void ListRemove( JABBER_LIST list, const TCHAR* jid );
+ void ListRemoveList( JABBER_LIST list );
+ void ListRemoveByIndex( int index );
+ int ListFindNext( JABBER_LIST list, int fromOffset );
+
+ JABBER_RESOURCE_STATUS *CJabberProto::ListFindResource( JABBER_LIST list, const TCHAR* jid );
+ int ListAddResource( JABBER_LIST list, const TCHAR* jid, int status, const TCHAR* statusMessage, char priority = 0, const TCHAR* nick = NULL );
+ void ListRemoveResource( JABBER_LIST list, const TCHAR* jid );
+ TCHAR* ListGetBestResourceNamePtr( const TCHAR* jid );
+ TCHAR* ListGetBestClientResourceNamePtr( const TCHAR* jid );
+
+ void SetMucConfig( HXML node, void *from );
+ void OnIqResultMucGetJidList( HXML iqNode, JABBER_MUC_JIDLIST_TYPE listType );
+
+ void OnIqResultServerDiscoInfo( HXML iqNode );
+ void OnIqResultGetVcardPhoto( const TCHAR* jid, HXML n, HANDLE hContact, BOOL& hasPhoto );
+ void SetBookmarkRequest (XmlNodeIq& iqId);
+
+ //---- jabber_menu.cpp ---------------------------------------------------------------
+
+ INT_PTR __cdecl OnMenuConvertChatContact( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuRosterAdd( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuHandleRequestAuth( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuHandleGrantAuth( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuOptions( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuTransportLogin( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuTransportResolve( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuBookmarkAdd( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuRevokeAuth( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnMenuHandleResource(WPARAM wParam, LPARAM lParam, LPARAM res);
+ INT_PTR __cdecl OnMenuHandleDirectPresence(WPARAM wParam, LPARAM lParam, LPARAM res);
+ INT_PTR __cdecl OnMenuSetPriority(WPARAM wParam, LPARAM lParam, LPARAM dwDelta);
+
+ void GlobalMenuInit( void );
+ void GlobalMenuUninit( void );
+
+ void MenuInit( void );
+
+ void MenuHideSrmmIcon(HANDLE hContact);
+ void MenuUpdateSrmmIcon(JABBER_LIST_ITEM *item);
+
+ void AuthWorker( HANDLE hContact, char* authReqType );
+
+ void UpdatePriorityMenu(short priority);
+
+ HGENMENU m_hMenuPriorityRoot;
+ short m_priorityMenuVal;
+ bool m_priorityMenuValSet;
+
+ //---- jabber_misc.c -----------------------------------------------------------------
+
+ INT_PTR __cdecl OnGetEventTextChatStates( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnGetEventTextPresence( WPARAM wParam, LPARAM lParam );
+
+ void AddContactToRoster( const TCHAR* jid, const TCHAR* nick, const TCHAR* grpName );
+ void DBAddAuthRequest( const TCHAR* jid, const TCHAR* nick );
+ BOOL AddDbPresenceEvent(HANDLE hContact, BYTE btEventType);
+ HANDLE DBCreateContact( const TCHAR* jid, const TCHAR* nick, BOOL temporary, BOOL stripResource );
+ void GetAvatarFileName( HANDLE hContact, TCHAR* pszDest, size_t cbLen );
+ void ResolveTransportNicks( const TCHAR* jid );
+ void SetServerStatus( int iNewStatus );
+ void FormatMirVer(JABBER_RESOURCE_STATUS *resource, TCHAR *buf, int bufSize);
+ void UpdateMirVer(JABBER_LIST_ITEM *item);
+ void UpdateMirVer(HANDLE hContact, JABBER_RESOURCE_STATUS *resource);
+ void UpdateSubscriptionInfo(HANDLE hContact, JABBER_LIST_ITEM *item);
+ void SetContactOfflineStatus( HANDLE hContact );
+ void InitCustomFolders( void );
+ void InitPopups( void );
+ void MsgPopup( HANDLE hContact, const TCHAR *szMsg, const TCHAR *szTitle );
+
+ //---- jabber_opt.cpp ----------------------------------------------------------------
+
+ CJabberDlgBase::CreateParam OptCreateAccount;
+ CJabberDlgBase::CreateParam OptCreateGc;
+ CJabberDlgBase::CreateParam OptCreateAdvanced;
+
+ INT_PTR __cdecl OnMenuHandleRosterControl( WPARAM wParam, LPARAM lParam );
+
+ void _RosterExportToFile(HWND hwndDlg);
+ void _RosterImportFromFile(HWND hwndDlg);
+ void _RosterSendRequest(HWND hwndDlg, BYTE rrAction);
+ void _RosterHandleGetRequest( HXML node );
+
+ //---- jabber_password.cpp --------------------------------------------------------------
+
+ INT_PTR __cdecl OnMenuHandleChangePassword( WPARAM wParam, LPARAM lParam );
+
+ //---- jabber_privacy.cpp ------------------------------------------------------------
+ ROSTERREQUSERDATA rrud;
+
+ INT_PTR __cdecl menuSetPrivacyList( WPARAM wParam, LPARAM lParam, LPARAM iList );
+ INT_PTR __cdecl OnMenuHandlePrivacyLists( WPARAM wParam, LPARAM lParam );
+
+ void BuildPrivacyMenu( void );
+ void BuildPrivacyListsMenu( bool bDeleteOld );
+
+ void QueryPrivacyLists( ThreadData *pThreadInfo = NULL );
+
+ BOOL OnIqRequestPrivacyLists( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultPrivacyList( HXML iqNode );
+ void OnIqResultPrivacyLists( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultPrivacyListActive( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultPrivacyListDefault( HXML iqNode, CJabberIqInfo* pInfo );
+ void OnIqResultPrivacyListModify( HXML iqNode, CJabberIqInfo* pInfo );
+
+ //---- jabber_proto.cpp --------------------------------------------------------------
+
+ void __cdecl BasicSearchThread( struct JABBER_SEARCH_BASIC *jsb );
+ void __cdecl GetAwayMsgThread( void* hContact );
+ void __cdecl SendMessageAckThread( void* hContact );
+
+ HANDLE AddToListByJID( const TCHAR* newJid, DWORD flags );
+ void WindowSubscribe(HWND hwnd);
+ void WindowUnsubscribe(HWND hwnd);
+ void WindowNotify(UINT msg, bool async = false);
+
+ void InfoFrame_OnSetup(CJabberInfoFrame_Event *evt);
+ void InfoFrame_OnTransport(CJabberInfoFrame_Event *evt);
+
+ //---- jabber_rc.cpp -----------------------------------------------------------------
+
+ int RcGetUnreadEventsCount( void );
+
+ //---- jabber_search.cpp -------------------------------------------------------------
+
+ void SearchReturnResults( HANDLE id, void* pvUsersInfo, U_TCHAR_MAP* pmAllFields );
+ void OnIqResultAdvancedSearch( HXML iqNode );
+ void OnIqResultGetSearchFields( HXML iqNode );
+ int SearchRenewFields( HWND hwndDlg, JabberSearchData * dat);
+ void SearchDeleteFromRecent( const TCHAR* szAddr, BOOL deleteLastFromDB = TRUE );
+ void SearchAddToRecent( const TCHAR* szAddr, HWND hwndDialog = NULL );
+
+ //---- jabber_std.cpp ----------------------------------------------
+
+ void JCreateService( const char* szService, JServiceFunc serviceProc );
+ void JCreateServiceParam( const char* szService, JServiceFuncParam serviceProc, LPARAM lParam );
+ HANDLE JCreateHookableEvent( const char* szService );
+ void JForkThread( JThreadFunc, void* );
+ HANDLE JForkThreadEx( JThreadFunc, void*, UINT* threadID = NULL );
+
+ void JDeleteSetting( HANDLE hContact, const char* valueName );
+// DWORD JGetByte( const char* valueName, int parDefltValue );
+ DWORD JGetByte( HANDLE hContact, const char* valueName, int parDefltValue );
+ char* JGetContactName( HANDLE hContact );
+ DWORD JGetDword( HANDLE hContact, const char* valueName, DWORD parDefltValue );
+ int JGetStaticString( const char* valueName, HANDLE hContact, char* dest, int dest_len );
+ int JGetStringUtf( HANDLE hContact, char* valueName, DBVARIANT* dbv );
+ int JGetStringT( HANDLE hContact, char* valueName, DBVARIANT* dbv );
+ TCHAR *JGetStringT( HANDLE hContact, char* valueName );
+ TCHAR *JGetStringT( HANDLE hContact, char* valueName, TCHAR *&out );
+ TCHAR *JGetStringT( HANDLE hContact, char* valueName, TCHAR *buf, int size );
+ WORD JGetWord( HANDLE hContact, const char* valueName, int parDefltValue );
+ void JHookEvent( const char*, JEventFunc );
+ int JSendBroadcast( HANDLE hContact, int type, int result, HANDLE hProcess, LPARAM lParam );
+// DWORD JSetByte( const char* valueName, int parValue );
+ DWORD JSetByte( HANDLE hContact, const char* valueName, int parValue );
+ DWORD JSetDword( HANDLE hContact, const char* valueName, DWORD parValue );
+ DWORD JSetString( HANDLE hContact, const char* valueName, const char* parValue );
+ DWORD JSetStringT( HANDLE hContact, const char* valueName, const TCHAR* parValue );
+ DWORD JSetStringUtf( HANDLE hContact, const char* valueName, const char* parValue );
+ DWORD JSetWord( HANDLE hContact, const char* valueName, int parValue );
+
+ TCHAR* JGetStringCrypt( HANDLE hContact, char* valueName );
+ DWORD JSetStringCrypt( HANDLE hContact, char* valueName, const TCHAR* parValue );
+
+ //---- jabber_svc.c ------------------------------------------------------------------
+
+ void CheckMenuItems();
+ void EnableMenuItems( BOOL bEnable );
+
+ INT_PTR __cdecl JabberGetAvatar( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl JabberGetAvatarCaps( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl JabberGetAvatarInfo( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl ServiceSendXML( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl JabberSetAvatar( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl JabberSetNickname( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl JabberSendNudge( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl JabberGCGetToolTipText( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl JabberServiceParseXmppURI( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl OnHttpAuthRequest( WPARAM wParam, LPARAM lParam );
+ INT_PTR __cdecl JabberGetApi( WPARAM wParam, LPARAM lParam );
+
+ void ExternalTempIqHandler( HXML node, CJabberIqInfo *pInfo );
+ BOOL ExternalIqHandler( HXML node, CJabberIqInfo *pInfo );
+ BOOL ExternalMessageHandler( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo );
+ BOOL ExternalPresenceHandler( HXML node, ThreadData *pThreadData, CJabberPresenceInfo* pInfo );
+ BOOL ExternalSendHandler( HXML node, ThreadData *pThreadData, CJabberSendInfo* pInfo );
+
+ BOOL SendHttpAuthReply( CJabberHttpAuthParams *pParams, BOOL bAuthorized );
+
+ //---- jabber_thread.c ----------------------------------------------
+
+ TCHAR m_savedPassword[512];
+
+ typedef struct {
+ bool isPlainAvailable;
+ bool isPlainOldAvailable;
+ bool isMd5Available;
+ bool isScramAvailable;
+ bool isNtlmAvailable;
+ bool isSpnegoAvailable;
+ bool isKerberosAvailable;
+ bool isAuthAvailable;
+ bool isSessionAvailable;
+ TCHAR *m_gssapiHostName;
+ } AUTHMECHS;
+
+ AUTHMECHS m_AuthMechs;
+
+ void __cdecl ServerThread( ThreadData* info );
+
+ void OnProcessFailure( HXML node, ThreadData *info );
+ void OnProcessError( HXML node, ThreadData *info );
+ void OnProcessSuccess( HXML node, ThreadData *info );
+ void OnProcessChallenge( HXML node, ThreadData *info );
+ void OnProcessProceed( HXML node, ThreadData *info );
+ void OnProcessCompressed( HXML node, ThreadData *info );
+ void OnProcessMessage( HXML node, ThreadData *info );
+ void OnProcessPresence( HXML node, ThreadData *info );
+ void OnProcessPresenceCapabilites( HXML node );
+ void OnProcessPubsubEvent( HXML node );
+
+ void OnProcessStreamOpening( HXML node, ThreadData *info );
+ void OnProcessProtocol( HXML node, ThreadData *info );
+
+ void UpdateJidDbSettings( const TCHAR *jid );
+ HANDLE CreateTemporaryContact( const TCHAR *szJid, JABBER_LIST_ITEM* chatItem );
+
+ void PerformRegistration( ThreadData* info );
+ void PerformIqAuth( ThreadData* info );
+ void PerformAuthentication( ThreadData* info );
+ void OnProcessFeatures( HXML node, ThreadData* info );
+
+ void xmlStreamInitialize( char *which );
+ void xmlStreamInitializeNow(ThreadData* info);
+
+ BOOL OnProcessJingle( HXML node );
+ void OnProcessIq( HXML node );
+ void OnProcessRegIq( HXML node, ThreadData* info );
+ void OnPingReply( HXML node, CJabberIqInfo* pInfo );
+
+ bool ProcessCaptcha( HXML node, HXML parentNode, ThreadData *info );
+
+ //---- jabber_util.c -----------------------------------------------------------------
+
+ JABBER_RESOURCE_STATUS* ResourceInfoFromJID( const TCHAR* jid );
+
+ void SerialInit( void );
+ void SerialUninit( void );
+ int SerialNext( void );
+
+ HANDLE HContactFromJID( const TCHAR* jid , BOOL bStripResource = 3);
+ HANDLE ChatRoomHContactFromJID( const TCHAR* jid );
+ void Log( const char* fmt, ... );
+ void SendVisibleInvisiblePresence( BOOL invisible );
+ void SendPresenceTo( int status, TCHAR* to, HXML extra, const TCHAR *msg = NULL );
+ void SendPresence( int m_iStatus, bool bSendToAll );
+ void StringAppend( char* *str, int *sizeAlloced, const char* fmt, ... );
+ TCHAR* GetClientJID( const TCHAR* jid, TCHAR*, size_t );
+ void RebuildInfoFrame( void );
+
+ void ComboLoadRecentStrings(HWND hwndDlg, UINT idcCombo, char *param, int recentCount=JABBER_DEFAULT_RECENT_COUNT);
+ void ComboAddRecentString(HWND hwndDlg, UINT idcCombo, char *param, TCHAR *string, int recentCount=JABBER_DEFAULT_RECENT_COUNT);
+ BOOL EnterString(TCHAR *result, size_t resultLen, TCHAR *caption=NULL, int type=0, char *windowName=NULL, int recentCount=JABBER_DEFAULT_RECENT_COUNT, int timeout=0);
+ BOOL IsMyOwnJID( LPCTSTR szJID );
+
+ void __cdecl LoadHttpAvatars(void* param);
+
+ //---- jabber_vcard.c -----------------------------------------------
+
+ int m_vCardUpdates;
+ HWND m_hwndPhoto;
+ bool m_bPhotoChanged;
+ TCHAR m_szPhotoFileName[MAX_PATH];
+ void OnUserInfoInit_VCard( WPARAM, LPARAM );
+
+ void GroupchatJoinByHContact( HANDLE hContact, bool autojoin=false );
+ int SendGetVcard( const TCHAR* jid );
+ void AppendVcardFromDB( HXML n, char* tag, char* key );
+ void SetServerVcard( BOOL bPhotoChanged, TCHAR* szPhotoFileName );
+ void SaveVcardToDB( HWND hwndPage, int iPage );
+
+ //---- jabber_ws.c -------------------------------------------------
+
+ JABBER_SOCKET WsConnect( char* host, WORD port );
+
+ BOOL WsInit(void);
+ void WsUninit(void);
+ int WsSend( JABBER_SOCKET s, char* data, int datalen, int flags );
+ int WsRecv( JABBER_SOCKET s, char* data, long datalen, int flags );
+
+ //---- jabber_xml.c ------------------------------------------------------------------
+
+ int OnXmlParse( char* buffer );
+ void OnConsoleProcessXml(HXML node, DWORD flags);
+
+ //---- jabber_xmlns.c ----------------------------------------------------------------
+
+ BOOL OnHandleDiscoInfoRequest( HXML iqNode, CJabberIqInfo* pInfo );
+ BOOL OnHandleDiscoItemsRequest( HXML iqNode, CJabberIqInfo* pInfo );
+
+ //---- jabber_xstatus.c --------------------------------------------------------------
+
+ INT_PTR __cdecl OnSetListeningTo( WPARAM wParam, LPARAM lParams );
+ INT_PTR __cdecl OnGetXStatusIcon( WPARAM wParam, LPARAM lParams );
+ INT_PTR __cdecl OnGetXStatus( WPARAM wParam, LPARAM lParams );
+ INT_PTR __cdecl OnSetXStatus( WPARAM wParam, LPARAM lParams );
+ INT_PTR __cdecl OnSetXStatusEx( WPARAM wParam, LPARAM lParams );
+
+ HICON GetXStatusIcon(int bStatus, UINT flags);
+
+ void RegisterAdvStatusSlot(const char *pszSlot);
+ void ResetAdvStatus(HANDLE hContact, const char *pszSlot);
+ void WriteAdvStatus(HANDLE hContact, const char *pszSlot, const TCHAR *pszMode, const char *pszIcon, const TCHAR *pszTitle, const TCHAR *pszText);
+ char* ReadAdvStatusA(HANDLE hContact, const char *pszSlot, const char *pszValue);
+ TCHAR* ReadAdvStatusT(HANDLE hContact, const char *pszSlot, const char *pszValue);
+
+ BOOL SendPepTune( TCHAR* szArtist, TCHAR* szLength, TCHAR* szSource, TCHAR* szTitle, TCHAR* szTrack, TCHAR* szUri );
+
+ void XStatusInit( void );
+ void XStatusUninit( void );
+
+ void SetContactTune( HANDLE hContact, LPCTSTR szArtist, LPCTSTR szLength, LPCTSTR szSource, LPCTSTR szTitle, LPCTSTR szTrack );
+
+ void InfoFrame_OnUserMood(CJabberInfoFrame_Event *evt);
+ void InfoFrame_OnUserActivity(CJabberInfoFrame_Event *evt);
+
+ int m_xsActivity;
+
+ CPepServiceList m_pepServices;
+
+private:
+ char* m_szXmlStreamToBeInitialized;
+
+ DWORD m_lastTicks;
+
+ CRITICAL_SECTION m_csSerial;
+ unsigned int m_nSerial;
+
+ HANDLE m_hInitChat;
+ HGENMENU m_hPrivacyMenuRoot;
+ BOOL m_menuItemsStatus;
+ LIST<void> m_hPrivacyMenuItems;
+
+ int m_nMenuResourceItems;
+ HANDLE* m_phMenuResourceItems;
+
+ HANDLE* m_phIconLibItems;
+};
+
+extern LIST<CJabberProto> g_Instances;
+
+#endif
diff --git a/protocols/JabberG/src/jabber_proxy.cpp b/protocols/JabberG/src/jabber_proxy.cpp new file mode 100644 index 0000000000..1702eb52de --- /dev/null +++ b/protocols/JabberG/src/jabber_proxy.cpp @@ -0,0 +1,157 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+int JabberHttpGatewayInit( HANDLE /*hConn*/, NETLIBOPENCONNECTION* /*nloc*/, NETLIBHTTPREQUEST* /*nlhr*/ )
+{
+#ifdef NNNN
+ WORD wLen, wVersion, wType;
+ WORD wIpLen;
+ DWORD dwSid1, dwSid2, dwSid3, dwSid4;
+ BYTE response[300], *buf;
+ int responseBytes, recvResult;
+ char szSid[33], szHttpServer[256], szHttpGetUrl[300], szHttpPostUrl[300];
+ NETLIBHTTPPROXYINFO nlhpi = {0};
+
+ for ( responseBytes = 0; ; ) {
+ recvResult = Netlib_Recv( hConn, response + responseBytes, sizeof( response ) - responseBytes, MSG_DUMPPROXY );
+ if ( recvResult<=0 ) break;
+ responseBytes += recvResult;
+ if ( responseBytes == sizeof( response ))
+ break;
+ }
+ if ( responseBytes < 31 )
+ {
+ SetLastError( ERROR_INVALID_DATA );
+ return 0;
+ }
+ buf = response;
+ unpackWord( &buf, &wLen );
+ unpackWord( &buf, &wVersion ); /* always 0x0443 */
+ unpackWord( &buf, &wType );
+ buf += 6; /* dunno */
+ unpackDWord( &buf, &dwSid1 );
+ unpackDWord( &buf, &dwSid2 );
+ unpackDWord( &buf, &dwSid3 );
+ unpackDWord( &buf, &dwSid4 );
+ sprintf( szSid, "%08x%08x%08x%08x", dwSid1, dwSid2, dwSid3, dwSid4 );
+ unpackWord( &buf, &wIpLen );
+ if ( responseBytes < 30 + wIpLen || wIpLen == 0 || wIpLen > sizeof( szHttpServer ) - 1 )
+ {
+ SetLastError( ERROR_INVALID_DATA );
+ return 0;
+ }
+ memcpy( szHttpServer, buf, wIpLen );
+ szHttpServer[wIpLen] = '\0';
+
+ nlhpi.cbSize = sizeof( nlhpi );
+ nlhpi.flags = NLHPIF_USEPOSTSEQUENCE;
+ nlhpi.szHttpGetUrl = szHttpGetUrl;
+ nlhpi.szHttpPostUrl = szHttpPostUrl;
+ nlhpi.firstPostSequence = 1;
+ sprintf( szHttpGetUrl, "http://%s/monitor?sid=%s", szHttpServer, szSid );
+ sprintf( szHttpPostUrl, "http://%s/data?sid=%s&seq=", szHttpServer, szSid );
+ return CallService( MS_NETLIB_SETHTTPPROXYINFO, ( WPARAM )hConn, ( LPARAM )&nlhpi );
+#endif
+ return 1;
+}
+
+int JabberHttpGatewayBegin( HANDLE /*hConn*/, NETLIBOPENCONNECTION* /*nloc*/ )
+{
+ /*
+ icq_packet packet;
+ int serverNameLen;
+
+ serverNameLen = strlen( nloc->szHost );
+
+ packet.wLen = ( WORD )( serverNameLen + 4 );
+ write_httphdr( &packet, HTTP_PACKETTYPE_LOGIN );
+ packWord( &packet, ( WORD )serverNameLen );
+ packString( &packet, nloc->szHost, ( WORD )serverNameLen );
+ packWord( &packet, nloc->wPort );
+ Netlib_Send( hConn, packet.pData, packet.wLen, MSG_DUMPPROXY|MSG_NOHTTPGATEWAYWRAP );
+ mir_free( packet.pData );
+ return 1;
+ */
+ return 1;
+}
+
+int JabberHttpGatewayWrapSend( HANDLE hConn, PBYTE buf, int len, int flags, MIRANDASERVICE pfnNetlibSend )
+{
+ TCHAR* strb = mir_utf8decodeW(( char* )buf );
+
+ TCHAR sid[25] = _T("");
+ unsigned __int64 rid = 0;
+
+ XmlNode hPayLoad( strb );
+ XmlNode body( _T("body"));
+ HXML hBody = body << XATTRI64( _T("rid"), rid++ ) << XATTR( _T("sid"), sid ) <<
+ XATTR( _T("xmlns"), _T( "http://jabber.org/protocol/httpbind" ));
+ xmlAddChild( hBody, hPayLoad );
+
+ TCHAR* str = xi.toString( hBody, NULL );
+
+ mir_free( strb );
+ char* utfStr = mir_utf8encodeT( str );
+ NETLIBBUFFER nlb = { utfStr, (int)strlen( utfStr ), flags };
+ int result = pfnNetlibSend(( WPARAM )hConn, ( LPARAM )&nlb);
+ mir_free( utfStr );
+ xi.freeMem( str );
+
+ return result;
+}
+
+#if 0
+PBYTE JabberHttpGatewayUnwrapRecv( NETLIBHTTPREQUEST *nlhr, PBYTE buf, int len, int *outBufLen, void *( *NetlibRealloc )( void *, size_t ))
+{
+ WORD wLen, wType;
+ PBYTE tbuf;
+ int i, copyBytes;
+
+ tbuf = buf;
+ for ( i = 0;; )
+ {
+ if ( tbuf - buf + 2 > len ) break;
+ unpackWord( &tbuf, &wLen );
+ if ( wLen < 12 ) break;
+ if ( tbuf - buf + wLen > len ) break;
+ tbuf += 2; /* version */
+ unpackWord( &tbuf, &wType );
+ tbuf += 8; /* flags & subtype */
+ if ( wType == HTTP_PACKETTYPE_FLAP )
+ {
+ copyBytes = wLen - 12;
+ if ( copyBytes > len - i )
+ {
+ /* invalid data - do our best to get something out of it */
+ copyBytes = len - i;
+ }
+ memcpy( buf + i, tbuf, copyBytes );
+ i += copyBytes;
+ }
+ tbuf += wLen - 12;
+ }
+ *outBufLen = i;
+ return buf;
+}
+#endif
diff --git a/protocols/JabberG/src/jabber_proxy.h b/protocols/JabberG/src/jabber_proxy.h new file mode 100644 index 0000000000..a42591f81e --- /dev/null +++ b/protocols/JabberG/src/jabber_proxy.h @@ -0,0 +1,29 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_PROXY_H_
+#define _JABBER_PROXY_H_
+
+int JabberHttpGatewayInit( HANDLE hConn, NETLIBOPENCONNECTION *nloc, NETLIBHTTPREQUEST *nlhr );
+int JabberHttpGatewayBegin( HANDLE hConn, NETLIBOPENCONNECTION *nloc );
+
+#endif
diff --git a/protocols/JabberG/src/jabber_rc.cpp b/protocols/JabberG/src/jabber_rc.cpp new file mode 100644 index 0000000000..0021733356 --- /dev/null +++ b/protocols/JabberG/src/jabber_rc.cpp @@ -0,0 +1,814 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+XEP-0146 support for Miranda IM
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_iq.h"
+#include "jabber_rc.h"
+#include "m_awaymsg.h"
+
+CJabberAdhocSession::CJabberAdhocSession( CJabberProto* global )
+{
+ m_pNext = NULL;
+ m_pUserData = NULL;
+ m_bAutofreeUserData = FALSE;
+ m_dwStage = 0;
+ ppro = global;
+ m_szSessionId.Format(_T("%u%u"), ppro->SerialNext(), GetTickCount());
+ m_dwStartTime = GetTickCount();
+}
+
+BOOL CJabberProto::IsRcRequestAllowedByACL( CJabberIqInfo* pInfo )
+{
+ if ( !pInfo || !pInfo->GetFrom())
+ return FALSE;
+
+ return IsMyOwnJID( pInfo->GetFrom());
+}
+
+BOOL CJabberProto::HandleAdhocCommandRequest( HXML iqNode, CJabberIqInfo* pInfo )
+{
+ if ( !pInfo->GetChildNode())
+ return TRUE;
+
+ if ( !m_options.EnableRemoteControl || !IsRcRequestAllowedByACL( pInfo )) {
+ // FIXME: send error and return
+ return TRUE;
+ }
+
+ const TCHAR* szNode = xmlGetAttrValue( pInfo->GetChildNode(), _T("node"));
+ if ( !szNode )
+ return TRUE;
+
+ m_adhocManager.HandleCommandRequest( iqNode, pInfo, ( TCHAR* )szNode );
+ return TRUE;
+}
+
+BOOL CJabberAdhocManager::HandleItemsRequest( HXML, CJabberIqInfo* pInfo, const TCHAR* szNode )
+{
+ if ( !szNode || !m_pProto->m_options.EnableRemoteControl || !m_pProto->IsRcRequestAllowedByACL( pInfo ))
+ return FALSE;
+
+ if ( !_tcscmp( szNode, _T(JABBER_FEAT_COMMANDS)))
+ {
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML resultQuery = iq << XQUERY( _T(JABBER_FEAT_DISCO_ITEMS)) << XATTR( _T("node"), _T(JABBER_FEAT_COMMANDS));
+
+ Lock();
+ CJabberAdhocNode* pNode = GetFirstNode();
+ while ( pNode ) {
+ TCHAR* szJid = pNode->GetJid();
+ if ( !szJid )
+ szJid = m_pProto->m_ThreadInfo->fullJID;
+
+ resultQuery << XCHILD( _T("item")) << XATTR( _T("jid"), szJid )
+ << XATTR( _T("node"), pNode->GetNode()) << XATTR( _T("name"), pNode->GetName());
+
+ pNode = pNode->GetNext();
+ }
+ Unlock();
+
+ m_pProto->m_ThreadInfo->send( iq );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CJabberAdhocManager::HandleInfoRequest( HXML, CJabberIqInfo* pInfo, const TCHAR* szNode )
+{
+ if ( !szNode || !m_pProto->m_options.EnableRemoteControl || !m_pProto->IsRcRequestAllowedByACL( pInfo ))
+ return FALSE;
+
+ // FIXME: same code twice
+ if ( !_tcscmp( szNode, _T(JABBER_FEAT_COMMANDS))) {
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML resultQuery = iq << XQUERY( _T(JABBER_FEAT_DISCO_INFO)) << XATTR( _T("node"), _T(JABBER_FEAT_COMMANDS));
+ resultQuery << XCHILD( _T("identity")) << XATTR( _T("name"), _T("Ad-hoc commands"))
+ << XATTR( _T("category"), _T("automation")) << XATTR( _T("type"), _T("command-node"));
+
+ resultQuery << XCHILD( _T("feature")) << XATTR( _T("var"), _T(JABBER_FEAT_COMMANDS));
+ resultQuery << XCHILD( _T("feature")) << XATTR( _T("var"), _T(JABBER_FEAT_DATA_FORMS));
+ resultQuery << XCHILD( _T("feature")) << XATTR( _T("var"), _T(JABBER_FEAT_DISCO_INFO));
+ resultQuery << XCHILD( _T("feature")) << XATTR( _T("var"), _T(JABBER_FEAT_DISCO_ITEMS));
+
+ m_pProto->m_ThreadInfo->send( iq );
+ return TRUE;
+ }
+
+ Lock();
+ CJabberAdhocNode *pNode = FindNode( szNode );
+ if ( pNode ) {
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML resultQuery = iq << XQUERY( _T(JABBER_FEAT_DISCO_INFO)) << XATTR( _T("node"), _T(JABBER_FEAT_DISCO_INFO));
+ resultQuery << XCHILD( _T("identity")) << XATTR( _T("name"), pNode->GetName())
+ << XATTR( _T("category"), _T("automation")) << XATTR( _T("type"), _T("command-node"));
+
+ resultQuery << XCHILD( _T("feature")) << XATTR( _T("var"), _T(JABBER_FEAT_COMMANDS));
+ resultQuery << XCHILD( _T("feature")) << XATTR( _T("var"), _T(JABBER_FEAT_DATA_FORMS));
+ resultQuery << XCHILD( _T("feature")) << XATTR( _T("var"), _T(JABBER_FEAT_DISCO_INFO));
+
+ Unlock();
+ m_pProto->m_ThreadInfo->send( iq );
+ return TRUE;
+ }
+ Unlock();
+ return FALSE;
+}
+
+BOOL CJabberAdhocManager::HandleCommandRequest( HXML iqNode, CJabberIqInfo* pInfo, const TCHAR* szNode )
+{
+ // ATTN: ACL and db settings checked in calling function
+
+ HXML commandNode = pInfo->GetChildNode();
+
+ Lock();
+ CJabberAdhocNode* pNode = FindNode( szNode );
+ if ( !pNode ) {
+ Unlock();
+
+ m_pProto->m_ThreadInfo->send(
+ XmlNodeIq( _T("error"), pInfo )
+ << XCHILD( _T("error")) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+
+ return FALSE;
+ }
+
+ const TCHAR* szSessionId = xmlGetAttrValue( commandNode, _T("sessionid"));
+
+ CJabberAdhocSession* pSession = NULL;
+ if ( szSessionId ) {
+ pSession = FindSession( szSessionId );
+ if ( !pSession ) {
+ Unlock();
+
+ XmlNodeIq iq( _T("error"), pInfo );
+ HXML errorNode = iq << XCHILD( _T("error")) << XATTR( _T("type"), _T("modify"));
+ errorNode << XCHILDNS( _T("bad-request"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"));
+ errorNode << XCHILDNS( _T("bad-sessionid"), _T(JABBER_FEAT_COMMANDS));
+ m_pProto->m_ThreadInfo->send( iq );
+ return FALSE;
+ }
+ }
+ else
+ pSession = AddNewSession();
+
+ if ( !pSession ) {
+ Unlock();
+
+ m_pProto->m_ThreadInfo->send(
+ XmlNodeIq( _T("error"), pInfo )
+ << XCHILD( _T("error")) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("forbidden"), _T("urn:ietf:params:xml:ns:xmpp-stanzas")));
+
+ return FALSE;
+ }
+
+ // session id and node exits here, call handler
+
+ int nResultCode = pNode->CallHandler( iqNode, pInfo, pSession );
+
+ if ( nResultCode == JABBER_ADHOC_HANDLER_STATUS_COMPLETED ) {
+ m_pProto->m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), pInfo )
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), szNode )
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("completed"))
+ << XCHILD( _T("note"), TranslateT("Command completed successfully")) << XATTR( _T("type"), _T("info")));
+
+ RemoveSession( pSession );
+ pSession = NULL;
+ }
+ else if ( nResultCode == JABBER_ADHOC_HANDLER_STATUS_CANCEL ) {
+ m_pProto->m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), pInfo )
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), szNode )
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("canceled"))
+ << XCHILD( _T("note"), TranslateT("Error occured during processing command")) << XATTR( _T("type"), _T("error")));
+
+ RemoveSession( pSession );
+ pSession = NULL;
+ }
+ else if ( nResultCode == JABBER_ADHOC_HANDLER_STATUS_REMOVE_SESSION ) {
+ RemoveSession( pSession );
+ pSession = NULL;
+ }
+ Unlock();
+ return TRUE;
+}
+
+BOOL CJabberAdhocManager::FillDefaultNodes()
+{
+ AddNode( NULL, _T(JABBER_FEAT_RC_SET_STATUS), TranslateT("Set status"), &CJabberProto::AdhocSetStatusHandler );
+ AddNode( NULL, _T(JABBER_FEAT_RC_SET_OPTIONS), TranslateT("Set options"), &CJabberProto::AdhocOptionsHandler );
+ AddNode( NULL, _T(JABBER_FEAT_RC_FORWARD), TranslateT("Forward unread messages"), &CJabberProto::AdhocForwardHandler );
+ AddNode( NULL, _T(JABBER_FEAT_RC_LEAVE_GROUPCHATS), TranslateT("Leave groupchats"), &CJabberProto::AdhocLeaveGroupchatsHandler );
+ AddNode( NULL, _T(JABBER_FEAT_RC_WS_LOCK), TranslateT("Lock workstation"), &CJabberProto::AdhocLockWSHandler );
+ AddNode( NULL, _T(JABBER_FEAT_RC_QUIT_MIRANDA), TranslateT("Quit Miranda NG"), &CJabberProto::AdhocQuitMirandaHandler );
+ return TRUE;
+}
+
+
+static char *StatusModeToDbSetting(int status,const char *suffix)
+{
+ char *prefix;
+ static char str[64];
+
+ switch(status) {
+ case ID_STATUS_AWAY: prefix="Away"; break;
+ case ID_STATUS_NA: prefix="Na"; break;
+ case ID_STATUS_DND: prefix="Dnd"; break;
+ case ID_STATUS_OCCUPIED: prefix="Occupied"; break;
+ case ID_STATUS_FREECHAT: prefix="FreeChat"; break;
+ case ID_STATUS_ONLINE: prefix="On"; break;
+ case ID_STATUS_OFFLINE: prefix="Off"; break;
+ case ID_STATUS_INVISIBLE: prefix="Inv"; break;
+ case ID_STATUS_ONTHEPHONE: prefix="Otp"; break;
+ case ID_STATUS_OUTTOLUNCH: prefix="Otl"; break;
+ case ID_STATUS_IDLE: prefix="Idl"; break;
+ default: return NULL;
+ }
+ lstrcpyA(str,prefix); lstrcatA(str,suffix);
+ return str;
+}
+
+int CJabberProto::AdhocSetStatusHandler( HXML, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession )
+{
+ if ( pSession->GetStage() == 0 ) {
+ // first form
+ pSession->SetStage( 1 );
+
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML xNode = iq
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), _T(JABBER_FEAT_RC_SET_STATUS))
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("executing"))
+ << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("form"));
+
+ xNode << XCHILD( _T("title"), TranslateT("Change Status"));
+ xNode << XCHILD( _T("instructions"), TranslateT("Choose the status and status message"));
+
+ xNode << XCHILD( _T("field")) << XATTR( _T("type"), _T("hidden")) << XATTR( _T("var"), _T("FORM_TYPE"))
+ << XATTR( _T("value"), _T(JABBER_FEAT_RC));
+
+ HXML fieldNode = xNode << XCHILD( _T("field")) << XATTR( _T("label"), TranslateT("Status"))
+ << XATTR( _T("type"), _T("list-single")) << XATTR( _T("var"), _T("status"));
+
+ fieldNode << XCHILD( _T("required"));
+
+ int status = CallService( MS_CLIST_GETSTATUSMODE, 0, 0 );
+ switch ( status ) {
+ case ID_STATUS_INVISIBLE:
+ fieldNode << XCHILD( _T("value"), _T("invisible"));
+ break;
+ case ID_STATUS_AWAY:
+ case ID_STATUS_ONTHEPHONE:
+ case ID_STATUS_OUTTOLUNCH:
+ fieldNode << XCHILD( _T("value"), _T("away"));
+ break;
+ case ID_STATUS_NA:
+ fieldNode << XCHILD( _T("value"), _T("xa"));
+ break;
+ case ID_STATUS_DND:
+ case ID_STATUS_OCCUPIED:
+ fieldNode << XCHILD( _T("value"), _T("dnd"));
+ break;
+ case ID_STATUS_FREECHAT:
+ fieldNode << XCHILD( _T("value"), _T("chat"));
+ break;
+ case ID_STATUS_ONLINE:
+ default:
+ fieldNode << XCHILD( _T("value"), _T("online"));
+ break;
+ }
+
+ fieldNode << XCHILD( _T("option")) << XATTR( _T("label"), TranslateT("Free for chat")) << XCHILD( _T("value"), _T("chat"));
+ fieldNode << XCHILD( _T("option")) << XATTR( _T("label"), TranslateT("Online")) << XCHILD( _T("value"), _T("online"));
+ fieldNode << XCHILD( _T("option")) << XATTR( _T("label"), TranslateT("Away")) << XCHILD( _T("value"), _T("away"));
+ fieldNode << XCHILD( _T("option")) << XATTR( _T("label"), TranslateT("Extended Away (N/A)")) << XCHILD( _T("value"), _T("xa"));
+ fieldNode << XCHILD( _T("option")) << XATTR( _T("label"), TranslateT("Do Not Disturb")) << XCHILD( _T("value"), _T("dnd"));
+ fieldNode << XCHILD( _T("option")) << XATTR( _T("label"), TranslateT("Invisible")) << XCHILD( _T("value"), _T("invisible"));
+ fieldNode << XCHILD( _T("option")) << XATTR( _T("label"), TranslateT("Offline")) << XCHILD( _T("value"), _T("offline"));
+
+ // priority
+ TCHAR szPriority[ 256 ];
+ mir_sntprintf( szPriority, SIZEOF(szPriority), _T("%d"), (short)JGetWord( NULL, "Priority", 5 ));
+ xNode << XCHILD( _T("field")) << XATTR( _T("label"), TranslateT("Priority")) << XATTR( _T("type"), _T("text-single"))
+ << XATTR( _T("var"), _T("status-priority")) << XCHILD( _T("value"), szPriority );
+
+ // status message text
+ xNode << XCHILD( _T("field")) << XATTR( _T("label"), TranslateT("Status message"))
+ << XATTR( _T("type"), _T("text-multi")) << XATTR( _T("var"), _T("status-message"));
+
+ // global status
+ fieldNode = xNode << XCHILD( _T("field")) << XATTR( _T("label"), TranslateT("Change global status"))
+ << XATTR( _T("type"), _T("boolean")) << XATTR( _T("var"), _T("status-global"));
+
+ char* szStatusMsg = (char *)CallService( MS_AWAYMSG_GETSTATUSMSG, status, 0 );
+ if ( szStatusMsg ) {
+ fieldNode << XCHILD( _T("value"), _A2T(szStatusMsg));
+ mir_free( szStatusMsg );
+ }
+
+ m_ThreadInfo->send( iq );
+ return JABBER_ADHOC_HANDLER_STATUS_EXECUTING;
+ }
+ else if ( pSession->GetStage() == 1 ) {
+ // result form here
+ HXML commandNode = pInfo->GetChildNode();
+ HXML xNode = xmlGetChildByTag( commandNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS));
+ if ( !xNode )
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+
+ HXML fieldNode = xmlGetChildByTag( xNode, "field", "var", _T("status"));
+ if ( !xNode )
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+
+ HXML valueNode = xmlGetChild( fieldNode , "value" );
+ if ( !valueNode || !xmlGetText( valueNode ))
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+
+ int status = 0;
+
+ if ( !_tcscmp( xmlGetText( valueNode ), _T("away"))) status = ID_STATUS_AWAY;
+ else if ( !_tcscmp( xmlGetText( valueNode ), _T("xa"))) status = ID_STATUS_NA;
+ else if ( !_tcscmp( xmlGetText( valueNode ), _T("dnd"))) status = ID_STATUS_DND;
+ else if ( !_tcscmp( xmlGetText( valueNode ), _T("chat"))) status = ID_STATUS_FREECHAT;
+ else if ( !_tcscmp( xmlGetText( valueNode ), _T("online"))) status = ID_STATUS_ONLINE;
+ else if ( !_tcscmp( xmlGetText( valueNode ), _T("invisible"))) status = ID_STATUS_INVISIBLE;
+ else if ( !_tcscmp( xmlGetText( valueNode ), _T("offline"))) status = ID_STATUS_OFFLINE;
+
+ if ( !status )
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+
+ int priority = -9999;
+
+ fieldNode = xmlGetChildByTag( xNode, "field", "var", _T("status-priority"));
+ if ( fieldNode && (valueNode = xmlGetChild( fieldNode , "value" ))) {
+ if ( xmlGetText( valueNode ))
+ priority = _ttoi( xmlGetText( valueNode ));
+ }
+
+ if ( priority >= -128 && priority <= 127 )
+ JSetWord( NULL, "Priority", (WORD)priority );
+
+ const TCHAR* szStatusMessage = NULL;
+ fieldNode = xmlGetChildByTag( xNode, "field", "var", _T("status-message"));
+ if ( fieldNode && (valueNode = xmlGetChild( fieldNode , "value" ))) {
+ if ( xmlGetText( valueNode ))
+ szStatusMessage = xmlGetText( valueNode );
+ }
+
+ // skip f...ng away dialog
+ int nNoDlg = DBGetContactSettingByte( NULL, "SRAway", StatusModeToDbSetting( status, "NoDlg" ), 0 );
+ DBWriteContactSettingByte( NULL, "SRAway", StatusModeToDbSetting( status, "NoDlg" ), 1 );
+
+ DBWriteContactSettingTString( NULL, "SRAway", StatusModeToDbSetting( status, "Msg" ), szStatusMessage ? szStatusMessage : _T( "" ));
+
+ fieldNode = xmlGetChildByTag( xNode, "field", "var", _T("status-global"));
+ if ( fieldNode && (valueNode = xmlGetChild( fieldNode , "value" ))) {
+ if ( xmlGetText( valueNode ) && _ttoi( xmlGetText( valueNode )))
+ CallService( MS_CLIST_SETSTATUSMODE, status, NULL );
+ else
+ CallProtoService( m_szModuleName, PS_SETSTATUS, status, NULL );
+ }
+ SetAwayMsg( status, szStatusMessage );
+
+ // return NoDlg setting
+ DBWriteContactSettingByte( NULL, "SRAway", StatusModeToDbSetting( status, "NoDlg" ), (BYTE)nNoDlg );
+
+ return JABBER_ADHOC_HANDLER_STATUS_COMPLETED;
+ }
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+}
+
+int CJabberProto::AdhocOptionsHandler( HXML, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession )
+{
+ if ( pSession->GetStage() == 0 ) {
+ // first form
+ pSession->SetStage( 1 );
+
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML xNode = iq
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), _T(JABBER_FEAT_RC_SET_OPTIONS))
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("executing"))
+ << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("form"));
+
+ xNode << XCHILD( _T("title"), TranslateT("Set Options"));
+ xNode << XCHILD( _T("instructions"), TranslateT("Set the desired options"));
+
+ xNode << XCHILD( _T("field" )) << XATTR( _T("type"), _T("hidden")) << XATTR( _T("var"), _T("FORM_TYPE"))
+ << XATTR( _T("value"), _T(JABBER_FEAT_RC));
+
+ // Automatically Accept File Transfers
+ TCHAR szTmpBuff[ 1024 ];
+ mir_sntprintf( szTmpBuff, SIZEOF(szTmpBuff), _T("%d"), DBGetContactSettingByte( NULL, "SRFile", "AutoAccept", 0 ));
+ xNode << XCHILD( _T("field" )) << XATTR( _T("label"), TranslateT("Automatically Accept File Transfers" ))
+ << XATTR( _T("type"), _T("boolean")) << XATTR( _T("var"), _T("auto-files")) << XCHILD( _T("value"), szTmpBuff );
+
+ // Use sounds
+ mir_sntprintf( szTmpBuff, SIZEOF(szTmpBuff), _T("%d"), DBGetContactSettingByte( NULL, "Skin", "UseSound", 0 ));
+ xNode << XCHILD( _T("field")) << XATTR( _T("label"), TranslateT("Play sounds"))
+ << XATTR( _T("type"), _T("boolean")) << XATTR( _T("var"), _T("sounds")) << XCHILD( _T("value"), szTmpBuff );
+
+ // Disable remote controlling
+ xNode << XCHILD( _T("field")) << XATTR( _T("label"), TranslateT("Disable remote controlling (check twice what you are doing)"))
+ << XATTR( _T("type"), _T("boolean")) << XATTR( _T("var"), _T("enable-rc")) << XCHILD( _T("value"), _T("0"));
+
+ m_ThreadInfo->send( iq );
+ return JABBER_ADHOC_HANDLER_STATUS_EXECUTING;
+ }
+
+ if ( pSession->GetStage() == 1 ) {
+ // result form here
+ HXML commandNode = pInfo->GetChildNode();
+ HXML xNode = xmlGetChildByTag( commandNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS));
+ if ( !xNode )
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+
+ HXML fieldNode, valueNode;
+
+ // Automatically Accept File Transfers
+ fieldNode = xmlGetChildByTag( xNode, "field", "var", _T("auto-files"));
+ if ( fieldNode && (valueNode = xmlGetChild( fieldNode , "value" ))) {
+ if ( xmlGetText( valueNode ))
+ DBWriteContactSettingByte( NULL, "SRFile", "AutoAccept", (BYTE)_ttoi( xmlGetText( valueNode )) );
+ }
+
+ // Use sounds
+ fieldNode = xmlGetChildByTag( xNode, "field", "var", _T("sounds"));
+ if ( fieldNode && (valueNode = xmlGetChild( fieldNode , "value" ))) {
+ if ( xmlGetText( valueNode ))
+ DBWriteContactSettingByte( NULL, "Skin", "UseSound", (BYTE)_ttoi( xmlGetText( valueNode )) );
+ }
+
+ // Disable remote controlling
+ fieldNode = xmlGetChildByTag( xNode, "field", "var", _T("enable-rc"));
+ if ( fieldNode && (valueNode = xmlGetChild( fieldNode , "value" ))) {
+ if ( xmlGetText( valueNode ) && _ttoi( xmlGetText( valueNode )))
+ m_options.EnableRemoteControl = 0;
+ }
+
+ return JABBER_ADHOC_HANDLER_STATUS_COMPLETED;
+ }
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+}
+
+int CJabberProto::RcGetUnreadEventsCount()
+{
+ int nEventsSent = 0;
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL ) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto != NULL && !strcmp( szProto, m_szModuleName )) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ HANDLE hDbEvent = (HANDLE)CallService( MS_DB_EVENT_FINDFIRSTUNREAD, (WPARAM)hContact, 0 );
+ while ( hDbEvent ) {
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService( MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0 );
+ if ( dbei.cbBlob != -1 ) {
+ dbei.pBlob = (PBYTE)mir_alloc( dbei.cbBlob + 1 );
+ int nGetTextResult = CallService( MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei );
+ if ( !nGetTextResult && dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_READ) && !(dbei.flags & DBEF_SENT)) {
+ TCHAR* szEventText = DbGetEventTextT( &dbei, CP_ACP );
+ if ( szEventText ) {
+ nEventsSent++;
+ mir_free( szEventText );
+ }
+ }
+ mir_free( dbei.pBlob );
+ }
+ hDbEvent = (HANDLE)CallService( MS_DB_EVENT_FINDNEXT, (WPARAM)hDbEvent, 0 );
+ }
+ JFreeVariant( &dbv );
+ }
+ }
+ hContact = db_find_next(hContact);
+ }
+ return nEventsSent;
+}
+
+int CJabberProto::AdhocForwardHandler( HXML, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession )
+{
+ TCHAR szMsg[ 1024 ];
+ if ( pSession->GetStage() == 0 ) {
+ int nUnreadEvents = RcGetUnreadEventsCount();
+ if ( !nUnreadEvents ) {
+ mir_sntprintf( szMsg, SIZEOF(szMsg), TranslateT("There is no messages to forward"));
+
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), pInfo )
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), _T(JABBER_FEAT_RC_FORWARD))
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("completed"))
+ << XCHILD( _T("note"), szMsg ) << XATTR( _T("type"), _T("info")));
+
+ return JABBER_ADHOC_HANDLER_STATUS_REMOVE_SESSION;
+ }
+
+ // first form
+ pSession->SetStage( 1 );
+
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML xNode = iq
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), _T(JABBER_FEAT_RC_FORWARD))
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("executing"))
+ << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("form"));
+
+ xNode << XCHILD( _T("title"), TranslateT("Forward options"));
+
+ mir_sntprintf( szMsg, SIZEOF(szMsg), TranslateT("%d message(s) to be forwarded"), nUnreadEvents );
+ xNode << XCHILD( _T("instructions"), szMsg );
+
+ xNode << XCHILD( _T("field")) << XATTR( _T("type"), _T("hidden")) << XATTR( _T("var"), _T("FORM_TYPE"))
+ << XCHILD( _T("value"), _T(JABBER_FEAT_RC));
+
+ // remove clist events
+ xNode << XCHILD( _T("field")) << XATTR( _T("label"), TranslateT("Mark messages as read")) << XATTR( _T("type"), _T("boolean"))
+ << XATTR( _T("var"), _T("remove-clist-events")) << XCHILD( _T("value"),
+ m_options.RcMarkMessagesAsRead == 1 ? _T("1") : _T("0"));
+
+ m_ThreadInfo->send( iq );
+ return JABBER_ADHOC_HANDLER_STATUS_EXECUTING;
+ }
+
+ if ( pSession->GetStage() == 1 ) {
+ // result form here
+ HXML commandNode = pInfo->GetChildNode();
+ HXML xNode = xmlGetChildByTag( commandNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS));
+ if ( !xNode )
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+
+ HXML fieldNode, valueNode;
+
+ BOOL bRemoveCListEvents = TRUE;
+
+ // remove clist events
+ fieldNode = xmlGetChildByTag( xNode,"field", "var", _T("remove-clist-events"));
+ if ( fieldNode && (valueNode = xmlGetChild( fieldNode , "value" ))) {
+ if ( xmlGetText( valueNode ) && !_ttoi( xmlGetText( valueNode )) ) {
+ bRemoveCListEvents = FALSE;
+ }
+ }
+ m_options.RcMarkMessagesAsRead = bRemoveCListEvents ? 1 : 0;
+
+ int nEventsSent = 0;
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL ) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto != NULL && !strcmp( szProto, m_szModuleName )) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+
+ HANDLE hDbEvent = (HANDLE)CallService( MS_DB_EVENT_FINDFIRSTUNREAD, (WPARAM)hContact, 0 );
+ while ( hDbEvent ) {
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService( MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0 );
+ if ( dbei.cbBlob != -1 ) {
+ dbei.pBlob = (PBYTE)mir_alloc( dbei.cbBlob + 1 );
+ int nGetTextResult = CallService( MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei );
+ if ( !nGetTextResult && dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_READ) && !(dbei.flags & DBEF_SENT)) {
+ TCHAR* szEventText = DbGetEventTextT( &dbei, CP_ACP );
+ if ( szEventText ) {
+ XmlNode msg( _T("message"));
+ msg << XATTR( _T("to"), pInfo->GetFrom()) << XATTRID( SerialNext())
+ << XCHILD( _T("body"), szEventText );
+
+ HXML addressesNode = msg << XCHILDNS( _T("addresses"), _T(JABBER_FEAT_EXT_ADDRESSING));
+ TCHAR szOFrom[ JABBER_MAX_JID_LEN ];
+ EnterCriticalSection( &m_csLastResourceMap );
+ TCHAR *szOResource = FindLastResourceByDbEvent( hDbEvent );
+ if ( szOResource )
+ mir_sntprintf( szOFrom, SIZEOF( szOFrom ), _T("%s/%s"), dbv.ptszVal, szOResource );
+ else
+ mir_sntprintf( szOFrom, SIZEOF( szOFrom ), _T("%s"), dbv.ptszVal );
+ LeaveCriticalSection( &m_csLastResourceMap );
+ addressesNode << XCHILD( _T("address")) << XATTR( _T("type"), _T("ofrom")) << XATTR( _T("jid"), szOFrom );
+ addressesNode << XCHILD( _T("address")) << XATTR( _T("type"), _T("oto")) << XATTR( _T("jid"), m_ThreadInfo->fullJID );
+
+ time_t ltime = ( time_t )dbei.timestamp;
+ struct tm *gmt = gmtime( <ime );
+ TCHAR stime[ 512 ];
+ wsprintf(stime, _T("%.4i-%.2i-%.2iT%.2i:%.2i:%.2iZ"), gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
+ gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
+ msg << XCHILDNS( _T("delay"), _T("urn:xmpp:delay")) << XATTR( _T("stamp"), stime );
+
+ m_ThreadInfo->send( msg );
+
+ nEventsSent++;
+
+ CallService( MS_DB_EVENT_MARKREAD, (WPARAM)hContact, (LPARAM)hDbEvent );
+ if ( bRemoveCListEvents )
+ CallService( MS_CLIST_REMOVEEVENT, (WPARAM)hContact, (LPARAM)hDbEvent );
+
+ mir_free( szEventText );
+ }
+ }
+ mir_free( dbei.pBlob );
+ }
+ hDbEvent = (HANDLE)CallService( MS_DB_EVENT_FINDNEXT, (WPARAM)hDbEvent, 0 );
+ }
+ JFreeVariant( &dbv );
+ }
+ }
+ hContact = db_find_next(hContact);
+ }
+
+ mir_sntprintf( szMsg, SIZEOF(szMsg), TranslateT("%d message(s) forwarded"), nEventsSent );
+
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), pInfo )
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), _T(JABBER_FEAT_RC_FORWARD))
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("completed"))
+ << XCHILD( _T("note"), szMsg ) << XATTR( _T("type"), _T("info")));
+
+ return JABBER_ADHOC_HANDLER_STATUS_REMOVE_SESSION;
+ }
+
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+}
+
+typedef BOOL (WINAPI *LWS )( VOID );
+
+int CJabberProto::AdhocLockWSHandler( HXML, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession )
+{
+ BOOL bOk = FALSE;
+ HMODULE hLibrary = LoadLibrary( _T("user32.dll"));
+ if ( hLibrary ) {
+ LWS pLws = (LWS)GetProcAddress( hLibrary, "LockWorkStation" );
+ if ( pLws )
+ bOk = pLws();
+ FreeLibrary( hLibrary );
+ }
+
+ TCHAR szMsg[ 1024 ];
+ if ( bOk )
+ mir_sntprintf( szMsg, SIZEOF(szMsg), TranslateT("Workstation successfully locked"));
+ else
+ mir_sntprintf( szMsg, SIZEOF(szMsg), TranslateT("Error %d occured during workstation lock"), GetLastError());
+
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), pInfo )
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), _T(JABBER_FEAT_RC_WS_LOCK))
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("completed"))
+ << XCHILD( _T("note"), szMsg ) << XATTR( _T("type"), bOk ? _T("info") : _T("error")));
+
+ return JABBER_ADHOC_HANDLER_STATUS_REMOVE_SESSION;
+}
+
+static void __stdcall JabberQuitMirandaIMThread( void* )
+{
+ CallService( "CloseAction", 0, 0 );
+}
+
+int CJabberProto::AdhocQuitMirandaHandler( HXML, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession )
+{
+ if ( pSession->GetStage() == 0 ) {
+ // first form
+ pSession->SetStage( 1 );
+
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML xNode = iq
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), _T(JABBER_FEAT_RC_QUIT_MIRANDA))
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("executing"))
+ << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("form"));
+
+ xNode << XCHILD( _T("title"), TranslateT("Confirmation needed"));
+ xNode << XCHILD( _T("instructions"), TranslateT("Please confirm Miranda NG shutdown"));
+
+ xNode << XCHILD( _T("field")) << XATTR( _T("type"), _T("hidden")) << XATTR( _T("var"), _T("FORM_TYPE"))
+ << XCHILD( _T("value"), _T(JABBER_FEAT_RC));
+
+ // I Agree checkbox
+ xNode << XCHILD( _T("field")) << XATTR( _T("label"), _T("I agree")) << XATTR( _T("type"), _T("boolean"))
+ << XATTR( _T("var"), _T("allow-shutdown")) << XCHILD( _T("value"), _T("0"));
+
+ m_ThreadInfo->send( iq );
+ return JABBER_ADHOC_HANDLER_STATUS_EXECUTING;
+ }
+
+ if ( pSession->GetStage() == 1 ) {
+ // result form here
+ HXML commandNode = pInfo->GetChildNode();
+ HXML xNode = xmlGetChildByTag( commandNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS));
+ if ( !xNode )
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+
+ HXML fieldNode, valueNode;
+
+ // I Agree checkbox
+ fieldNode = xmlGetChildByTag( xNode,"field", "var", _T("allow-shutdown"));
+ if ( fieldNode && (valueNode = xmlGetChild( fieldNode , "value" )))
+ if ( xmlGetText( valueNode ) && _ttoi( xmlGetText( valueNode )))
+ CallFunctionAsync(JabberQuitMirandaIMThread, 0);
+
+ return JABBER_ADHOC_HANDLER_STATUS_COMPLETED;
+ }
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+}
+
+int CJabberProto::AdhocLeaveGroupchatsHandler( HXML, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession )
+{
+ int i = 0;
+ if ( pSession->GetStage() == 0 ) {
+ // first form
+ TCHAR szMsg[ 1024 ];
+
+ ListLock();
+ int nChatsCount = 0;
+ LISTFOREACH_NODEF(i, this, LIST_CHATROOM)
+ {
+ JABBER_LIST_ITEM *item = ListGetItemPtrFromIndex( i );
+ if ( item != NULL )
+ nChatsCount++;
+ }
+ ListUnlock();
+
+ if ( !nChatsCount ) {
+ mir_sntprintf( szMsg, SIZEOF(szMsg), TranslateT("There is no groupchats to leave"));
+
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("result"), pInfo )
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), _T(JABBER_FEAT_RC_LEAVE_GROUPCHATS))
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("completed"))
+ << XCHILD( _T("note"), szMsg ) << XATTR( _T("type"), _T("info")));
+
+ return JABBER_ADHOC_HANDLER_STATUS_REMOVE_SESSION;
+ }
+
+ pSession->SetStage( 1 );
+
+ XmlNodeIq iq( _T("result"), pInfo );
+ HXML xNode = iq
+ << XCHILDNS( _T("command"), _T(JABBER_FEAT_COMMANDS)) << XATTR( _T("node"), _T(JABBER_FEAT_RC_LEAVE_GROUPCHATS))
+ << XATTR( _T("sessionid"), pSession->GetSessionId()) << XATTR( _T("status"), _T("executing"))
+ << XCHILDNS( _T("x"), _T(JABBER_FEAT_DATA_FORMS)) << XATTR( _T("type"), _T("form"));
+
+ xNode << XCHILD( _T("title"), TranslateT("Leave groupchats"));
+ xNode << XCHILD( _T("instructions"), TranslateT("Choose the groupchats you want to leave"));
+
+ xNode << XCHILD( _T("field")) << XATTR( _T("type"), _T("hidden")) << XATTR( _T("var"), _T("FORM_TYPE"))
+ << XATTR( _T("value"), _T(JABBER_FEAT_RC));
+
+ // Groupchats
+ HXML fieldNode = xNode << XCHILD( _T("field")) << XATTR( _T("label"), NULL ) << XATTR( _T("type"), _T("list-multi")) << XATTR( _T("var"), _T("groupchats"));
+ fieldNode << XCHILD( _T("required"));
+
+ ListLock();
+ LISTFOREACH_NODEF(i, this, LIST_CHATROOM)
+ {
+ JABBER_LIST_ITEM *item = ListGetItemPtrFromIndex( i );
+ if ( item != NULL )
+ fieldNode << XCHILD( _T("option")) << XATTR( _T("label"), item->jid ) << XCHILD( _T("value"), item->jid );
+ }
+ ListUnlock();
+
+ m_ThreadInfo->send( iq );
+ return JABBER_ADHOC_HANDLER_STATUS_EXECUTING;
+ }
+
+ if ( pSession->GetStage() == 1 ) {
+ // result form here
+ HXML commandNode = pInfo->GetChildNode();
+ HXML xNode = xmlGetChildByTag( commandNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS));
+ if ( !xNode )
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+
+ // Groupchat list here:
+ HXML fieldNode = xmlGetChildByTag( xNode,"field", "var", _T("groupchats"));
+ if ( fieldNode ) {
+ for ( i = 0; i < xmlGetChildCount( fieldNode ); i++ ) {
+ HXML valueNode = xmlGetChild( fieldNode, i );
+ if ( valueNode && xmlGetName( valueNode ) && xmlGetText( valueNode ) && !_tcscmp( xmlGetName( valueNode ), _T("value"))) {
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_CHATROOM, xmlGetText( valueNode ));
+ if ( item )
+ GcQuit( item, 0, NULL );
+ }
+ }
+ }
+
+ return JABBER_ADHOC_HANDLER_STATUS_COMPLETED;
+ }
+ return JABBER_ADHOC_HANDLER_STATUS_CANCEL;
+}
\ No newline at end of file diff --git a/protocols/JabberG/src/jabber_rc.h b/protocols/JabberG/src/jabber_rc.h new file mode 100644 index 0000000000..cb3bf1b9cc --- /dev/null +++ b/protocols/JabberG/src/jabber_rc.h @@ -0,0 +1,314 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+XEP-0146 support for Miranda IM
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_RC_H_
+#define _JABBER_RC_H_
+
+class CJabberAdhocSession;
+
+#define JABBER_ADHOC_HANDLER_STATUS_EXECUTING 1
+#define JABBER_ADHOC_HANDLER_STATUS_COMPLETED 2
+#define JABBER_ADHOC_HANDLER_STATUS_CANCEL 3
+#define JABBER_ADHOC_HANDLER_STATUS_REMOVE_SESSION 4
+typedef int ( CJabberProto::*JABBER_ADHOC_HANDLER )( HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession );
+
+// 5 minutes to fill out form :)
+#define JABBER_ADHOC_SESSION_EXPIRE_TIME 300000
+
+class CJabberAdhocSession
+{
+protected:
+ CMString m_szSessionId;
+ CJabberAdhocSession* m_pNext;
+ DWORD m_dwStartTime;
+
+ void* m_pUserData;
+ BOOL m_bAutofreeUserData;
+
+ DWORD m_dwStage;
+public:
+ CJabberProto* ppro;
+ CJabberAdhocSession( CJabberProto* global );
+ ~CJabberAdhocSession()
+ {
+ if ( m_bAutofreeUserData && m_pUserData )
+ mir_free( m_pUserData );
+ if ( m_pNext )
+ delete m_pNext;
+ }
+ CJabberAdhocSession* GetNext()
+ {
+ return m_pNext;
+ }
+ CJabberAdhocSession* SetNext( CJabberAdhocSession *pNext )
+ {
+ CJabberAdhocSession *pRetVal = m_pNext;
+ m_pNext = pNext;
+ return pRetVal;
+ }
+ DWORD GetSessionStartTime()
+ {
+ return m_dwStartTime;
+ }
+ LPCTSTR GetSessionId()
+ {
+ return m_szSessionId;
+ }
+ BOOL SetUserData( void* pUserData, BOOL bAutofree = FALSE )
+ {
+ if ( m_bAutofreeUserData && m_pUserData )
+ mir_free( m_pUserData );
+ m_pUserData = pUserData;
+ m_bAutofreeUserData = bAutofree;
+ return TRUE;
+ }
+ DWORD SetStage( DWORD dwStage )
+ {
+ DWORD dwRetVal = m_dwStage;
+ m_dwStage = dwStage;
+ return dwRetVal;
+ }
+ DWORD GetStage()
+ {
+ return m_dwStage;
+ }
+};
+
+class CJabberAdhocNode;
+class CJabberAdhocNode
+{
+protected:
+ TCHAR* m_szJid;
+ TCHAR* m_szNode;
+ TCHAR* m_szName;
+ CJabberAdhocNode* m_pNext;
+ JABBER_ADHOC_HANDLER m_pHandler;
+ CJabberProto* m_pProto;
+public:
+ CJabberAdhocNode( CJabberProto* pProto, TCHAR* szJid, TCHAR* szNode, TCHAR* szName, JABBER_ADHOC_HANDLER pHandler )
+ {
+ ZeroMemory( this, sizeof( CJabberAdhocNode ));
+ replaceStrT( m_szJid, szJid );
+ replaceStrT( m_szNode, szNode );
+ replaceStrT( m_szName, szName );
+ m_pHandler = pHandler;
+ m_pProto = pProto;
+ }
+ ~CJabberAdhocNode()
+ {
+ if ( m_szJid)
+ mir_free( m_szJid );
+ if ( m_szNode )
+ mir_free( m_szNode );
+ if ( m_szName )
+ mir_free( m_szName );
+ if ( m_pNext )
+ delete m_pNext;
+ }
+ CJabberAdhocNode* GetNext()
+ {
+ return m_pNext;
+ }
+ CJabberAdhocNode* SetNext( CJabberAdhocNode *pNext )
+ {
+ CJabberAdhocNode *pRetVal = m_pNext;
+ m_pNext = pNext;
+ return pRetVal;
+ }
+ TCHAR* GetJid()
+ {
+ return m_szJid;
+ }
+ TCHAR* GetNode()
+ {
+ return m_szNode;
+ }
+ TCHAR* GetName()
+ {
+ return m_szName;
+ }
+ BOOL CallHandler( HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession )
+ {
+ if ( m_pHandler == NULL )
+ return FALSE;
+ return (m_pProto->*m_pHandler)( iqNode, pInfo, pSession );
+ }
+};
+
+class CJabberAdhocManager
+{
+protected:
+ CJabberProto* m_pProto;
+ CJabberAdhocNode* m_pNodes;
+ CJabberAdhocSession* m_pSessions;
+ CRITICAL_SECTION m_cs;
+
+ CJabberAdhocSession* FindSession( const TCHAR* szSession )
+ {
+ CJabberAdhocSession* pSession = m_pSessions;
+ while ( pSession ) {
+ if ( !_tcscmp( pSession->GetSessionId(), szSession ))
+ return pSession;
+ pSession = pSession->GetNext();
+ }
+ return NULL;
+ }
+
+ CJabberAdhocSession* AddNewSession()
+ {
+ CJabberAdhocSession* pSession = new CJabberAdhocSession( m_pProto );
+ if ( !pSession )
+ return NULL;
+
+ pSession->SetNext( m_pSessions );
+ m_pSessions = pSession;
+
+ return pSession;
+ }
+
+ CJabberAdhocNode* FindNode( const TCHAR* szNode )
+ {
+ CJabberAdhocNode* pNode = m_pNodes;
+ while ( pNode ) {
+ if ( !_tcscmp( pNode->GetNode(), szNode ))
+ return pNode;
+ pNode = pNode->GetNext();
+ }
+ return NULL;
+ }
+
+ BOOL RemoveSession( CJabberAdhocSession* pSession )
+ {
+ if ( !m_pSessions )
+ return FALSE;
+
+ if ( pSession == m_pSessions ) {
+ m_pSessions = m_pSessions->GetNext();
+ pSession->SetNext( NULL );
+ delete pSession;
+ return TRUE;
+ }
+
+ CJabberAdhocSession* pTmp = m_pSessions;
+ while ( pTmp->GetNext()) {
+ if ( pTmp->GetNext() == pSession ) {
+ pTmp->SetNext( pSession->GetNext());
+ pSession->SetNext( NULL );
+ delete pSession;
+ return TRUE;
+ }
+ pTmp = pTmp->GetNext();
+ }
+ return FALSE;
+ }
+
+ BOOL _ExpireSession( DWORD dwExpireTime )
+ {
+ if ( !m_pSessions )
+ return FALSE;
+
+ CJabberAdhocSession* pSession = m_pSessions;
+ if ( pSession->GetSessionStartTime() < dwExpireTime ) {
+ m_pSessions = pSession->GetNext();
+ pSession->SetNext( NULL );
+ delete pSession;
+ return TRUE;
+ }
+
+ while ( pSession->GetNext()) {
+ if ( pSession->GetNext()->GetSessionStartTime() < dwExpireTime ) {
+ CJabberAdhocSession* pRetVal = pSession->GetNext();
+ pSession->SetNext( pSession->GetNext()->GetNext());
+ pRetVal->SetNext( NULL );
+ delete pRetVal;
+ return TRUE;
+ }
+ pSession = pSession->GetNext();
+ }
+ return FALSE;
+ }
+
+public:
+ CJabberAdhocManager( CJabberProto* pProto )
+ {
+ ZeroMemory( this, sizeof( CJabberAdhocManager ));
+ m_pProto = pProto;
+ InitializeCriticalSection( &m_cs );
+ }
+ ~CJabberAdhocManager()
+ {
+ if ( m_pNodes )
+ delete m_pNodes;
+ if ( m_pSessions )
+ delete m_pSessions;
+ DeleteCriticalSection( &m_cs );
+ }
+ void Lock()
+ {
+ EnterCriticalSection( &m_cs );
+ }
+ void Unlock()
+ {
+ LeaveCriticalSection( &m_cs );
+ }
+ BOOL FillDefaultNodes();
+ BOOL AddNode( TCHAR* szJid, TCHAR* szNode, TCHAR* szName, JABBER_ADHOC_HANDLER pHandler )
+ {
+ CJabberAdhocNode* pNode = new CJabberAdhocNode( m_pProto, szJid, szNode, szName, pHandler );
+ if ( !pNode )
+ return FALSE;
+
+ Lock();
+ if ( !m_pNodes )
+ m_pNodes = pNode;
+ else {
+ CJabberAdhocNode* pTmp = m_pNodes;
+ while ( pTmp->GetNext())
+ pTmp = pTmp->GetNext();
+ pTmp->SetNext( pNode );
+ }
+ Unlock();
+
+ return TRUE;
+ }
+ CJabberAdhocNode* GetFirstNode()
+ {
+ return m_pNodes;
+ }
+ BOOL HandleItemsRequest( HXML iqNode, CJabberIqInfo* pInfo, const TCHAR* szNode );
+ BOOL HandleInfoRequest( HXML iqNode, CJabberIqInfo* pInfo, const TCHAR* szNode );
+ BOOL HandleCommandRequest( HXML iqNode, CJabberIqInfo* pInfo, const TCHAR* szNode );
+
+ BOOL ExpireSessions()
+ {
+ Lock();
+ DWORD dwExpireTime = GetTickCount() - JABBER_ADHOC_SESSION_EXPIRE_TIME;
+ while ( _ExpireSession( dwExpireTime ));
+ Unlock();
+ return TRUE;
+ }
+};
+
+#endif //_JABBER_RC_H_
diff --git a/protocols/JabberG/src/jabber_search.cpp b/protocols/JabberG/src/jabber_search.cpp new file mode 100644 index 0000000000..e242294a16 --- /dev/null +++ b/protocols/JabberG/src/jabber_search.cpp @@ -0,0 +1,762 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Artem Shpynov
+
+Module implements a search according to XEP-0055: Jabber Search
+http://www.xmpp.org/extensions/xep-0055.html
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include <CommCtrl.h>
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Subclassing of IDC_FRAME to implement more user-friendly fields scrolling
+//
+static int JabberSearchFrameProc(HWND hwnd, int msg, WPARAM wParam, LPARAM lParam)
+{
+ if ( msg == WM_COMMAND && lParam != 0 ) {
+ HWND hwndDlg=GetParent(hwnd);
+ JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if ( dat && lParam ) {
+ int pos=dat->curPos;
+ RECT MineRect;
+ RECT FrameRect;
+ GetWindowRect(GetDlgItem( hwndDlg, IDC_FRAME ),&FrameRect);
+ GetWindowRect((HWND)lParam, &MineRect);
+ if ( MineRect.top-10 < FrameRect.top ) {
+ pos=dat->curPos+(MineRect.top-14-FrameRect.top);
+ if (pos<0) pos=0;
+ }
+ else if ( MineRect.bottom > FrameRect.bottom ) {
+ pos=dat->curPos+(MineRect.bottom-FrameRect.bottom);
+ if (dat->frameHeight+pos>dat->CurrentHeight)
+ pos=dat->CurrentHeight-dat->frameHeight;
+ }
+ if ( pos != dat->curPos ) {
+ ScrollWindow( GetDlgItem( hwndDlg, IDC_FRAME ), 0, dat->curPos - pos, NULL, &( dat->frameRect ));
+ SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, pos, TRUE );
+ RECT Invalid=dat->frameRect;
+ if (dat->curPos - pos >0)
+ Invalid.bottom=Invalid.top+(dat->curPos - pos);
+ else
+ Invalid.top=Invalid.bottom+(dat->curPos - pos);
+
+ RedrawWindow(GetDlgItem( hwndDlg, IDC_FRAME ), NULL, NULL, RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW |RDW_ALLCHILDREN);
+ dat->curPos = pos;
+ } }
+ if (HIWORD(wParam)==EN_SETFOCUS) { //Transmit focus set notification to parent window
+ PostMessage(GetParent(hwndDlg),WM_COMMAND, MAKEWPARAM(0,EN_SETFOCUS), (LPARAM)hwndDlg);
+ }
+ }
+
+ if ( msg == WM_PAINT ) {
+ PAINTSTRUCT ps;
+ HDC hdc=BeginPaint(hwnd, &ps);
+ FillRect(hdc,&(ps.rcPaint),GetSysColorBrush(COLOR_BTNFACE));
+ EndPaint(hwnd, &ps);
+ }
+
+ return DefWindowProc(hwnd,msg,wParam,lParam);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Add Search field to form
+//
+static int JabberSearchAddField(HWND hwndDlg, Data* FieldDat )
+{
+ if (!FieldDat || !FieldDat->Label || !FieldDat->Var) return FALSE;
+ HFONT hFont = ( HFONT ) SendMessage( hwndDlg, WM_GETFONT, 0, 0 );
+ HWND hwndParent=GetDlgItem(hwndDlg,IDC_FRAME);
+ LONG frameExStyle = GetWindowLongPtr( hwndParent, GWL_EXSTYLE );
+ frameExStyle |= WS_EX_CONTROLPARENT;
+ SetWindowLongPtr( hwndParent, GWL_EXSTYLE, frameExStyle );
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_FRAME),GWLP_WNDPROC,(LONG_PTR)JabberSearchFrameProc);
+
+ int CornerX=1;
+ int CornerY=1;
+ RECT rect;
+ GetClientRect(hwndParent,&rect);
+ int width=rect.right-5-CornerX;
+
+ int Order=(FieldDat->bHidden) ? -1 : FieldDat->Order;
+
+ HWND hwndLabel=CreateWindowEx(0,_T("STATIC"),(LPCTSTR)TranslateTS(FieldDat->Label),WS_CHILD, CornerX, CornerY + Order*40, width, 13,hwndParent,NULL,hInst,0);
+ HWND hwndVar=CreateWindowEx(0|WS_EX_CLIENTEDGE,_T("EDIT"),(LPCTSTR)FieldDat->defValue,WS_CHILD|WS_TABSTOP, CornerX+5, CornerY + Order*40+14, width ,20,hwndParent,NULL,hInst,0);
+ SendMessage(hwndLabel, WM_SETFONT, (WPARAM)hFont,0);
+ SendMessage(hwndVar, WM_SETFONT, (WPARAM)hFont,0);
+ if (!FieldDat->bHidden)
+ {
+ ShowWindow(hwndLabel,SW_SHOW);
+ ShowWindow(hwndVar,SW_SHOW);
+ EnableWindow(hwndLabel,!FieldDat->bReadOnly);
+ SendMessage(hwndVar, EM_SETREADONLY, (WPARAM)FieldDat->bReadOnly,0);
+ }
+ //remade list
+ //reallocation
+ JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if ( dat ) {
+ dat->pJSInf=(JabberSearchFieldsInfo*) realloc(dat->pJSInf, sizeof(JabberSearchFieldsInfo)*(dat->nJSInfCount+1));
+ dat->pJSInf[dat->nJSInfCount].hwndCaptionItem=hwndLabel;
+ dat->pJSInf[dat->nJSInfCount].hwndValueItem=hwndVar;
+ dat->pJSInf[dat->nJSInfCount].szFieldCaption=_tcsdup(FieldDat->Label);
+ dat->pJSInf[dat->nJSInfCount].szFieldName=_tcsdup(FieldDat->Var);
+ dat->nJSInfCount++;
+ }
+ return CornerY + Order*40+14 +20;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Available search field request result handler (XEP-0055. Examples 2, 7)
+
+void CJabberProto::OnIqResultGetSearchFields( HXML iqNode )
+{
+ if ( !searchHandleDlg )
+ return;
+
+ LPCTSTR type = xmlGetAttrValue( iqNode, _T("type"));
+ if ( !type )
+ return;
+
+ if ( !lstrcmp( type, _T("result"))) {
+ HXML queryNode = xmlGetNthChild( iqNode, _T("query"), 1 );
+ HXML xNode = xmlGetChildByTag( queryNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS));
+
+ ShowWindow(searchHandleDlg,SW_HIDE);
+ if ( xNode ) {
+ //1. Form
+ PostMessage( searchHandleDlg, WM_USER+11, ( WPARAM )xi.copyNode( xNode ), ( LPARAM )0 );
+ HXML xcNode = xmlGetNthChild( xNode, _T("instructions"), 1 );
+ if ( xcNode )
+ SetDlgItemText( searchHandleDlg, IDC_INSTRUCTIONS, xmlGetText( xcNode ));
+ }
+ else {
+ int Order=0;
+ for ( int i = 0; ; i++ ) {
+ HXML chNode = xmlGetChild( queryNode, i );
+ if ( !chNode )
+ break;
+
+ if ( !_tcsicmp( xmlGetName( chNode ), _T("instructions")) && xmlGetText( chNode ))
+ SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,TranslateTS(xmlGetText( chNode )));
+ else if ( xmlGetName( chNode )) {
+ Data *MyData=(Data*)malloc(sizeof(Data));
+ memset(MyData,0,sizeof(Data));
+
+ MyData->Label = mir_tstrdup( xmlGetName( chNode ));
+ MyData->Var = mir_tstrdup( xmlGetName( chNode ));
+ MyData->defValue = mir_tstrdup(xmlGetText( chNode ));
+ MyData->Order = Order;
+ if (MyData->defValue) MyData->bReadOnly = TRUE;
+ PostMessage(searchHandleDlg,WM_USER+10,(WPARAM)FALSE,(LPARAM)MyData);
+ Order++;
+ } } }
+ const TCHAR* szFrom = xmlGetAttrValue( iqNode, _T("from"));
+ if (szFrom)
+ SearchAddToRecent(szFrom,searchHandleDlg);
+ PostMessage(searchHandleDlg,WM_USER+10,(WPARAM)0,(LPARAM)0);
+ ShowWindow(searchHandleDlg,SW_SHOW);
+ }
+ else if ( !lstrcmp( type, _T("error"))) {
+ const TCHAR* code=NULL;
+ const TCHAR* description=NULL;
+ TCHAR buff[255];
+ HXML errorNode = xmlGetChild( iqNode, "error");
+ if ( errorNode ) {
+ code = xmlGetAttrValue( errorNode, _T("code"));
+ description=xmlGetText( errorNode );
+ }
+ _sntprintf(buff,SIZEOF(buff),TranslateT("Error %s %s\r\nPlease select other server"),code ? code : _T(""),description?description:_T(""));
+ SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,buff);
+ }
+ else SetDlgItemText( searchHandleDlg, IDC_INSTRUCTIONS, TranslateT( "Error Unknown reply recieved\r\nPlease select other server" ));
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Return results to search dialog
+// The pmFields is the pointer to map of <field Name, field Label> Not unical but ordered
+// This can help to made result parser routines more simple
+
+void CJabberProto::SearchReturnResults( HANDLE id, void * pvUsersInfo, U_TCHAR_MAP * pmAllFields )
+{
+ LIST<TCHAR> ListOfNonEmptyFields(20,(LIST<TCHAR>::FTSortFunc)TCharKeyCmp);
+ LIST<TCHAR> ListOfFields(20);
+ LIST<void>* plUsersInfo = ( LIST<void>* )pvUsersInfo;
+ int i, nUsersFound = plUsersInfo->getCount();
+
+ // lets fill the ListOfNonEmptyFields but in users order
+ for ( i=0; i < nUsersFound; i++ ) {
+ U_TCHAR_MAP* pmUserData = ( U_TCHAR_MAP* )plUsersInfo->operator [](i);
+ int nUserFields = pmUserData->getCount();
+ for (int j=0; j < nUserFields; j++) {
+ TCHAR* var = pmUserData->getKeyName(j);
+ if (var && ListOfNonEmptyFields.getIndex(var) < 0)
+ ListOfNonEmptyFields.insert(var);
+ } }
+
+ // now fill the ListOfFields but order is from pmAllFields
+ int nAllCount = pmAllFields->getCount();
+ for ( i=0; i < nAllCount; i++ ) {
+ TCHAR * var=pmAllFields->getUnOrderedKeyName(i);
+ if ( var && ListOfNonEmptyFields.getIndex(var) < 0 )
+ continue;
+ ListOfFields.insert(var);
+ }
+
+ // now lets transfer field names
+ int nFieldCount = ListOfFields.getCount();
+
+ JABBER_CUSTOMSEARCHRESULTS Results={0};
+ Results.nSize=sizeof(Results);
+ Results.pszFields=(TCHAR**)mir_alloc(sizeof(TCHAR*)*nFieldCount);
+ Results.nFieldCount=nFieldCount;
+
+ /* Sending Columns Titles */
+ for ( i=0; i < nFieldCount; i++ ) {
+ TCHAR* var = ListOfFields[i];
+ if ( var )
+ Results.pszFields[i] = pmAllFields->operator [](var);
+ }
+
+ Results.jsr.hdr.cbSize = 0; // sending column names
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM) &Results );
+
+ /* Sending Users Data */
+ Results.jsr.hdr.cbSize = sizeof(Results.jsr); // sending user data
+
+ for ( i=0; i < nUsersFound; i++ ) {
+ TCHAR buff[200]=_T("");
+ Results.jsr.jid[0]=_T('\0');
+ U_TCHAR_MAP * pmUserData = (U_TCHAR_MAP *) plUsersInfo->operator [](i);
+ for ( int j=0; j < nFieldCount; j++ ) {
+ TCHAR* var = ListOfFields[j];
+ TCHAR* value = pmUserData->operator [](var);
+ Results.pszFields[j] = value ? value : (TCHAR *)_T(" ");
+ if (!_tcsicmp(var,_T("jid")) && value )
+ _tcsncpy(Results.jsr.jid, value, SIZEOF(Results.jsr.jid));
+ }
+ {
+ TCHAR * nickfields[]={ _T("nick"), _T("nickname"),
+ _T("fullname"), _T("name"),
+ _T("given"), _T("first"),
+ _T("jid"), NULL };
+ TCHAR * nick=NULL;
+ int k=0;
+ while (nickfields[k] && !nick) nick=pmUserData->operator [](nickfields[k++]);
+ if (_tcsicmp(nick, Results.jsr.jid))
+ _sntprintf(buff,SIZEOF(buff),_T("%s ( %s )"),nick, Results.jsr.jid);
+ else
+ _tcsncpy(buff, nick, SIZEOF(buff));
+ Results.jsr.hdr.nick = nick ? buff : NULL;
+ Results.jsr.hdr.flags = PSR_TCHAR;
+ }
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM) &Results );
+ Results.jsr.hdr.nick=NULL;
+
+ }
+ mir_free( Results.pszFields );
+}
+
+void DestroyKey( TCHAR* key )
+{
+ mir_free( key );
+}
+
+TCHAR* CopyKey( TCHAR* key )
+{
+ return mir_tstrdup( key );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Search field request result handler (XEP-0055. Examples 3, 8)
+
+void CJabberProto::OnIqResultAdvancedSearch( HXML iqNode )
+{
+ const TCHAR* type;
+ int id;
+
+ U_TCHAR_MAP mColumnsNames(10);
+ LIST<void> SearchResults(2);
+
+ if ((( id = JabberGetPacketID( iqNode )) == -1 ) || (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL )) {
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 );
+ return;
+ }
+
+ if ( !lstrcmp( type, _T("result"))) {
+ HXML queryNode = xmlGetNthChild( iqNode, _T("query"), 1 );
+ HXML xNode = xmlGetChildByTag( queryNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS));
+ if (xNode) {
+ //1. Form search results info
+ HXML reportNode = xmlGetNthChild( xNode, _T("reported"), 1 );
+ if (reportNode) {
+ int i = 1;
+ while ( HXML fieldNode = xmlGetNthChild( reportNode, _T("field"), i++ )) {
+ TCHAR* var = ( TCHAR* )xmlGetAttrValue( fieldNode, _T( "var" ));
+ if ( var ) {
+ TCHAR* Label = ( TCHAR* )xmlGetAttrValue( fieldNode, _T( "label" ));
+ mColumnsNames.insert(var, (Label!=NULL) ? Label : var);
+ } } }
+
+ int i=1;
+ HXML itemNode;
+ while ( itemNode = xmlGetNthChild( xNode, _T("item"), i++ )) {
+ U_TCHAR_MAP *pUserColumn = new U_TCHAR_MAP(10);
+ int j = 1;
+ while ( HXML fieldNode = xmlGetNthChild( itemNode, _T("field"), j++ )) {
+ if ( TCHAR* var = (TCHAR*)xmlGetAttrValue( fieldNode, _T("var"))) {
+ if ( TCHAR* Text = (TCHAR*)xmlGetText( xmlGetChild( fieldNode, _T("value")))) {
+ if ( !mColumnsNames[var] )
+ mColumnsNames.insert(var,var);
+ pUserColumn->insert(var,Text);
+ } } }
+
+ SearchResults.insert((void*)pUserColumn);
+ }
+ }
+ else {
+ //2. Field list search results info
+ int i=1;
+ while ( HXML itemNode = xmlGetNthChild( queryNode, _T("item"), i++ )) {
+ U_TCHAR_MAP *pUserColumn=new U_TCHAR_MAP(10);
+
+ TCHAR* jid = (TCHAR*)xmlGetAttrValue( itemNode, _T("jid"));
+ TCHAR* keyReturned;
+ mColumnsNames.insertCopyKey( _T("jid"),_T("jid"),&keyReturned, CopyKey, DestroyKey );
+ mColumnsNames.insert( _T("jid"), keyReturned );
+ pUserColumn->insertCopyKey( _T("jid"), jid, NULL, CopyKey, DestroyKey );
+
+ for ( int j=0; ; j++ ) {
+ HXML child = xmlGetChild( itemNode, j );
+ if ( !child )
+ break;
+
+ const TCHAR* szColumnName = xmlGetName( child );
+ if ( szColumnName ) {
+ if ( xmlGetText( child ) && xmlGetText( child )[0] != _T('\0')) {
+ mColumnsNames.insertCopyKey(( TCHAR* )szColumnName,_T(""),&keyReturned, CopyKey, DestroyKey);
+ mColumnsNames.insert(( TCHAR* )szColumnName,keyReturned);
+ pUserColumn->insertCopyKey(( TCHAR* )szColumnName, ( TCHAR* )xmlGetText( child ),NULL, CopyKey, DestroyKey);
+ } } }
+
+ SearchResults.insert((void*)pUserColumn);
+ } }
+ }
+ else if (!lstrcmp( type, _T("error"))) {
+ const TCHAR* code=NULL;
+ const TCHAR* description=NULL;
+ TCHAR buff[255];
+ HXML errorNode = xmlGetChild( iqNode , "error" );
+ if (errorNode) {
+ code = xmlGetAttrValue( errorNode, _T("code"));
+ description = xmlGetText( errorNode );
+ }
+
+ _sntprintf(buff,SIZEOF(buff),TranslateT("Error %s %s\r\nTry to specify more detailed"),code ? code : _T(""),description?description:_T(""));
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 );
+ if (searchHandleDlg )
+ SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,buff);
+ else
+ MessageBox(NULL, buff, TranslateT("Search error"), MB_OK|MB_ICONSTOP);
+ return;
+ }
+
+ SearchReturnResults((HANDLE)id, (void*)&SearchResults, (U_TCHAR_MAP *)&mColumnsNames);
+
+ for (int i=0; i < SearchResults.getCount(); i++ )
+ delete ((U_TCHAR_MAP *)SearchResults[i]);
+
+ //send success to finish searching
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 );
+}
+
+static BOOL CALLBACK DeleteChildWindowsProc( HWND hwnd, LPARAM )
+{
+ DestroyWindow(hwnd);
+ return TRUE;
+}
+
+static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat)
+{
+ //lock
+ if ( !dat->fSearchRequestIsXForm && dat->nJSInfCount && dat->pJSInf ) {
+ for ( int i=0; i < dat->nJSInfCount; i++ ) {
+ if (dat->pJSInf[i].hwndValueItem)
+ DestroyWindow(dat->pJSInf[i].hwndValueItem);
+ if (dat->pJSInf[i].hwndCaptionItem)
+ DestroyWindow(dat->pJSInf[i].hwndCaptionItem);
+ if (dat->pJSInf[i].szFieldCaption)
+ free(dat->pJSInf[i].szFieldCaption);
+ if (dat->pJSInf[i].szFieldName)
+ free(dat->pJSInf[i].szFieldName);
+ }
+ free(dat->pJSInf);
+ dat->pJSInf=NULL;
+ }
+ else EnumChildWindows(GetDlgItem(hwndDlg,IDC_FRAME),DeleteChildWindowsProc,0);
+
+ if ( dat->xNode )
+ xi.destroyNode( dat->xNode );
+
+ SendMessage(GetDlgItem(hwndDlg,IDC_FRAME), WM_SETFONT, (WPARAM) SendMessage( hwndDlg, WM_GETFONT, 0, 0 ),0 );
+ dat->nJSInfCount=0;
+ ShowWindow(GetDlgItem(hwndDlg,IDC_VSCROLL),SW_HIDE);
+ SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,TranslateT("Select/type search service URL above and press <Go>"));
+ //unlock
+}
+
+static void JabberSearchRefreshFrameScroll(HWND hwndDlg, JabberSearchData * dat)
+{
+ HWND hFrame = GetDlgItem( hwndDlg, IDC_FRAME );
+ HWND hwndScroll = GetDlgItem( hwndDlg, IDC_VSCROLL );
+ RECT rc;
+ GetClientRect( hFrame, &rc );
+ GetClientRect( hFrame, &dat->frameRect );
+ dat->frameHeight = rc.bottom-rc.top;
+ if ( dat->frameHeight < dat->CurrentHeight) {
+ ShowWindow( hwndScroll, SW_SHOW );
+ EnableWindow( hwndScroll, TRUE );
+ }
+ else ShowWindow( hwndScroll, SW_HIDE );
+
+ SetScrollRange( hwndScroll, SB_CTL, 0, dat->CurrentHeight-dat->frameHeight, FALSE );
+}
+
+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 );
+
+ SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,m_bJabberOnline ? TranslateT("Please wait...\r\nConnecting search server...") : 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;
+}
+
+static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const TCHAR* szAddr)
+{
+ int lResult = SendMessage( GetDlgItem(hwndDlg,IDC_SERVER), (UINT) CB_FINDSTRING, 0, (LPARAM)szAddr );
+ if ( lResult == -1 )
+ SendDlgItemMessage( hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, ( LPARAM )szAddr );
+}
+
+void CJabberProto::SearchDeleteFromRecent( const TCHAR* szAddr, BOOL deleteLastFromDB )
+{
+ DBVARIANT dbv;
+ char key[30];
+ //search in recent
+ for ( int i=0; i<10; i++ ) {
+ sprintf(key,"RecentlySearched_%d",i);
+ if ( !JGetStringT( NULL, key, &dbv )) {
+ if ( !_tcsicmp( szAddr, dbv.ptszVal )) {
+ JFreeVariant( &dbv );
+ for ( int j=i; j<10; j++ ) {
+ sprintf( key, "RecentlySearched_%d", j+1 );
+ if ( !JGetStringT( NULL, key, &dbv )) {
+ sprintf(key,"RecentlySearched_%d",j);
+ JSetStringT(NULL,key,dbv.ptszVal);
+ JFreeVariant( &dbv );
+ }
+ else {
+ if ( deleteLastFromDB ) {
+ sprintf(key,"RecentlySearched_%d",j);
+ JDeleteSetting(NULL,key);
+ }
+ break;
+ } }
+ break;
+ }
+ else JFreeVariant( &dbv );
+} } }
+
+void CJabberProto::SearchAddToRecent( const TCHAR* szAddr, HWND hwndDialog )
+{
+ DBVARIANT dbv;
+ char key[30];
+ SearchDeleteFromRecent( szAddr );
+ for ( int j=9; j > 0; j-- ) {
+ sprintf( key, "RecentlySearched_%d", j-1 );
+ if ( !JGetStringT( NULL, key, &dbv )) {
+ sprintf(key,"RecentlySearched_%d",j);
+ JSetStringT(NULL,key,dbv.ptszVal);
+ JFreeVariant(&dbv);
+ } }
+
+ sprintf( key, "RecentlySearched_%d", 0 );
+ JSetStringT( NULL, key, szAddr );
+ if ( hwndDialog )
+ JabberSearchAddUrlToRecentCombo( hwndDialog, szAddr );
+}
+
+static INT_PTR CALLBACK JabberSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ JabberSearchData* dat = ( JabberSearchData* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ dat = ( JabberSearchData * )mir_alloc( sizeof( JabberSearchData ));
+ memset( dat, 0, sizeof( JabberSearchData ));
+ dat->ppro = ( CJabberProto* )lParam;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, (LONG_PTR)dat );
+
+ /* Server Combo box */
+ char szServerName[100];
+ if ( dat->ppro->JGetStaticString( "Jud", NULL, szServerName, sizeof szServerName ))
+ strcpy( szServerName, "users.jabber.org" );
+ SetDlgItemTextA(hwndDlg,IDC_SERVER,szServerName);
+ SendDlgItemMessageA(hwndDlg,IDC_SERVER,CB_ADDSTRING,0,(LPARAM)szServerName);
+ //TO DO: Add Transports here
+ int i, transpCount = dat->ppro->m_lstTransports.getCount();
+ for ( i=0; i < transpCount; i++ ) {
+ TCHAR* szTransp = dat->ppro->m_lstTransports[i];
+ if ( szTransp )
+ JabberSearchAddUrlToRecentCombo(hwndDlg, szTransp );
+ }
+
+ DBVARIANT dbv;
+ char key[30];
+ for ( i=0; i < 10; i++ ) {
+ sprintf(key,"RecentlySearched_%d",i);
+ if ( !dat->ppro->JGetStringT( NULL, key, &dbv )) {
+ JabberSearchAddUrlToRecentCombo(hwndDlg, dbv.ptszVal );
+ JFreeVariant( &dbv );
+ } }
+
+ //TO DO: Add 4 recently used
+ dat->lastRequestIq = dat->ppro->SearchRenewFields(hwndDlg,dat);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ if ( LOWORD(wParam) == IDC_SERVER ) {
+ switch ( HIWORD( wParam )) {
+ case CBN_SETFOCUS:
+ PostMessage(GetParent(hwndDlg),WM_COMMAND, MAKEWPARAM(0,EN_SETFOCUS), (LPARAM)hwndDlg);
+ return TRUE;
+
+ case CBN_EDITCHANGE:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE);
+ return TRUE;
+
+ case CBN_EDITUPDATE:
+ JabberSearchFreeData(hwndDlg, dat);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE);
+ return TRUE;
+
+ case CBN_SELENDOK:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE);
+ PostMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_GO,BN_CLICKED),0);
+ return TRUE;
+ }
+ }
+ else if ( LOWORD(wParam) == IDC_GO && HIWORD(wParam) == BN_CLICKED ) {
+ dat->ppro->SearchRenewFields( hwndDlg, dat );
+ return TRUE;
+ }
+ break;
+
+ case WM_SIZE:
+ {
+ //Resize IDC_FRAME to take full size
+ RECT rcForm;
+ GetWindowRect(hwndDlg, &rcForm);
+ RECT rcFrame;
+ GetWindowRect( GetDlgItem(hwndDlg, IDC_FRAME), &rcFrame );
+ rcFrame.bottom = rcForm.bottom;
+ SetWindowPos(GetDlgItem(hwndDlg,IDC_FRAME),NULL,0,0,rcFrame.right-rcFrame.left,rcFrame.bottom-rcFrame.top,SWP_NOZORDER|SWP_NOMOVE);
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_VSCROLL), &rcForm);
+ SetWindowPos(GetDlgItem(hwndDlg,IDC_VSCROLL),NULL,0,0,rcForm.right-rcForm.left,rcFrame.bottom-rcFrame.top,SWP_NOZORDER|SWP_NOMOVE);
+ JabberSearchRefreshFrameScroll(hwndDlg, dat);
+ }
+ return TRUE;
+
+ case WM_USER+11:
+ {
+ dat->fSearchRequestIsXForm=TRUE;
+ dat->xNode = ( HXML )wParam;
+ JabberFormCreateUI( GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode, &dat->CurrentHeight,TRUE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_FRAME), SW_SHOW);
+ dat->nJSInfCount=1;
+ return TRUE;
+ }
+ case WM_USER+10:
+ {
+ Data* MyDat = ( Data* )lParam;
+ if ( MyDat ) {
+ dat->fSearchRequestIsXForm = ( BOOL )wParam;
+ dat->CurrentHeight = JabberSearchAddField(hwndDlg,MyDat);
+ mir_free( MyDat->Label );
+ mir_free( MyDat->Var );
+ mir_free( MyDat->defValue );
+ free( MyDat );
+ }
+ else
+ {
+ JabberSearchRefreshFrameScroll(hwndDlg,dat);
+ ScrollWindow( GetDlgItem( hwndDlg, IDC_FRAME ), 0, dat->curPos - 0, NULL, &( dat->frameRect ));
+ SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, 0, FALSE );
+ dat->curPos=0;
+ }
+ return TRUE;
+ }
+ case WM_MOUSEWHEEL:
+ {
+ int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
+ if ( zDelta ) {
+ int nScrollLines=0;
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,(void*)&nScrollLines,0);
+ for (int i=0; i<(nScrollLines+1)/2; i++)
+ SendMessage(hwndDlg,WM_VSCROLL, (zDelta<0)?SB_LINEDOWN:SB_LINEUP,0);
+ } }
+ return TRUE;
+
+ case WM_VSCROLL:
+ {
+ int pos;
+ if ( dat != NULL ) {
+ pos = dat->curPos;
+ switch ( LOWORD( wParam )) {
+ case SB_LINEDOWN:
+ pos += 10;
+ break;
+ case SB_LINEUP:
+ pos -= 10;
+ break;
+ case SB_PAGEDOWN:
+ pos += ( dat->CurrentHeight - 10 );
+ break;
+ case SB_PAGEUP:
+ pos -= ( dat->CurrentHeight - 10 );
+ break;
+ case SB_THUMBTRACK:
+ pos = HIWORD( wParam );
+ break;
+ }
+ if ( pos > ( dat->CurrentHeight - dat->frameHeight ))
+ pos = dat->CurrentHeight - dat->frameHeight;
+ if ( pos < 0 )
+ pos = 0;
+ if ( dat->curPos != pos ) {
+ ScrollWindow( GetDlgItem( hwndDlg, IDC_FRAME ), 0, dat->curPos - pos, NULL , &( dat->frameRect ));
+ SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, pos, TRUE );
+ RECT Invalid=dat->frameRect;
+ if (dat->curPos - pos >0)
+ Invalid.bottom=Invalid.top+(dat->curPos - pos);
+ else
+ Invalid.top=Invalid.bottom+(dat->curPos - pos);
+
+ RedrawWindow(GetDlgItem( hwndDlg, IDC_FRAME ), NULL, NULL, RDW_UPDATENOW |RDW_ALLCHILDREN);
+ dat->curPos = pos;
+ } } }
+ return TRUE;
+
+ case WM_DESTROY:
+ JabberSearchFreeData( hwndDlg, dat );
+ JabberFormDestroyUI( GetDlgItem( hwndDlg, IDC_FRAME ));
+ mir_free( dat );
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, 0 );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+HWND __cdecl CJabberProto::CreateExtendedSearchUI( HWND parent )
+{
+ TCHAR szServer[128];
+ if (parent && hInst && (!JGetStringT(NULL, "LoginServer", szServer, SIZEOF(szServer)) || _tcsicmp(szServer, _T("S.ms"))))
+ return CreateDialogParam( hInst, MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, ( LPARAM )this );
+
+ return 0; // Failure
+}
+
+//////////////////////////////////////////////////////////////////////////
+// The function formats request to server
+
+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;
+}
diff --git a/protocols/JabberG/src/jabber_search.h b/protocols/JabberG/src/jabber_search.h new file mode 100644 index 0000000000..7450b652d1 --- /dev/null +++ b/protocols/JabberG/src/jabber_search.h @@ -0,0 +1,273 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Artem Shpynov
+
+Module implements a search according to XEP-0055: Jabber Search
+http://www.xmpp.org/extensions/xep-0055.html
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+typedef struct _tagJabberSearchFieldsInfo
+{
+ TCHAR * szFieldName;
+ TCHAR * szFieldCaption;
+ HWND hwndCaptionItem;
+ HWND hwndValueItem;
+} JabberSearchFieldsInfo;
+
+typedef struct _tagJabberSearchData
+{
+ struct CJabberProto* ppro;
+ JabberSearchFieldsInfo * pJSInf;
+ HXML xNode;
+ int nJSInfCount;
+ int lastRequestIq;
+ int CurrentHeight;
+ int curPos;
+ int frameHeight;
+ RECT frameRect;
+ BOOL fSearchRequestIsXForm;
+
+}JabberSearchData;
+
+typedef struct tag_Data
+{
+ TCHAR *Label;
+ TCHAR * Var;
+ TCHAR * defValue;
+ BOOL bHidden;
+ BOOL bReadOnly;
+ int Order;
+
+} Data;
+
+
+typedef struct tagJABBER_CUSTOMSEARCHRESULTS
+{
+ size_t nSize;
+ int nFieldCount;
+ TCHAR ** pszFields;
+ JABBER_SEARCH_RESULT jsr;
+}JABBER_CUSTOMSEARCHRESULTS;
+
+static HWND searchHandleDlg=NULL;
+
+//local functions declarations
+static int JabberSearchFrameProc(HWND hwnd, int msg, WPARAM wParam, LPARAM lParam);
+static int JabberSearchAddField(HWND hwndDlg, Data* FieldDat );
+static void JabberIqResultGetSearchFields( HXML iqNode, void *userdata );
+static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat);
+static void JabberSearchRefreshFrameScroll(HWND hwndDlg, JabberSearchData * dat);
+static INT_PTR CALLBACK JabberSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static void JabberSearchDeleteFromRecent(TCHAR * szAddr,BOOL deleteLastFromDB);
+void SearchAddToRecent(TCHAR * szAddr, HWND hwnd);
+
+// Implementation of MAP class (the list
+template <typename _KEYTYPE , int (*COMPARATOR)(_KEYTYPE*, _KEYTYPE*) >
+class UNIQUE_MAP
+{
+
+public:
+ typedef _KEYTYPE* (*COPYKEYPROC)(_KEYTYPE*);
+ typedef void (*DESTROYKEYPROC)(_KEYTYPE*);
+
+private:
+ typedef struct _tagRECORD
+ {
+ _tagRECORD(_KEYTYPE * key, TCHAR * value=NULL) { _key=key; _value=value; _order=0; _destroyKeyProc=NULL; }
+ ~_tagRECORD()
+ {
+ if (_key && _destroyKeyProc)
+ _destroyKeyProc(_key);
+ _key=NULL;
+ _destroyKeyProc=NULL;
+ }
+ _KEYTYPE *_key;
+ TCHAR * _value;
+ int _order;
+ DESTROYKEYPROC _destroyKeyProc;
+ } _RECORD;
+
+ int _nextOrder;
+ LIST<_RECORD> _Records;
+
+ static int _KeysEqual( const _RECORD* p1, const _RECORD* p2 )
+ {
+ if (COMPARATOR)
+ return (int)(COMPARATOR((p1->_key),(p2->_key)));
+ else
+ return (int) (p1->_key < p2->_key);
+ }
+
+ inline int _remove(_RECORD* p)
+ {
+ int _itemOrder=p->_order;
+ if (_Records.remove(p))
+ {
+ delete(p);
+ _nextOrder--;
+ for (int i=0; i<_Records.getCount(); i++)
+ {
+ _RECORD * temp=_Records[i];
+ if (temp && temp->_order>_itemOrder)
+ temp->_order--;
+ }
+ return 1;
+ }
+ return 0;
+ }
+ inline _RECORD * _getUnorderedRec (int index)
+ {
+ for (int i=0; i<_Records.getCount(); i++)
+ {
+ _RECORD * rec=_Records[i];
+ if (rec->_order==index) return rec;
+ }
+ return NULL;
+ }
+
+public:
+ UNIQUE_MAP(int incr):_Records(incr,_KeysEqual)
+ {
+ _nextOrder=0;
+ };
+ ~UNIQUE_MAP()
+ {
+ _RECORD * record;
+ int i=0;
+ while (record=_Records[i++]) delete record;
+ }
+
+ int insert(_KEYTYPE* Key, TCHAR *Value)
+ {
+ _RECORD * rec= new _RECORD(Key,Value);
+ int index=_Records.getIndex(rec);
+ if (index<0)
+ {
+ if (!_Records.insert(rec)) delete rec;
+ else
+ {
+ index=_Records.getIndex(rec);
+ rec->_order=_nextOrder++;
+ }
+ }
+ else
+ {
+ _Records[index]->_value=Value;
+ delete rec;
+ }
+ return index;
+ }
+ int insertCopyKey(_KEYTYPE* Key, TCHAR *Value, _KEYTYPE** _KeyReturn, COPYKEYPROC CopyProc, DESTROYKEYPROC DestroyProc )
+ {
+ _RECORD * rec= new _RECORD(Key,Value);
+ int index=_Records.getIndex(rec);
+ if (index<0)
+ {
+ _KEYTYPE* newKey=CopyProc(Key);
+ if (!_Records.insert(rec))
+ {
+ delete rec;
+ DestroyProc(newKey);
+ if (_KeyReturn) *_KeyReturn=NULL;
+ }
+ else
+ {
+ rec->_key=newKey;
+ rec->_destroyKeyProc=DestroyProc;
+ index=_Records.getIndex(rec);
+ rec->_order=_nextOrder++;
+ if (_KeyReturn) *_KeyReturn=newKey;
+ }
+ }
+ else
+ {
+ _Records[index]->_value=Value;
+ if (_KeyReturn) *_KeyReturn=_Records[index]->_key;
+ delete rec;
+ }
+ return index;
+ }
+ inline TCHAR* operator[]( _KEYTYPE* _KEY ) const
+ {
+ _RECORD rec(_KEY);
+ int index=_Records.getIndex(&rec);
+ _RECORD * rv=_Records[index];
+ if (rv)
+ {
+ if (rv->_value)
+ return rv->_value;
+ else
+ return _T("");
+ }
+ else
+ return NULL;
+ }
+ inline TCHAR* operator[]( int index ) const
+ {
+ _RECORD * rv=_Records[index];
+ if (rv) return rv->_value;
+ else return NULL;
+ }
+ inline _KEYTYPE* getKeyName(int index)
+ {
+ _RECORD * rv=_Records[index];
+ if (rv) return rv->_key;
+ else return NULL;
+ }
+ inline TCHAR * getUnOrdered(int index)
+ {
+ _RECORD * rec=_getUnorderedRec(index);
+ if (rec) return rec->_value;
+ else return NULL;
+ }
+ inline _KEYTYPE * getUnOrderedKeyName(int index)
+ {
+ _RECORD * rec=_getUnorderedRec(index);
+ if (rec) return rec->_key;
+ else return NULL;
+ }
+ inline int getCount()
+ {
+ return _Records.getCount();
+ }
+ inline int removeUnOrdered(int index)
+ {
+ _RECORD * p=_getUnorderedRec(index);
+ if (p) return _remove(p);
+ else return 0;
+ }
+ inline int remove(int index)
+ {
+ _RECORD * p=_Records[index];
+ if (p) return _remove(p);
+ else return 0;
+ }
+ inline int getIndex(_KEYTYPE * key)
+ {
+ _RECORD temp(key);
+ return _Records.getIndex(&temp);
+ }
+};
+
+inline int TCharKeyCmp(TCHAR* a, TCHAR* b)
+{
+ return (int)(_tcsicmp(a,b));
+}
diff --git a/protocols/JabberG/src/jabber_secur.cpp b/protocols/JabberG/src/jabber_secur.cpp new file mode 100644 index 0000000000..c48ded22d6 --- /dev/null +++ b/protocols/JabberG/src/jabber_secur.cpp @@ -0,0 +1,469 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_secur.h"
+
+typedef BYTE (WINAPI *GetUserNameExType )( int NameFormat, LPTSTR lpNameBuffer, PULONG nSize );
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// ntlm auth - LanServer based authorization
+
+TNtlmAuth::TNtlmAuth( ThreadData* info, const char* mechanism, const TCHAR* hostname ) :
+ TJabberAuth( info )
+{
+ szName = mechanism;
+ szHostName = hostname;
+
+ const TCHAR *szProvider;
+ if ( !strcmp( mechanism, "GSS-SPNEGO" ))
+ szProvider = _T("Negotiate");
+ else if ( !strcmp( mechanism, "GSSAPI" ))
+ szProvider = _T("GSSAPI");
+ else if ( !strcmp( mechanism, "NTLM" ))
+ szProvider = _T("NTLM");
+ else {
+ bIsValid = false;
+ return;
+ }
+
+ TCHAR szSpn[ 1024 ] = _T( "" );
+ if ( strcmp( mechanism, "NTLM" )) {
+ if ( !getSpn( szSpn, SIZEOF( szSpn )) && !strcmp( mechanism, "GSSAPI" )) {
+ bIsValid = false;
+ return;
+ } }
+
+ if (( hProvider = Netlib_InitSecurityProvider2( szProvider, szSpn )) == NULL )
+ bIsValid = false;
+}
+
+TNtlmAuth::~TNtlmAuth()
+{
+ if ( hProvider != NULL )
+ Netlib_DestroySecurityProvider( NULL, hProvider );
+}
+
+bool TNtlmAuth::getSpn( TCHAR* szSpn, size_t dwSpnLen )
+{
+ GetUserNameExType myGetUserNameEx =
+ ( GetUserNameExType )GetProcAddress( GetModuleHandleA( "secur32.dll" ), "GetUserNameExW" );
+
+ if ( !myGetUserNameEx ) return false;
+
+ TCHAR szFullUserName[128] = _T( "" );
+ ULONG szFullUserNameLen = SIZEOF( szFullUserName );
+ if (!myGetUserNameEx( 12, szFullUserName, &szFullUserNameLen )) {
+ szFullUserName[ 0 ] = 0;
+ szFullUserNameLen = SIZEOF( szFullUserName );
+ myGetUserNameEx( 2, szFullUserName, &szFullUserNameLen );
+ }
+
+ TCHAR* name = _tcsrchr( szFullUserName, '\\' );
+ if ( name ) *name = 0;
+ else return false;
+
+ if ( szHostName && szHostName[0] ) {
+ TCHAR *szFullUserNameU = _tcsupr( mir_tstrdup( szFullUserName ));
+ mir_sntprintf( szSpn, dwSpnLen, _T( "xmpp/%s/%s@%s" ), szHostName, szFullUserName, szFullUserNameU );
+ mir_free( szFullUserNameU );
+ } else {
+ const char* connectHost = info->manualHost[0] ? info->manualHost : info->server;
+
+ unsigned long ip = inet_addr( connectHost );
+ // Convert host name to FQDN
+// PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname( szHost ) : gethostbyaddr(( char* )&ip, 4, AF_INET );
+ PHOSTENT host = (ip == INADDR_NONE) ? NULL : gethostbyaddr(( char* )&ip, 4, AF_INET );
+ if ( host && host->h_name )
+ connectHost = host->h_name;
+
+ TCHAR *connectHostT = mir_a2t( connectHost );
+ mir_sntprintf( szSpn, dwSpnLen, _T( "xmpp/%s@%s" ), connectHostT, _tcsupr( szFullUserName ));
+ mir_free( connectHostT );
+ }
+
+ Netlib_Logf( NULL, "SPN: " TCHAR_STR_PARAM, szSpn );
+
+
+ return true;
+}
+
+char* TNtlmAuth::getInitialRequest()
+{
+ if ( !hProvider )
+ return NULL;
+
+ // This generates login method advertisement packet
+ char* result;
+ if ( info->password[0] != 0 )
+ result = Netlib_NtlmCreateResponse2( hProvider, "", info->username, info->password, &complete );
+ else
+ result = Netlib_NtlmCreateResponse2( hProvider, "", NULL, NULL, &complete );
+
+ return result;
+}
+
+char* TNtlmAuth::getChallenge( const TCHAR* challenge )
+{
+ if ( !hProvider )
+ return NULL;
+
+ char *text = ( !lstrcmp( challenge, _T("="))) ? mir_strdup( "" ) : mir_t2a( challenge ), *result;
+ if ( info->password[0] != 0 )
+ result = Netlib_NtlmCreateResponse2( hProvider, text, info->username, info->password, &complete );
+ else
+ result = Netlib_NtlmCreateResponse2( hProvider, text, NULL, NULL, &complete );
+
+ mir_free( text );
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// md5 auth - digest-based authorization
+
+TMD5Auth::TMD5Auth( ThreadData* info ) :
+ TJabberAuth( info ),
+ iCallCount( 0 )
+{
+ szName = "DIGEST-MD5";
+}
+
+TMD5Auth::~TMD5Auth()
+{
+}
+
+char* TMD5Auth::getChallenge( const TCHAR* challenge )
+{
+ if ( iCallCount > 0 )
+ return NULL;
+
+ iCallCount++;
+
+ int resultLen;
+ char* text = JabberBase64DecodeT( challenge, &resultLen );
+
+ TStringPairs pairs( text );
+ const char *realm = pairs["realm"], *nonce = pairs["nonce"];
+
+ char cnonce[40], tmpBuf[40];
+ DWORD digest[4], hash1[4], hash2[4];
+ mir_md5_state_t ctx;
+
+ CallService( MS_UTILS_GETRANDOM, sizeof( digest ), ( LPARAM )digest );
+ sprintf( cnonce, "%08x%08x%08x%08x", htonl(digest[0]), htonl(digest[1]), htonl(digest[2]), htonl(digest[3]));
+
+ char *uname = mir_utf8encodeT( info->username ),
+ *passw = mir_utf8encodeT( info->password ),
+ *serv = mir_utf8encode( info->server );
+
+ mir_md5_init( &ctx );
+ mir_md5_append( &ctx, ( BYTE* )uname, (int)strlen( uname ));
+ mir_md5_append( &ctx, ( BYTE* )":", 1 );
+ mir_md5_append( &ctx, ( BYTE* )realm, (int)strlen( realm ));
+ mir_md5_append( &ctx, ( BYTE* )":", 1 );
+ mir_md5_append( &ctx, ( BYTE* )passw, (int)strlen( passw ));
+ mir_md5_finish( &ctx, ( BYTE* )hash1 );
+
+ mir_md5_init( &ctx );
+ mir_md5_append( &ctx, ( BYTE* )hash1, 16 );
+ mir_md5_append( &ctx, ( BYTE* )":", 1 );
+ mir_md5_append( &ctx, ( BYTE* )nonce, (int)strlen( nonce ));
+ mir_md5_append( &ctx, ( BYTE* )":", 1 );
+ mir_md5_append( &ctx, ( BYTE* )cnonce, (int)strlen( cnonce ));
+ mir_md5_finish( &ctx, ( BYTE* )hash1 );
+
+ mir_md5_init( &ctx );
+ mir_md5_append( &ctx, ( BYTE* )"AUTHENTICATE:xmpp/", 18 );
+ mir_md5_append( &ctx, ( BYTE* )serv, (int)strlen( serv ));
+ mir_md5_finish( &ctx, ( BYTE* )hash2 );
+
+ mir_md5_init( &ctx );
+ sprintf( tmpBuf, "%08x%08x%08x%08x", htonl(hash1[0]), htonl(hash1[1]), htonl(hash1[2]), htonl(hash1[3]));
+ mir_md5_append( &ctx, ( BYTE* )tmpBuf, (int)strlen( tmpBuf ));
+ mir_md5_append( &ctx, ( BYTE* )":", 1 );
+ mir_md5_append( &ctx, ( BYTE* )nonce, (int)strlen( nonce ));
+ sprintf( tmpBuf, ":%08d:", iCallCount );
+ mir_md5_append( &ctx, ( BYTE* )tmpBuf, (int)strlen( tmpBuf ));
+ mir_md5_append( &ctx, ( BYTE* )cnonce, (int)strlen( cnonce ));
+ mir_md5_append( &ctx, ( BYTE* )":auth:", 6 );
+ sprintf( tmpBuf, "%08x%08x%08x%08x", htonl(hash2[0]), htonl(hash2[1]), htonl(hash2[2]), htonl(hash2[3]));
+ mir_md5_append( &ctx, ( BYTE* )tmpBuf, (int)strlen( tmpBuf ));
+ mir_md5_finish( &ctx, ( BYTE* )digest );
+
+ char* buf = (char*)alloca(8000);
+ int cbLen = mir_snprintf( buf, 8000,
+ "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\",nc=%08d,"
+ "qop=auth,digest-uri=\"xmpp/%s\",charset=utf-8,response=%08x%08x%08x%08x",
+ uname, realm, nonce, cnonce, iCallCount, serv,
+ htonl(digest[0]), htonl(digest[1]), htonl(digest[2]), htonl(digest[3]));
+
+ mir_free( uname );
+ mir_free( passw );
+ mir_free( serv );
+ mir_free( text );
+
+ return JabberBase64Encode( buf, cbLen );
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SCRAM-SHA-1 authorization
+
+void hmac_sha1(mir_sha1_byte_t *md, mir_sha1_byte_t *key, size_t keylen, mir_sha1_byte_t *text, size_t textlen)
+{
+ const unsigned SHA_BLOCKSIZE = 64;
+
+ unsigned char mdkey[MIR_SHA1_HASH_SIZE];
+ unsigned char k_ipad[SHA_BLOCKSIZE], k_opad[SHA_BLOCKSIZE];
+ mir_sha1_ctx ctx;
+
+ if (keylen > SHA_BLOCKSIZE)
+ {
+ mir_sha1_init(&ctx);
+ mir_sha1_append(&ctx, key, (int)keylen);
+ mir_sha1_finish(&ctx, mdkey);
+ keylen = 20;
+ key = mdkey;
+ }
+
+ memcpy(k_ipad, key, keylen);
+ memcpy(k_opad, key, keylen);
+ memset(k_ipad+keylen, 0x36, SHA_BLOCKSIZE - keylen);
+ memset(k_opad+keylen, 0x5c, SHA_BLOCKSIZE - keylen);
+
+ for (unsigned i = 0; i < keylen; i++)
+ {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+
+ mir_sha1_init(&ctx);
+ mir_sha1_append(&ctx, k_ipad, SHA_BLOCKSIZE);
+ mir_sha1_append(&ctx, text, (int)textlen);
+ mir_sha1_finish(&ctx, md);
+
+ mir_sha1_init(&ctx);
+ mir_sha1_append(&ctx, k_opad, SHA_BLOCKSIZE);
+ mir_sha1_append(&ctx, md, MIR_SHA1_HASH_SIZE);
+ mir_sha1_finish(&ctx, md);
+}
+
+TScramAuth::TScramAuth( ThreadData* info ) :
+ TJabberAuth( info )
+{
+ szName = "SCRAM-SHA-1";
+ cnonce = msg1 = serverSignature = NULL;
+}
+
+TScramAuth::~TScramAuth()
+{
+ mir_free( cnonce );
+ mir_free( msg1 );
+ mir_free( serverSignature );
+}
+
+void TScramAuth::Hi( mir_sha1_byte_t* res , char* passw, size_t passwLen, char* salt, size_t saltLen, int ind )
+{
+ mir_sha1_byte_t u[ MIR_SHA1_HASH_SIZE ];
+ memcpy( u, salt, saltLen ); *( unsigned* )( u + saltLen ) = htonl( 1 ); saltLen += 4;
+ memset( res, 0, MIR_SHA1_HASH_SIZE );
+
+ for (int i = 0; i < ind; i++)
+ {
+ hmac_sha1( u, (mir_sha1_byte_t*)passw, passwLen, u, saltLen);
+ saltLen = sizeof( u );
+
+ for (unsigned j = 0; j < sizeof( u ); j++)
+ res[j] ^= u[j];
+ }
+}
+
+char* TScramAuth::getChallenge( const TCHAR* challenge )
+{
+ int chlLen;
+ char *chl = JabberBase64DecodeT( challenge, &chlLen );
+
+ char *r = strstr( chl, "r=" ); if ( !r ) { mir_free( chl ); return NULL; }
+ char *e = strchr( r, ',' ); if ( e ) *e = 0;
+ char *snonce = mir_strdup(r + 2);
+ if ( e ) *e = ',';
+
+ size_t cnlen = strlen( cnonce );
+ if (strncmp(cnonce, snonce, cnlen )) { mir_free( chl ); mir_free( snonce ); return NULL; }
+
+ char *s = strstr( chl, "s=" ); if ( !s ) { mir_free( chl ); mir_free( snonce ); return NULL; }
+ e = strchr( s, ',' ); if ( e ) *e = 0;
+ int saltLen;
+ char *salt = JabberBase64Decode( s + 2, &saltLen );
+ if ( e ) *e = ',';
+
+ if ( saltLen > 16 ) {
+ mir_free( salt );
+ mir_free( snonce );
+ mir_free( chl );
+ return NULL;
+ }
+
+ char *in = strstr( chl, "i=" ); if ( !in ) return NULL;
+ e = strchr( in, ',' ); if ( e ) *e = 0;
+ int ind = atoi( in + 2 );
+ if ( e ) *e = ',';
+
+ char *passw = mir_utf8encodeT( info->password );
+ size_t passwLen = strlen( passw );
+
+ mir_sha1_byte_t saltedPassw[ MIR_SHA1_HASH_SIZE ];
+ Hi( saltedPassw, passw, passwLen, salt, saltLen, ind );
+
+ mir_free( salt );
+ mir_free( passw );
+
+ mir_sha1_byte_t clientKey[ MIR_SHA1_HASH_SIZE ];
+ hmac_sha1( clientKey, saltedPassw, sizeof( saltedPassw ), (mir_sha1_byte_t*)"Client Key", 10 );
+
+ mir_sha1_byte_t storedKey[ MIR_SHA1_HASH_SIZE ];
+
+ mir_sha1_ctx ctx;
+ mir_sha1_init( &ctx );
+ mir_sha1_append( &ctx, clientKey, MIR_SHA1_HASH_SIZE );
+ mir_sha1_finish( &ctx, storedKey );
+
+ char authmsg[4096];
+ int authmsgLen = mir_snprintf( authmsg, sizeof( authmsg ), "%s,%s,c=biws,r=%s", msg1, chl, snonce);
+
+ mir_sha1_byte_t clientSig[ MIR_SHA1_HASH_SIZE ];
+ hmac_sha1( clientSig, storedKey, sizeof( storedKey ), (mir_sha1_byte_t*)authmsg, authmsgLen);
+
+ mir_sha1_byte_t clientProof[ MIR_SHA1_HASH_SIZE ];
+ for (unsigned j = 0; j < sizeof( clientKey ); j++)
+ clientProof[j] = clientKey[j] ^ clientSig[j];
+
+ /* Calculate the server signature */
+ mir_sha1_byte_t serverKey[ MIR_SHA1_HASH_SIZE ];
+ hmac_sha1( serverKey, saltedPassw, sizeof( saltedPassw ), (mir_sha1_byte_t*)"Server Key", 10 );
+
+ mir_sha1_byte_t srvSig[ MIR_SHA1_HASH_SIZE ];
+ hmac_sha1( srvSig, serverKey, sizeof( serverKey ), (mir_sha1_byte_t*)authmsg, authmsgLen);
+ serverSignature = JabberBase64Encode(( char* )srvSig, sizeof( srvSig ));
+
+ char buf[4096];
+ char *encproof = JabberBase64Encode(( char* )clientProof, sizeof( clientProof ));
+ int cbLen = mir_snprintf( buf, sizeof( buf ), "c=biws,r=%s,p=%s", snonce, encproof );
+
+ mir_free( encproof );
+ mir_free( snonce );
+ mir_free( chl );
+
+ return JabberBase64Encode( buf, cbLen );
+}
+
+char* TScramAuth::getInitialRequest()
+{
+ char *uname = mir_utf8encodeT( info->username ),
+ *serv = mir_utf8encode( info->server );
+
+ unsigned char nonce[24];
+ CallService( MS_UTILS_GETRANDOM, sizeof(nonce), ( LPARAM )nonce );
+ cnonce = JabberBase64Encode(( char* )nonce, sizeof( nonce ));
+
+ char buf[4096];
+ int cbLen = mir_snprintf( buf, sizeof( buf ), "n,,n=%s@%s,r=%s", uname, serv, cnonce );
+ msg1 = mir_strdup( buf + 3 );
+
+ mir_free( serv );
+ mir_free( uname );
+
+ return JabberBase64Encode( buf, cbLen );
+}
+
+bool TScramAuth::validateLogin( const TCHAR* challenge )
+{
+ int chlLen;
+ char* chl = JabberBase64DecodeT( challenge, &chlLen );
+ bool res = chl && strncmp( chl + 2, serverSignature, chlLen - 2 ) == 0;
+ mir_free( chl );
+
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// plain auth - the most simple one
+
+TPlainAuth::TPlainAuth( ThreadData* info, bool old ) :
+ TJabberAuth( info )
+{
+ szName = "PLAIN";
+ bOld = old;
+}
+
+TPlainAuth::~TPlainAuth()
+{
+}
+
+char* TPlainAuth::getInitialRequest()
+{
+ char *uname = mir_utf8encodeT( info->username ),
+ *passw = mir_utf8encodeT( info->password );
+
+ size_t size = 2 * strlen( uname ) + strlen( passw ) + strlen( info->server ) + 4;
+ char *toEncode = ( char* )alloca( size );
+ if ( bOld )
+ size = mir_snprintf( toEncode, size, "%s@%s%c%s%c%s", uname, info->server, 0, uname, 0, passw );
+ else
+ size = mir_snprintf( toEncode, size, "%c%s%c%s", 0, uname, 0, passw );
+
+ mir_free( uname );
+ mir_free( passw );
+
+ return JabberBase64Encode( toEncode, (int)size );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// basic type
+
+TJabberAuth::TJabberAuth( ThreadData* pInfo ) :
+ bIsValid( true ),
+ szName( NULL ),
+ info( pInfo )
+{
+}
+
+TJabberAuth::~TJabberAuth()
+{
+}
+
+char* TJabberAuth::getInitialRequest()
+{
+ return NULL;
+}
+
+char* TJabberAuth::getChallenge( const TCHAR* )
+{
+ return NULL;
+}
+
+bool TJabberAuth::validateLogin( const TCHAR* )
+{
+ return true;
+}
+
diff --git a/protocols/JabberG/src/jabber_secur.h b/protocols/JabberG/src/jabber_secur.h new file mode 100644 index 0000000000..0c3287b88d --- /dev/null +++ b/protocols/JabberG/src/jabber_secur.h @@ -0,0 +1,113 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+// basic class - provides interface for various Jabber auth
+
+class TJabberAuth
+{
+
+protected: bool bIsValid;
+ const char* szName;
+ unsigned complete;
+ ThreadData* info;
+
+public:
+ TJabberAuth( ThreadData* );
+ virtual ~TJabberAuth();
+
+ virtual char* getInitialRequest();
+ virtual char* getChallenge( const TCHAR* challenge );
+ virtual bool validateLogin( const TCHAR* challenge );
+
+ inline const char* getName() const
+ { return szName;
+ }
+
+ inline bool isValid() const
+ { return bIsValid;
+ }
+};
+
+// plain auth - the most simple one
+
+class TPlainAuth : public TJabberAuth
+{
+ typedef TJabberAuth CSuper;
+
+ bool bOld;
+
+
+public: TPlainAuth( ThreadData*, bool );
+ virtual ~TPlainAuth();
+
+ virtual char* getInitialRequest();
+};
+
+// md5 auth - digest-based authorization
+
+class TMD5Auth : public TJabberAuth
+{
+ typedef TJabberAuth CSuper;
+
+ int iCallCount;
+public:
+ TMD5Auth( ThreadData* );
+ virtual ~TMD5Auth();
+
+ virtual char* getChallenge( const TCHAR* challenge );
+};
+
+class TScramAuth : public TJabberAuth
+{
+ typedef TJabberAuth CSuper;
+
+ char *cnonce, *msg1, *serverSignature;
+public:
+ TScramAuth( ThreadData* );
+ virtual ~TScramAuth();
+
+ virtual char* getInitialRequest();
+ virtual char* getChallenge( const TCHAR* challenge );
+ virtual bool validateLogin( const TCHAR* challenge );
+
+ void Hi( mir_sha1_byte_t* res , char* passw, size_t passwLen, char* salt, size_t saltLen, int ind );
+};
+
+// ntlm auth - LanServer based authorization
+
+class TNtlmAuth : public TJabberAuth
+{
+ typedef TJabberAuth CSuper;
+
+ HANDLE hProvider;
+ const TCHAR *szHostName;
+public:
+ TNtlmAuth( ThreadData*, const char* mechanism, const TCHAR* hostname = NULL );
+ virtual ~TNtlmAuth();
+
+ virtual char* getInitialRequest();
+ virtual char* getChallenge( const TCHAR* challenge );
+
+ bool getSpn( TCHAR* szSpn, size_t dwSpnLen );
+};
diff --git a/protocols/JabberG/src/jabber_send_manager.cpp b/protocols/JabberG/src/jabber_send_manager.cpp new file mode 100644 index 0000000000..b37b1d2f09 --- /dev/null +++ b/protocols/JabberG/src/jabber_send_manager.cpp @@ -0,0 +1,51 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-08 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2008-09 Dmitriy Chervov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_send_manager.h"
+
+BOOL CJabberSendManager::FillPermanentHandlers()
+{
+ return TRUE;
+}
+
+BOOL CJabberSendManager::HandleSendPermanent(HXML node, ThreadData *pThreadData)
+{
+ BOOL bStopHandling = FALSE;
+ Lock();
+ CJabberSendPermanentInfo *pInfo = m_pPermanentHandlers;
+ while ( pInfo && !bStopHandling ) {
+ CJabberSendInfo sendInfo;
+ sendInfo.m_pUserData = pInfo->m_pUserData;
+
+ if ((ppro->*(pInfo->m_pHandler))(node, pThreadData, &sendInfo)) {
+ bStopHandling = TRUE;
+ break;
+ }
+ pInfo = pInfo->m_pNext;
+ }
+ Unlock();
+
+ return bStopHandling;
+}
diff --git a/protocols/JabberG/src/jabber_send_manager.h b/protocols/JabberG/src/jabber_send_manager.h new file mode 100644 index 0000000000..fe4916686f --- /dev/null +++ b/protocols/JabberG/src/jabber_send_manager.h @@ -0,0 +1,195 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-08 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+Copyright ( C ) 2008-09 Dmitriy Chervov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_SEND_MANAGER_H_
+#define _JABBER_SEND_MANAGER_H_
+
+#include "jabber_xml.h"
+
+struct CJabberProto;
+typedef void ( CJabberProto::*JABBER_SEND_PFUNC )( HXML node, void *usedata );
+typedef void ( *SEND_USER_DATA_FREE_FUNC )( void *pUserData );
+
+class CJabberSendInfo;
+
+typedef BOOL ( CJabberProto::*JABBER_SEND_HANDLER )( HXML node, ThreadData *pThreadData, CJabberSendInfo* pInfo );
+
+class CJabberSendInfo
+{
+protected:
+ friend class CJabberSendManager;
+ JABBER_SEND_HANDLER m_pHandler;
+ CJabberSendInfo* m_pNext;
+
+public:
+ void *m_pUserData;
+
+ CJabberSendInfo()
+ {
+ ZeroMemory(this, sizeof(*this));
+ }
+ ~CJabberSendInfo()
+ {
+ }
+ void* GetUserData()
+ {
+ return m_pUserData;
+ }
+};
+
+class CJabberSendPermanentInfo
+{
+ friend class CJabberSendManager;
+
+ CJabberSendPermanentInfo* m_pNext;
+
+ JABBER_SEND_HANDLER m_pHandler;
+ void *m_pUserData;
+ SEND_USER_DATA_FREE_FUNC m_pUserDataFree;
+ int m_iPriority;
+public:
+ CJabberSendPermanentInfo()
+ {
+ ZeroMemory(this, sizeof(CJabberSendPermanentInfo));
+ }
+ ~CJabberSendPermanentInfo()
+ {
+ if ( m_pUserDataFree )
+ m_pUserDataFree(m_pUserData);
+ }
+};
+
+class CJabberSendManager
+{
+protected:
+ CJabberProto* ppro;
+ CRITICAL_SECTION m_cs;
+ CJabberSendPermanentInfo* m_pPermanentHandlers;
+
+public:
+ CJabberSendManager( CJabberProto* proto )
+ {
+ InitializeCriticalSection(&m_cs);
+ m_pPermanentHandlers = NULL;
+ ppro = proto;
+ }
+ ~CJabberSendManager()
+ {
+ Lock();
+ CJabberSendPermanentInfo *pInfo = m_pPermanentHandlers;
+ while ( pInfo )
+ {
+ CJabberSendPermanentInfo *pTmp = pInfo->m_pNext;
+ delete pInfo;
+ pInfo = pTmp;
+ }
+ m_pPermanentHandlers = NULL;
+ Unlock();
+ DeleteCriticalSection(&m_cs);
+ }
+ BOOL Start()
+ {
+ return TRUE;
+ }
+ BOOL Shutdown()
+ {
+ return TRUE;
+ }
+ void Lock()
+ {
+ EnterCriticalSection(&m_cs);
+ }
+ void Unlock()
+ {
+ LeaveCriticalSection(&m_cs);
+ }
+ CJabberSendPermanentInfo* AddPermanentHandler(JABBER_SEND_HANDLER pHandler, void *pUserData = NULL, SEND_USER_DATA_FREE_FUNC pUserDataFree = NULL, int iPriority = JH_PRIORITY_DEFAULT)
+ {
+ CJabberSendPermanentInfo* pInfo = new CJabberSendPermanentInfo();
+ if (!pInfo)
+ return NULL;
+
+ pInfo->m_pHandler = pHandler;
+ pInfo->m_pUserData = pUserData;
+ pInfo->m_pUserDataFree = pUserDataFree;
+ pInfo->m_iPriority = iPriority;
+
+ Lock();
+ if (!m_pPermanentHandlers)
+ m_pPermanentHandlers = pInfo;
+ else
+ {
+ if (m_pPermanentHandlers->m_iPriority > pInfo->m_iPriority) {
+ pInfo->m_pNext = m_pPermanentHandlers;
+ m_pPermanentHandlers = pInfo;
+ } else
+ {
+ CJabberSendPermanentInfo* pTmp = m_pPermanentHandlers;
+ while (pTmp->m_pNext && pTmp->m_pNext->m_iPriority <= pInfo->m_iPriority)
+ pTmp = pTmp->m_pNext;
+ pInfo->m_pNext = pTmp->m_pNext;
+ pTmp->m_pNext = pInfo;
+ }
+ }
+ Unlock();
+
+ return pInfo;
+ }
+ BOOL DeletePermanentHandler(CJabberSendPermanentInfo *pInfo)
+ { // returns TRUE when pInfo found, or FALSE otherwise
+ Lock();
+ if (!m_pPermanentHandlers)
+ {
+ Unlock();
+ return FALSE;
+ }
+ if (m_pPermanentHandlers == pInfo) // check first item
+ {
+ m_pPermanentHandlers = m_pPermanentHandlers->m_pNext;
+ delete pInfo;
+ Unlock();
+ return TRUE;
+ } else
+ {
+ CJabberSendPermanentInfo* pTmp = m_pPermanentHandlers;
+ while (pTmp->m_pNext)
+ {
+ if (pTmp->m_pNext == pInfo)
+ {
+ pTmp->m_pNext = pTmp->m_pNext->m_pNext;
+ delete pInfo;
+ Unlock();
+ return TRUE;
+ }
+ pTmp = pTmp->m_pNext;
+ }
+ }
+ Unlock();
+ return FALSE;
+ }
+ BOOL HandleSendPermanent(HXML node, ThreadData *pThreadData);
+ BOOL FillPermanentHandlers();
+};
+
+#endif
diff --git a/protocols/JabberG/src/jabber_std.cpp b/protocols/JabberG/src/jabber_std.cpp new file mode 100644 index 0000000000..099c28b993 --- /dev/null +++ b/protocols/JabberG/src/jabber_std.cpp @@ -0,0 +1,246 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+void CJabberProto::JCreateService( const char* szService, JServiceFunc serviceProc )
+{
+ char str[ MAXMODULELABELLENGTH ];
+ strcpy( str, m_szModuleName );
+ strcat( str, szService );
+ ::CreateServiceFunctionObj( str, ( MIRANDASERVICEOBJ )*( void** )&serviceProc, this );
+}
+
+void CJabberProto::JCreateServiceParam( const char* szService, JServiceFuncParam serviceProc, LPARAM lParam )
+{
+ char str[ MAXMODULELABELLENGTH ];
+ strcpy( str, m_szModuleName );
+ strcat( str, szService );
+ ::CreateServiceFunctionObjParam( str, ( MIRANDASERVICEOBJPARAM )*( void** )&serviceProc, this, lParam );
+}
+
+void CJabberProto::JHookEvent( const char* szEvent, JEventFunc handler )
+{
+ ::HookEventObj( szEvent, ( MIRANDAHOOKOBJ )*( void** )&handler, this );
+}
+
+HANDLE CJabberProto::JCreateHookableEvent( const char* szService )
+{
+ char str[ MAXMODULELABELLENGTH ];
+ strcpy( str, m_szModuleName );
+ strcat( str, szService );
+ return CreateHookableEvent( str );
+}
+
+void CJabberProto::JForkThread( JThreadFunc pFunc, void *param )
+{
+ UINT threadID;
+ CloseHandle(( HANDLE )::mir_forkthreadowner(( pThreadFuncOwner ) *( void** )&pFunc, this, param, &threadID ));
+}
+
+HANDLE CJabberProto::JForkThreadEx( JThreadFunc pFunc, void *param, UINT* threadID )
+{
+ UINT lthreadID;
+ return ( HANDLE )::mir_forkthreadowner(( pThreadFuncOwner ) *( void** )&pFunc, this, param, threadID ? threadID : <hreadID);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CJabberProto::JDeleteSetting( HANDLE hContact, const char* valueName )
+{
+ DBDeleteContactSetting( hContact, m_szModuleName, valueName );
+}
+/*
+DWORD CJabberProto::JGetByte( const char* valueName, int parDefltValue )
+{
+ return DBGetContactSettingByte( NULL, m_szModuleName, valueName, parDefltValue );
+}
+*/
+DWORD CJabberProto::JGetByte( HANDLE hContact, const char* valueName, int parDefltValue )
+{
+ return DBGetContactSettingByte( hContact, m_szModuleName, valueName, parDefltValue );
+}
+
+char* __stdcall JGetContactName( HANDLE hContact )
+{
+ return ( char* )CallService( MS_CLIST_GETCONTACTDISPLAYNAME, WPARAM( hContact ), 0 );
+}
+
+DWORD CJabberProto::JGetDword( HANDLE hContact, const char* valueName, DWORD parDefltValue )
+{
+ return DBGetContactSettingDword( hContact, m_szModuleName, valueName, parDefltValue );
+}
+
+int CJabberProto::JGetStaticString( const char* valueName, HANDLE hContact, char* dest, int dest_len )
+{
+ DBVARIANT dbv;
+ dbv.pszVal = dest;
+ dbv.cchVal = dest_len;
+ dbv.type = DBVT_ASCIIZ;
+
+ DBCONTACTGETSETTING sVal;
+ sVal.pValue = &dbv;
+ sVal.szModule = m_szModuleName;
+ sVal.szSetting = valueName;
+ if ( CallService( MS_DB_CONTACT_GETSETTINGSTATIC, ( WPARAM )hContact, ( LPARAM )&sVal ) != 0 )
+ return 1;
+
+ return ( dbv.type != DBVT_ASCIIZ );
+}
+
+int CJabberProto::JGetStringUtf( HANDLE hContact, char* valueName, DBVARIANT* dbv )
+{
+ return DBGetContactSettingStringUtf( hContact, m_szModuleName, valueName, dbv );
+}
+
+int CJabberProto::JGetStringT( HANDLE hContact, char* valueName, DBVARIANT* dbv )
+{
+ return DBGetContactSettingTString( hContact, m_szModuleName, valueName, dbv );
+}
+
+TCHAR *CJabberProto::JGetStringT( HANDLE hContact, char* valueName )
+{
+ DBVARIANT dbv = {0};
+ if (JGetStringT(hContact, valueName, &dbv))
+ return NULL;
+
+ TCHAR *res = mir_tstrdup(dbv.ptszVal);
+ JFreeVariant(&dbv);
+ return res;
+}
+
+TCHAR *CJabberProto::JGetStringT( HANDLE hContact, char* valueName, TCHAR *&out )
+{
+ return out = JGetStringT( hContact, valueName );
+}
+
+TCHAR *CJabberProto::JGetStringT( HANDLE hContact, char* valueName, TCHAR *buf, int size )
+{
+ DBVARIANT dbv = {0};
+ if (JGetStringT(hContact, valueName, &dbv))
+ return NULL;
+
+ lstrcpyn(buf, dbv.ptszVal, size);
+ JFreeVariant(&dbv);
+ return buf;
+}
+
+WORD CJabberProto::JGetWord( HANDLE hContact, const char* valueName, int parDefltValue )
+{
+ return DBGetContactSettingWord( hContact, m_szModuleName, valueName, parDefltValue );
+}
+
+void __fastcall JFreeVariant( DBVARIANT* dbv )
+{
+ DBFreeVariant( dbv );
+}
+
+int CJabberProto::JSendBroadcast( HANDLE hContact, int type, int result, HANDLE hProcess, LPARAM lParam )
+{
+ // clear saved passowrd on login error. ugly hack, but at least this is centralized
+ if (type == ACKTYPE_LOGIN && (lParam == LOGINERR_WRONGPASSWORD || lParam == LOGINERR_BADUSERID))
+ *m_savedPassword = 0;
+
+ ACKDATA ack = {0};
+ ack.cbSize = sizeof( ACKDATA );
+ ack.szModule = m_szModuleName;
+ ack.hContact = hContact;
+ ack.type = type;
+ ack.result = result;
+ ack.hProcess = hProcess;
+ ack.lParam = lParam;
+ return CallService( MS_PROTO_BROADCASTACK, 0, ( LPARAM )&ack );
+}
+/*
+DWORD CJabberProto::JSetByte( const char* valueName, int parValue )
+{
+ return DBWriteContactSettingByte( NULL, m_szModuleName, valueName, parValue );
+}
+*/
+DWORD CJabberProto::JSetByte( HANDLE hContact, const char* valueName, int parValue )
+{
+ return DBWriteContactSettingByte( hContact, m_szModuleName, valueName, parValue );
+}
+
+DWORD CJabberProto::JSetDword( HANDLE hContact, const char* valueName, DWORD parValue )
+{
+ return DBWriteContactSettingDword( hContact, m_szModuleName, valueName, parValue );
+}
+
+DWORD CJabberProto::JSetString( HANDLE hContact, const char* valueName, const char* parValue )
+{
+ return DBWriteContactSettingString( hContact, m_szModuleName, valueName, parValue );
+}
+
+DWORD CJabberProto::JSetStringT( HANDLE hContact, const char* valueName, const TCHAR* parValue )
+{
+ return DBWriteContactSettingTString( hContact, m_szModuleName, valueName, parValue );
+}
+
+DWORD CJabberProto::JSetStringUtf( HANDLE hContact, const char* valueName, const char* parValue )
+{
+ return DBWriteContactSettingStringUtf( hContact, m_szModuleName, valueName, parValue );
+}
+
+DWORD CJabberProto::JSetWord( HANDLE hContact, const char* valueName, int parValue )
+{
+ return DBWriteContactSettingWord( hContact, m_szModuleName, valueName, parValue );
+}
+
+char* __fastcall JTranslate( const char* str )
+{
+ return Translate( str );
+}
+
+// save/load crypted strings
+void __forceinline sttCryptString(char *str)
+{
+ for (;*str; ++str)
+ {
+ const char c = *str ^ 0xc3;
+ if (c) *str = c;
+ }
+}
+
+TCHAR* CJabberProto::JGetStringCrypt( HANDLE hContact, char* valueName )
+{
+ DBVARIANT dbv;
+
+ if (DBGetContactSettingString( hContact, m_szModuleName, valueName, &dbv ))
+ return NULL;
+
+ sttCryptString(dbv.pszVal);
+ WCHAR *res = mir_utf8decodeW(dbv.pszVal);
+
+
+ DBFreeVariant(&dbv);
+ return res;
+}
+
+DWORD CJabberProto::JSetStringCrypt( HANDLE hContact, char* valueName, const TCHAR* parValue )
+{
+ char *tmp = mir_utf8encodeT(parValue);
+ sttCryptString(tmp);
+ DWORD res = JSetString( hContact, valueName, tmp );
+ mir_free(tmp);
+ return res;
+}
diff --git a/protocols/JabberG/src/jabber_svc.cpp b/protocols/JabberG/src/jabber_svc.cpp new file mode 100644 index 0000000000..6344751ead --- /dev/null +++ b/protocols/JabberG/src/jabber_svc.cpp @@ -0,0 +1,1138 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "jabber_list.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+#include "m_file.h"
+#include "m_addcontact.h"
+#include "jabber_disco.h"
+#include "m_proto_listeningto.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// GetMyAwayMsg - obtain the current away message
+
+INT_PTR __cdecl CJabberProto::GetMyAwayMsg(WPARAM wParam, LPARAM lParam)
+{
+ TCHAR *szStatus = NULL;
+ INT_PTR nRetVal = 0;
+
+ EnterCriticalSection( &m_csModeMsgMutex );
+ switch ( wParam ? (int)wParam : m_iStatus ) {
+ case ID_STATUS_ONLINE:
+ szStatus = m_modeMsgs.szOnline;
+ break;
+ case ID_STATUS_AWAY:
+ case ID_STATUS_ONTHEPHONE:
+ case ID_STATUS_OUTTOLUNCH:
+ szStatus = m_modeMsgs.szAway;
+ break;
+ case ID_STATUS_NA:
+ szStatus = m_modeMsgs.szNa;
+ break;
+ case ID_STATUS_DND:
+ case ID_STATUS_OCCUPIED:
+ szStatus = m_modeMsgs.szDnd;
+ break;
+ case ID_STATUS_FREECHAT:
+ szStatus = m_modeMsgs.szFreechat;
+ break;
+ default:
+ // Should not reach here
+ break;
+ }
+ if ( szStatus )
+ nRetVal = ( lParam & SGMA_UNICODE ) ? ( INT_PTR )mir_t2u( szStatus ) : ( INT_PTR )mir_t2a( szStatus );
+ LeaveCriticalSection( &m_csModeMsgMutex );
+
+ return nRetVal;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetAvatar - retrieves the file name of my own avatar
+
+INT_PTR __cdecl CJabberProto::JabberGetAvatar( WPARAM wParam, LPARAM lParam )
+{
+ TCHAR* buf = ( TCHAR* )wParam;
+ int size = ( int )lParam;
+
+ if ( buf == NULL || size <= 0 )
+ return -1;
+
+ if ( !m_options.EnableAvatars )
+ return -2;
+
+ GetAvatarFileName( NULL, buf, size );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetAvatarCaps - returns directives how to process avatars
+
+INT_PTR __cdecl CJabberProto::JabberGetAvatarCaps( WPARAM wParam, LPARAM lParam )
+{
+ switch( wParam ) {
+ case AF_MAXSIZE:
+ {
+ POINT* size = (POINT*)lParam;
+ if ( size )
+ size->x = size->y = 96;
+ }
+ return 0;
+
+ case AF_PROPORTION:
+ return PIP_NONE;
+
+ case AF_FORMATSUPPORTED: // Jabber supports avatars of virtually all formats
+ return 1;
+
+ case AF_ENABLED:
+ return m_options.EnableAvatars;
+ }
+ return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetAvatarInfo - retrieves the avatar info
+
+INT_PTR __cdecl CJabberProto::JabberGetAvatarInfo( WPARAM wParam, LPARAM lParam )
+{
+ if ( !m_options.EnableAvatars )
+ return GAIR_NOAVATAR;
+
+ PROTO_AVATAR_INFORMATIONT* AI = ( PROTO_AVATAR_INFORMATIONT* )lParam;
+
+ char szHashValue[ MAX_PATH ];
+ if ( JGetStaticString( "AvatarHash", AI->hContact, szHashValue, sizeof szHashValue )) {
+ Log( "No avatar" );
+ return GAIR_NOAVATAR;
+ }
+
+ TCHAR tszFileName[ MAX_PATH ];
+ GetAvatarFileName( AI->hContact, tszFileName, SIZEOF(tszFileName));
+ _tcsncpy( AI->filename, tszFileName, SIZEOF(AI->filename));
+
+ AI->format = ( AI->hContact == NULL ) ? PA_FORMAT_PNG : JGetByte( AI->hContact, "AvatarType", 0 );
+
+ if ( ::_taccess( AI->filename, 0 ) == 0 ) {
+ char szSavedHash[ 256 ];
+ if ( !JGetStaticString( "AvatarSaved", AI->hContact, szSavedHash, sizeof szSavedHash )) {
+ if ( !strcmp( szSavedHash, szHashValue )) {
+ Log( "Avatar is Ok: %s == %s", szSavedHash, szHashValue );
+ return GAIR_SUCCESS;
+ } } }
+
+ if (( wParam & GAIF_FORCE ) != 0 && AI->hContact != NULL && m_bJabberOnline ) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( AI->hContact, "jid", &dbv )) {
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_ROSTER, dbv.ptszVal );
+ if ( item != NULL ) {
+ BOOL isXVcard = JGetByte( AI->hContact, "AvatarXVcard", 0 );
+
+ TCHAR szJid[ JABBER_MAX_JID_LEN ];
+ if ( item->resourceCount != NULL && !isXVcard ) {
+ TCHAR *bestResName = ListGetBestClientResourceNamePtr(dbv.ptszVal);
+ mir_sntprintf( szJid, SIZEOF( szJid ), bestResName?_T("%s/%s"):_T("%s"), dbv.ptszVal, bestResName );
+ }
+ else lstrcpyn( szJid, dbv.ptszVal, SIZEOF( szJid ));
+
+ Log( "Rereading %s for " TCHAR_STR_PARAM, isXVcard ? JABBER_FEAT_VCARD_TEMP : JABBER_FEAT_AVATAR, szJid );
+
+ int iqId = SerialNext();
+ if ( isXVcard )
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetVCardAvatar );
+ else
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetClientAvatar );
+
+ XmlNodeIq iq( _T("get"), iqId, szJid );
+ if ( isXVcard )
+ iq << XCHILDNS( _T("vCard"), _T(JABBER_FEAT_VCARD_TEMP));
+ else
+ iq << XQUERY( isXVcard ? _T("") : _T(JABBER_FEAT_AVATAR));
+ m_ThreadInfo->send( iq );
+
+ JFreeVariant( &dbv );
+ return GAIR_WAITFOR;
+ }
+ JFreeVariant( &dbv );
+ }
+ }
+
+ Log( "No avatar" );
+ return GAIR_NOAVATAR;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetEventTextChatStates - retrieves a chat state description from an event
+
+INT_PTR __cdecl CJabberProto::OnGetEventTextChatStates( WPARAM, LPARAM lParam )
+{
+ DBEVENTGETTEXT *pdbEvent = ( DBEVENTGETTEXT * )lParam;
+
+ INT_PTR nRetVal = 0;
+
+ if ( pdbEvent->dbei->cbBlob > 0 ) {
+ if ( pdbEvent->dbei->pBlob[0] == JABBER_DB_EVENT_CHATSTATES_GONE ) {
+ if ( pdbEvent->datatype == DBVT_WCHAR )
+ nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("closed chat session")));
+ else if ( pdbEvent->datatype == DBVT_ASCIIZ )
+ nRetVal = (INT_PTR)mir_strdup(Translate("closed chat session"));
+ }
+ }
+
+ return nRetVal;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// OnGetEventTextPresence - retrieves presence state description from an event
+
+INT_PTR __cdecl CJabberProto::OnGetEventTextPresence( WPARAM, LPARAM lParam )
+{
+ DBEVENTGETTEXT *pdbEvent = ( DBEVENTGETTEXT * )lParam;
+
+ INT_PTR nRetVal = 0;
+
+ if ( pdbEvent->dbei->cbBlob > 0 ) {
+ switch ( pdbEvent->dbei->pBlob[0] )
+ {
+ case JABBER_DB_EVENT_PRESENCE_SUBSCRIBE:
+ {
+ if ( pdbEvent->datatype == DBVT_WCHAR )
+ nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("sent subscription request")));
+ else if ( pdbEvent->datatype == DBVT_ASCIIZ )
+ nRetVal = (INT_PTR)mir_strdup(Translate("sent subscription request"));
+ break;
+ }
+ case JABBER_DB_EVENT_PRESENCE_SUBSCRIBED:
+ {
+ if ( pdbEvent->datatype == DBVT_WCHAR )
+ nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("approved subscription request")));
+ else if ( pdbEvent->datatype == DBVT_ASCIIZ )
+ nRetVal = (INT_PTR)mir_strdup(Translate("approved subscription request"));
+ break;
+ }
+ case JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBE:
+ {
+ if ( pdbEvent->datatype == DBVT_WCHAR )
+ nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("declined subscription")));
+ else if ( pdbEvent->datatype == DBVT_ASCIIZ )
+ nRetVal = (INT_PTR)mir_strdup(Translate("declined subscription"));
+ break;
+ }
+ case JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBED:
+ {
+ if ( pdbEvent->datatype == DBVT_WCHAR )
+ nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("declined subscription")));
+ else if ( pdbEvent->datatype == DBVT_ASCIIZ )
+ nRetVal = (INT_PTR)mir_strdup(Translate("declined subscription"));
+ break;
+ }
+ case JABBER_DB_EVENT_PRESENCE_ERROR:
+ {
+ if ( pdbEvent->datatype == DBVT_WCHAR )
+ nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("sent error presence")));
+ else if ( pdbEvent->datatype == DBVT_ASCIIZ )
+ nRetVal = (INT_PTR)mir_strdup(Translate("sent error presence"));
+ break;
+ }
+ default:
+ {
+ if ( pdbEvent->datatype == DBVT_WCHAR )
+ nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("sent unknown presence type")));
+ else if ( pdbEvent->datatype == DBVT_ASCIIZ )
+ nRetVal = (INT_PTR)mir_strdup(Translate("sent unknown presence type"));
+ break;
+ }
+ }
+ }
+
+ return nRetVal;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSetAvatar - sets an avatar without UI
+
+INT_PTR __cdecl CJabberProto::JabberSetAvatar( WPARAM, LPARAM lParam )
+{
+ TCHAR* tszFileName = ( TCHAR* )lParam;
+
+ if ( m_bJabberOnline ) {
+ SetServerVcard( TRUE, tszFileName );
+ SendPresence( m_iDesiredStatus, false );
+ }
+ else if ( tszFileName == NULL || tszFileName[0] == 0 ) {
+ // Remove avatar
+ TCHAR tFileName[ MAX_PATH ];
+ GetAvatarFileName( NULL, tFileName, MAX_PATH );
+ DeleteFile( tFileName );
+
+ JDeleteSetting( NULL, "AvatarSaved" );
+ JDeleteSetting( NULL, "AvatarHash" );
+ }
+ else {
+ int fileIn = _topen( tszFileName, O_RDWR | O_BINARY, S_IREAD | S_IWRITE );
+ if ( fileIn == -1 ) {
+ mir_free(tszFileName);
+ return 1;
+ }
+
+ long dwPngSize = _filelength( fileIn );
+ char* pResult = new char[ dwPngSize ];
+ if ( pResult == NULL ) {
+ _close( fileIn );
+ mir_free(tszFileName);
+ return 2;
+ }
+
+ _read( fileIn, pResult, dwPngSize );
+ _close( fileIn );
+
+ mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE];
+ mir_sha1_ctx sha1ctx;
+ mir_sha1_init( &sha1ctx );
+ mir_sha1_append( &sha1ctx, (mir_sha1_byte_t*)pResult, dwPngSize );
+ mir_sha1_finish( &sha1ctx, digest );
+
+ TCHAR tFileName[ MAX_PATH ];
+ GetAvatarFileName( NULL, tFileName, MAX_PATH );
+ DeleteFile( tFileName );
+
+ char buf[MIR_SHA1_HASH_SIZE*2+1];
+ for ( int i=0; i<MIR_SHA1_HASH_SIZE; i++ )
+ sprintf( buf+( i<<1 ), "%02x", digest[i] );
+
+ m_options.AvatarType = JabberGetPictureType( pResult );
+
+ GetAvatarFileName( NULL, tFileName, MAX_PATH );
+ FILE* out = _tfopen( tFileName, _T("wb"));
+ if ( out != NULL ) {
+ fwrite( pResult, dwPngSize, 1, out );
+ fclose( out );
+ }
+ delete[] pResult;
+
+ JSetString( NULL, "AvatarSaved", buf );
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSetNickname - sets the user nickname without UI
+
+INT_PTR __cdecl CJabberProto::JabberSetNickname( WPARAM wParam, LPARAM lParam )
+{
+ TCHAR *nickname = ( wParam & SMNN_UNICODE ) ? mir_u2t( (WCHAR *) lParam ) : mir_a2t( (char *) lParam );
+
+ JSetStringT( NULL, "Nick", nickname );
+ SetServerVcard( FALSE, _T(""));
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// "/SendXML" - Allows external plugins to send XML to the server
+
+INT_PTR __cdecl CJabberProto::ServiceSendXML( WPARAM, LPARAM lParam )
+{
+ return m_ThreadInfo->send( (char*)lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// "/GCGetToolTipText" - gets tooltip text
+
+static const TCHAR * JabberEnum2AffilationStr[]={ _T("None"), _T("Outcast"), _T("Member"), _T("Admin"), _T("Owner") };
+static const TCHAR * JabberEnum2RoleStr[]={ _T("None"), _T("Visitor"), _T("Participant"), _T("Moderator") };
+
+//FIXME Table conversion fast but is not safe
+static const TCHAR * JabberEnum2StatusStr[]= { _T("Offline"), _T("Online"), _T("Away"), _T("DND"),
+ _T("NA"), _T("Occupied"), _T("Free for chat"),
+ _T("Invisible"), _T("On the phone"), _T("Out to lunch"),
+ _T("Idle") };
+
+static void appendString( bool bIsTipper, const TCHAR* tszTitle, const TCHAR* tszValue, TCHAR* buf, size_t bufSize )
+{
+ if ( *buf ) {
+ const TCHAR* szSeparator = (bIsTipper) ? _T("\n") : ((IsWinVerMEPlus()) ? _T("\r\n") : _T(" | "));
+ _tcsncat( buf, szSeparator, bufSize );
+ }
+
+ size_t len = _tcslen(buf);
+ buf += len;
+ bufSize -= len;
+
+ if ( bIsTipper )
+ mir_sntprintf(buf, bufSize, _T("%s%s%s%s"), _T("<b>"), TranslateTS(tszTitle), _T("</b>\t"), tszValue);
+ else {
+ TCHAR* p = TranslateTS(tszTitle);
+ mir_sntprintf(buf, bufSize, _T("%s%s\t%s"), p, _tcslen(p)<=7 ? _T("\t") : _T(""), tszValue);
+ }
+}
+
+INT_PTR __cdecl CJabberProto::JabberGCGetToolTipText( WPARAM wParam, LPARAM lParam )
+{
+ if ( !wParam || !lParam )
+ return 0; //room global tooltip not supported yet
+
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_CHATROOM, (TCHAR*)wParam);
+ if ( item == NULL )
+ return 0; //no room found
+
+ JABBER_RESOURCE_STATUS * info = NULL;
+ for ( int i=0; i < item->resourceCount; i++ ) {
+ JABBER_RESOURCE_STATUS& p = item->resource[i];
+ if ( !lstrcmp( p.resourceName, (TCHAR*)lParam )) {
+ info = &p;
+ break;
+ } }
+
+ if ( info == NULL )
+ return 0; //no info found
+
+ // ok process info output will be:
+ // JID: real@jid/resource or
+ // Nick: Nickname
+ // Status: StatusText
+ // Role: Moderator
+ // Affiliation: Affiliation
+
+ TCHAR outBuf[2048];
+ outBuf[0]=_T('\0');
+
+ bool bIsTipper = DBGetContactSettingByte(NULL, "Tab_SRMsg", "adv_TipperTooltip", 1) && ServiceExists("mToolTip/HideTip");
+
+ //JID:
+ if ( _tcschr(info->resourceName, _T('@')) != NULL )
+ appendString(bIsTipper, _T("JID:"), info->resourceName, outBuf, SIZEOF(outBuf));
+ else if (lParam) { //or simple nick
+ appendString(bIsTipper, _T("Nick:"), (TCHAR*) lParam, outBuf, SIZEOF(outBuf));
+ }
+
+ // status
+ if ( info->status >= ID_STATUS_OFFLINE && info->status <= ID_STATUS_IDLE )
+ appendString(bIsTipper, _T("Status:"), TranslateTS(JabberEnum2StatusStr[info->status-ID_STATUS_OFFLINE]), outBuf, SIZEOF(outBuf));
+
+ // status text
+ if ( info->statusMessage )
+ appendString(bIsTipper, _T("Status text:"), info->statusMessage, outBuf, SIZEOF(outBuf));
+
+ // Role
+ appendString(bIsTipper, _T("Role:"), TranslateTS(JabberEnum2RoleStr[info->role]), outBuf, SIZEOF(outBuf));
+
+ // Affiliation
+ appendString(bIsTipper, _T("Affiliation:"), TranslateTS(JabberEnum2AffilationStr[info->affiliation]), outBuf, SIZEOF(outBuf));
+
+ // real jid
+ if ( info->szRealJid )
+ appendString(bIsTipper, _T("Real JID:"), info->szRealJid, outBuf, SIZEOF(outBuf));
+
+ return (INT_PTR)( outBuf[0] == 0 ? NULL : mir_tstrdup( outBuf ));
+}
+
+// File Association Manager plugin support
+
+INT_PTR __cdecl CJabberProto::JabberServiceParseXmppURI( WPARAM wParam, LPARAM lParam )
+{
+ UNREFERENCED_PARAMETER( wParam );
+
+ TCHAR *arg = ( TCHAR * )lParam;
+ if ( arg == NULL )
+ return 1;
+
+ TCHAR szUri[ 1024 ];
+ mir_sntprintf( szUri, SIZEOF(szUri), _T("%s"), arg );
+
+ TCHAR *szJid = szUri;
+
+ // skip leading prefix
+ szJid = _tcschr( szJid, _T( ':' ));
+ if ( szJid == NULL )
+ return 1;
+
+ // skip //
+ for ( ++szJid; *szJid == _T( '/' ); ++szJid );
+
+ // empty jid?
+ if ( !*szJid )
+ return 1;
+
+ // command code
+ TCHAR *szCommand = szJid;
+ szCommand = _tcschr( szCommand, _T( '?' ));
+ if ( szCommand )
+ *( szCommand++ ) = 0;
+
+ // parameters
+ TCHAR *szSecondParam = szCommand ? _tcschr( szCommand, _T( ';' )) : NULL;
+ if ( szSecondParam )
+ *( szSecondParam++ ) = 0;
+
+// TCHAR *szThirdParam = szSecondParam ? _tcschr( szSecondParam, _T( ';' )) : NULL;
+// if ( szThirdParam )
+// *( szThirdParam++ ) = 0;
+
+ // no command or message command
+ if ( !szCommand || ( szCommand && !_tcsicmp( szCommand, _T( "message" )))) {
+ // message
+ if ( ServiceExists( MS_MSG_SENDMESSAGE )) {
+ HANDLE hContact = HContactFromJID( szJid, TRUE );
+ TCHAR *szMsgBody = NULL;
+ if ( !hContact )
+ hContact = DBCreateContact( szJid, szJid, TRUE, TRUE );
+ if ( !hContact )
+ return 1;
+
+ if ( szSecondParam ) { //there are parameters to message
+ szMsgBody = _tcsstr(szSecondParam, _T( "body=" ));
+ if ( szMsgBody ) {
+ szMsgBody += 5;
+ TCHAR* szDelim = _tcschr( szMsgBody, _T( ';' ));
+ if ( szDelim )
+ szDelim = 0;
+ JabberHttpUrlDecode( szMsgBody );
+ } }
+
+ CallService(MS_MSG_SENDMESSAGE "W",(WPARAM)hContact, (LPARAM)szMsgBody);
+
+ return 0;
+ }
+ return 1;
+ }
+ else if ( !_tcsicmp( szCommand, _T( "roster" )))
+ {
+ if ( !HContactFromJID( szJid )) {
+ JABBER_SEARCH_RESULT jsr = { 0 };
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+ jsr.hdr.flags = PSR_TCHAR;
+ jsr.hdr.nick = szJid;
+ jsr.hdr.id = szJid;
+ _tcsncpy( jsr.jid, szJid, SIZEOF(jsr.jid) - 1 );
+
+ ADDCONTACTSTRUCT acs;
+ acs.handleType = HANDLE_SEARCHRESULT;
+ acs.szProto = m_szModuleName;
+ acs.psr = &jsr.hdr;
+ CallService( MS_ADDCONTACT_SHOW, (WPARAM)NULL, (LPARAM)&acs );
+ }
+ return 0;
+ }
+ else if ( !_tcsicmp( szCommand, _T( "join" )))
+ {
+ // chat join invitation
+ GroupchatJoinRoomByJid( NULL, szJid );
+ return 0;
+ }
+ else if ( !_tcsicmp( szCommand, _T( "disco" )))
+ {
+ // service discovery request
+ OnMenuHandleServiceDiscovery( 0, ( LPARAM )szJid );
+ return 0;
+ }
+ else if ( !_tcsicmp( szCommand, _T( "command" )))
+ {
+ // ad-hoc commands
+ if ( szSecondParam ) {
+ if ( !_tcsnicmp( szSecondParam, _T( "node=" ), 5 )) {
+ szSecondParam += 5;
+ if (!*szSecondParam)
+ szSecondParam = NULL;
+ }
+ else
+ szSecondParam = NULL;
+ }
+ CJabberAdhocStartupParams* pStartupParams = new CJabberAdhocStartupParams( this, szJid, szSecondParam );
+ ContactMenuRunCommands( 0, ( LPARAM )pStartupParams );
+ return 0;
+ }
+ else if ( !_tcsicmp( szCommand, _T( "sendfile" )))
+ {
+ // send file
+ if ( ServiceExists( MS_FILE_SENDFILE )) {
+ HANDLE hContact = HContactFromJID( szJid, TRUE );
+ if ( !hContact )
+ hContact = DBCreateContact( szJid, szJid, TRUE, TRUE );
+ if ( !hContact )
+ return 1;
+ CallService( MS_FILE_SENDFILE, ( WPARAM )hContact, ( LPARAM )NULL );
+ return 0;
+ }
+ return 1;
+ }
+
+ return 1; /* parse failed */
+}
+
+// XEP-0224 support (Attention/Nudge)
+INT_PTR __cdecl CJabberProto::JabberSendNudge( WPARAM wParam, LPARAM )
+{
+ if ( !m_bJabberOnline )
+ return 0;
+
+ HANDLE hContact = ( HANDLE )wParam;
+ DBVARIANT dbv;
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ TCHAR tszJid[ JABBER_MAX_JID_LEN ];
+ TCHAR *szResource = ListGetBestClientResourceNamePtr( dbv.ptszVal );
+ if ( szResource )
+ mir_sntprintf( tszJid, SIZEOF(tszJid), _T("%s/%s"), dbv.ptszVal, szResource );
+ else
+ mir_sntprintf( tszJid, SIZEOF(tszJid), _T("%s"), dbv.ptszVal );
+ JFreeVariant( &dbv );
+
+ JabberCapsBits jcb = GetResourceCapabilites( tszJid, FALSE );
+
+ m_ThreadInfo->send(
+ XmlNode( _T("message")) << XATTR( _T("type"), _T("headline")) << XATTR( _T("to"), tszJid )
+ << XCHILDNS( _T("attention"),
+ jcb & JABBER_CAPS_ATTENTION ? _T(JABBER_FEAT_ATTENTION) : _T(JABBER_FEAT_ATTENTION_0 )));
+ }
+ return 0;
+}
+
+BOOL CJabberProto::SendHttpAuthReply( CJabberHttpAuthParams *pParams, BOOL bAuthorized )
+{
+ if ( !m_bJabberOnline || !pParams || !m_ThreadInfo )
+ return FALSE;
+
+ if ( pParams->m_nType == CJabberHttpAuthParams::IQ ) {
+ XmlNodeIq iq( bAuthorized ? _T("result") : _T("error"), pParams->m_szIqId, pParams->m_szFrom );
+ if ( !bAuthorized ) {
+ iq << XCHILDNS( _T("confirm"), _T(JABBER_FEAT_HTTP_AUTH)) << XATTR( _T("id"), pParams->m_szId )
+ << XATTR( _T("method"), pParams->m_szMethod ) << XATTR( _T("url"), pParams->m_szUrl );
+ iq << XCHILD( _T("error")) << XATTRI( _T("code"), 401 ) << XATTR( _T("type"), _T("auth"))
+ << XCHILDNS( _T("not-authorized"), _T("urn:ietf:params:xml:xmpp-stanzas"));
+ }
+ m_ThreadInfo->send( iq );
+ }
+ else if ( pParams->m_nType == CJabberHttpAuthParams::MSG ) {
+ XmlNode msg( _T("message"));
+ msg << XATTR( _T("to"), pParams->m_szFrom );
+ if ( !bAuthorized )
+ msg << XATTR( _T("type"), _T("error"));
+ if ( pParams->m_szThreadId )
+ msg << XCHILD( _T("thread"), pParams->m_szThreadId );
+
+ msg << XCHILDNS( _T("confirm"), _T(JABBER_FEAT_HTTP_AUTH)) << XATTR( _T("id"), pParams->m_szId )
+ << XATTR( _T("method"), pParams->m_szMethod ) << XATTR( _T("url"), pParams->m_szUrl );
+
+ if ( !bAuthorized )
+ msg << XCHILD( _T("error")) << XATTRI( _T("code"), 401 ) << XATTR( _T("type"), _T("auth"))
+ << XCHILDNS( _T("not-authorized"), _T("urn:ietf:params:xml:xmpp-stanzas"));
+
+ m_ThreadInfo->send( msg );
+ }
+ else
+ return FALSE;
+
+
+ return TRUE;
+}
+
+class CJabberDlgHttpAuth: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+
+public:
+ CJabberDlgHttpAuth(CJabberProto *proto, HWND hwndParent, CJabberHttpAuthParams *pParams):
+ CSuper(proto, IDD_HTTP_AUTH, hwndParent, true),
+ m_txtInfo(this, IDC_EDIT_HTTP_AUTH_INFO),
+ m_btnAuth(this, IDOK),
+ m_btnDeny(this, IDCANCEL),
+ m_pParams(pParams)
+ {
+ m_btnAuth.OnClick = Callback( this, &CJabberDlgHttpAuth::btnAuth_OnClick );
+ m_btnDeny.OnClick = Callback( this, &CJabberDlgHttpAuth::btnDeny_OnClick );
+ }
+
+ void OnInitDialog()
+ {
+ CSuper::OnInitDialog();
+
+ WindowSetIcon( m_hwnd, m_proto, "openid" );
+
+ SetDlgItemText(m_hwnd, IDC_TXT_URL, m_pParams->m_szUrl);
+ SetDlgItemText(m_hwnd, IDC_TXT_FROM, m_pParams->m_szFrom);
+ SetDlgItemText(m_hwnd, IDC_TXT_ID, m_pParams->m_szId);
+ SetDlgItemText(m_hwnd, IDC_TXT_METHOD, m_pParams->m_szMethod);
+ }
+
+ BOOL SendReply( BOOL bAuthorized )
+ {
+ BOOL bRetVal = m_proto->SendHttpAuthReply( m_pParams, bAuthorized );
+ m_pParams->Free();
+ mir_free( m_pParams );
+ m_pParams = NULL;
+ return bRetVal;
+ }
+
+ void btnAuth_OnClick(CCtrlButton*)
+ {
+ SendReply( TRUE );
+ Close();
+ }
+ void btnDeny_OnClick(CCtrlButton*)
+ {
+ SendReply( FALSE );
+ Close();
+ }
+
+ UI_MESSAGE_MAP(CJabberDlgHttpAuth, CSuper);
+ UI_MESSAGE(WM_CTLCOLORSTATIC, OnCtlColorStatic);
+ UI_MESSAGE_MAP_END();
+
+ INT_PTR OnCtlColorStatic(UINT, WPARAM, LPARAM)
+ {
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+ }
+
+private:
+ CCtrlEdit m_txtInfo;
+ CCtrlButton m_btnAuth;
+ CCtrlButton m_btnDeny;
+
+ CJabberHttpAuthParams *m_pParams;
+};
+
+// XEP-0070 support (http auth)
+INT_PTR __cdecl CJabberProto::OnHttpAuthRequest( WPARAM wParam, LPARAM lParam )
+{
+ CLISTEVENT *pCle = (CLISTEVENT *)lParam;
+ CJabberHttpAuthParams *pParams = (CJabberHttpAuthParams *)pCle->lParam;
+ if ( !pParams )
+ return 0;
+
+ CJabberDlgHttpAuth *pDlg = new CJabberDlgHttpAuth( this, (HWND)wParam, pParams );
+ if ( !pDlg ) {
+ pParams->Free();
+ mir_free( pParams );
+ return 0;
+ }
+
+ pDlg->Show();
+
+ return 0;
+}
+
+
+// Jabber API functions
+INT_PTR __cdecl CJabberProto::JabberGetApi( WPARAM wParam, LPARAM lParam )
+{
+ IJabberInterface **ji = (IJabberInterface**)lParam;
+ if ( !ji )
+ return -1;
+ *ji = &m_JabberApi;
+ return 0;
+}
+
+DWORD CJabberInterface::GetFlags() const
+{
+ return JIF_UNICODE;
+}
+
+int CJabberInterface::GetVersion() const
+{
+ return 1;
+}
+
+DWORD CJabberInterface::GetJabberVersion() const
+{
+ return __VERSION_DWORD;
+}
+
+IJabberSysInterface *CJabberInterface::Sys() const
+{
+ return &m_psProto->m_JabberSysApi;
+}
+
+IJabberNetInterface *CJabberInterface::Net() const
+{
+ return &m_psProto->m_JabberNetApi;
+}
+
+int CJabberSysInterface::GetVersion() const
+{
+ return 1;
+}
+
+int CJabberSysInterface::CompareJIDs( LPCTSTR jid1, LPCTSTR jid2 )
+{
+ if ( !jid1 || !jid2 ) return 0;
+ return JabberCompareJids( jid1, jid2 );
+}
+
+HANDLE CJabberSysInterface::ContactFromJID( LPCTSTR jid )
+{
+ if ( !jid ) return NULL;
+ return m_psProto->HContactFromJID( jid );
+}
+
+LPTSTR CJabberSysInterface::ContactToJID( HANDLE hContact )
+{
+ return m_psProto->JGetStringT( hContact, m_psProto->JGetByte( hContact, "ChatRoom", 0 ) ? "ChatRoomID" : "jid" );
+}
+
+LPTSTR CJabberSysInterface::GetBestResourceName( LPCTSTR jid )
+{
+ if ( jid == NULL )
+ return NULL;
+ LPCTSTR p = _tcschr( jid, '/' );
+ if ( p == NULL ) {
+ m_psProto->ListLock(); // make sure we allow access to the list only after making mir_tstrdup() of resource name
+ LPTSTR res = mir_tstrdup( m_psProto->ListGetBestClientResourceNamePtr( jid ));
+ m_psProto->ListUnlock();
+ return res;
+ }
+ return mir_tstrdup( jid );
+}
+
+LPTSTR CJabberSysInterface::GetResourceList( LPCTSTR jid )
+{
+ if ( !jid )
+ return NULL;
+
+ m_psProto->ListLock();
+ JABBER_LIST_ITEM *item = NULL;
+ if (( item = m_psProto->ListGetItemPtr( LIST_VCARD_TEMP, jid )) == NULL)
+ item = m_psProto->ListGetItemPtr( LIST_ROSTER, jid );
+ if ( item == NULL ) {
+ m_psProto->ListUnlock();
+ return NULL;
+ }
+
+ if ( item->resource == NULL ) {
+ m_psProto->ListUnlock();
+ return NULL;
+ }
+
+ int i;
+ int iLen = 1; // 1 for extra zero terminator at the end of the string
+ // calculate total necessary string length
+ for ( i=0; i<item->resourceCount; i++ ) {
+ iLen += lstrlen(item->resource[i].resourceName) + 1;
+ }
+
+ // allocate memory and fill it
+ LPTSTR str = (LPTSTR)mir_alloc(iLen * sizeof(TCHAR));
+ LPTSTR p = str;
+ for ( i=0; i<item->resourceCount; i++ ) {
+ lstrcpy(p, item->resource[i].resourceName);
+ p += lstrlen(item->resource[i].resourceName) + 1;
+ }
+ *p = 0; // extra zero terminator
+
+ m_psProto->ListUnlock();
+ return str;
+}
+
+char *CJabberSysInterface::GetModuleName() const
+{
+ return m_psProto->m_szModuleName;
+}
+
+int CJabberNetInterface::GetVersion() const
+{
+ return 1;
+}
+
+unsigned int CJabberNetInterface::SerialNext()
+{
+ return m_psProto->SerialNext();
+}
+
+int CJabberNetInterface::SendXmlNode( HXML node )
+{
+ return m_psProto->m_ThreadInfo->send(node);
+}
+
+
+typedef struct
+{
+ JABBER_HANDLER_FUNC Func;
+ void *pUserData;
+} sHandlerData;
+
+void CJabberProto::ExternalTempIqHandler( HXML node, CJabberIqInfo *pInfo )
+{
+ sHandlerData *d = (sHandlerData*)pInfo->GetUserData();
+ d->Func(&m_JabberApi, node, d->pUserData);
+ free(d); // free IqHandlerData allocated in CJabberNetInterface::AddIqHandler below
+}
+
+BOOL CJabberProto::ExternalIqHandler( HXML node, CJabberIqInfo *pInfo )
+{
+ sHandlerData *d = (sHandlerData*)pInfo->GetUserData();
+ return d->Func(&m_JabberApi, node, d->pUserData);
+}
+
+BOOL CJabberProto::ExternalMessageHandler( HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo )
+{
+ sHandlerData *d = (sHandlerData*)pInfo->GetUserData();
+ return d->Func(&m_JabberApi, node, d->pUserData);
+}
+
+BOOL CJabberProto::ExternalPresenceHandler( HXML node, ThreadData *pThreadData, CJabberPresenceInfo* pInfo )
+{
+ sHandlerData *d = (sHandlerData*)pInfo->GetUserData();
+ return d->Func(&m_JabberApi, node, d->pUserData);
+}
+
+BOOL CJabberProto::ExternalSendHandler( HXML node, ThreadData *pThreadData, CJabberSendInfo* pInfo )
+{
+ sHandlerData *d = (sHandlerData*)pInfo->GetUserData();
+ return d->Func(&m_JabberApi, node, d->pUserData);
+}
+
+HJHANDLER CJabberNetInterface::AddPresenceHandler( JABBER_HANDLER_FUNC Func, void *pUserData, int iPriority )
+{
+ sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData));
+ d->Func = Func;
+ d->pUserData = pUserData;
+ return (HJHANDLER)m_psProto->m_presenceManager.AddPermanentHandler( &CJabberProto::ExternalPresenceHandler, d, free, iPriority );
+}
+
+HJHANDLER CJabberNetInterface::AddMessageHandler( JABBER_HANDLER_FUNC Func, int iMsgTypes, LPCTSTR szXmlns, LPCTSTR szTag, void *pUserData, int iPriority )
+{
+ sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData));
+ d->Func = Func;
+ d->pUserData = pUserData;
+ return (HJHANDLER)m_psProto->m_messageManager.AddPermanentHandler( &CJabberProto::ExternalMessageHandler, iMsgTypes, 0, szXmlns, FALSE, szTag, d, free, iPriority );
+}
+
+HJHANDLER CJabberNetInterface::AddIqHandler( JABBER_HANDLER_FUNC Func, int iIqTypes, LPCTSTR szXmlns, LPCTSTR szTag, void *pUserData, int iPriority )
+{
+ sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData));
+ d->Func = Func;
+ d->pUserData = pUserData;
+ return (HJHANDLER)m_psProto->m_iqManager.AddPermanentHandler( &CJabberProto::ExternalIqHandler, iIqTypes, 0, szXmlns, FALSE, szTag, d, free, iPriority );
+}
+
+HJHANDLER CJabberNetInterface::AddTemporaryIqHandler( JABBER_HANDLER_FUNC Func, int iIqTypes, int iIqId, void *pUserData, DWORD dwTimeout, int iPriority )
+{
+ sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData));
+ d->Func = Func;
+ d->pUserData = pUserData;
+ CJabberIqInfo* pInfo = m_psProto->m_iqManager.AddHandler( &CJabberProto::ExternalTempIqHandler, iIqTypes, NULL, 0, iIqId, d, iPriority );
+ if ( pInfo && dwTimeout > 0 )
+ pInfo->SetTimeout( dwTimeout );
+ return (HJHANDLER)pInfo;
+}
+
+HJHANDLER CJabberNetInterface::AddSendHandler( JABBER_HANDLER_FUNC Func, void *pUserData, int iPriority )
+{
+ sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData));
+ d->Func = Func;
+ d->pUserData = pUserData;
+ return (HJHANDLER)m_psProto->m_sendManager.AddPermanentHandler( &CJabberProto::ExternalSendHandler, d, free, iPriority );
+}
+
+int CJabberNetInterface::RemoveHandler( HJHANDLER hHandler )
+{
+ return m_psProto->m_sendManager.DeletePermanentHandler( (CJabberSendPermanentInfo*)hHandler ) ||
+ m_psProto->m_presenceManager.DeletePermanentHandler( (CJabberPresencePermanentInfo*)hHandler ) ||
+ m_psProto->m_messageManager.DeletePermanentHandler( (CJabberMessagePermanentInfo*)hHandler ) ||
+ m_psProto->m_iqManager.DeletePermanentHandler( (CJabberIqPermanentInfo*)hHandler ) ||
+ m_psProto->m_iqManager.DeleteHandler( (CJabberIqInfo*)hHandler );
+}
+
+JabberFeatCapPairDynamic *CJabberNetInterface::FindFeature(LPCTSTR szFeature)
+{
+ int i;
+ for ( i = 0; i < m_psProto->m_lstJabberFeatCapPairsDynamic.getCount(); i++ )
+ if ( !lstrcmp( m_psProto->m_lstJabberFeatCapPairsDynamic[i]->szFeature, szFeature ))
+ return m_psProto->m_lstJabberFeatCapPairsDynamic[i];
+ return NULL;
+}
+
+int CJabberNetInterface::RegisterFeature( LPCTSTR szFeature, LPCTSTR szDescription )
+{
+ if ( !szFeature ) {
+ return false;
+ }
+
+ // check for this feature in core features, and return false if it's present, to prevent re-registering a core feature
+ int i;
+ for ( i = 0; g_JabberFeatCapPairs[i].szFeature; i++ )
+ {
+ if ( !lstrcmp( g_JabberFeatCapPairs[i].szFeature, szFeature ))
+ {
+ return false;
+ }
+ }
+
+ m_psProto->ListLock();
+ JabberFeatCapPairDynamic *fcp = FindFeature( szFeature );
+ if ( !fcp ) {
+ // if the feature is not registered yet, allocate new bit for it
+ JabberCapsBits jcb = JABBER_CAPS_OTHER_SPECIAL; // set all bits not included in g_JabberFeatCapPairs
+
+ // set all bits occupied by g_JabberFeatCapPairs
+ for ( i = 0; g_JabberFeatCapPairs[i].szFeature; i++ )
+ jcb |= g_JabberFeatCapPairs[i].jcbCap;
+
+ // set all bits already occupied by external plugins
+ for ( i = 0; i < m_psProto->m_lstJabberFeatCapPairsDynamic.getCount(); i++ )
+ jcb |= m_psProto->m_lstJabberFeatCapPairsDynamic[i]->jcbCap;
+
+ // Now get first zero bit. The line below is a fast way to do it. If there are no zero bits, it returns 0.
+ jcb = (~jcb) & (JabberCapsBits)(-(__int64)(~jcb));
+
+ if ( !jcb ) {
+ // no more free bits
+ m_psProto->ListUnlock();
+ return false;
+ }
+
+ LPTSTR szExt = mir_tstrdup( szFeature );
+ LPTSTR pSrc, pDst;
+ for ( pSrc = szExt, pDst = szExt; *pSrc; pSrc++ ) {
+ // remove unnecessary symbols from szFeature to make the string shorter, and use it as szExt
+ if ( _tcschr( _T("bcdfghjklmnpqrstvwxz0123456789"), *pSrc )) {
+ *pDst++ = *pSrc;
+ }
+ }
+ *pDst = 0;
+ m_psProto->m_clientCapsManager.SetClientCaps( _T(JABBER_CAPS_MIRANDA_NODE), szExt, jcb );
+
+ fcp = new JabberFeatCapPairDynamic();
+ fcp->szExt = szExt; // will be deallocated along with other values of JabberFeatCapPairDynamic in CJabberProto destructor
+ fcp->szFeature = mir_tstrdup( szFeature );
+ fcp->szDescription = szDescription ? mir_tstrdup( szDescription ) : NULL;
+ fcp->jcbCap = jcb;
+ m_psProto->m_lstJabberFeatCapPairsDynamic.insert( fcp );
+ } else if ( szDescription ) {
+ // update description
+ if ( fcp->szDescription )
+ mir_free( fcp->szDescription );
+ fcp->szDescription = mir_tstrdup( szDescription );
+ }
+ m_psProto->ListUnlock();
+ return true;
+}
+
+int CJabberNetInterface::AddFeatures( LPCTSTR szFeatures )
+{
+ if ( !szFeatures ) {
+ return false;
+ }
+
+ m_psProto->ListLock();
+ BOOL ret = true;
+ LPCTSTR szFeat = szFeatures;
+ while ( szFeat[0] ) {
+ JabberFeatCapPairDynamic *fcp = FindFeature( szFeat );
+ // if someone is trying to add one of core features, RegisterFeature() will return false, so we don't have to perform this check here
+ if ( !fcp ) {
+ // if the feature is not registered yet
+ if ( !RegisterFeature( szFeat, NULL )) {
+ ret = false;
+ } else {
+ fcp = FindFeature( szFeat ); // update fcp after RegisterFeature()
+ }
+ }
+ if ( fcp ) {
+ m_psProto->m_uEnabledFeatCapsDynamic |= fcp->jcbCap;
+ } else {
+ ret = false;
+ }
+ szFeat += lstrlen( szFeat ) + 1;
+ }
+ m_psProto->ListUnlock();
+
+ if ( m_psProto->m_bJabberOnline ) {
+ m_psProto->SendPresence( m_psProto->m_iStatus, true );
+ }
+ return ret;
+}
+
+int CJabberNetInterface::RemoveFeatures( LPCTSTR szFeatures )
+{
+ if ( !szFeatures ) {
+ return false;
+ }
+
+ m_psProto->ListLock();
+ BOOL ret = true;
+ LPCTSTR szFeat = szFeatures;
+ while ( szFeat[0] ) {
+ JabberFeatCapPairDynamic *fcp = FindFeature( szFeat );
+ if ( fcp ) {
+ m_psProto->m_uEnabledFeatCapsDynamic &= ~fcp->jcbCap;
+ } else {
+ ret = false; // indicate that there was an error removing at least one of the specified features
+ }
+ szFeat += lstrlen( szFeat ) + 1;
+ }
+ m_psProto->ListUnlock();
+
+ if ( m_psProto->m_bJabberOnline ) {
+ m_psProto->SendPresence( m_psProto->m_iStatus, true );
+ }
+ return ret;
+}
+
+LPTSTR CJabberNetInterface::GetResourceFeatures( LPCTSTR jid )
+{
+ JabberCapsBits jcb = m_psProto->GetResourceCapabilites( jid, true );
+ if ( !( jcb & JABBER_RESOURCE_CAPS_ERROR )) {
+ m_psProto->ListLock(); // contents of m_lstJabberFeatCapPairsDynamic must not change from the moment we calculate total length and to the moment when we fill the string
+ int i;
+ int iLen = 1; // 1 for extra zero terminator at the end of the string
+ // calculate total necessary string length
+ for ( i = 0; g_JabberFeatCapPairs[i].szFeature; i++ ) {
+ if ( jcb & g_JabberFeatCapPairs[i].jcbCap ) {
+ iLen += lstrlen( g_JabberFeatCapPairs[i].szFeature ) + 1;
+ }
+ }
+ for ( i = 0; i < m_psProto->m_lstJabberFeatCapPairsDynamic.getCount(); i++ ) {
+ if ( jcb & m_psProto->m_lstJabberFeatCapPairsDynamic[i]->jcbCap ) {
+ iLen += lstrlen( m_psProto->m_lstJabberFeatCapPairsDynamic[i]->szFeature ) + 1;
+ }
+ }
+
+ // allocate memory and fill it
+ LPTSTR str = (LPTSTR)mir_alloc( iLen * sizeof(TCHAR));
+ LPTSTR p = str;
+ for ( i = 0; g_JabberFeatCapPairs[i].szFeature; i++ ) {
+ if ( jcb & g_JabberFeatCapPairs[i].jcbCap ) {
+ lstrcpy( p, g_JabberFeatCapPairs[i].szFeature );
+ p += lstrlen( g_JabberFeatCapPairs[i].szFeature ) + 1;
+ }
+ }
+ for ( i = 0; i < m_psProto->m_lstJabberFeatCapPairsDynamic.getCount(); i++ ) {
+ if ( jcb & m_psProto->m_lstJabberFeatCapPairsDynamic[i]->jcbCap ) {
+ lstrcpy( p, m_psProto->m_lstJabberFeatCapPairsDynamic[i]->szFeature );
+ p += lstrlen( m_psProto->m_lstJabberFeatCapPairsDynamic[i]->szFeature ) + 1;
+ }
+ }
+ *p = 0; // extra zero terminator
+ m_psProto->ListUnlock();
+ return str;
+ }
+ return NULL;
+}
diff --git a/protocols/JabberG/src/jabber_thread.cpp b/protocols/JabberG/src/jabber_thread.cpp new file mode 100644 index 0000000000..d71fd2ab60 --- /dev/null +++ b/protocols/JabberG/src/jabber_thread.cpp @@ -0,0 +1,2160 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+#include <windns.h> // requires Windows Platform SDK
+
+#include "jabber_list.h"
+#include "jabber_iq.h"
+#include "jabber_secur.h"
+#include "jabber_caps.h"
+#include "jabber_privacy.h"
+#include "jabber_rc.h"
+#include "jabber_proto.h"
+
+#ifndef DNS_TYPE_SRV
+#define DNS_TYPE_SRV 0x0021
+#endif
+
+// <iq/> identification number for various actions
+// for JABBER_REGISTER thread
+int iqIdRegGetReg;
+int iqIdRegSetReg;
+
+// XML Console
+#define JCPF_IN 0x01UL
+#define JCPF_OUT 0x02UL
+#define JCPF_ERROR 0x04UL
+
+//extern int bSecureIM;
+static VOID CALLBACK JabberDummyApcFunc( DWORD_PTR )
+{
+ return;
+}
+
+struct JabberPasswordDlgParam
+{
+ CJabberProto* pro;
+
+ BOOL saveOnlinePassword;
+ WORD dlgResult;
+ TCHAR onlinePassword[128];
+ HANDLE hEventPasswdDlg;
+ TCHAR* ptszJid;
+};
+
+static INT_PTR CALLBACK JabberPasswordDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ JabberPasswordDlgParam* param = (JabberPasswordDlgParam*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault( hwndDlg );
+ {
+ param = (JabberPasswordDlgParam*)lParam;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+
+ TCHAR text[512];
+ mir_sntprintf( text, SIZEOF(text), _T("%s %s"), TranslateT( "Enter password for" ), ( TCHAR* )param->ptszJid );
+ SetDlgItemText( hwndDlg, IDC_JID, text );
+
+ int bSavePassword = param->pro->JGetByte( NULL, "SaveSessionPassword", 0 );
+ CheckDlgButton( hwndDlg, IDC_SAVEPASSWORD, ( bSavePassword ) ? BST_CHECKED : BST_UNCHECKED );
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDOK:
+ param->saveOnlinePassword = IsDlgButtonChecked( hwndDlg, IDC_SAVEPASSWORD );
+ param->pro->JSetByte( NULL, "SaveSessionPassword", param->saveOnlinePassword );
+
+ GetDlgItemText( hwndDlg, IDC_PASSWORD, param->onlinePassword, SIZEOF( param->onlinePassword ));
+ // Fall through
+ case IDCANCEL:
+ param->dlgResult = LOWORD( wParam );
+ SetEvent( param->hEventPasswdDlg );
+ DestroyWindow( hwndDlg );
+ return TRUE;
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+static VOID CALLBACK JabberPasswordCreateDialogApcProc( void* param )
+{
+ CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_PASSWORD ), NULL, JabberPasswordDlgProc, ( LPARAM )param );
+}
+
+static VOID CALLBACK JabberOfflineChatWindows( void* param )
+{
+ CJabberProto* ppro = ( CJabberProto* )param;
+ GCDEST gcd = { ppro->m_szModuleName, NULL, GC_EVENT_CONTROL };
+ GCEVENT gce = { 0 };
+ gce.cbSize = sizeof(GCEVENT);
+ gce.pDest = &gcd;
+ CallService( MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Jabber keep-alive thread
+
+void CJabberProto::OnPingReply( HXML, CJabberIqInfo* pInfo )
+{
+ if ( !pInfo )
+ return;
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_FAIL ) {
+ // disconnect because of timeout
+ m_ThreadInfo->send( "</stream:stream>" );
+ m_ThreadInfo->shutdown();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+typedef DNS_STATUS (WINAPI *DNSQUERYA)(IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PIP4_ARRAY aipServers OPTIONAL, IN OUT PDNS_RECORDA *ppQueryResults OPTIONAL, IN OUT PVOID *pReserved OPTIONAL);
+typedef void (WINAPI *DNSFREELIST)(IN OUT PDNS_RECORDA pRecordList, IN DNS_FREE_TYPE FreeType);
+
+static int CompareDNS(const DNS_SRV_DATAA* dns1, const DNS_SRV_DATAA* dns2)
+{
+ return (int)dns1->wPriority - (int)dns2->wPriority;
+}
+
+void ThreadData::xmpp_client_query( void )
+{
+ if (inet_addr(server) != INADDR_NONE)
+ return;
+
+ HMODULE hDnsapi = LoadLibraryA( "dnsapi.dll" );
+ if ( hDnsapi == NULL )
+ return;
+
+ DNSQUERYA pDnsQuery = (DNSQUERYA)GetProcAddress(hDnsapi, "DnsQuery_A");
+ DNSFREELIST pDnsRecordListFree = (DNSFREELIST)GetProcAddress(hDnsapi, "DnsRecordListFree");
+ if ( pDnsQuery == NULL ) {
+ //dnsapi.dll is not the needed dnsapi ;)
+ FreeLibrary( hDnsapi );
+ return;
+ }
+
+ char temp[256];
+ mir_snprintf( temp, SIZEOF(temp), "_xmpp-client._tcp.%s", server );
+
+ DNS_RECORDA *results = NULL;
+ DNS_STATUS status = pDnsQuery(temp, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &results, NULL);
+ if (SUCCEEDED(status) && results) {
+ LIST<DNS_SRV_DATAA> dnsList(5, CompareDNS);
+
+ for (DNS_RECORDA *rec = results; rec; rec = rec->pNext) {
+ if (rec->Data.Srv.pNameTarget && rec->wType == DNS_TYPE_SRV)
+ dnsList.insert(&rec->Data.Srv);
+ }
+
+ for (int i = 0; i < dnsList.getCount(); ++i) {
+ WORD dnsPort = port == 0 || port == 5222 ? dnsList[i]->wPort : port;
+ char* dnsHost = dnsList[i]->pNameTarget;
+
+ proto->Log("%s%s resolved to %s:%d", "_xmpp-client._tcp.", server, dnsHost, dnsPort);
+ s = proto->WsConnect(dnsHost, dnsPort);
+ if (s) {
+ mir_snprintf( manualHost, SIZEOF( manualHost ), "%s", dnsHost );
+ port = dnsPort;
+ break;
+ } }
+ dnsList.destroy();
+ pDnsRecordListFree(results, DnsFreeRecordList);
+ }
+ else
+ proto->Log("%s not resolved", temp);
+
+ FreeLibrary(hDnsapi);
+}
+
+void CJabberProto::xmlStreamInitialize(char *szWhich)
+{
+ Log("Stream will be initialized %s", szWhich);
+ if ( m_szXmlStreamToBeInitialized )
+ free( m_szXmlStreamToBeInitialized );
+ m_szXmlStreamToBeInitialized = _strdup( szWhich );
+}
+
+void CJabberProto::xmlStreamInitializeNow(ThreadData* info)
+{
+ Log( "Stream is initializing %s",
+ m_szXmlStreamToBeInitialized ? m_szXmlStreamToBeInitialized : "after connect" );
+ if (m_szXmlStreamToBeInitialized) {
+ free( m_szXmlStreamToBeInitialized );
+ m_szXmlStreamToBeInitialized = NULL;
+ }
+
+ HXML n = xi.createNode( _T("xml"), NULL, 1 ) << XATTR( _T("version"), _T("1.0")) << XATTR( _T("encoding"), _T("UTF-8"));
+
+ HXML stream = n << XCHILDNS( _T("stream:stream" ), _T("jabber:client")) << XATTR( _T("to"), _A2T(info->server))
+ << XATTR( _T("xmlns:stream"), _T("http://etherx.jabber.org/streams"));
+
+ if ( m_tszSelectedLang )
+ xmlAddAttr( stream, _T("xml:lang"), m_tszSelectedLang );
+
+ if ( !m_options.Disable3920auth )
+ xmlAddAttr( stream, _T("version"), _T("1.0"));
+
+ LPTSTR xmlQuery = xi.toString( n, NULL );
+ char* buf = mir_utf8encodeT( xmlQuery );
+ int bufLen = (int)strlen( buf );
+ if ( bufLen > 2 ) {
+ strdel( buf + bufLen - 2, 1 );
+ bufLen--;
+ }
+
+ info->send( buf, bufLen );
+ mir_free( buf );
+ xi.freeMem( xmlQuery );
+ xi.destroyNode( n );
+}
+
+void CJabberProto::ServerThread( ThreadData* info )
+{
+ DBVARIANT dbv;
+ char* buffer;
+ int datalen;
+ int oldStatus;
+
+ Log( "Thread started: type=%d", info->type );
+
+ info->resolveID = -1;
+ info->auth = NULL;
+
+ if ( m_options.ManualConnect == TRUE ) {
+ if ( !DBGetContactSettingString( NULL, m_szModuleName, "ManualHost", &dbv )) {
+ strncpy( info->manualHost, dbv.pszVal, SIZEOF( info->manualHost ));
+ info->manualHost[SIZEOF( info->manualHost )-1] = '\0';
+ JFreeVariant( &dbv );
+ }
+ info->port = JGetWord( NULL, "ManualPort", JABBER_DEFAULT_PORT );
+ }
+ else info->port = JGetWord( NULL, "Port", JABBER_DEFAULT_PORT );
+
+ info->useSSL = m_options.UseSSL;
+
+ if ( info->type == JABBER_SESSION_NORMAL ) {
+
+ // Normal server connection, we will fetch all connection parameters
+ // e.g. username, password, etc. from the database.
+
+ if ( m_ThreadInfo != NULL ) {
+ // Will not start another connection thread if a thread is already running.
+ // Make APC call to the main thread. This will immediately wake the thread up
+ // in case it is asleep in the reconnect loop so that it will immediately
+ // reconnect.
+ QueueUserAPC( JabberDummyApcFunc, m_ThreadInfo->hThread, 0 );
+ Log( "Thread ended, another normal thread is running" );
+LBL_Exit:
+ delete info;
+ return;
+ }
+
+ m_ThreadInfo = info;
+ if ( m_szStreamId ) mir_free( m_szStreamId );
+ m_szStreamId = NULL;
+
+ if ( !JGetStringT( NULL, "LoginName", &dbv )) {
+ _tcsncpy( info->username, dbv.ptszVal, SIZEOF( info->username )-1 );
+ JFreeVariant( &dbv );
+ }
+
+ if ( *trtrim(info->username) == '\0' ) {
+ DWORD dwSize = SIZEOF( info->username );
+ if ( GetUserName( info->username, &dwSize ))
+ JSetStringT( NULL, "LoginName", info->username );
+ else
+ info->username[0] = 0;
+ }
+
+ if ( *trtrim(info->username) == '\0' ) {
+ Log( "Thread ended, login name is not configured" );
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID );
+LBL_FatalError:
+ m_ThreadInfo = NULL;
+ oldStatus = m_iStatus;
+ m_iDesiredStatus = m_iStatus = ID_STATUS_OFFLINE;
+ JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, m_iStatus );
+ goto LBL_Exit;
+ }
+
+ if ( !DBGetContactSettingString( NULL, m_szModuleName, "LoginServer", &dbv )) {
+ strncpy( info->server, dbv.pszVal, SIZEOF( info->server )-1 );
+ JFreeVariant( &dbv );
+ }
+ else {
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK );
+ Log( "Thread ended, login server is not configured" );
+ goto LBL_FatalError;
+ }
+
+ if ( m_options.HostNameAsResource == FALSE ) {
+ if ( !JGetStringT( NULL, "Resource", &dbv )) {
+ _tcsncpy( info->resource, dbv.ptszVal, SIZEOF( info->resource ) - 1 );
+ JFreeVariant( &dbv );
+ }
+ else _tcscpy( info->resource, _T("Miranda"));
+ }
+ else {
+ DWORD dwCompNameLen = SIZEOF( info->resource ) - 1;
+ if ( !GetComputerName( info->resource, &dwCompNameLen ))
+ _tcscpy( info->resource, _T( "Miranda" ));
+ }
+
+ TCHAR jidStr[512];
+ mir_sntprintf( jidStr, SIZEOF( jidStr ), _T("%s@") _T(TCHAR_STR_PARAM) _T("/%s"), info->username, info->server, info->resource );
+ _tcsncpy( info->fullJID, jidStr, SIZEOF( info->fullJID )-1 );
+
+ if ( m_options.SavePassword == FALSE ) {
+ if (*m_savedPassword) {
+ _tcsncpy( info->password, m_savedPassword, SIZEOF( info->password ));
+ info->password[ SIZEOF( info->password )-1] = '\0';
+ }
+ else {
+ mir_sntprintf( jidStr, SIZEOF( jidStr ), _T("%s@") _T(TCHAR_STR_PARAM), info->username, info->server );
+
+ JabberPasswordDlgParam param;
+ param.pro = this;
+ param.ptszJid = jidStr;
+ param.hEventPasswdDlg = CreateEvent( NULL, FALSE, FALSE, NULL );
+ CallFunctionAsync( JabberPasswordCreateDialogApcProc, ¶m );
+ WaitForSingleObject( param.hEventPasswdDlg, INFINITE );
+ CloseHandle( param.hEventPasswdDlg );
+
+ if ( param.dlgResult == IDCANCEL ) {
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID );
+ Log( "Thread ended, password request dialog was canceled" );
+ goto LBL_FatalError;
+ }
+
+ if ( param.saveOnlinePassword ) lstrcpy(m_savedPassword, param.onlinePassword);
+ else *m_savedPassword = 0;
+
+ _tcsncpy( info->password, param.onlinePassword, SIZEOF( info->password ));
+ info->password[ SIZEOF( info->password )-1] = '\0';
+ }
+ }
+ else {
+ TCHAR *passw = JGetStringCrypt(NULL, "LoginPassword");
+ if ( passw == NULL ) {
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID );
+ Log( "Thread ended, password is not configured" );
+ goto LBL_FatalError;
+ }
+ _tcsncpy( info->password, passw, SIZEOF( info->password ));
+ info->password[SIZEOF( info->password )-1] = '\0';
+ mir_free( passw );
+ } }
+
+ else if ( info->type == JABBER_SESSION_REGISTER ) {
+ // Register new user connection, all connection parameters are already filled-in.
+ // Multiple thread allowed, although not possible : )
+ // thinking again.. multiple thread should not be allowed
+ info->reg_done = FALSE;
+ SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 25, ( LPARAM )TranslateT( "Connecting..." ));
+ iqIdRegGetReg = -1;
+ iqIdRegSetReg = -1;
+ }
+ else {
+ Log( "Thread ended, invalid session type" );
+ goto LBL_FatalError;
+ }
+
+ int jabberNetworkBufferSize = 2048;
+ if (( buffer=( char* )mir_alloc( jabberNetworkBufferSize + 1 )) == NULL ) { // +1 is for '\0' when debug logging this buffer
+ Log( "Cannot allocate network buffer, thread ended" );
+ if ( info->type == JABBER_SESSION_NORMAL ) {
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK );
+ }
+ else if ( info->type == JABBER_SESSION_REGISTER ) {
+ SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Not enough memory" ));
+ }
+ Log( "Thread ended, network buffer cannot be allocated" );
+ goto LBL_FatalError;
+ }
+
+ if ( info->manualHost[0] == 0 ) {
+ info->xmpp_client_query();
+ if ( info->s == NULL ) {
+ strncpy( info->manualHost, info->server, SIZEOF(info->manualHost));
+ info->s = WsConnect( info->manualHost, info->port );
+ }
+ }
+ else
+ info->s = WsConnect( info->manualHost, info->port );
+
+ Log( "Thread type=%d server='%s' port='%d'", info->type, info->manualHost, info->port );
+ if ( info->s == NULL ) {
+ Log( "Connection failed ( %d )", WSAGetLastError());
+ if ( info->type == JABBER_SESSION_NORMAL ) {
+ if ( m_ThreadInfo == info ) {
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK );
+ } }
+ else if ( info->type == JABBER_SESSION_REGISTER )
+ SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Cannot connect to the server" ));
+
+ Log( "Thread ended, connection failed" );
+ mir_free( buffer );
+ goto LBL_FatalError;
+ }
+
+ // Determine local IP
+ if ( info->useSSL ) {
+ Log( "Intializing SSL connection" );
+ if (!CallService( MS_NETLIB_STARTSSL, ( WPARAM )info->s, 0)) {
+ Log( "SSL intialization failed" );
+ if ( info->type == JABBER_SESSION_NORMAL ) {
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK );
+ }
+ else if ( info->type == JABBER_SESSION_REGISTER ) {
+ SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Cannot connect to the server" ));
+ }
+ mir_free( buffer );
+ info->close();
+ Log( "Thread ended, SSL connection failed" );
+ goto LBL_FatalError;
+ } }
+
+ // User may change status to OFFLINE while we are connecting above
+ if ( m_iDesiredStatus != ID_STATUS_OFFLINE || info->type == JABBER_SESSION_REGISTER ) {
+
+ if ( info->type == JABBER_SESSION_NORMAL ) {
+ m_bJabberConnected = TRUE;
+ size_t len = _tcslen( info->username ) + strlen( info->server )+1;
+ m_szJabberJID = ( TCHAR* )mir_alloc( sizeof( TCHAR)*( len+1 ));
+ mir_sntprintf( m_szJabberJID, len+1, _T("%s@") _T(TCHAR_STR_PARAM), info->username, info->server );
+ m_bSendKeepAlive = m_options.KeepAlive != 0;
+ JSetStringT(NULL, "jid", m_szJabberJID); // store jid in database
+ }
+
+ xmlStreamInitializeNow( info );
+ const TCHAR* tag = _T("stream:stream");
+
+ Log( "Entering main recv loop" );
+ datalen = 0;
+
+ // cache values
+ DWORD dwConnectionKeepAliveInterval = m_options.ConnectionKeepAliveInterval;
+ for ( ;; ) {
+ if ( !info->useZlib || info->zRecvReady ) {
+ DWORD dwIdle = GetTickCount() - m_lastTicks;
+ if ( dwIdle >= dwConnectionKeepAliveInterval )
+ dwIdle = dwConnectionKeepAliveInterval - 10; // now!
+
+ NETLIBSELECT nls = {0};
+ nls.cbSize = sizeof( NETLIBSELECT );
+ nls.dwTimeout = dwConnectionKeepAliveInterval - dwIdle;
+ nls.hReadConns[0] = info->s;
+ int nSelRes = CallService( MS_NETLIB_SELECT, 0, ( LPARAM )&nls );
+ if ( nSelRes == -1 ) // error
+ break;
+ else if ( nSelRes == 0 && m_bSendKeepAlive ) {
+ if ( m_ThreadInfo->jabberServerCaps & JABBER_CAPS_PING ) {
+ CJabberIqInfo* pInfo = m_iqManager.AddHandler( &CJabberProto::OnPingReply, JABBER_IQ_TYPE_GET, NULL, 0, -1, this );
+ pInfo->SetTimeout( m_options.ConnectionKeepAliveTimeout );
+ info->send( XmlNodeIq( pInfo ) << XATTR( _T("from"), m_ThreadInfo->fullJID ) << XCHILDNS( _T("ping"), _T(JABBER_FEAT_PING)));
+ }
+ else info->send( " \t " );
+ continue;
+ } }
+
+ int recvResult = info->recv( buffer + datalen, jabberNetworkBufferSize - datalen);
+ Log( "recvResult = %d", recvResult );
+ if ( recvResult <= 0 )
+ break;
+ datalen += recvResult;
+
+recvRest:
+ buffer[datalen] = '\0';
+
+ TCHAR* str;
+ str = mir_utf8decodeW( buffer );
+
+ int bytesParsed = 0;
+ XmlNode root( str, &bytesParsed, tag );
+ if ( root && tag )
+ {
+ char *p = strstr( buffer, "stream:stream" );
+ if ( p ) p = strchr( p, '>' );
+ if ( p )
+ bytesParsed = p - buffer + 1;
+ else {
+ root = XmlNode();
+ bytesParsed = 0;
+ }
+
+ mir_free(str);
+
+ }
+ else {
+ if ( root ) str[ bytesParsed ] = 0;
+ bytesParsed = ( root ) ? mir_utf8lenW( str ) : 0;
+ mir_free(str);
+ }
+
+ Log( "bytesParsed = %d", bytesParsed );
+ if ( root ) tag = NULL;
+
+ if ( xmlGetName( root ) == NULL ) {
+ for ( int i=0; ; i++ ) {
+ HXML n = xmlGetChild( root , i );
+ if ( !n )
+ break;
+ OnProcessProtocol( n, info );
+ }
+ }
+ else OnProcessProtocol( root, info );
+
+ if ( bytesParsed > 0 ) {
+ if ( bytesParsed < datalen )
+ memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
+ datalen -= bytesParsed;
+ }
+ else if ( datalen >= jabberNetworkBufferSize ) {
+ //jabberNetworkBufferSize += 65536;
+ jabberNetworkBufferSize *= 2;
+ Log( "Increasing network buffer size to %d", jabberNetworkBufferSize );
+ if (( buffer=( char* )mir_realloc( buffer, jabberNetworkBufferSize + 1 )) == NULL ) {
+ Log( "Cannot reallocate more network buffer, go offline now" );
+ break;
+ } }
+ else Log( "Unknown state: bytesParsed=%d, datalen=%d, jabberNetworkBufferSize=%d", bytesParsed, datalen, jabberNetworkBufferSize );
+
+ if ( m_szXmlStreamToBeInitialized ) {
+ xmlStreamInitializeNow( info );
+ tag = _T("stream:stream");
+ }
+ if ( root && datalen )
+ goto recvRest;
+ }
+
+ if ( info->type == JABBER_SESSION_NORMAL ) {
+ m_iqManager.ExpireAll( info );
+ m_bJabberOnline = FALSE;
+ m_bJabberConnected = FALSE;
+ info->zlibUninit();
+ EnableMenuItems( FALSE );
+ RebuildInfoFrame();
+ if ( m_hwndJabberChangePassword ) {
+ //DestroyWindow( hwndJabberChangePassword );
+ // Since this is a different thread, simulate the click on the cancel button instead
+ SendMessage( m_hwndJabberChangePassword, WM_COMMAND, MAKEWORD( IDCANCEL, 0 ), 0 );
+ }
+
+ if ( jabberChatDllPresent )
+ CallFunctionAsync( JabberOfflineChatWindows, this );
+
+ ListRemoveList( LIST_CHATROOM );
+ ListRemoveList( LIST_BOOKMARK );
+ //UI_SAFE_NOTIFY(m_pDlgJabberJoinGroupchat, WM_JABBER_CHECK_ONLINE);
+ UI_SAFE_NOTIFY_HWND(m_hwndJabberAddBookmark, WM_JABBER_CHECK_ONLINE);
+ //UI_SAFE_NOTIFY(m_pDlgBookmarks, WM_JABBER_CHECK_ONLINE);
+ WindowNotify(WM_JABBER_CHECK_ONLINE);
+
+ // Set status to offline
+ oldStatus = m_iStatus;
+ m_iDesiredStatus = m_iStatus = ID_STATUS_OFFLINE;
+ JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, m_iStatus );
+
+ // Set all contacts to offline
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL ) {
+ if ( !lstrcmpA(( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 ), m_szModuleName ))
+ {
+ SetContactOfflineStatus( hContact );
+ MenuHideSrmmIcon( hContact );
+ }
+
+ hContact = db_find_next(hContact);
+ }
+
+ mir_free( m_szJabberJID );
+ m_szJabberJID = NULL;
+ m_tmJabberLoggedInTime = 0;
+ ListWipe();
+
+ WindowNotify(WM_JABBER_REFRESH_VCARD);
+ }
+ else if ( info->type==JABBER_SESSION_REGISTER && !info->reg_done )
+ SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Connection lost" ));
+ }
+ else if ( info->type == JABBER_SESSION_NORMAL ) {
+ oldStatus = m_iStatus;
+ m_iDesiredStatus = m_iStatus = ID_STATUS_OFFLINE;
+ JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, m_iStatus );
+ }
+
+ Log( "Thread ended: type=%d server='%s'", info->type, info->server );
+
+ if ( info->type==JABBER_SESSION_NORMAL && m_ThreadInfo==info ) {
+ if ( m_szStreamId ) mir_free( m_szStreamId );
+ m_szStreamId = NULL;
+ m_ThreadInfo = NULL;
+ }
+
+ info->close();
+ mir_free( buffer );
+ Log( "Exiting ServerThread" );
+ goto LBL_Exit;
+}
+
+void CJabberProto::PerformRegistration( ThreadData* info )
+{
+ iqIdRegGetReg = SerialNext();
+ info->send( XmlNodeIq( _T("get"), iqIdRegGetReg, NULL) << XQUERY( _T(JABBER_FEAT_REGISTER )));
+
+ SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 50, ( LPARAM )TranslateT( "Requesting registration instruction..." ));
+}
+
+void CJabberProto::PerformIqAuth( ThreadData* info )
+{
+ if ( info->type == JABBER_SESSION_NORMAL ) {
+ int iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetAuth );
+ info->send( XmlNodeIq( _T("get"), iqId ) << XQUERY( _T("jabber:iq:auth" )) << XCHILD( _T("username"), info->username ));
+ }
+ else if ( info->type == JABBER_SESSION_REGISTER )
+ PerformRegistration( info );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CJabberProto::OnProcessStreamOpening( HXML node, ThreadData *info )
+{
+ if ( lstrcmp( xmlGetName( node ), _T("stream:stream")))
+ return;
+
+ if ( info->type == JABBER_SESSION_NORMAL ) {
+ const TCHAR* sid = xmlGetAttrValue( node, _T("id"));
+ if ( sid != NULL ) {
+ char* pszSid = mir_t2a( sid );
+ replaceStr( info->proto->m_szStreamId, pszSid );
+ mir_free( pszSid );
+ } }
+
+ // old server - disable SASL then
+ if ( xmlGetAttrValue( node, _T("version")) == NULL )
+ info->proto->m_options.Disable3920auth = TRUE;
+
+ if ( info->proto->m_options.Disable3920auth )
+ info->proto->PerformIqAuth( info );
+}
+
+void CJabberProto::PerformAuthentication( ThreadData* info )
+{
+ TJabberAuth* auth = NULL;
+ char* request = NULL;
+
+ if ( info->auth ) {
+ delete info->auth;
+ info->auth = NULL;
+ }
+
+ if ( m_AuthMechs.isSpnegoAvailable ) {
+ m_AuthMechs.isSpnegoAvailable = false;
+ auth = new TNtlmAuth( info, "GSS-SPNEGO" );
+ if ( !auth->isValid()) {
+ delete auth;
+ auth = NULL;
+ } }
+
+ if ( auth == NULL && m_AuthMechs.isNtlmAvailable ) {
+ m_AuthMechs.isNtlmAvailable = false;
+ auth = new TNtlmAuth( info, "NTLM" );
+ if ( !auth->isValid()) {
+ delete auth;
+ auth = NULL;
+ } }
+
+ if ( auth == NULL && m_AuthMechs.isKerberosAvailable ) {
+ m_AuthMechs.isKerberosAvailable = false;
+ auth = new TNtlmAuth( info, "GSSAPI", m_AuthMechs.m_gssapiHostName );
+ if ( !auth->isValid()) {
+ delete auth;
+ auth = NULL;
+ } else {
+ request = auth->getInitialRequest();
+ if ( !request ) {
+ delete auth;
+ auth = NULL;
+ } } }
+
+ if ( auth == NULL && m_AuthMechs.isScramAvailable ) {
+ m_AuthMechs.isScramAvailable = false;
+ auth = new TScramAuth( info );
+ }
+
+ if ( auth == NULL && m_AuthMechs.isMd5Available ) {
+ m_AuthMechs.isMd5Available = false;
+ auth = new TMD5Auth( info );
+ }
+
+ if ( auth == NULL && m_AuthMechs.isPlainAvailable ) {
+ m_AuthMechs.isPlainAvailable = false;
+ auth = new TPlainAuth( info, false );
+ }
+
+ if ( auth == NULL && m_AuthMechs.isPlainOldAvailable ) {
+ m_AuthMechs.isPlainOldAvailable = false;
+ auth = new TPlainAuth( info, true );
+ }
+
+ if ( auth == NULL ) {
+ if ( m_AuthMechs.isAuthAvailable ) { // no known mechanisms but iq_auth is available
+ m_AuthMechs.isAuthAvailable = false;
+ PerformIqAuth( info );
+ return;
+ }
+
+ TCHAR text[1024];
+ mir_sntprintf( text, SIZEOF( text ), _T("%s %s@")_T(TCHAR_STR_PARAM)_T("."), TranslateT( "Authentication failed for" ), info->username, info->server );
+ MsgPopup( NULL, text, TranslateT( "Jabber Authentication" ));
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD );
+ info->send( "</stream:stream>" );
+ m_ThreadInfo = NULL;
+ return;
+ }
+
+ info->auth = auth;
+
+ if ( !request ) request = auth->getInitialRequest();
+ info->send( XmlNode( _T("auth"), _A2T(request)) << XATTR( _T("xmlns"), _T("urn:ietf:params:xml:ns:xmpp-sasl"))
+ << XATTR( _T("mechanism"), _A2T(auth->getName())));
+ mir_free( request );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CJabberProto::OnProcessFeatures( HXML node, ThreadData* info )
+{
+ bool isRegisterAvailable = false;
+ bool areMechanismsDefined = false;
+
+ for ( int i=0; ;i++ ) {
+ HXML n = xmlGetChild( node ,i);
+ if ( !n )
+ break;
+
+ if ( !_tcscmp( xmlGetName( n ), _T("starttls"))) {
+ if ( !info->useSSL && m_options.UseTLS ) {
+ Log( "Requesting TLS" );
+ info->send( XmlNode( xmlGetName( n )) << XATTR( _T("xmlns"), _T("urn:ietf:params:xml:ns:xmpp-tls" )));
+ return;
+ } }
+
+ if ( !_tcscmp( xmlGetName( n ), _T("compression")) && m_options.EnableZlib == TRUE ) {
+ Log("Server compression available");
+ for ( int k=0; ; k++ ) {
+ HXML c = xmlGetChild( n ,k);
+ if ( !c )
+ break;
+
+ if ( !_tcscmp( xmlGetName( c ), _T("method"))) {
+ if ( !_tcscmp( xmlGetText( c ), _T("zlib")) && info->zlibInit() == TRUE ) {
+ Log("Requesting Zlib compression");
+ info->send( XmlNode( _T("compress")) << XATTR( _T("xmlns"), _T("http://jabber.org/protocol/compress"))
+ << XCHILD( _T("method"), _T("zlib")));
+ return;
+ } } } }
+
+ if ( !_tcscmp( xmlGetName( n ), _T("mechanisms"))) {
+ m_AuthMechs.isPlainAvailable = false;
+ m_AuthMechs.isPlainOldAvailable = false;
+ m_AuthMechs.isMd5Available = false;
+ m_AuthMechs.isScramAvailable = false;
+ m_AuthMechs.isNtlmAvailable = false;
+ m_AuthMechs.isSpnegoAvailable = false;
+ m_AuthMechs.isKerberosAvailable = false;
+ mir_free( m_AuthMechs.m_gssapiHostName ); m_AuthMechs.m_gssapiHostName = NULL;
+
+ areMechanismsDefined = true;
+ //JabberLog("%d mechanisms\n",n->numChild);
+ for ( int k=0; ; k++ ) {
+ HXML c = xmlGetChild( n ,k);
+ if ( !c )
+ break;
+
+ if ( !_tcscmp( xmlGetName( c ), _T("mechanism"))) {
+ //JabberLog("Mechanism: %s",xmlGetText( c ));
+ if ( !_tcscmp( xmlGetText( c ), _T("PLAIN"))) m_AuthMechs.isPlainOldAvailable = m_AuthMechs.isPlainAvailable = true;
+ else if ( !_tcscmp( xmlGetText( c ), _T("DIGEST-MD5"))) m_AuthMechs.isMd5Available = true;
+ else if ( !_tcscmp( xmlGetText( c ), _T("SCRAM-SHA-1"))) m_AuthMechs.isScramAvailable = true;
+ else if ( !_tcscmp( xmlGetText( c ), _T("NTLM"))) m_AuthMechs.isNtlmAvailable = true;
+ else if ( !_tcscmp( xmlGetText( c ), _T("GSS-SPNEGO"))) m_AuthMechs.isSpnegoAvailable = true;
+ else if ( !_tcscmp( xmlGetText( c ), _T("GSSAPI"))) m_AuthMechs.isKerberosAvailable = true;
+ }
+ else if ( !_tcscmp( xmlGetName( c ), _T("hostname"))) {
+ const TCHAR *mech = xmlGetAttrValue( c, _T("mechanism"));
+ if ( mech && _tcsicmp( mech, _T("GSSAPI")) == 0 ) {
+ m_AuthMechs.m_gssapiHostName = mir_tstrdup( xmlGetText( c ));
+ }
+ }
+ } }
+ else if ( !_tcscmp( xmlGetName( n ), _T("register" ))) isRegisterAvailable = true;
+ else if ( !_tcscmp( xmlGetName( n ), _T("auth" ))) m_AuthMechs.isAuthAvailable = true;
+ else if ( !_tcscmp( xmlGetName( n ), _T("session" ))) m_AuthMechs.isSessionAvailable = true;
+ }
+
+ if ( areMechanismsDefined ) {
+ if ( info->type == JABBER_SESSION_NORMAL )
+ PerformAuthentication( info );
+ else if ( info->type == JABBER_SESSION_REGISTER )
+ PerformRegistration( info );
+ else
+ info->send( "</stream:stream>" );
+ return;
+ }
+
+ // mechanisms are not defined.
+ if ( info->auth ) { //We are already logged-in
+ info->send(
+ XmlNodeIq( m_iqManager.AddHandler( &CJabberProto::OnIqResultBind, JABBER_IQ_TYPE_SET ))
+ << XCHILDNS( _T("bind"), _T("urn:ietf:params:xml:ns:xmpp-bind" ))
+ << XCHILD( _T("resource"), info->resource ));
+
+ if ( m_AuthMechs.isSessionAvailable )
+ info->bIsSessionAvailable = TRUE;
+
+ return;
+ }
+
+ //mechanisms not available and we are not logged in
+ PerformIqAuth( info );
+}
+
+void CJabberProto::OnProcessFailure( HXML node, ThreadData* info )
+{
+ const TCHAR* type;
+//failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"
+ if (( type = xmlGetAttrValue( node, _T("xmlns"))) == NULL ) return;
+ if ( !_tcscmp( type, _T("urn:ietf:params:xml:ns:xmpp-sasl"))) {
+ PerformAuthentication( info );
+} }
+
+void CJabberProto::OnProcessError( HXML node, ThreadData* info )
+{
+ TCHAR *buff;
+ int i;
+ int pos;
+ bool skipMsg = false;
+
+ //failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"
+ if ( !xmlGetChild( node ,0))
+ return;
+
+ buff = (TCHAR *)mir_alloc(1024*sizeof(TCHAR));
+ pos=0;
+ for ( i=0; ; i++ ) {
+ HXML n = xmlGetChild( node , i );
+ if ( !n )
+ break;
+
+ const TCHAR *name = xmlGetName( n );
+ const TCHAR *desc = xmlGetText( n );
+ if ( desc )
+ pos += mir_sntprintf( buff+pos, 1024-pos, _T("%s: %s\r\n"), name, desc );
+ else
+ pos += mir_sntprintf( buff+pos, 1024-pos, _T("%s\r\n"), name );
+
+ if ( !_tcscmp( name, _T("conflict")))
+ JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_OTHERLOCATION);
+ else if ( !_tcscmp( name, _T("see-other-host"))) {
+ skipMsg = true;
+ }
+ }
+ if (!skipMsg) MsgPopup( NULL, buff, TranslateT( "Jabber Error" ));
+ mir_free(buff);
+ info->send( "</stream:stream>" );
+}
+
+void CJabberProto::OnProcessSuccess( HXML node, ThreadData* info )
+{
+ const TCHAR* type;
+// int iqId;
+ // RECVED: <success ...
+ // ACTION: if successfully logged in, continue by requesting roster list and set my initial status
+ if (( type = xmlGetAttrValue( node, _T("xmlns"))) == NULL )
+ return;
+
+ if ( !_tcscmp( type, _T("urn:ietf:params:xml:ns:xmpp-sasl"))) {
+ DBVARIANT dbv;
+
+ if ( !info->auth->validateLogin( xmlGetText( node ))) {
+ info->send( "</stream:stream>" );
+ return;
+ }
+
+ Log( "Success: Logged-in." );
+ if ( DBGetContactSettingString( NULL, m_szModuleName, "Nick", &dbv ))
+ JSetStringT( NULL, "Nick", info->username );
+ else
+ JFreeVariant( &dbv );
+ xmlStreamInitialize( "after successful sasl" );
+ }
+ else Log( "Success: unknown action "TCHAR_STR_PARAM".",type);
+}
+
+void CJabberProto::OnProcessChallenge( HXML node, ThreadData* info )
+{
+ if ( info->auth == NULL ) {
+ Log( "No previous auth have been made, exiting..." );
+ return;
+ }
+
+ if ( lstrcmp( xmlGetAttrValue( node, _T("xmlns")), _T("urn:ietf:params:xml:ns:xmpp-sasl")))
+ return;
+
+ char* challenge = info->auth->getChallenge( xmlGetText( node ));
+ info->send( XmlNode( _T("response"), _A2T(challenge)) << XATTR( _T("xmlns"), _T("urn:ietf:params:xml:ns:xmpp-sasl")));
+ mir_free( challenge );
+}
+
+void CJabberProto::OnProcessProtocol( HXML node, ThreadData* info )
+{
+ OnConsoleProcessXml(node, JCPF_IN);
+
+ if ( !lstrcmp( xmlGetName( node ), _T("proceed")))
+ OnProcessProceed( node, info );
+ else if ( !lstrcmp( xmlGetName( node ), _T("compressed")))
+ OnProcessCompressed( node, info );
+ else if ( !lstrcmp( xmlGetName( node ), _T("stream:features")))
+ OnProcessFeatures( node, info );
+ else if ( !lstrcmp( xmlGetName( node ), _T("stream:stream")))
+ OnProcessStreamOpening( node, info );
+ else if ( !lstrcmp( xmlGetName( node ), _T("success")))
+ OnProcessSuccess( node, info );
+ else if ( !lstrcmp( xmlGetName( node ), _T("failure")))
+ OnProcessFailure( node, info );
+ else if ( !lstrcmp( xmlGetName( node ), _T("stream:error")))
+ OnProcessError( node, info );
+ else if ( !lstrcmp( xmlGetName( node ), _T("challenge")))
+ OnProcessChallenge( node, info );
+ else if ( info->type == JABBER_SESSION_NORMAL ) {
+ if ( !lstrcmp( xmlGetName( node ), _T("message")))
+ OnProcessMessage( node, info );
+ else if ( !lstrcmp( xmlGetName( node ), _T("presence")))
+ OnProcessPresence( node, info );
+ else if ( !lstrcmp( xmlGetName( node ), _T("iq")))
+ OnProcessIq( node );
+ else
+ Log( "Invalid top-level tag ( only <message/> <presence/> and <iq/> allowed )" );
+ }
+ else if ( info->type == JABBER_SESSION_REGISTER ) {
+ if ( !lstrcmp( xmlGetName( node ), _T("iq")))
+ OnProcessRegIq( node, info );
+ else
+ Log( "Invalid top-level tag ( only <iq/> allowed )" );
+} }
+
+void CJabberProto::OnProcessProceed( HXML node, ThreadData* info )
+{
+ const TCHAR* type;
+ if (( type = xmlGetAttrValue( node, _T("xmlns"))) != NULL && !lstrcmp( type, _T("error")))
+ return;
+
+ if ( !lstrcmp( type, _T("urn:ietf:params:xml:ns:xmpp-tls" ))) {
+ Log("Starting TLS...");
+
+ char* gtlk = strstr(info->manualHost, "google.com");
+ bool isHosted = gtlk && !gtlk[10] && stricmp(info->server, "gmail.com") &&
+ stricmp(info->server, "googlemail.com");
+
+ NETLIBSSL ssl = {0};
+ ssl.cbSize = sizeof(ssl);
+ ssl.host = isHosted ? info->manualHost : info->server;
+ if (!CallService( MS_NETLIB_STARTSSL, ( WPARAM )info->s, ( LPARAM )&ssl)) {
+ Log( "SSL initialization failed" );
+ info->send( "</stream:stream>" );
+ info->shutdown();
+ }
+ else
+ xmlStreamInitialize( "after successful StartTLS" );
+} }
+
+void CJabberProto::OnProcessCompressed( HXML node, ThreadData* info )
+{
+ const TCHAR* type;
+
+ Log( "Compression confirmed" );
+
+ if (( type = xmlGetAttrValue( node, _T("xmlns"))) != NULL && !lstrcmp( type, _T( "error" )))
+ return;
+ if ( lstrcmp( type, _T( "http://jabber.org/protocol/compress" )))
+ return;
+
+ Log( "Starting Zlib stream compression..." );
+
+ info->useZlib = TRUE;
+ info->zRecvData = ( char* )mir_alloc( ZLIB_CHUNK_SIZE );
+
+ xmlStreamInitialize( "after successful Zlib init" );
+}
+
+void CJabberProto::OnProcessPubsubEvent( HXML node )
+{
+ const TCHAR* from = xmlGetAttrValue( node, _T("from"));
+ if ( !from )
+ return;
+
+ HXML eventNode = xmlGetChildByTag( node, "event", "xmlns", _T(JABBER_FEAT_PUBSUB_EVENT));
+ if ( !eventNode )
+ return;
+
+ m_pepServices.ProcessEvent(from, eventNode);
+
+ HANDLE hContact = HContactFromJID( from );
+ if ( !hContact )
+ return;
+
+ HXML itemsNode;
+ if ( m_options.EnableUserTune && (itemsNode = xmlGetChildByTag( eventNode, "items", "node", _T(JABBER_FEAT_USER_TUNE)))) {
+ // node retract?
+ if ( xmlGetChild( itemsNode , "retract" )) {
+ SetContactTune( hContact, NULL, NULL, NULL, NULL, NULL );
+ return;
+ }
+
+ HXML tuneNode = XPath( itemsNode, _T("item/tune[@xmlns='") _T(JABBER_FEAT_USER_TUNE) _T("']"));
+ if ( !tuneNode )
+ return;
+
+ const TCHAR *szArtist = XPathT( tuneNode, "artist" );
+ const TCHAR *szLength = XPathT( tuneNode, "length" );
+ const TCHAR *szSource = XPathT( tuneNode, "source" );
+ const TCHAR *szTitle = XPathT( tuneNode, "title" );
+ const TCHAR *szTrack = XPathT( tuneNode, "track" );
+
+ TCHAR szLengthInTime[32];
+ szLengthInTime[0] = _T('\0');
+ if ( szLength ) {
+ int nLength = _ttoi( szLength );
+ mir_sntprintf( szLengthInTime, SIZEOF( szLengthInTime ), _T("%02d:%02d:%02d"),
+ nLength / 3600, (nLength / 60) % 60, nLength % 60 );
+ }
+
+ SetContactTune( hContact, szArtist, szLength ? szLengthInTime : NULL, szSource, szTitle, szTrack );
+ }
+}
+
+// returns 0, if error or no events
+DWORD JabberGetLastContactMessageTime( HANDLE hContact )
+{
+ // TODO: time cache can improve performance
+ HANDLE hDbEvent = (HANDLE)CallService( MS_DB_EVENT_FINDLAST, (WPARAM)hContact, 0 );
+ if ( !hDbEvent )
+ return 0;
+
+ DWORD dwTime = 0;
+
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService( MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0 );
+ if ( dbei.cbBlob != -1 ) {
+ dbei.pBlob = (PBYTE)mir_alloc( dbei.cbBlob + 1 );
+ int nGetTextResult = CallService( MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei );
+ if ( !nGetTextResult )
+ dwTime = dbei.timestamp;
+ mir_free( dbei.pBlob );
+ }
+ return dwTime;
+}
+
+HANDLE CJabberProto::CreateTemporaryContact( const TCHAR *szJid, JABBER_LIST_ITEM* chatItem )
+{
+ HANDLE hContact = NULL;
+ if ( chatItem ) {
+ const TCHAR* p = _tcschr( szJid, '/' );
+ if ( p != NULL && p[1] != '\0' )
+ p++;
+ else
+ p = szJid;
+ hContact = DBCreateContact( szJid, p, TRUE, FALSE );
+
+ for ( int i=0; i < chatItem->resourceCount; i++ ) {
+ if ( !lstrcmp( chatItem->resource[i].resourceName, p )) {
+ JSetWord( hContact, "Status", chatItem->resource[i].status );
+ break;
+ }
+ }
+ }
+ else {
+ TCHAR *nick = JabberNickFromJID( szJid );
+ hContact = DBCreateContact( szJid, nick, TRUE, TRUE );
+ mir_free( nick );
+ }
+ return hContact;
+}
+
+void CJabberProto::OnProcessMessage( HXML node, ThreadData* info )
+{
+ HXML subjectNode, xNode, inviteNode, idNode, n;
+ LPCTSTR from, type, idStr, fromResource;
+ HANDLE hContact;
+
+ if ( !xmlGetName( node ) || _tcscmp( xmlGetName( node ), _T("message")))
+ return;
+
+ type = xmlGetAttrValue( node, _T("type"));
+ if (( from = xmlGetAttrValue( node, _T("from"))) == NULL )
+ return;
+
+ idStr = xmlGetAttrValue( node, _T("id"));
+ JABBER_RESOURCE_STATUS *resourceStatus = ResourceInfoFromJID( from );
+
+ // Message receipts delivery request. Reply here, before a call to HandleMessagePermanent() to make sure message receipts are handled for external plugins too.
+ if ( ( !type || _tcsicmp( type, _T("error"))) && xmlGetChildByTag( node, "request", "xmlns", _T( JABBER_FEAT_MESSAGE_RECEIPTS ))) {
+ info->send(
+ XmlNode( _T("message")) << XATTR( _T("to"), from ) << XATTR( _T("id"), idStr )
+ << XCHILDNS( _T("received"), _T(JABBER_FEAT_MESSAGE_RECEIPTS)) << XATTR( _T("id"), idStr ));
+
+ if ( resourceStatus )
+ resourceStatus->jcbManualDiscoveredCaps |= JABBER_CAPS_MESSAGE_RECEIPTS;
+ }
+
+ if ( m_messageManager.HandleMessagePermanent( node, info ))
+ return;
+
+ hContact = HContactFromJID( from );
+ JABBER_LIST_ITEM *chatItem = ListGetItemPtr( LIST_CHATROOM, from );
+ if ( chatItem ) {
+ HXML xCaptcha = xmlGetChild( node, "captcha" );
+ if ( xCaptcha )
+ if ( ProcessCaptcha( xCaptcha, node, info ))
+ return;
+ }
+
+ const TCHAR* szMessage = NULL;
+ HXML bodyNode = xmlGetChildByTag( node , "body", "xml:lang", m_tszSelectedLang );
+ if ( bodyNode == NULL )
+ bodyNode = xmlGetChild( node , "body" );
+ if ( bodyNode != NULL && xmlGetText( bodyNode ))
+ szMessage = xmlGetText( bodyNode );
+ if (( subjectNode = xmlGetChild( node , "subject" )) && xmlGetText( subjectNode ) && xmlGetText( subjectNode )[0] != _T('\0')) {
+ size_t cbLen = (szMessage ? _tcslen( szMessage ) : 0) + _tcslen( xmlGetText( subjectNode )) + 128;
+ TCHAR* szTmp = ( TCHAR * )alloca( sizeof(TCHAR) * cbLen );
+ szTmp[0] = _T('\0');
+ if ( szMessage )
+ _tcscat( szTmp, _T("Subject: "));
+ _tcscat( szTmp, xmlGetText( subjectNode ));
+ if ( szMessage ) {
+ _tcscat( szTmp, _T("\r\n"));
+ _tcscat( szTmp, szMessage );
+ }
+ szMessage = szTmp;
+ }
+
+ if ( szMessage && (n = xmlGetChildByTag( node, "addresses", "xmlns", _T(JABBER_FEAT_EXT_ADDRESSING)))) {
+ HXML addressNode = xmlGetChildByTag( n, "address", "type", _T("ofrom"));
+ if ( addressNode ) {
+ const TCHAR* szJid = xmlGetAttrValue( addressNode, _T("jid"));
+ if ( szJid ) {
+ size_t cbLen = _tcslen( szMessage ) + 1000;
+ TCHAR* p = ( TCHAR* )alloca( sizeof( TCHAR ) * cbLen );
+ mir_sntprintf( p, cbLen, TranslateT("Message redirected from: %s\r\n%s"), from, szMessage );
+ szMessage = p;
+ from = szJid;
+ // rewrite hContact
+ hContact = HContactFromJID( from );
+ }
+ }
+ }
+
+ // If message is from a stranger ( not in roster ), item is NULL
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_ROSTER, from );
+ if ( !item )
+ item = ListGetItemPtr( LIST_VCARD_TEMP, from );
+
+ time_t msgTime = 0;
+ BOOL isChatRoomInvitation = FALSE;
+ const TCHAR* inviteRoomJid = NULL;
+ const TCHAR* inviteFromJid = NULL;
+ const TCHAR* inviteReason = NULL;
+ const TCHAR* invitePassword = NULL;
+ BOOL delivered = FALSE;
+
+ // check chatstates availability
+ if ( resourceStatus && xmlGetChildByTag( node, "active", "xmlns", _T( JABBER_FEAT_CHATSTATES )))
+ resourceStatus->jcbManualDiscoveredCaps |= JABBER_CAPS_CHATSTATES;
+
+ // chatstates composing event
+ if ( hContact && xmlGetChildByTag( node, "composing", "xmlns", _T( JABBER_FEAT_CHATSTATES )))
+ CallService( MS_PROTO_CONTACTISTYPING, ( WPARAM )hContact, 60 );
+
+ // chatstates paused event
+ if ( hContact && xmlGetChildByTag( node, "paused", "xmlns", _T( JABBER_FEAT_CHATSTATES )))
+ CallService( MS_PROTO_CONTACTISTYPING, ( WPARAM )hContact, PROTOTYPE_CONTACTTYPING_OFF );
+
+ // chatstates inactive event
+ if ( hContact && xmlGetChildByTag( node, "inactive", "xmlns", _T( JABBER_FEAT_CHATSTATES )))
+ CallService( MS_PROTO_CONTACTISTYPING, ( WPARAM )hContact, PROTOTYPE_CONTACTTYPING_OFF );
+
+ // message receipts delivery notification
+ if ( n = xmlGetChildByTag( node, "received", "xmlns", _T( JABBER_FEAT_MESSAGE_RECEIPTS ))) {
+ int nPacketId = JabberGetPacketID( n );
+ if ( nPacketId == -1 )
+ nPacketId = JabberGetPacketID( node );
+ if ( nPacketId != -1)
+ JSendBroadcast( hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, ( HANDLE ) nPacketId, 0 );
+ }
+
+ // XEP-0203 delay support
+ if ( n = xmlGetChildByTag( node, "delay", "xmlns", _T("urn:xmpp:delay")) ) {
+ const TCHAR* ptszTimeStamp = xmlGetAttrValue( n, _T("stamp"));
+ if ( ptszTimeStamp != NULL ) {
+ // skip '-' chars
+ TCHAR* szStamp = mir_tstrdup( ptszTimeStamp );
+ int si = 0, sj = 0;
+ while (1) {
+ if ( szStamp[si] == _T('-'))
+ si++;
+ else
+ if ( !( szStamp[sj++] = szStamp[si++] ))
+ break;
+ };
+ msgTime = JabberIsoToUnixTime( szStamp );
+ mir_free( szStamp );
+ }
+ }
+
+ // XEP-0224 support (Attention/Nudge)
+ if ( xmlGetChildByTag( node, "attention", "xmlns", _T( JABBER_FEAT_ATTENTION )) ||
+ xmlGetChildByTag( node, "attention", "xmlns", _T( JABBER_FEAT_ATTENTION_0 ))) {
+ if ( !hContact )
+ hContact = CreateTemporaryContact( from, chatItem );
+ if ( hContact )
+ NotifyEventHooks( m_hEventNudge, (WPARAM)hContact, 0 );
+ }
+
+ // chatstates gone event
+ if ( hContact && xmlGetChildByTag( node, "gone", "xmlns", _T( JABBER_FEAT_CHATSTATES )) && m_options.LogChatstates ) {
+ DBEVENTINFO dbei;
+ BYTE bEventType = JABBER_DB_EVENT_CHATSTATES_GONE; // gone event
+ dbei.cbSize = sizeof(dbei);
+ dbei.pBlob = &bEventType;
+ dbei.cbBlob = 1;
+ dbei.eventType = JABBER_DB_EVENT_TYPE_CHATSTATES;
+ dbei.flags = DBEF_READ;
+ dbei.timestamp = time(NULL);
+ dbei.szModule = m_szModuleName;
+ CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei);
+ }
+
+ if (( n = xmlGetChildByTag( node, "confirm", "xmlns", _T( JABBER_FEAT_HTTP_AUTH ))) && m_options.AcceptHttpAuth ) {
+ const TCHAR *szId = xmlGetAttrValue( n, _T("id"));
+ const TCHAR *szMethod = xmlGetAttrValue( n, _T("method"));
+ const TCHAR *szUrl = xmlGetAttrValue( n, _T("url"));
+ if ( !szId || !szMethod || !szUrl )
+ return;
+
+ CJabberHttpAuthParams *pParams = (CJabberHttpAuthParams *)mir_alloc( sizeof( CJabberHttpAuthParams ));
+ if ( !pParams )
+ return;
+
+ ZeroMemory( pParams, sizeof( CJabberHttpAuthParams ));
+ pParams->m_nType = CJabberHttpAuthParams::MSG;
+ pParams->m_szFrom = mir_tstrdup( from );
+ HXML pThreadNode = xmlGetChild( node , "thread" );
+ if ( pThreadNode && xmlGetText( pThreadNode ) && xmlGetText( pThreadNode )[0] )
+ pParams->m_szThreadId = mir_tstrdup( xmlGetText( pThreadNode ));
+ pParams->m_szId = mir_tstrdup( szId );
+ pParams->m_szMethod = mir_tstrdup( szMethod );
+ pParams->m_szUrl = mir_tstrdup( szUrl );
+
+ AddClistHttpAuthEvent( pParams );
+ return;
+ }
+
+ for ( int i = 0; ( xNode = xmlGetChild( node, i )) != NULL; i++ ) {
+ xNode = xmlGetNthChild( node, _T("x"), i + 1 );
+ if ( xNode == NULL ) {
+ xNode = xmlGetNthChild( node, _T("user:x"), i + 1 );
+ if ( xNode == NULL )
+ continue;
+ }
+
+ const TCHAR* ptszXmlns = xmlGetAttrValue( xNode, _T("xmlns"));
+ if ( ptszXmlns == NULL )
+ ptszXmlns = xmlGetAttrValue( xNode, _T("xmlns:user"));
+ if ( ptszXmlns == NULL )
+ continue;
+
+ if ( !_tcscmp( ptszXmlns, _T(JABBER_FEAT_MIRANDA_NOTES))) {
+ if (OnIncomingNote(from, xmlGetChild(xNode, "note")))
+ return;
+ }
+ else if ( !_tcscmp( ptszXmlns, _T("jabber:x:encrypted" ))) {
+ if ( xmlGetText( xNode ) == NULL )
+ return;
+
+ TCHAR* prolog = _T("-----BEGIN PGP MESSAGE-----\r\n\r\n");
+ TCHAR* epilog = _T("\r\n-----END PGP MESSAGE-----\r\n");
+ TCHAR* tempstring = ( TCHAR* )alloca( sizeof( TCHAR ) * ( _tcslen( prolog ) + _tcslen( xmlGetText( xNode )) + _tcslen( epilog ) + 3 ));
+ _tcsncpy( tempstring, prolog, _tcslen( prolog ) + 1 );
+ _tcsncpy( tempstring + _tcslen( prolog ), xmlGetText( xNode ), _tcslen( xmlGetText( xNode )) + 1);
+ _tcsncpy( tempstring + _tcslen( prolog ) + _tcslen(xmlGetText( xNode )), epilog, _tcslen( epilog ) + 1);
+ szMessage = tempstring;
+ }
+ else if ( !_tcscmp( ptszXmlns, _T(JABBER_FEAT_DELAY)) && msgTime == 0 ) {
+ const TCHAR* ptszTimeStamp = xmlGetAttrValue( xNode, _T("stamp"));
+ if ( ptszTimeStamp != NULL )
+ msgTime = JabberIsoToUnixTime( ptszTimeStamp );
+ }
+ else if ( !_tcscmp( ptszXmlns, _T(JABBER_FEAT_MESSAGE_EVENTS))) {
+
+ // set events support only if we discovered caps and if events not already set
+ JabberCapsBits jcbCaps = GetResourceCapabilites( from, TRUE );
+ if ( jcbCaps & JABBER_RESOURCE_CAPS_ERROR )
+ jcbCaps = JABBER_RESOURCE_CAPS_NONE;
+ // FIXME: disabled due to expired XEP-0022 and problems with bombus delivery checks
+// if ( jcbCaps && resourceStatus && (!(jcbCaps & JABBER_CAPS_MESSAGE_EVENTS)))
+// resourceStatus->jcbManualDiscoveredCaps |= (JABBER_CAPS_MESSAGE_EVENTS | JABBER_CAPS_MESSAGE_EVENTS_NO_DELIVERY);
+
+ if ( bodyNode == NULL ) {
+ idNode = xmlGetChild( xNode , "id" );
+ if ( xmlGetChild( xNode , "delivered" ) != NULL || xmlGetChild( xNode , "offline" ) != NULL ) {
+ int id = -1;
+ if ( idNode != NULL && xmlGetText( idNode ) != NULL )
+ if ( !_tcsncmp( xmlGetText( idNode ), _T(JABBER_IQID), strlen( JABBER_IQID )))
+ id = _ttoi(( xmlGetText( idNode ))+strlen( JABBER_IQID ));
+
+ if ( id != -1 )
+ JSendBroadcast( hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 );
+ }
+
+ if ( hContact && xmlGetChild( xNode , "composing" ) != NULL )
+ CallService( MS_PROTO_CONTACTISTYPING, ( WPARAM ) hContact, 60 );
+
+ // Maybe a cancel to the previous composing
+ HXML child = xmlGetChild( xNode ,0);
+ if ( hContact && ( !child || ( child && idNode != NULL )))
+ CallService( MS_PROTO_CONTACTISTYPING, ( WPARAM ) hContact, PROTOTYPE_CONTACTTYPING_OFF );
+ }
+ else {
+ // Check whether any event is requested
+ if ( !delivered && ( n = xmlGetChild( xNode , "delivered" )) != NULL ) {
+ delivered = TRUE;
+
+ XmlNode m( _T("message" )); m << XATTR( _T("to"), from );
+ HXML x = m << XCHILDNS( _T("x"), _T(JABBER_FEAT_MESSAGE_EVENTS));
+ x << XCHILD( _T("delivered"));
+ x << XCHILD( _T("id"), idStr );
+ info->send( m );
+ }
+ if ( item != NULL && xmlGetChild( xNode , "composing" ) != NULL ) {
+ if ( item->messageEventIdStr )
+ mir_free( item->messageEventIdStr );
+ item->messageEventIdStr = ( idStr==NULL )?NULL:mir_tstrdup( idStr );
+ } }
+ }
+ else if ( !_tcscmp( ptszXmlns, _T(JABBER_FEAT_OOB2))) {
+ HXML urlNode;
+ if ( ((urlNode = xmlGetChild( xNode , "url" )) != NULL) && xmlGetText( urlNode ) && xmlGetText( urlNode )[0] != _T('\0')) {
+ size_t cbLen = (szMessage ? _tcslen( szMessage ) : 0) + _tcslen( xmlGetText( urlNode )) + 32;
+ TCHAR* szTmp = ( TCHAR * )alloca( sizeof(TCHAR) * cbLen );
+ _tcscpy( szTmp, xmlGetText( urlNode ));
+ if ( szMessage ) {
+ _tcscat( szTmp, _T("\r\n"));
+ _tcscat( szTmp, szMessage );
+ }
+ szMessage = szTmp;
+ }
+ }
+ else if ( !_tcscmp( ptszXmlns, _T(JABBER_FEAT_MUC_USER))) {
+ inviteNode = xmlGetChild( xNode , _T("invite"));
+ if ( inviteNode == NULL )
+ inviteNode = xmlGetChild( xNode , _T("user:invite"));
+
+ if ( inviteNode != NULL ) {
+ inviteFromJid = xmlGetAttrValue( inviteNode, _T("from"));
+ n = xmlGetChild( inviteNode , _T("reason"));
+ if ( n == NULL )
+ n = xmlGetChild( inviteNode , _T("user:reason"));
+ if ( n != NULL )
+ inviteReason = xmlGetText( n );
+ }
+ inviteRoomJid = from;
+ if ( !inviteReason )
+ inviteReason = szMessage;
+ isChatRoomInvitation = TRUE;
+ if (( n = xmlGetChild( xNode , "password" )) != NULL )
+ invitePassword = xmlGetText( n );
+ }
+ else if ( !_tcscmp( ptszXmlns, _T(JABBER_FEAT_ROSTER_EXCHANGE)) &&
+ item != NULL && (item->subscription == SUB_BOTH || item->subscription == SUB_TO)) {
+ TCHAR chkJID[JABBER_MAX_JID_LEN] = _T("@");
+ JabberStripJid( from, chkJID + 1, SIZEOF(chkJID) - 1 );
+ for ( int i = 1; ; ++i ) {
+ HXML iNode = xmlGetNthChild( xNode , _T("item"), i );
+ if ( iNode == NULL ) break;
+ const TCHAR *action = xmlGetAttrValue( iNode, _T("action"));
+ const TCHAR *jid = xmlGetAttrValue( iNode, _T("jid"));
+ const TCHAR *nick = xmlGetAttrValue( iNode, _T("name"));
+ const TCHAR *group = xmlGetText( xmlGetChild( iNode, _T("group")));
+ if ( action && jid && _tcsstr( jid, chkJID )) {
+ if ( !_tcscmp( action, _T("add"))) {
+ HANDLE hContact = DBCreateContact( jid, nick, FALSE, FALSE );
+ if ( group )
+ DBWriteContactSettingTString( hContact, "CList", "Group", group );
+ }
+ else if ( !_tcscmp( action, _T("modify"))) {
+// HANDLE hContact = HContactFromJID( jid );
+ }
+ else if ( !_tcscmp( action, _T("delete"))) {
+ HANDLE hContact = HContactFromJID( jid );
+ if ( hContact )
+ CallService( MS_DB_CONTACT_DELETE, ( WPARAM ) hContact, 0 );
+ }
+ }
+ }
+ }
+ else if ( !isChatRoomInvitation && !_tcscmp( ptszXmlns, _T("jabber:x:conference"))) {
+ inviteRoomJid = xmlGetAttrValue( xNode, _T("jid"));
+ inviteFromJid = from;
+ if ( inviteReason == NULL )
+ inviteReason = xmlGetText( xNode );
+ if ( !inviteReason )
+ inviteReason = szMessage;
+ isChatRoomInvitation = TRUE;
+ }
+ }
+
+ if ( isChatRoomInvitation ) {
+ if ( inviteRoomJid != NULL ) {
+ if ( m_options.IgnoreMUCInvites ) {
+ // FIXME: temporary disabled due to MUC inconsistence on server side
+ /*
+ XmlNode m( "message" ); xmlAddAttr( m, "to", from );
+ XmlNode xNode = xmlAddChild( m, "x" );
+ xmlAddAttr( xNode, "xmlns", JABBER_FEAT_MUC_USER );
+ XmlNode declineNode = xmlAddChild( xNode, "decline" );
+ xmlAddAttr( declineNode, "from", inviteRoomJid );
+ XmlNode reasonNode = xmlAddChild( declineNode, "reason", "The user has chosen to not accept chat invites" );
+ info->send( m );
+ */
+ }
+ else
+ GroupchatProcessInvite( inviteRoomJid, inviteFromJid, inviteReason, invitePassword );
+ }
+ return;
+ }
+
+ if ( szMessage ) {
+ if (( szMessage = JabberUnixToDosT( szMessage )) == NULL )
+ szMessage = mir_tstrdup( _T(""));
+
+ char* buf = mir_utf8encodeW( szMessage );
+
+ if ( item != NULL ) {
+ if ( resourceStatus ) resourceStatus->bMessageSessionActive = TRUE;
+ if ( hContact != NULL )
+ CallService( MS_PROTO_CONTACTISTYPING, ( WPARAM ) hContact, PROTOTYPE_CONTACTTYPING_OFF );
+
+ // no we will monitor last resource in all modes
+ if ( /*item->resourceMode==RSMODE_LASTSEEN &&*/ ( fromResource = _tcschr( from, '/' ))!=NULL ) {
+ fromResource++;
+ if ( *fromResource != '\0' ) {
+ for ( int i=0; i<item->resourceCount; i++ ) {
+ if ( !lstrcmp( item->resource[i].resourceName, fromResource )) {
+ int nLastSeenResource = item->lastSeenResource;
+ item->lastSeenResource = i;
+ if ((item->resourceMode==RSMODE_LASTSEEN) && (i != nLastSeenResource))
+ UpdateMirVer(item);
+ break;
+ } } } } }
+
+ // Create a temporary contact
+ if ( hContact == NULL )
+ hContact = CreateTemporaryContact( from, chatItem );
+
+ time_t now = time( NULL );
+ if ( !msgTime )
+ msgTime = now;
+
+ if ( m_options.FixIncorrectTimestamps && ( msgTime > now || ( msgTime < ( time_t )JabberGetLastContactMessageTime( hContact ))))
+ msgTime = now;
+
+ PROTORECVEVENT recv;
+ recv.flags = PREF_UTF;
+ recv.timestamp = ( DWORD )msgTime;
+ recv.szMessage = buf;
+
+ EnterCriticalSection( &m_csLastResourceMap );
+ recv.lParam = (LPARAM)AddToLastResourceMap( from );
+ LeaveCriticalSection( &m_csLastResourceMap );
+
+ CCSDATA ccs;
+ ccs.hContact = hContact;
+ ccs.wParam = 0;
+ ccs.szProtoService = PSR_MESSAGE;
+ ccs.lParam = ( LPARAM )&recv;
+ CallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs );
+
+ mir_free(( void* )szMessage );
+ mir_free( buf );
+ }
+}
+
+// XEP-0115: Entity Capabilities
+void CJabberProto::OnProcessPresenceCapabilites( HXML node )
+{
+ const TCHAR* from;
+ if (( from = xmlGetAttrValue( node, _T("from"))) == NULL )
+ return;
+
+ Log("presence: for jid " TCHAR_STR_PARAM, from);
+
+ JABBER_RESOURCE_STATUS *r = ResourceInfoFromJID( from );
+ if ( r == NULL ) return;
+
+ HXML n;
+
+ // check XEP-0115 support, and old style:
+ if (( n = xmlGetChildByTag( node, "c", "xmlns", _T(JABBER_FEAT_ENTITY_CAPS))) != NULL ||
+ ( n = xmlGetChildByTag( node, "caps:c", "xmlns:caps", _T(JABBER_FEAT_ENTITY_CAPS))) != NULL ||
+ ( n = xmlGetChild( node, "c" )) != NULL ) {
+ const TCHAR *szNode = xmlGetAttrValue( n, _T("node"));
+ const TCHAR *szVer = xmlGetAttrValue( n, _T("ver"));
+ const TCHAR *szExt = xmlGetAttrValue( n, _T("ext"));
+ if ( szNode && szVer ) {
+ replaceStrT( r->szCapsNode, szNode );
+ replaceStrT( r->szCapsVer, szVer );
+ replaceStrT( r->szCapsExt, szExt );
+ HANDLE hContact = HContactFromJID( from );
+ if ( hContact )
+ UpdateMirVer( hContact, r );
+ }
+ }
+
+ // update user's caps
+ // JabberCapsBits jcbCaps = GetResourceCapabilites( from, TRUE );
+}
+
+void CJabberProto::UpdateJidDbSettings( const TCHAR *jid )
+{
+ JABBER_LIST_ITEM *item = ListGetItemPtr( LIST_ROSTER, jid );
+ if ( !item ) return;
+ HANDLE hContact = HContactFromJID( jid );
+ if ( !hContact ) return;
+
+ int status = ID_STATUS_OFFLINE;
+ if ( !item->resourceCount ) {
+ // set offline only if jid has resources
+ if ( _tcschr( jid, '/' )==NULL )
+ status = item->itemResource.status;
+ if ( item->itemResource.statusMessage )
+ DBWriteContactSettingTString( hContact, "CList", "StatusMsg", item->itemResource.statusMessage );
+ else
+ DBDeleteContactSetting( hContact, "CList", "StatusMsg" );
+ }
+
+ // Determine status to show for the contact based on the remaining resources
+ int nSelectedResource = -1, i = 0;
+ int nMaxPriority = -999; // -128...+127 valid range
+ for ( i = 0; i < item->resourceCount; i++ )
+ {
+ if ( item->resource[i].priority > nMaxPriority ) {
+ nMaxPriority = item->resource[i].priority;
+ status = item->resource[i].status;
+ nSelectedResource = i;
+ }
+ else if ( item->resource[i].priority == nMaxPriority) {
+ if (( status = JabberCombineStatus( status, item->resource[i].status )) == item->resource[i].status )
+ nSelectedResource = i;
+ }
+ }
+ item->itemResource.status = status;
+ if ( nSelectedResource != -1 ) {
+ Log("JabberUpdateJidDbSettings: updating jid " TCHAR_STR_PARAM " to rc " TCHAR_STR_PARAM, item->jid, item->resource[nSelectedResource].resourceName );
+ if ( item->resource[nSelectedResource].statusMessage )
+ DBWriteContactSettingTString( hContact, "CList", "StatusMsg", item->resource[nSelectedResource].statusMessage );
+ else
+ DBDeleteContactSetting( hContact, "CList", "StatusMsg" );
+ UpdateMirVer( hContact, &item->resource[nSelectedResource] );
+ }
+ else {
+ JDeleteSetting( hContact, DBSETTING_DISPLAY_UID );
+ }
+
+ if ( _tcschr( jid, '@' )!=NULL || m_options.ShowTransport==TRUE )
+ if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != status )
+ JSetWord( hContact, "Status", ( WORD )status );
+
+ if (status == ID_STATUS_OFFLINE)
+ { // remove x-status icon
+ JDeleteSetting( hContact, DBSETTING_XSTATUSID );
+ JDeleteSetting( hContact, DBSETTING_XSTATUSNAME );
+ JDeleteSetting( hContact, DBSETTING_XSTATUSMSG );
+ //JabberUpdateContactExtraIcon(hContact);
+ }
+
+ MenuUpdateSrmmIcon( item );
+}
+
+void CJabberProto::OnProcessPresence( HXML node, ThreadData* info )
+{
+ HANDLE hContact;
+ HXML showNode, statusNode, priorityNode;
+ JABBER_LIST_ITEM *item;
+ LPCTSTR from, show, p;
+ TCHAR *nick;
+
+ if ( !node || !xmlGetName( node ) ||_tcscmp( xmlGetName( node ), _T("presence"))) return;
+ if (( from = xmlGetAttrValue( node, _T("from"))) == NULL ) return;
+
+ if ( m_presenceManager.HandlePresencePermanent( node, info ))
+ return;
+
+ if ( ListExist( LIST_CHATROOM, from )) {
+ GroupchatProcessPresence( node );
+ return;
+ }
+
+ BOOL bSelfPresence = FALSE;
+ TCHAR szBareFrom[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( from, szBareFrom, SIZEOF( szBareFrom ));
+ TCHAR szBareOurJid[ JABBER_MAX_JID_LEN ];
+ JabberStripJid( info->fullJID, szBareOurJid, SIZEOF( szBareOurJid ));
+
+ if ( !_tcsicmp( szBareFrom, szBareOurJid ))
+ bSelfPresence = TRUE;
+
+ LPCTSTR type = xmlGetAttrValue( node, _T("type"));
+ if ( type == NULL || !_tcscmp( type, _T("available"))) {
+ if (( nick = JabberNickFromJID( from )) == NULL )
+ return;
+
+ if (( hContact = HContactFromJID( from )) == NULL ) {
+ if ( !_tcsicmp( info->fullJID, from ) || ( !bSelfPresence && !ListExist( LIST_ROSTER, from ))) {
+ Log("SKIP Receive presence online from "TCHAR_STR_PARAM" ( who is not in my roster and not in list - skiping)", from );
+ mir_free( nick );
+ return;
+ }
+ hContact = DBCreateContact( from, nick, TRUE, TRUE );
+ }
+ if ( !ListExist( LIST_ROSTER, from )) {
+ Log("Receive presence online from "TCHAR_STR_PARAM" ( who is not in my roster )", from );
+ ListAdd( LIST_ROSTER, from );
+ }
+ DBCheckIsTransportedContact( from, hContact );
+ int status = ID_STATUS_ONLINE;
+ if (( showNode = xmlGetChild( node , "show" )) != NULL ) {
+ if (( show = xmlGetText( showNode )) != NULL ) {
+ if ( !_tcscmp( show, _T("away"))) status = ID_STATUS_AWAY;
+ else if ( !_tcscmp( show, _T("xa"))) status = ID_STATUS_NA;
+ else if ( !_tcscmp( show, _T("dnd"))) status = ID_STATUS_DND;
+ else if ( !_tcscmp( show, _T("chat"))) status = ID_STATUS_FREECHAT;
+ } }
+
+ char priority = 0;
+ if (( priorityNode = xmlGetChild( node , "priority" )) != NULL && xmlGetText( priorityNode ) != NULL )
+ priority = (char)_ttoi( xmlGetText( priorityNode ));
+
+ if (( statusNode = xmlGetChild( node , "status" )) != NULL && xmlGetText( statusNode ) != NULL )
+ p = xmlGetText( statusNode );
+ else
+ p = NULL;
+ ListAddResource( LIST_ROSTER, from, status, p, priority );
+
+ // XEP-0115: Entity Capabilities
+ OnProcessPresenceCapabilites( node );
+
+ UpdateJidDbSettings( from );
+
+ if ( _tcschr( from, '@' )==NULL ) {
+ UI_SAFE_NOTIFY(m_pDlgServiceDiscovery, WM_JABBER_TRANSPORT_REFRESH);
+ }
+ Log( TCHAR_STR_PARAM " ( " TCHAR_STR_PARAM " ) online, set contact status to %s", nick, from, CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION,(WPARAM)status,0 ));
+ mir_free( nick );
+
+ HXML xNode;
+ if ( m_options.EnableAvatars ) {
+ BOOL hasAvatar = false;
+ BOOL removedAvatar = false;
+
+ Log( "Avatar enabled" );
+ for ( int i = 1; ( xNode=xmlGetNthChild( node, _T("x"), i )) != NULL; i++ ) {
+ if ( !lstrcmp( xmlGetAttrValue( xNode, _T("xmlns")), _T("jabber:x:avatar"))) {
+ if (( xNode = xmlGetChild( xNode , "hash" )) != NULL && xmlGetText( xNode ) != NULL ) {
+ JDeleteSetting(hContact,"AvatarXVcard");
+ Log( "AvatarXVcard deleted" );
+ JSetStringT( hContact, "AvatarHash", xmlGetText( xNode ));
+ hasAvatar = true;
+ DBVARIANT dbv;
+ int result = JGetStringT( hContact, "AvatarSaved", &dbv );
+ if ( result || lstrcmp( dbv.ptszVal, xmlGetText( xNode ))) {
+ Log( "Avatar was changed" );
+ JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, NULL );
+ } else Log( "Not broadcasting avatar changed" );
+ if ( !result ) JFreeVariant( &dbv );
+ } else {
+ removedAvatar = true;
+ }
+ } }
+ if ( !hasAvatar ) { //no jabber:x:avatar. try vcard-temp:x:update
+ Log( "Not hasXAvatar" );
+ for ( int i = 1; ( xNode=xmlGetNthChild( node, _T("x"), i )) != NULL; i++ ) {
+ if ( !lstrcmp( xmlGetAttrValue( xNode, _T("xmlns")), _T("vcard-temp:x:update"))) {
+ if (( xNode = xmlGetChild( xNode , "photo" )) != NULL ) {
+ LPCTSTR txt = xmlGetText( xNode );
+ if ( txt != NULL && txt[0] != 0) {
+ JSetByte( hContact, "AvatarXVcard", 1 );
+ Log( "AvatarXVcard set" );
+ JSetStringT( hContact, "AvatarHash", txt );
+ hasAvatar = true;
+ DBVARIANT dbv;
+ int result = JGetStringT( hContact, "AvatarSaved", &dbv );
+ if ( result || lstrcmp( dbv.ptszVal, txt )) {
+ Log( "Avatar was changed. Using vcard-temp:x:update" );
+ JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, NULL );
+ }
+ else Log( "Not broadcasting avatar changed" );
+ if ( !result ) JFreeVariant( &dbv );
+ } else {
+ removedAvatar = true;
+ }
+ } } } }
+ if ( !hasAvatar && removedAvatar ) {
+ Log( "Has no avatar" );
+ JDeleteSetting( hContact, "AvatarHash" );
+ DBVARIANT dbv = {0};
+ if ( !JGetStringT( hContact, "AvatarSaved", &dbv )) {
+ JFreeVariant( &dbv );
+ JDeleteSetting( hContact, "AvatarSaved" );
+ JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, NULL, NULL );
+ } } }
+ return;
+ }
+
+ if ( !_tcscmp( type, _T("unavailable"))) {
+ hContact = HContactFromJID( from );
+ if (( item = ListGetItemPtr( LIST_ROSTER, from )) != NULL ) {
+ ListRemoveResource( LIST_ROSTER, from );
+
+ hContact = HContactFromJID( from );
+ if ( hContact && DBGetContactSettingByte( hContact, "CList", "NotOnList", 0) == 1 ) {
+ // remove selfcontact, if where is no more another resources
+ if ( item->resourceCount == 1 && ResourceInfoFromJID( info->fullJID ))
+ ListRemoveResource( LIST_ROSTER, info->fullJID );
+ }
+
+
+ // set status only if no more available resources
+ if ( !item->resourceCount )
+ {
+ item->itemResource.status = ID_STATUS_OFFLINE;
+ if ((( statusNode = xmlGetChild( node , "status" )) != NULL ) && xmlGetText( statusNode ))
+ replaceStrT( item->itemResource.statusMessage, xmlGetText( statusNode ));
+ else
+ replaceStrT( item->itemResource.statusMessage, NULL );
+ }
+ }
+ else Log( "SKIP Receive presence offline from " TCHAR_STR_PARAM " ( who is not in my roster )", from );
+
+ UpdateJidDbSettings( from );
+
+ if ( _tcschr( from, '@' )==NULL ) {
+ UI_SAFE_NOTIFY(m_pDlgServiceDiscovery, WM_JABBER_TRANSPORT_REFRESH);
+ }
+ DBCheckIsTransportedContact(from, hContact);
+ return;
+ }
+
+ if ( !_tcscmp( type, _T("subscribe"))) {
+ if (hContact = HContactFromJID( from ))
+ AddDbPresenceEvent( hContact, JABBER_DB_EVENT_PRESENCE_SUBSCRIBE );
+
+ // automatically send authorization allowed to agent/transport
+ if ( _tcschr( from, '@' ) == NULL || m_options.AutoAcceptAuthorization ) {
+ ListAdd( LIST_ROSTER, from );
+ info->send( XmlNode( _T("presence")) << XATTR( _T("to"), from ) << XATTR( _T("type"), _T("subscribed")));
+
+ if ( m_options.AutoAdd == TRUE ) {
+ if (( item = ListGetItemPtr( LIST_ROSTER, from )) == NULL || ( item->subscription != SUB_BOTH && item->subscription != SUB_TO )) {
+ Log( "Try adding contact automatically jid = " TCHAR_STR_PARAM, from );
+ if (( hContact=AddToListByJID( from, 0 )) != NULL ) {
+ // Trigger actual add by removing the "NotOnList" added by AddToListByJID()
+ // See AddToListByJID() and JabberDbSettingChanged().
+ DBDeleteContactSetting( hContact, "CList", "NotOnList" );
+ } } }
+ RebuildInfoFrame();
+ }
+ else {
+ HXML n = xmlGetChild( node , "nick" );
+ nick = ( n == NULL ) ? JabberNickFromJID( from ) : mir_tstrdup( xmlGetText( n ));
+ if ( nick != NULL ) {
+ Log( TCHAR_STR_PARAM " ( " TCHAR_STR_PARAM " ) requests authorization", nick, from );
+ DBAddAuthRequest( from, nick );
+ mir_free( nick );
+ } }
+ return;
+ }
+
+ if ( !_tcscmp( type, _T("unsubscribe"))) {
+ if (hContact = HContactFromJID( from ))
+ AddDbPresenceEvent( hContact, JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBE );
+ }
+
+ if ( !_tcscmp( type, _T("unsubscribed"))) {
+ if (hContact = HContactFromJID( from ))
+ AddDbPresenceEvent( hContact, JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBED );
+ }
+
+ if ( !_tcscmp( type, _T("error"))) {
+ if (hContact = HContactFromJID( from )) {
+ AddDbPresenceEvent( hContact, JABBER_DB_EVENT_PRESENCE_ERROR );
+ }
+ }
+
+ if ( !_tcscmp( type, _T("subscribed"))) {
+
+ if (hContact = HContactFromJID( from ))
+ AddDbPresenceEvent( hContact, JABBER_DB_EVENT_PRESENCE_SUBSCRIBED );
+
+ if (( item=ListGetItemPtr( LIST_ROSTER, from )) != NULL ) {
+ if ( item->subscription == SUB_FROM ) item->subscription = SUB_BOTH;
+ else if ( item->subscription == SUB_NONE ) {
+ item->subscription = SUB_TO;
+ if ( _tcschr( from, '@' )==NULL ) {
+ UI_SAFE_NOTIFY(m_pDlgServiceDiscovery, WM_JABBER_TRANSPORT_REFRESH);
+ }
+ }
+ UpdateSubscriptionInfo( hContact, item );
+ }
+ }
+}
+
+void CJabberProto::OnIqResultVersion( HXML /*node*/, CJabberIqInfo *pInfo )
+{
+ JABBER_RESOURCE_STATUS *r = ResourceInfoFromJID( pInfo->GetFrom());
+ if ( r == NULL ) return;
+
+ r->dwVersionRequestTime = -1;
+
+ replaceStrT( r->software, NULL );
+ replaceStrT( r->version, NULL );
+ replaceStrT( r->system, NULL );
+
+ HXML queryNode = pInfo->GetChildNode();
+
+ if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT && queryNode) {
+ HXML n;
+ if (( n = xmlGetChild( queryNode , "name" ))!=NULL && xmlGetText( n ))
+ r->software = mir_tstrdup( xmlGetText( n ));
+ if (( n = xmlGetChild( queryNode , "version" ))!=NULL && xmlGetText( n ))
+ r->version = mir_tstrdup( xmlGetText( n ));
+ if (( n = xmlGetChild( queryNode , "os" ))!=NULL && xmlGetText( n ))
+ r->system = mir_tstrdup( xmlGetText( n ));
+ }
+
+ GetResourceCapabilites( pInfo->GetFrom(), TRUE );
+ if ( pInfo->GetHContact())
+ UpdateMirVer( pInfo->GetHContact(), r );
+
+ JabberUserInfoUpdate(pInfo->GetHContact());
+}
+
+BOOL CJabberProto::OnProcessJingle( HXML node )
+{
+ LPCTSTR type;
+ HXML child = xmlGetChildByTag( node, _T("jingle"), _T("xmlns"), _T(JABBER_FEAT_JINGLE));
+
+ if ( child ) {
+ if (( type=xmlGetAttrValue( node, _T("type"))) == NULL ) return FALSE;
+ if (( !_tcscmp( type, _T("get")) || !_tcscmp( type, _T("set")))) {
+ LPCTSTR szAction = xmlGetAttrValue( child, _T("action"));
+ LPCTSTR idStr = xmlGetAttrValue( node, _T("id"));
+ LPCTSTR from = xmlGetAttrValue( node, _T("from"));
+ if ( szAction && !_tcscmp( szAction, _T("session-initiate"))) {
+ // if this is a Jingle 'session-initiate' and noone processed it yet, reply with "unsupported-applications"
+ m_ThreadInfo->send( XmlNodeIq( _T("result"), idStr, from ));
+
+ XmlNodeIq iq( _T("set"), SerialNext(), from );
+ HXML jingleNode = iq << XCHILDNS( _T("jingle"), _T(JABBER_FEAT_JINGLE));
+
+ jingleNode << XATTR( _T("action"), _T("session-terminate"));
+ LPCTSTR szInitiator = xmlGetAttrValue( child, _T("initiator"));
+ if ( szInitiator )
+ jingleNode << XATTR( _T("initiator"), szInitiator );
+ LPCTSTR szSid = xmlGetAttrValue( child, _T("sid"));
+ if ( szSid )
+ jingleNode << XATTR( _T("sid"), szSid );
+
+ jingleNode << XCHILD( _T("reason"))
+ << XCHILD( _T("unsupported-applications"));
+ m_ThreadInfo->send( iq );
+ return TRUE;
+ }
+ else {
+ // if it's something else than 'session-initiate' and noone processed it yet, reply with "unknown-session"
+ XmlNodeIq iq( _T("error"), idStr, from );
+ HXML errNode = iq << XCHILD( _T("error"));
+ errNode << XATTR( _T("type"), _T("cancel"));
+ errNode << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"));
+ errNode << XCHILDNS( _T("unknown-session"), _T("urn:xmpp:jingle:errors:1"));
+ m_ThreadInfo->send( iq );
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+void CJabberProto::OnProcessIq( HXML node )
+{
+ HXML queryNode;
+ const TCHAR *type, *xmlns;
+ JABBER_IQ_PFUNC pfunc;
+
+ if ( !xmlGetName( node ) || _tcscmp( xmlGetName( node ), _T("iq"))) return;
+ if (( type=xmlGetAttrValue( node, _T("type"))) == NULL ) return;
+
+ int id = JabberGetPacketID( node );
+ const TCHAR* idStr = xmlGetAttrValue( node, _T("id"));
+
+ queryNode = xmlGetChild( node , "query" );
+ xmlns = xmlGetAttrValue( queryNode, _T("xmlns"));
+
+ // new match by id
+ if ( m_iqManager.HandleIq( id, node ))
+ return;
+
+ // new iq handler engine
+ if ( m_iqManager.HandleIqPermanent( node ))
+ return;
+
+ // Jingle support
+ if ( OnProcessJingle( node ))
+ return;
+
+ /////////////////////////////////////////////////////////////////////////
+ // OLD MATCH BY ID
+ /////////////////////////////////////////////////////////////////////////
+ if ( ( !_tcscmp( type, _T("result")) || !_tcscmp( type, _T("error"))) && (( pfunc=JabberIqFetchFunc( id )) != NULL )) {
+ Log( "Handling iq request for id=%d", id );
+ (this->*pfunc)( node );
+ return;
+ }
+ // RECVED: <iq type='error'> ...
+ else if ( !_tcscmp( type, _T("error"))) {
+ Log( "XXX on entry" );
+ // Check for file transfer deny by comparing idStr with ft->iqId
+ LISTFOREACH(i, this, LIST_FILE)
+ {
+ JABBER_LIST_ITEM *item = ListGetItemPtrFromIndex( i );
+ if ( item->ft != NULL && item->ft->state == FT_CONNECTING && !_tcscmp( idStr, item->ft->iqId )) {
+ Log( "Denying file sending request" );
+ item->ft->state = FT_DENIED;
+ if ( item->ft->hFileEvent != NULL )
+ SetEvent( item->ft->hFileEvent ); // Simulate the termination of file server connection
+ }
+ } }
+ else if (( !_tcscmp( type, _T("get")) || !_tcscmp( type, _T("set")))) {
+ XmlNodeIq iq( _T("error"), idStr, xmlGetAttrValue( node, _T("from")));
+
+ HXML pFirstChild = xmlGetChild( node , 0 );
+ if ( pFirstChild )
+ xmlAddChild( iq, pFirstChild );
+
+ iq << XCHILD( _T("error")) << XATTR( _T("type"), _T("cancel"))
+ << XCHILDNS( _T("service-unavailable"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"));
+ m_ThreadInfo->send( iq );
+ }
+}
+
+void CJabberProto::OnProcessRegIq( HXML node, ThreadData* info )
+{
+ HXML errorNode;
+ const TCHAR *type;
+
+ if ( !xmlGetName( node ) || _tcscmp( xmlGetName( node ), _T("iq"))) return;
+ if (( type=xmlGetAttrValue( node, _T("type"))) == NULL ) return;
+
+ int id = JabberGetPacketID( node );
+
+ if ( !_tcscmp( type, _T("result"))) {
+
+ // RECVED: result of the request for registration mechanism
+ // ACTION: send account registration information
+ if ( id == iqIdRegGetReg ) {
+ iqIdRegSetReg = SerialNext();
+
+ XmlNodeIq iq( _T("set"), iqIdRegSetReg );
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_REGISTER));
+ query << XCHILD( _T("password"), info->password );
+ query << XCHILD( _T("username"), info->username );
+ info->send( iq );
+
+ SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 75, ( LPARAM )TranslateT( "Sending registration information..." ));
+ }
+ // RECVED: result of the registration process
+ // ACTION: account registration successful
+ else if ( id == iqIdRegSetReg ) {
+ info->send( "</stream:stream>" );
+ SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Registration successful" ));
+ info->reg_done = TRUE;
+ } }
+
+ else if ( !_tcscmp( type, _T("error"))) {
+ errorNode = xmlGetChild( node , "error" );
+ TCHAR* str = JabberErrorMsg( errorNode );
+ SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )str );
+ mir_free( str );
+ info->reg_done = TRUE;
+ info->send( "</stream:stream>" );
+} }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// ThreadData constructor & destructor
+
+ThreadData::ThreadData( CJabberProto* aproto, JABBER_SESSION_TYPE parType )
+{
+ memset( this, 0, sizeof( *this ));
+ type = parType;
+ proto = aproto;
+ iomutex = CreateMutex(NULL, FALSE, NULL);
+}
+
+ThreadData::~ThreadData()
+{
+ if ( auth ) delete auth;
+ mir_free( zRecvData );
+ CloseHandle( iomutex );
+ CloseHandle(hThread);
+}
+
+void ThreadData::close( void )
+{
+ if ( s ) {
+ Netlib_CloseHandle(s);
+ s = NULL;
+} }
+
+void ThreadData::shutdown( void )
+{
+ if ( s )
+ Netlib_Shutdown(s);
+}
+
+int ThreadData::recvws( char* buf, size_t len, int flags )
+{
+ if ( this == NULL )
+ return 0;
+
+ return proto->WsRecv( s, buf, (int)len, flags );
+}
+
+int ThreadData::recv( char* buf, size_t len )
+{
+ if ( useZlib )
+ return zlibRecv( buf, (long)len );
+
+ return recvws( buf, len, MSG_DUMPASTEXT );
+}
+
+int ThreadData::sendws( char* buffer, size_t bufsize, int flags )
+{
+ return proto->WsSend( s, buffer, (int)bufsize, flags );
+}
+
+int ThreadData::send( char* buffer, int bufsize )
+{
+ if ( this == NULL )
+ return 0;
+
+ int result;
+
+ WaitForSingleObject( iomutex, 6000 );
+
+ if ( useZlib )
+ result = zlibSend( buffer, bufsize );
+ else
+ result = sendws( buffer, bufsize, MSG_DUMPASTEXT );
+
+ ReleaseMutex( iomutex );
+ return result;
+}
+
+// Caution: DO NOT use ->send() to send binary ( non-string ) data
+int ThreadData::send( HXML node )
+{
+ if ( this == NULL )
+ return 0;
+
+ while ( HXML parent = xi.getParent( node ))
+ node = parent;
+
+ if ( proto->m_sendManager.HandleSendPermanent( node, this ))
+ return 0;
+
+ proto->OnConsoleProcessXml(node, JCPF_OUT);
+
+ TCHAR* str = xi.toString( node, NULL );
+
+ // strip forbidden control characters from outgoing XML stream
+ TCHAR *q = str;
+ for (TCHAR *p = str; *p; ++p) {
+
+ WCHAR c = *p;
+
+ if (c < 0x9 || c > 0x9 && c < 0xA || c > 0xA && c < 0xD || c > 0xD && c < 0x20 || c > 0xD7FF && c < 0xE000 || c > 0xFFFD)
+ continue;
+
+ *q++ = *p;
+ }
+ *q = 0;
+
+
+ char* utfStr = mir_utf8encodeT( str );
+ int result = send( utfStr, (int)strlen( utfStr ));
+ mir_free( utfStr );
+
+ xi.freeMem( str );
+ return result;
+}
+
+int ThreadData::send( const char* fmt, ... )
+{
+ if ( this == NULL )
+ return 0;
+
+ va_list vararg;
+ va_start( vararg, fmt );
+ int size = 512;
+ char* str = ( char* )mir_alloc( size );
+ while ( _vsnprintf( str, size, fmt, vararg ) == -1 ) {
+ size += 512;
+ str = ( char* )mir_realloc( str, size );
+ }
+ va_end( vararg );
+
+ int result = send( str, (int)strlen( str ));
+
+ mir_free( str );
+ return result;
+}
diff --git a/protocols/JabberG/src/jabber_treelist.cpp b/protocols/JabberG/src/jabber_treelist.cpp new file mode 100644 index 0000000000..8e1e28cb45 --- /dev/null +++ b/protocols/JabberG/src/jabber_treelist.cpp @@ -0,0 +1,583 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+#define TLIF_VISIBLE 0x01
+#define TLIF_EXPANDED 0x02
+#define TLIF_MODIFIED 0x04
+#define TLIF_ROOT 0X08
+#define TLIF_HASITEM 0X10
+#define TLIF_REBUILD 0x20
+#define TLIF_FAKEPARENT 0x40
+#define TLIF_FILTERED 0x80
+
+struct TTreeList_ItemInfo
+{
+ BYTE flags;
+ int indent, sortIndex;
+
+ struct TTreeList_ItemInfo *parent;
+ int iIcon, iOverlay;
+ LIST<TCHAR> text;
+ LPARAM data;
+ LIST<TTreeList_ItemInfo> subItems;
+
+ TTreeList_ItemInfo(int columns = 3, int children = 5):
+ text(columns), subItems(children), parent(NULL),
+ flags(0), indent(0), sortIndex(0), iIcon(0), iOverlay(0), data(0) {}
+ ~TTreeList_ItemInfo()
+ {
+ int i;
+ for (i = text.getCount(); i--; )
+ mir_free(text[i]);
+ text.destroy();
+ for (i = subItems.getCount(); i--; )
+ delete subItems[i];
+ subItems.destroy();
+ }
+};
+
+struct TTreeList_Data
+{
+ int mode, sortMode;
+ TCHAR *filter;
+ HTREELISTITEM hItemSelected;
+ TTreeList_ItemInfo *root;
+
+ TTreeList_Data()
+ {
+ sortMode = 0;
+ filter = NULL;
+ mode = TLM_TREE;
+ root = NULL;
+ }
+ ~TTreeList_Data()
+ {
+ if (root) delete root;
+ if (filter) mir_free(filter);
+ }
+};
+
+// static utilities
+static void sttTreeList_ResetIndex(HTREELISTITEM hItem, LPARAM data);
+static void sttTreeList_SortItems(HTREELISTITEM hItem, LPARAM data);
+static void sttTreeList_FilterItems(HTREELISTITEM hItem, LPARAM data);
+static void sttTreeList_CreateItems(HTREELISTITEM hItem, LPARAM data);
+static void sttTreeList_CreateItems_List(HTREELISTITEM hItem, LPARAM data);
+static int CALLBACK sttTreeList_SortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
+
+static __forceinline void sttTreeList_SeWindowData(HWND hwnd, HANDLE data)
+{
+ SetPropA(hwnd, "Miranda.TreeList", (HANDLE)data);
+}
+
+static __forceinline HANDLE sttTreeList_GeWindowData(HWND hwnd)
+{
+ return GetPropA(hwnd, "Miranda.TreeList");
+}
+
+// tree list implementation
+LPARAM TreeList_GetData(HTREELISTITEM hItem)
+{
+ return hItem->data;
+}
+
+HTREELISTITEM TreeList_GetRoot(HWND hwnd)
+{
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+ return data->root;
+}
+
+int TreeList_GetChildrenCount(HTREELISTITEM hItem)
+{
+ return hItem->subItems.getCount();
+}
+
+HTREELISTITEM TreeList_GetChild(HTREELISTITEM hItem, int i)
+{
+ return hItem->subItems[i];
+}
+
+void TreeList_Create(HWND hwnd)
+{
+ TTreeList_Data *data = new TTreeList_Data;
+ data->root = new TTreeList_ItemInfo;
+ data->root->flags = TLIF_EXPANDED|TLIF_VISIBLE|TLIF_ROOT;
+ data->root->indent = -1;
+ data->hItemSelected = data->root;
+ sttTreeList_SeWindowData(hwnd, data);
+
+ ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_GRIDLINES | LVS_EX_INFOTIP );
+
+ HIMAGELIST hIml;
+ hIml = ImageList_Create(16, 16, ILC_MASK + ( IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16 ), 2, 1);
+ ListView_SetImageList (hwnd, hIml, LVSIL_SMALL);
+
+ hIml = ImageList_Create(16, 16, ILC_MASK + ( IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16 ), 2, 1);
+ ImageList_AddIcon_Icolib(hIml, (HICON)CallService( MS_SKIN_LOADICON, SKINICON_OTHER_GROUPOPEN, 0 ));
+ ImageList_AddIcon_Icolib(hIml, (HICON)CallService( MS_SKIN_LOADICON, SKINICON_OTHER_GROUPSHUT, 0 ));
+ ImageList_AddIcon_Icolib(hIml, (HICON)CallService( MS_SKIN_LOADICON, SKINICON_OTHER_DOWNARROW, 0 ));
+ ListView_SetImageList (hwnd, hIml, LVSIL_STATE);
+}
+
+void TreeList_Destroy(HWND hwnd)
+{
+ ListView_DeleteAllItems(hwnd);
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+ delete data;
+}
+
+void TreeList_Reset(HWND hwnd)
+{
+ ListView_DeleteAllItems(hwnd);
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+ delete data->root;
+ data->root = new TTreeList_ItemInfo;
+ data->root->flags = TLIF_EXPANDED|TLIF_VISIBLE|TLIF_ROOT;
+ data->root->indent = -1;
+ data->hItemSelected = data->root;
+}
+
+void TreeList_SetMode(HWND hwnd, int mode)
+{
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+ data->mode = mode;
+ ListView_DeleteAllItems(hwnd);
+ TreeList_Update(hwnd);
+}
+
+void TreeList_SetSortMode(HWND hwnd, int col, BOOL descending)
+{
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+ if ((col >= 0) && (col < 2))
+ data->sortMode = 1 + col * 2 + (descending ? 1 : 0);
+ else
+ data->sortMode = 0;
+ TreeList_Update(hwnd);
+}
+
+void TreeList_SetFilter(HWND hwnd, TCHAR *filter)
+{
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+ if (data->filter) mir_free(data->filter);
+ data->filter = NULL;
+ if (filter) data->filter = mir_tstrdup(filter);
+ TreeList_Update(hwnd);
+}
+
+HTREELISTITEM TreeList_GetActiveItem(HWND hwnd)
+{
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0)
+ return (data->hItemSelected->flags & TLIF_ROOT) ? NULL : data->hItemSelected;
+ ListView_GetItem(hwnd, &lvi);
+ return (HTREELISTITEM)lvi.lParam;
+}
+
+HTREELISTITEM TreeList_AddItem(HWND hwnd, HTREELISTITEM hParent, TCHAR *text, LPARAM nodeDdata)
+{
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+ if (!hParent) hParent = data->root;
+
+ TTreeList_ItemInfo *item = new TTreeList_ItemInfo;
+ item->data = nodeDdata;
+ item->parent = hParent;
+ item->text.insert(mir_tstrdup(text));
+ item->flags |= TLIF_MODIFIED;
+ if (hParent->flags & TLIF_ROOT)
+ {
+ item->flags |= TLIF_EXPANDED;
+ data->hItemSelected = item;
+ }
+ item->indent = hParent->indent+1;
+ hParent->subItems.insert(item);
+ return item;
+}
+
+void TreeList_ResetItem(HWND hwnd, HTREELISTITEM hParent)
+{
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+
+ for (int i = hParent->subItems.getCount(); i--; )
+ delete hParent->subItems[i];
+ hParent->subItems.destroy();
+
+ data->hItemSelected = hParent;
+ ListView_DeleteAllItems(hwnd);
+}
+
+void TreeList_MakeFakeParent(HTREELISTITEM hItem, BOOL flag)
+{
+ if (flag)
+ hItem->flags |= TLIF_FAKEPARENT;
+ else
+ hItem->flags &= ~TLIF_FAKEPARENT;
+ hItem->flags |= TLIF_MODIFIED;
+}
+
+void TreeList_AppendColumn(HTREELISTITEM hItem, TCHAR *text)
+{
+ hItem->text.insert(mir_tstrdup(text));
+ hItem->flags |= TLIF_MODIFIED;
+}
+
+int TreeList_AddIcon(HWND hwnd, HICON hIcon, int iOverlay)
+{
+ HIMAGELIST hIml = ListView_GetImageList(hwnd, LVSIL_SMALL);
+ int idx = ImageList_AddIcon(hIml, hIcon);
+ g_ReleaseIcon(hIcon);
+ if (iOverlay) ImageList_SetOverlayImage(hIml, idx, iOverlay);
+ return idx;
+}
+
+void TreeList_SetIcon(HTREELISTITEM hItem, int iIcon, int iOverlay)
+{
+ if (iIcon >= 0) hItem->iIcon = iIcon;
+ if (iOverlay >= 0) hItem->iOverlay = iOverlay;
+ if ((iIcon >= 0) || (iOverlay >= 0)) hItem->flags |= TLIF_MODIFIED;
+}
+
+void TreeList_RecursiveApply(HTREELISTITEM hItem, void (*func)(HTREELISTITEM, LPARAM), LPARAM data)
+{
+ for ( int i = 0; i < hItem->subItems.getCount(); i++ ) {
+ func( hItem->subItems[i], data );
+ TreeList_RecursiveApply( hItem->subItems[i], func, data );
+} }
+
+void TreeList_Update(HWND hwnd)
+{
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(hwnd);
+ HTREELISTITEM hItem = data->root;
+ int sortIndex = 0;
+
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+ if (data->sortMode)
+ TreeList_RecursiveApply(hItem, sttTreeList_SortItems, (LPARAM)data->sortMode);
+ TreeList_RecursiveApply(hItem, sttTreeList_ResetIndex, (LPARAM)&sortIndex);
+ if (data->filter)
+ TreeList_RecursiveApply(hItem, sttTreeList_FilterItems, (LPARAM)data->filter);
+ for ( int i = ListView_GetItemCount(hwnd); i--; ) {
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = i;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hwnd, &lvi);
+
+ HTREELISTITEM ptli = ( HTREELISTITEM )lvi.lParam;
+ if (( ptli->flags & TLIF_VISIBLE ) && (!data->filter || ( ptli->flags & TLIF_FILTERED ))) {
+ ptli->flags |= TLIF_HASITEM;
+ if ( ptli->flags & TLIF_MODIFIED ) {
+ lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE | LVIF_TEXT;
+ lvi.iItem = i;
+ lvi.iSubItem = 0;
+ lvi.pszText = ptli->text[0];
+ lvi.stateMask = LVIS_OVERLAYMASK|LVIS_STATEIMAGEMASK;
+ lvi.iImage = ptli->iIcon;
+ if (data->mode == TLM_TREE)
+ {
+ lvi.state =
+ INDEXTOSTATEIMAGEMASK(
+ ((ptli->subItems.getCount() == 0) && !(ptli->flags & TLIF_FAKEPARENT)) ? 0 :
+ (ptli->flags & TLIF_EXPANDED) ? 1 : 2 ) |
+ INDEXTOOVERLAYMASK( ptli->iOverlay );
+ } else
+ {
+ lvi.state =
+ INDEXTOSTATEIMAGEMASK(
+ ((ptli->subItems.getCount() == 0) && !(ptli->flags & TLIF_FAKEPARENT)) ? 0 : 3 ) |
+ INDEXTOOVERLAYMASK( ptli->iOverlay );
+ }
+ ListView_SetItem(hwnd, &lvi);
+ for (int j = 1; j < ptli->text.getCount(); ++j)
+ ListView_SetItemText( hwnd, i, j, ptli->text[j]);
+ }
+ }
+ else ListView_DeleteItem(hwnd, i);
+ }
+ if (data->mode == TLM_TREE)
+ TreeList_RecursiveApply(hItem, sttTreeList_CreateItems, (LPARAM)hwnd);
+ else
+ {
+ for (int i = data->hItemSelected->subItems.getCount(); i--; )
+ sttTreeList_CreateItems_List(data->hItemSelected->subItems[i], (LPARAM)hwnd);
+ for (HTREELISTITEM hItem = data->hItemSelected; !(hItem->flags & TLIF_ROOT); hItem = hItem->parent)
+ sttTreeList_CreateItems_List(hItem, (LPARAM)hwnd);
+ }
+ ListView_SortItems(hwnd, sttTreeList_SortFunc, 0);
+ SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
+ UpdateWindow(hwnd);
+}
+
+BOOL TreeList_ProcessMessage(HWND hwnd, UINT msg, WPARAM, LPARAM lparam, UINT idc, BOOL* )
+{
+ LVITEM lvi = {0};
+
+ switch (msg) {
+ case WM_NOTIFY:
+ {
+ if (((LPNMHDR)lparam)->idFrom != idc)
+ break;
+
+ TTreeList_Data *data = (TTreeList_Data *)sttTreeList_GeWindowData(GetDlgItem(hwnd, idc));
+ switch (((LPNMHDR)lparam)->code) {
+ case LVN_COLUMNCLICK:
+ {
+ LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lparam;
+ TreeList_SetSortMode(lpnmlv->hdr.hwndFrom, lpnmlv->iSubItem, FALSE);
+ }
+ break;
+
+ case LVN_ITEMACTIVATE:
+ if (data->mode == TLM_REPORT) {
+ LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lparam;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = lpnmia->iItem;
+ ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi);
+
+ HTREELISTITEM hItem = (lvi.iItem < 0) ? data-> root : (HTREELISTITEM)lvi.lParam;
+ if (!hItem->subItems.getCount() && !(hItem->flags & TLIF_FAKEPARENT)) break;
+ data->hItemSelected = hItem;
+
+ NMTREEVIEW nmtv;
+ nmtv.hdr.code = TVN_ITEMEXPANDED;
+ nmtv.hdr.hwndFrom = lpnmia->hdr.hwndFrom;
+ nmtv.hdr.idFrom = lpnmia->hdr.idFrom;
+ nmtv.itemNew.hItem = (HTREEITEM)lvi.lParam;
+ SendMessage(hwnd, WM_NOTIFY, lpnmia->hdr.idFrom, (LPARAM)&nmtv);
+
+ if (data->mode == TLM_REPORT)
+ {
+ ListView_DeleteAllItems(lpnmia->hdr.hwndFrom);
+ TreeList_Update(lpnmia->hdr.hwndFrom);
+ }
+ }
+ break;
+
+ case LVN_KEYDOWN:
+ if (data->mode == TLM_TREE) {
+ LPNMLVKEYDOWN lpnmlvk = (LPNMLVKEYDOWN)lparam;
+
+ lvi.mask = LVIF_PARAM|LVIF_INDENT;
+ lvi.iItem = ListView_GetNextItem(lpnmlvk->hdr.hwndFrom, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0) return FALSE;
+ lvi.iSubItem = 0;
+ ListView_GetItem(lpnmlvk->hdr.hwndFrom, &lvi);
+ HTREELISTITEM hItem = (HTREELISTITEM)lvi.lParam;
+
+ switch (lpnmlvk->wVKey) {
+ case VK_SUBTRACT:
+ case VK_LEFT:
+ {
+ if ( hItem->subItems.getCount() && (hItem->flags & TLIF_EXPANDED )) {
+ hItem->flags &= ~TLIF_EXPANDED;
+ hItem->flags |= TLIF_MODIFIED;
+ TreeList_Update( lpnmlvk->hdr.hwndFrom );
+ }
+ else if ( hItem->indent && (lpnmlvk->wVKey != VK_SUBTRACT )) {
+ for ( int i = lvi.iItem; i--; ) {
+ lvi.mask = LVIF_INDENT;
+ lvi.iItem = i;
+ lvi.iSubItem = 0;
+ ListView_GetItem(lpnmlvk->hdr.hwndFrom, &lvi);
+ if (lvi.iIndent < hItem->indent) {
+ lvi.mask = LVIF_STATE;
+ lvi.iItem = i;
+ lvi.iSubItem = 0;
+ lvi.state = lvi.stateMask = LVIS_FOCUSED|LVNI_SELECTED;
+ ListView_SetItem(lpnmlvk->hdr.hwndFrom, &lvi);
+ break;
+ } } }
+ break;
+ }
+
+ case VK_ADD:
+ case VK_RIGHT:
+ if ( (hItem->subItems.getCount() || (hItem->flags & TLIF_FAKEPARENT)) &&
+ !( hItem->flags & TLIF_EXPANDED ))
+ {
+ hItem->flags |= TLIF_EXPANDED;
+ hItem->flags |= TLIF_MODIFIED;
+
+ NMTREEVIEW nmtv;
+ nmtv.hdr.code = TVN_ITEMEXPANDED;
+ nmtv.hdr.hwndFrom = lpnmlvk->hdr.hwndFrom;
+ nmtv.hdr.idFrom = lpnmlvk->hdr.idFrom;
+ nmtv.itemNew.hItem = (HTREEITEM)hItem;
+ SendMessage(hwnd, WM_NOTIFY, lpnmlvk->hdr.idFrom, (LPARAM)&nmtv);
+ TreeList_Update( lpnmlvk->hdr.hwndFrom );
+ }
+ break;
+ } }
+ break;
+
+ case NM_CLICK:
+ if (data->mode == TLM_TREE) {
+ LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lparam;
+ LVHITTESTINFO lvhti = {0};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = lpnmia->iItem;
+ ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi);
+ lvhti.pt = lpnmia->ptAction;
+ ListView_HitTest(lpnmia->hdr.hwndFrom, &lvhti);
+
+ HTREELISTITEM ptli = ( HTREELISTITEM )lvi.lParam;
+ if ((lvhti.iSubItem == 0) && ( (lvhti.flags&LVHT_ONITEM) == LVHT_ONITEMSTATEICON ) &&
+ (ptli->subItems.getCount() || ptli->flags & TLIF_FAKEPARENT))
+ {
+ if ( ptli->flags & TLIF_EXPANDED )
+ ptli->flags &= ~TLIF_EXPANDED;
+ else {
+ ptli->flags |= TLIF_EXPANDED;
+
+ NMTREEVIEW nmtv;
+ nmtv.hdr.code = TVN_ITEMEXPANDED;
+ nmtv.hdr.hwndFrom = lpnmia->hdr.hwndFrom;
+ nmtv.hdr.idFrom = lpnmia->hdr.idFrom;
+ nmtv.itemNew.hItem = (HTREEITEM)lvi.lParam;
+ SendMessage(hwnd, WM_NOTIFY, lpnmia->hdr.idFrom, (LPARAM)&nmtv);
+ }
+ ptli->flags |= TLIF_MODIFIED;
+ TreeList_Update( lpnmia->hdr.hwndFrom );
+ } }
+ break;
+ }
+ break;
+ } }
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////
+static int sttTreeList_SortItems_Cmp0(const void *p1, const void *p2) { return lstrcmp((*(HTREELISTITEM *)p1)->text[0], (*(HTREELISTITEM *)p2)->text[0]); }
+static int sttTreeList_SortItems_Cmp1(const void *p1, const void *p2) { return -lstrcmp((*(HTREELISTITEM *)p1)->text[0], (*(HTREELISTITEM *)p2)->text[0]); }
+static int sttTreeList_SortItems_Cmp2(const void *p1, const void *p2) { return lstrcmp((*(HTREELISTITEM *)p1)->text[1], (*(HTREELISTITEM *)p2)->text[1]); }
+static int sttTreeList_SortItems_Cmp3(const void *p1, const void *p2) { return -lstrcmp((*(HTREELISTITEM *)p1)->text[1], (*(HTREELISTITEM *)p2)->text[1]); }
+static int sttTreeList_SortItems_Cmp4(const void *p1, const void *p2) { return lstrcmp((*(HTREELISTITEM *)p1)->text[2], (*(HTREELISTITEM *)p2)->text[2]); }
+static int sttTreeList_SortItems_Cmp5(const void *p1, const void *p2) { return -lstrcmp((*(HTREELISTITEM *)p1)->text[2], (*(HTREELISTITEM *)p2)->text[2]); }
+
+static void sttTreeList_SortItems(HTREELISTITEM hItem, LPARAM data)
+{
+ if (!hItem->subItems.getCount()) return;
+
+ typedef int (__cdecl *TQSortCmp)(const void *, const void *);
+ static TQSortCmp funcs[] =
+ {
+ sttTreeList_SortItems_Cmp0,
+ sttTreeList_SortItems_Cmp1,
+ sttTreeList_SortItems_Cmp2,
+ sttTreeList_SortItems_Cmp3,
+ sttTreeList_SortItems_Cmp4,
+ sttTreeList_SortItems_Cmp5,
+ };
+ qsort(((SortedList *)&hItem->subItems)->items, hItem->subItems.getCount(), sizeof(void *), funcs[data-1]);
+}
+
+static void sttTreeList_ResetIndex(HTREELISTITEM hItem, LPARAM data)
+{
+ hItem->flags &= ~TLIF_HASITEM;
+
+ if ( !hItem->parent || (hItem->parent->flags & TLIF_VISIBLE) && (hItem->parent->flags & TLIF_EXPANDED ))
+ hItem->flags |= TLIF_VISIBLE;
+ else
+ hItem->flags &= ~TLIF_VISIBLE;
+
+ hItem->sortIndex = (*(int *)data)++;
+}
+
+static void sttTreeList_FilterItems(HTREELISTITEM hItem, LPARAM data)
+{
+ int i = 0;
+ for (i = 0; i < hItem->text.getCount(); ++i)
+ if (JabberStrIStr(hItem->text[i], (TCHAR *)data))
+ break;
+
+ if (i < hItem->text.getCount())
+ {
+ while (!(hItem->flags & TLIF_ROOT))
+ {
+ hItem->flags |= TLIF_FILTERED;
+ hItem = hItem->parent;
+ }
+ } else
+ {
+ hItem->flags &= ~TLIF_FILTERED;
+ }
+}
+
+static void sttTreeList_CreateItems(HTREELISTITEM hItem, LPARAM data)
+{
+ TTreeList_Data *listData = (TTreeList_Data *)sttTreeList_GeWindowData((HWND)data);
+ if (( hItem->flags & TLIF_VISIBLE ) && (!listData->filter || ( hItem->flags & TLIF_FILTERED )) && !( hItem->flags & TLIF_HASITEM ) && !( hItem->flags & TLIF_ROOT )) {
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_INDENT | LVIF_PARAM | LVIF_IMAGE | LVIF_TEXT | LVIF_STATE;
+ lvi.iIndent = hItem->indent;
+ lvi.lParam = (LPARAM)hItem;
+ lvi.pszText = hItem->text[0];
+ lvi.stateMask = LVIS_OVERLAYMASK|LVIS_STATEIMAGEMASK;
+ lvi.iImage = hItem->iIcon;
+ lvi.state =
+ INDEXTOSTATEIMAGEMASK(
+ ((hItem->subItems.getCount() == 0) && !(hItem->flags & TLIF_FAKEPARENT)) ? 0 :
+ (hItem->flags & TLIF_EXPANDED) ? 1 : 2 ) |
+ INDEXTOOVERLAYMASK(hItem->iOverlay);
+
+ int idx = ListView_InsertItem((HWND)data, &lvi);
+ for ( int i = 1; i < hItem->text.getCount(); i++ )
+ ListView_SetItemText((HWND)data, idx, i, hItem->text[i]);
+} }
+
+static void sttTreeList_CreateItems_List(HTREELISTITEM hItem, LPARAM data)
+{
+ TTreeList_Data *listData = (TTreeList_Data *)sttTreeList_GeWindowData((HWND)data);
+ if ((!listData->filter || ( hItem->flags & TLIF_FILTERED )) && !( hItem->flags & TLIF_HASITEM ) && !( hItem->flags & TLIF_ROOT )) {
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_INDENT | LVIF_PARAM | LVIF_IMAGE | LVIF_TEXT | LVIF_STATE;
+ lvi.iIndent = hItem->indent;
+ lvi.lParam = (LPARAM)hItem;
+ lvi.pszText = hItem->text[0];
+ lvi.stateMask = LVIS_OVERLAYMASK|LVIS_STATEIMAGEMASK;
+ lvi.iImage = hItem->iIcon;
+ lvi.state =
+ INDEXTOSTATEIMAGEMASK(
+ ((hItem->subItems.getCount() == 0) && !(hItem->flags & TLIF_FAKEPARENT)) ? 0 : 3 ) |
+ INDEXTOOVERLAYMASK( hItem->iOverlay );
+
+ int idx = ListView_InsertItem((HWND)data, &lvi);
+ for ( int i = 1; i < hItem->text.getCount(); i++ )
+ ListView_SetItemText((HWND)data, idx, i, hItem->text[i]);
+} }
+
+static int CALLBACK sttTreeList_SortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM )
+{
+ HTREELISTITEM p1 = ( HTREELISTITEM )lParam1, p2 = ( HTREELISTITEM )lParam2;
+ if ( p1->sortIndex < p2->sortIndex )
+ return -1;
+
+ if ( p1->sortIndex > p2->sortIndex )
+ return +1;
+
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber_userinfo.cpp b/protocols/JabberG/src/jabber_userinfo.cpp new file mode 100644 index 0000000000..9d8b476851 --- /dev/null +++ b/protocols/JabberG/src/jabber_userinfo.cpp @@ -0,0 +1,896 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "m_icolib.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+
+#include "jabber_list.h"
+
+static HANDLE hUserInfoList = NULL;
+
+struct UserInfoStringBuf
+{
+ enum { STRINGBUF_INCREMENT = 1024 };
+
+ TCHAR *buf;
+ int size;
+ int offset;
+
+ UserInfoStringBuf() { buf = 0; size = 0; offset = 0; }
+ ~UserInfoStringBuf() { mir_free(buf); }
+
+ void append( TCHAR *str ) {
+ if ( !str ) return;
+
+ int length = lstrlen( str );
+ if ( size - offset < length + 1 ) {
+ size += ( length + STRINGBUF_INCREMENT );
+ buf = ( TCHAR * )mir_realloc( buf, size * sizeof( TCHAR ));
+ }
+ lstrcpy( buf + offset, str );
+ offset += length;
+ }
+
+ TCHAR *allocate( int length ) {
+ if ( size - offset < length ) {
+ size += ( length + STRINGBUF_INCREMENT );
+ buf = ( TCHAR * )mir_realloc( buf, size * sizeof( TCHAR ));
+ }
+ return buf + offset;
+ }
+
+ void actualize() {
+ if ( buf ) offset = lstrlen( buf );
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserInfoDlgProc - main user info dialog
+
+struct JabberUserInfoDlgData
+{
+ CJabberProto* ppro;
+ HANDLE hContact;
+ JABBER_LIST_ITEM* item;
+ int resourceCount;
+};
+
+enum
+{
+ INFOLINE_DELETE = 0x80000000,
+ INFOLINE_MASK = 0x7fffffff,
+ INFOLINE_BAD_ID = 0x7fffffff,
+
+ INFOLINE_NAME = 1,
+ INFOLINE_MOOD,
+ INFOLINE_ACTIVITY,
+ INFOLINE_TUNE,
+ INFOLINE_OFFLINE,
+ INFOLINE_MESSAGE,
+ INFOLINE_SOFTWARE,
+ INFOLINE_VERSION,
+ INFOLINE_SYSTEM,
+ INFOLINE_PRIORITY,
+ INFOLINE_IDLE,
+ INFOLINE_CAPS,
+ INFOLINE_SOFTWARE_INFORMATION,
+ INFOLINE_SUBSCRIPTION,
+ INFOLINE_LOGOFF,
+ INFOLINE_LOGOFF_MSG,
+ INFOLINE_LASTACTIVE,
+};
+
+__forceinline DWORD sttInfoLineId(DWORD res, DWORD type, DWORD line=0)
+{
+ return
+ ( type << 24 ) & 0x7f000000 |
+ ( res << 12 ) & 0x00fff000 |
+ ( line ) & 0x00000fff;
+}
+
+static HTREEITEM sttFindInfoLine( HWND hwndTree, HTREEITEM htiRoot, LPARAM id=INFOLINE_BAD_ID )
+{
+ if ( id == INFOLINE_BAD_ID ) return NULL;
+ for (HTREEITEM hti = TreeView_GetChild(hwndTree, htiRoot); hti; hti = TreeView_GetNextSibling(hwndTree, hti))
+ {
+ TVITEMEX tvi = {0};
+ tvi.mask = TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem = hti;
+ TreeView_GetItem(hwndTree, &tvi);
+ if ((tvi.lParam&INFOLINE_MASK) == (id&INFOLINE_MASK))
+ return hti;
+ }
+ return NULL;
+}
+
+void sttCleanupInfo(HWND hwndTree, int stage)
+{
+ HTREEITEM hItem = TreeView_GetRoot(hwndTree);
+ while (hItem) {
+ TVITEMEX tvi = {0};
+ tvi.mask = TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem = hItem;
+ TreeView_GetItem(hwndTree, &tvi);
+
+ switch (stage) {
+ case 0:
+ tvi.lParam |= INFOLINE_DELETE;
+ TreeView_SetItem(hwndTree, &tvi);
+ break;
+
+ case 1:
+ if (tvi.lParam & INFOLINE_DELETE) {
+ hItem = TreeView_GetNextSibling(hwndTree, hItem);
+ TreeView_DeleteItem(hwndTree, tvi.hItem);
+ continue;
+ }
+ break;
+ }
+
+ HTREEITEM hItemTmp = 0;
+ if (hItemTmp = TreeView_GetChild(hwndTree, hItem))
+ hItem = hItemTmp;
+ else if (hItemTmp = TreeView_GetNextSibling(hwndTree, hItem))
+ hItem = hItemTmp;
+ else {
+ while (1) {
+ if (!(hItem = TreeView_GetParent(hwndTree, hItem))) break;
+ if (hItemTmp = TreeView_GetNextSibling(hwndTree, hItem)) {
+ hItem = hItemTmp;
+ break;
+ }
+ }
+ }
+ }
+}
+
+static HTREEITEM sttFillInfoLine( HWND hwndTree, HTREEITEM htiRoot, HICON hIcon, TCHAR *title, TCHAR *value, LPARAM id=INFOLINE_BAD_ID, bool expand=false )
+{
+ HTREEITEM hti = sttFindInfoLine(hwndTree, htiRoot, id);
+
+ TCHAR buf[256];
+ if ( title )
+ mir_sntprintf( buf, SIZEOF(buf), _T("%s: %s"), title, value );
+ else
+ lstrcpyn( buf, value, SIZEOF( buf ));
+
+ TVINSERTSTRUCT tvis = {0};
+ tvis.hParent = htiRoot;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.itemex.mask = TVIF_TEXT|TVIF_PARAM;
+ tvis.itemex.pszText = buf;
+ tvis.itemex.lParam = id;
+
+ if ( hIcon ) {
+ HIMAGELIST himl = TreeView_GetImageList( hwndTree, TVSIL_NORMAL );
+ tvis.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvis.itemex.iImage =
+ tvis.itemex.iSelectedImage = ImageList_AddIcon( himl, hIcon );
+ g_ReleaseIcon( hIcon );
+ }
+
+ if ( hti ) {
+ tvis.itemex.mask |= TVIF_HANDLE;
+ tvis.itemex.hItem = hti;
+ TreeView_SetItem( hwndTree, &tvis.itemex );
+ }
+ else {
+ tvis.itemex.mask |= TVIF_STATE;
+ tvis.itemex.stateMask = TVIS_EXPANDED;
+ tvis.itemex.state = expand ? TVIS_EXPANDED : 0;
+ hti = TreeView_InsertItem( hwndTree, &tvis );
+ }
+
+ return hti;
+}
+
+static void sttFillResourceInfo( CJabberProto* ppro, HWND hwndTree, HTREEITEM htiRoot, JABBER_LIST_ITEM *item, int resource )
+{
+ TCHAR buf[256];
+ HTREEITEM htiResource = htiRoot;
+ JABBER_RESOURCE_STATUS *res = resource ? &item->resource[resource-1] : &item->itemResource;
+
+ if ( res->resourceName && *res->resourceName )
+ htiResource = sttFillInfoLine( hwndTree, htiRoot, LoadSkinnedProtoIcon( ppro->m_szModuleName, res->status ),
+ TranslateT("Resource"), res->resourceName, sttInfoLineId(resource, INFOLINE_NAME), true );
+
+ // StatusMsg
+ sttFillInfoLine( hwndTree, htiResource, NULL /*LoadSkinnedIcon( SKINICON_EVENT_MESSAGE )*/,
+ TranslateT( "Message" ), res->statusMessage ? res->statusMessage : TranslateT( "<not specified>" ),
+ sttInfoLineId(resource, INFOLINE_MESSAGE));
+
+ // Software
+ HICON hIcon = NULL;
+ if (ServiceExists(MS_FP_GETCLIENTICONT)) {
+ if (res->software != NULL) {
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s %s"), res->software, res->version);
+ hIcon = (HICON)CallService( MS_FP_GETCLIENTICONT, (WPARAM)buf, 0 );
+ } }
+
+ sttFillInfoLine( hwndTree, htiResource, hIcon, TranslateT( "Software" ),
+ res->software ? res->software : TranslateT( "<not specified>" ),
+ sttInfoLineId(resource, INFOLINE_SOFTWARE));
+
+ if(hIcon)
+ DestroyIcon(hIcon);
+
+ // Version
+ sttFillInfoLine( hwndTree, htiResource, NULL, TranslateT( "Version" ),
+ res->version ? res->version : TranslateT( "<not specified>" ),
+ sttInfoLineId(resource, INFOLINE_VERSION));
+
+ // System
+ sttFillInfoLine( hwndTree, htiResource, NULL, TranslateT( "System" ),
+ res->system ? res->system : TranslateT( "<not specified>" ),
+ sttInfoLineId(resource, INFOLINE_SYSTEM));
+
+ // Resource priority
+ TCHAR szPriority[128];
+ mir_sntprintf( szPriority, SIZEOF( szPriority ), _T("%d"), (int)res->priority );
+ sttFillInfoLine( hwndTree, htiResource, NULL, TranslateT( "Resource priority" ), szPriority, sttInfoLineId(resource, INFOLINE_PRIORITY));
+
+ // Idle
+ if ( res->idleStartTime > 0 ) {
+ lstrcpyn(buf, _tctime( &res->idleStartTime ), SIZEOF( buf ));
+ int len = lstrlen(buf);
+ if (len > 0) buf[len-1] = 0;
+ }
+ else if ( !res->idleStartTime )
+ lstrcpyn(buf, TranslateT( "unknown" ), SIZEOF( buf ));
+ else
+ lstrcpyn(buf, TranslateT( "<not specified>" ), SIZEOF( buf ));
+
+ sttFillInfoLine( hwndTree, htiResource, NULL, TranslateT("Idle since"), buf, sttInfoLineId(resource, INFOLINE_IDLE));
+
+ // caps
+ mir_sntprintf( buf, SIZEOF(buf), _T("%s/%s"), item->jid, res->resourceName );
+ JabberCapsBits jcb = ppro->GetResourceCapabilites( buf, TRUE );
+
+ if ( !( jcb & JABBER_RESOURCE_CAPS_ERROR )) {
+ HTREEITEM htiCaps = sttFillInfoLine( hwndTree, htiResource, ppro->LoadIconEx( "main" ), NULL, TranslateT( "Client capabilities" ), sttInfoLineId(resource, INFOLINE_CAPS));
+ int i;
+ for ( i = 0; g_JabberFeatCapPairs[i].szFeature; i++ )
+ if ( jcb & g_JabberFeatCapPairs[i].jcbCap ) {
+ TCHAR szDescription[ 1024 ];
+ if ( g_JabberFeatCapPairs[i].szDescription )
+ mir_sntprintf( szDescription, SIZEOF( szDescription ), _T("%s (%s)"), TranslateTS(g_JabberFeatCapPairs[i].szDescription), g_JabberFeatCapPairs[i].szFeature );
+ else
+ mir_sntprintf( szDescription, SIZEOF( szDescription ), _T("%s"), g_JabberFeatCapPairs[i].szFeature );
+ sttFillInfoLine( hwndTree, htiCaps, NULL, NULL, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i));
+ }
+
+ for ( int j = 0; j < ppro->m_lstJabberFeatCapPairsDynamic.getCount(); j++, i++ )
+ if ( jcb & ppro->m_lstJabberFeatCapPairsDynamic[j]->jcbCap ) {
+ TCHAR szDescription[ 1024 ];
+ if ( ppro->m_lstJabberFeatCapPairsDynamic[j]->szDescription )
+ mir_sntprintf( szDescription, SIZEOF( szDescription ), _T("%s (%s)"), TranslateTS(ppro->m_lstJabberFeatCapPairsDynamic[j]->szDescription), ppro->m_lstJabberFeatCapPairsDynamic[j]->szFeature );
+ else
+ mir_sntprintf( szDescription, SIZEOF( szDescription ), _T("%s"), ppro->m_lstJabberFeatCapPairsDynamic[j]->szFeature );
+ sttFillInfoLine( hwndTree, htiCaps, NULL, NULL, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i));
+ }
+ }
+
+ // Software info
+ if ( res->pSoftwareInfo ) {
+ HTREEITEM htiSoftwareInfo = sttFillInfoLine( hwndTree, htiResource, ppro->LoadIconEx( "main" ), NULL, TranslateT( "Software information" ), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION));
+ int nLineId = 0;
+ if ( res->pSoftwareInfo->szOs )
+ sttFillInfoLine( hwndTree, htiSoftwareInfo, NULL, TranslateT("Operating system"), res->pSoftwareInfo->szOs, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if ( res->pSoftwareInfo->szOsVersion )
+ sttFillInfoLine( hwndTree, htiSoftwareInfo, NULL, TranslateT("Operating system version"), res->pSoftwareInfo->szOsVersion, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if ( res->pSoftwareInfo->szSoftware )
+ sttFillInfoLine( hwndTree, htiSoftwareInfo, NULL, TranslateT("Software"), res->pSoftwareInfo->szSoftware, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if ( res->pSoftwareInfo->szSoftwareVersion )
+ sttFillInfoLine( hwndTree, htiSoftwareInfo, NULL, TranslateT("Software version"), res->pSoftwareInfo->szSoftwareVersion, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if ( res->pSoftwareInfo->szXMirandaCoreVersion ) {
+ sttFillInfoLine( hwndTree, htiSoftwareInfo, NULL, TranslateT("Miranda NG core version"), res->pSoftwareInfo->szXMirandaCoreVersion, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ sttFillInfoLine( hwndTree, htiSoftwareInfo, NULL, TranslateT("Unicode build"), res->pSoftwareInfo->bXMirandaIsUnicode ? TranslateT("Yes") : TranslateT("No"), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ sttFillInfoLine( hwndTree, htiSoftwareInfo, NULL, TranslateT("Alpha build"), res->pSoftwareInfo->bXMirandaIsAlpha ? TranslateT("Yes") : TranslateT("No"), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ sttFillInfoLine( hwndTree, htiSoftwareInfo, NULL, TranslateT("Debug build"), res->pSoftwareInfo->bXMirandaIsDebug ? TranslateT("Yes") : TranslateT("No"), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ }
+ }
+}
+
+static void sttFillAdvStatusInfo( CJabberProto* ppro, HWND hwndTree, HTREEITEM htiRoot, DWORD dwInfoLine, HANDLE hContact, TCHAR *szTitle, char *pszSlot )
+{
+ char *szAdvStatusIcon = ppro->ReadAdvStatusA(hContact, pszSlot, ADVSTATUS_VAL_ICON);
+ TCHAR *szAdvStatusTitle = ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TITLE);
+ TCHAR *szAdvStatusText = ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TEXT);
+
+ if (szAdvStatusIcon && szAdvStatusTitle && *szAdvStatusTitle) {
+ TCHAR szText[2048];
+ if ( szAdvStatusText && *szAdvStatusText )
+ mir_sntprintf(szText, 2047, _T("%s (%s)"), TranslateTS(szAdvStatusTitle), szAdvStatusText);
+ else
+ mir_sntprintf(szText, 2047, _T("%s"), TranslateTS(szAdvStatusTitle));
+ sttFillInfoLine( hwndTree, htiRoot, (HICON)CallService(MS_SKIN2_GETICON, 0,
+ (LPARAM)szAdvStatusIcon), szTitle, szText, dwInfoLine);
+ }
+
+ mir_free(szAdvStatusIcon);
+ mir_free(szAdvStatusTitle);
+ mir_free(szAdvStatusText);
+}
+
+static void sttFillUserInfo( CJabberProto* ppro, HWND hwndTree, JABBER_LIST_ITEM *item )
+{
+ SendMessage( hwndTree, WM_SETREDRAW, FALSE, 0 );
+
+ sttCleanupInfo(hwndTree, 0);
+
+ HTREEITEM htiRoot = sttFillInfoLine( hwndTree, NULL, ppro->LoadIconEx( "main" ), _T( "JID" ), item->jid, sttInfoLineId(0, INFOLINE_NAME), true );
+ TCHAR buf[256];
+
+ if (HANDLE hContact = ppro->HContactFromJID(item->jid)) {
+ sttFillAdvStatusInfo( ppro, hwndTree, htiRoot, sttInfoLineId(0, INFOLINE_MOOD), hContact, TranslateT("Mood"), ADVSTATUS_MOOD );
+ sttFillAdvStatusInfo( ppro, hwndTree, htiRoot, sttInfoLineId(0, INFOLINE_ACTIVITY), hContact, TranslateT("Activity"), ADVSTATUS_ACTIVITY );
+ sttFillAdvStatusInfo( ppro, hwndTree, htiRoot, sttInfoLineId(0, INFOLINE_TUNE), hContact, TranslateT("Tune"), ADVSTATUS_TUNE );
+ }
+
+ // subscription
+ switch ( item->subscription ) {
+ case SUB_BOTH:
+ sttFillInfoLine( hwndTree, htiRoot, NULL, TranslateT( "Subscription" ), TranslateT( "both" ), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ case SUB_TO:
+ sttFillInfoLine( hwndTree, htiRoot, NULL, TranslateT( "Subscription" ), TranslateT( "to" ), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ case SUB_FROM:
+ sttFillInfoLine( hwndTree, htiRoot, NULL, TranslateT( "Subscription" ), TranslateT( "from" ), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ default:
+ sttFillInfoLine( hwndTree, htiRoot, NULL, TranslateT( "Subscription" ), TranslateT( "none" ), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ }
+
+ // logoff
+ if ( item->itemResource.idleStartTime > 0 ) {
+ lstrcpyn( buf, _tctime( &item->itemResource.idleStartTime ), SIZEOF( buf ));
+ int len = lstrlen(buf);
+ if (len > 0) buf[len-1] = 0;
+ }
+ else if ( !item->itemResource.idleStartTime )
+ lstrcpyn( buf, TranslateT( "unknown" ), SIZEOF( buf ));
+ else
+ lstrcpyn( buf, TranslateT( "<not specified>" ), SIZEOF( buf ));
+
+ sttFillInfoLine( hwndTree, htiRoot, NULL,
+ ( item->jid && _tcschr( item->jid, _T( '@' ))) ? TranslateT( "Last logoff time" ) : TranslateT( "Uptime"), buf,
+ sttInfoLineId(0, INFOLINE_LOGOFF));
+
+ // logoff msg
+ sttFillInfoLine( hwndTree, htiRoot, NULL, TranslateT( "Logoff message" ),
+ item->itemResource.statusMessage ? item->itemResource.statusMessage : TranslateT( "<not specified>" ), sttInfoLineId(0, INFOLINE_LOGOFF_MSG));
+
+ // activity
+ if (( item->lastSeenResource >= 0 ) && ( item->lastSeenResource < item->resourceCount ))
+ lstrcpyn( buf, item->resource[item->lastSeenResource].resourceName, SIZEOF( buf ));
+ else
+ lstrcpyn( buf, TranslateT( "<no information available>" ), SIZEOF( buf ));
+
+ sttFillInfoLine( hwndTree, htiRoot, NULL, TranslateT( "Last active resource" ), buf,
+ sttInfoLineId(0, INFOLINE_LASTACTIVE));
+
+ // resources
+ if ( item->resourceCount ) {
+ for (int i = 0; i < item->resourceCount; ++i)
+ sttFillResourceInfo( ppro, hwndTree, htiRoot, item, i+1 );
+ }
+ else if ( !_tcschr(item->jid, _T('@')) || (item->itemResource.status != ID_STATUS_OFFLINE))
+ sttFillResourceInfo( ppro, hwndTree, htiRoot, item, 0 );
+
+ sttCleanupInfo(hwndTree, 1);
+ SendMessage( hwndTree, WM_SETREDRAW, TRUE, 0 );
+
+ RedrawWindow( hwndTree, NULL, NULL, RDW_INVALIDATE );
+}
+
+static void sttGetNodeText( HWND hwndTree, HTREEITEM hti, UserInfoStringBuf *buf, int indent = 0 )
+{
+ for ( int i = 0; i < indent; ++i )
+ buf->append( _T( "\t" ));
+
+ TVITEMEX tvi = {0};
+ tvi.mask = TVIF_HANDLE|TVIF_TEXT|TVIF_STATE;
+ tvi.hItem = hti;
+ tvi.cchTextMax = 256;
+ tvi.pszText = buf->allocate( tvi.cchTextMax );
+ if (!TreeView_GetItem( hwndTree, &tvi )) { // failure, maybe item was removed...
+ buf->buf[ buf->offset ] = 0;
+ buf->actualize();
+ return;
+ }
+
+ buf->actualize();
+ buf->append( _T( "\r\n" ));
+
+ if ( tvi.state & TVIS_EXPANDED )
+ for ( hti = TreeView_GetChild( hwndTree, hti ); hti; hti = TreeView_GetNextSibling( hwndTree, hti ))
+ sttGetNodeText( hwndTree, hti, buf, indent + 1 );
+}
+
+static INT_PTR CALLBACK JabberUserInfoDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ JabberUserInfoDlgData *dat = (JabberUserInfoDlgData *)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ // lParam is hContact
+ TranslateDialogDefault( hwndDlg );
+
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIconBig(SKINICON_OTHER_USERDETAILS));
+ SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinnedIcon(SKINICON_OTHER_USERDETAILS));
+
+ dat = (JabberUserInfoDlgData *)mir_alloc(sizeof(JabberUserInfoDlgData));
+ ZeroMemory(dat, sizeof(JabberUserInfoDlgData));
+ dat->resourceCount = -1;
+
+ if ( CallService(MS_DB_CONTACT_IS, (WPARAM)lParam, 0 ))
+ dat->hContact = (HANDLE)lParam;
+ else if (!IsBadReadPtr((void *)lParam, sizeof(JABBER_LIST_ITEM))) {
+ dat->hContact = NULL;
+ dat->item = (JABBER_LIST_ITEM *)lParam;
+ }
+
+ {
+ RECT rc; GetClientRect( hwndDlg, &rc );
+ MoveWindow( GetDlgItem( hwndDlg, IDC_TV_INFO ), 5, 5, rc.right-10, rc.bottom-10, TRUE );
+
+ HIMAGELIST himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR|ILC_COLOR32|ILC_MASK, 5, 1);
+ ImageList_AddIcon_Icolib(himl, LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT));
+ TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_TV_INFO), himl, TVSIL_NORMAL);
+
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ WindowList_Add(hUserInfoList, hwndDlg, dat->hContact);
+ }
+ break;
+
+ case WM_JABBER_REFRESH:
+ if ( !dat ) break;
+
+ if ( !dat->item ) {
+ DBVARIANT dbv = {0};
+ if ( dat->ppro->JGetStringT(dat->hContact, "jid", &dbv))
+ break;
+
+ if (!(dat->item = dat->ppro->ListGetItemPtr(LIST_VCARD_TEMP, dbv.ptszVal)))
+ dat->item = dat->ppro->ListGetItemPtr(LIST_ROSTER, dbv.ptszVal);
+
+ if (!dat->item)
+ {
+ HWND hwndTree = GetDlgItem(hwndDlg, IDC_TV_INFO);
+ TreeView_DeleteAllItems( hwndTree );
+ HTREEITEM htiRoot = sttFillInfoLine( hwndTree, NULL, dat->ppro->LoadIconEx( "main" ), _T( "JID" ), dbv.ptszVal, sttInfoLineId(0, INFOLINE_NAME), true );
+ sttFillInfoLine( hwndTree, htiRoot, dat->ppro->LoadIconEx("vcard"), NULL,
+ TranslateT("Please switch online to see more details."));
+
+ JFreeVariant(&dbv);
+ break;
+ }
+
+ JFreeVariant(&dbv);
+ }
+ sttFillUserInfo( dat->ppro, GetDlgItem(hwndDlg, IDC_TV_INFO), dat->item);
+ break;
+
+ case WM_SIZE:
+ MoveWindow(GetDlgItem(hwndDlg, IDC_TV_INFO), 5, 5, LOWORD(lParam)-10, HIWORD(lParam)-10, TRUE);
+ break;
+
+ case WM_CONTEXTMENU:
+ if ( GetWindowLongPtr(( HWND )wParam, GWL_ID ) == IDC_TV_INFO ) {
+ HWND hwndTree = GetDlgItem( hwndDlg, IDC_TV_INFO );
+ POINT pt = { (signed short)LOWORD( lParam ), (signed short)HIWORD( lParam ) };
+ HTREEITEM hItem = 0;
+
+ if (( pt.x == -1 ) && ( pt.y == -1 )) {
+ if (hItem = TreeView_GetSelection( hwndTree )) {
+ RECT rc;
+ TreeView_GetItemRect( hwndTree, hItem, &rc, TRUE );
+ pt.x = rc.left;
+ pt.y = rc.bottom;
+ ClientToScreen( hwndTree, &pt );
+ }
+ }
+ else {
+ TVHITTESTINFO tvhti = {0};
+ tvhti.pt = pt;
+ ScreenToClient( hwndTree, &tvhti.pt );
+ TreeView_HitTest( hwndTree, &tvhti );
+ if ( tvhti.flags & TVHT_ONITEM ) {
+ hItem = tvhti.hItem;
+ TreeView_Select(hwndTree, hItem, TVGN_CARET);
+ } }
+
+ if ( hItem ) {
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)1, TranslateT("Copy"));
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)2, TranslateT("Copy only this value"));
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel"));
+ int nReturnCmd = TrackPopupMenu( hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL );
+ if ( nReturnCmd == 1 ) {
+ UserInfoStringBuf buf;
+ sttGetNodeText( hwndTree, hItem, &buf );
+ JabberCopyText( hwndDlg, buf.buf );
+ }
+ else if ( nReturnCmd == 2 ) {
+ TCHAR szBuffer[ 1024 ];
+ TVITEMEX tvi = {0};
+ tvi.mask = TVIF_HANDLE|TVIF_TEXT|TVIF_STATE;
+ tvi.hItem = hItem;
+ tvi.cchTextMax = SIZEOF( szBuffer );
+ tvi.pszText = szBuffer;
+ if ( TreeView_GetItem( hwndTree, &tvi )) {
+ if (TCHAR *str = _tcsstr(szBuffer, _T(": ")))
+ JabberCopyText( hwndDlg, str+2 );
+ else
+ JabberCopyText( hwndDlg, szBuffer );
+ } }
+ DestroyMenu( hMenu );
+ } }
+ break;
+
+ case WM_NOTIFY:
+ if (( ( LPNMHDR )lParam )->idFrom == 0 ) {
+ switch (( ( LPNMHDR )lParam )->code ) {
+ case PSN_INFOCHANGED:
+ {
+ HANDLE hContact = ( HANDLE ) (( LPPSHNOTIFY ) lParam )->lParam;
+ SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, ( LPARAM )hContact );
+ }
+ break;
+
+ case PSN_PARAMCHANGED:
+ dat->ppro = ( CJabberProto* )( CJabberProto* )(( PSHNOTIFY* )lParam )->lParam;
+ if ( dat->hContact != NULL ) {
+ DBVARIANT dbv = {0};
+ if ( dat->ppro->JGetStringT(dat->hContact, "jid", &dbv))
+ break;
+
+ if ( !(dat->item = dat->ppro->ListGetItemPtr( LIST_VCARD_TEMP, dbv.ptszVal )))
+ dat->item = dat->ppro->ListGetItemPtr( LIST_ROSTER, dbv.ptszVal );
+ JFreeVariant(&dbv);
+ }
+ break;
+ } }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ WindowList_Remove(hUserInfoList, hwndDlg);
+ if ( dat ) {
+ mir_free(dat);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ }
+ ImageList_Destroy(TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_TV_INFO), NULL, TVSIL_NORMAL));
+ WindowFreeIcon( hwndDlg );
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserPhotoDlgProc - Jabber photo dialog
+
+struct USER_PHOTO_INFO
+{
+ HANDLE hContact;
+ HBITMAP hBitmap;
+ CJabberProto* ppro;
+};
+
+static INT_PTR CALLBACK JabberUserPhotoDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ USER_PHOTO_INFO *photoInfo;
+
+ photoInfo = ( USER_PHOTO_INFO * ) GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ // lParam is hContact
+ TranslateDialogDefault( hwndDlg );
+ photoInfo = ( USER_PHOTO_INFO * ) mir_alloc( sizeof( USER_PHOTO_INFO ));
+ photoInfo->hContact = ( HANDLE ) lParam;
+ photoInfo->ppro = NULL;
+ photoInfo->hBitmap = NULL;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, ( LONG_PTR ) photoInfo );
+ SendDlgItemMessage( hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadImage( hInst, MAKEINTRESOURCE( IDI_SAVE ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 ));
+ SendDlgItemMessage( hwndDlg, IDC_SAVE, BUTTONSETASFLATBTN, TRUE, 0);
+ ShowWindow( GetDlgItem( hwndDlg, IDC_LOAD ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_DELETE ), SW_HIDE );
+ break;
+
+ case WM_NOTIFY:
+ switch ((( LPNMHDR )lParam )->idFrom ) {
+ case 0:
+ switch ((( LPNMHDR )lParam )->code ) {
+ case PSN_INFOCHANGED:
+ SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 );
+ break;
+
+ case PSN_PARAMCHANGED:
+ photoInfo->ppro = ( CJabberProto* )(( PSHNOTIFY* )lParam )->lParam;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_JABBER_REFRESH:
+ {
+ JABBER_LIST_ITEM *item;
+ DBVARIANT dbv;
+
+ if ( photoInfo->hBitmap ) {
+ DeleteObject( photoInfo->hBitmap );
+ photoInfo->hBitmap = NULL;
+ }
+ ShowWindow( GetDlgItem( hwndDlg, IDC_SAVE ), SW_HIDE );
+ if ( !photoInfo->ppro->JGetStringT( photoInfo->hContact, "jid", &dbv )) {
+ TCHAR* jid = dbv.ptszVal;
+ if (( item = photoInfo->ppro->ListGetItemPtr( LIST_VCARD_TEMP, jid )) == NULL)
+ item = photoInfo->ppro->ListGetItemPtr( LIST_ROSTER, jid );
+ if ( item != NULL ) {
+ if ( item->photoFileName ) {
+ photoInfo->ppro->Log( "Showing picture from " TCHAR_STR_PARAM, item->photoFileName );
+ char* p = mir_t2a( item->photoFileName );
+ photoInfo->hBitmap = ( HBITMAP ) CallService( MS_UTILS_LOADBITMAP, 0, ( LPARAM )p );
+ mir_free( p );
+ JabberBitmapPremultiplyChannels(photoInfo->hBitmap);
+ ShowWindow( GetDlgItem( hwndDlg, IDC_SAVE ), SW_SHOW );
+ }
+ }
+ JFreeVariant( &dbv );
+ }
+ InvalidateRect( hwndDlg, NULL, TRUE );
+ UpdateWindow( hwndDlg );
+ }
+ break;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDC_SAVE:
+ {
+ DBVARIANT dbv;
+ JABBER_LIST_ITEM *item;
+ HANDLE hFile;
+ static TCHAR szFilter[512];
+ unsigned char buffer[3];
+ TCHAR szFileName[MAX_PATH];
+ DWORD n;
+
+ if ( photoInfo->ppro->JGetStringT( photoInfo->hContact, "jid", &dbv ))
+ break;
+
+ TCHAR* jid = dbv.ptszVal;
+ if (( item = photoInfo->ppro->ListGetItemPtr( LIST_VCARD_TEMP, jid )) == NULL)
+ item = photoInfo->ppro->ListGetItemPtr( LIST_ROSTER, jid );
+ if ( item != NULL ) {
+ if (( hFile=CreateFile( item->photoFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) != INVALID_HANDLE_VALUE ) {
+ if ( ReadFile( hFile, buffer, 3, &n, NULL ) && n==3 ) {
+ if ( !strncmp(( char* )buffer, "BM", 2 )) {
+ mir_sntprintf( szFilter, SIZEOF( szFilter ), _T("BMP %s ( *.bmp )"), TranslateT( "format" ));
+ n = (DWORD)_tcslen( szFilter );
+ _tcsncpy( szFilter+n+1, _T("*.BMP"), SIZEOF( szFilter )-n-2 );
+ }
+ else if ( !strncmp(( char* )buffer, "GIF", 3 )) {
+ mir_sntprintf( szFilter, SIZEOF( szFilter ), _T("GIF %s ( *.gif )"), TranslateT( "format" ));
+ n = (DWORD)_tcslen( szFilter );
+ _tcsncpy( szFilter+n+1, _T("*.GIF"), SIZEOF( szFilter )-n-2 );
+ }
+ else if ( buffer[0]==0xff && buffer[1]==0xd8 && buffer[2]==0xff ) {
+ mir_sntprintf( szFilter, SIZEOF( szFilter ), _T("JPEG %s ( *.jpg;*.jpeg )"), TranslateT( "format" ));
+ n = (DWORD)_tcslen( szFilter );
+ _tcsncpy( szFilter+n+1, _T("*.JPG;*.JPEG"), SIZEOF( szFilter )-n-2 );
+ }
+ else {
+ mir_sntprintf( szFilter, SIZEOF( szFilter ), _T("%s ( *.* )"), TranslateT( "Unknown format" ));
+ n = (DWORD)_tcslen( szFilter );
+ _tcsncpy( szFilter+n+1, _T("*.*"), SIZEOF( szFilter )-n-2 );
+ }
+ szFilter[SIZEOF( szFilter )-1] = 0;
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ ofn.hInstance = NULL;
+ ofn.lpstrFilter = szFilter;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nMaxCustFilter = 0;
+ ofn.nFilterIndex = 0;
+ ofn.lpstrFile = szFileName;
+ ofn.nMaxFile = _MAX_PATH;
+ ofn.lpstrFileTitle = NULL;
+ ofn.nMaxFileTitle = 0;
+ ofn.lpstrInitialDir = NULL;
+ ofn.lpstrTitle = NULL;
+ ofn.Flags = OFN_OVERWRITEPROMPT;
+ ofn.nFileOffset = 0;
+ ofn.nFileExtension = 0;
+ ofn.lpstrDefExt = NULL;
+ ofn.lCustData = 0L;
+ ofn.lpfnHook = NULL;
+ ofn.lpTemplateName = NULL;
+ szFileName[0] = '\0';
+ if ( GetSaveFileName( &ofn )) {
+ photoInfo->ppro->Log( "File selected is %s", szFileName );
+ CopyFile( item->photoFileName, szFileName, FALSE );
+ }
+ }
+ CloseHandle( hFile );
+ }
+ }
+ JFreeVariant( &dbv );
+
+ }
+ break;
+ }
+ break;
+
+ case WM_PAINT:
+ if ( !photoInfo->ppro->m_bJabberOnline )
+ SetDlgItemText( hwndDlg, IDC_CANVAS, TranslateT( "<Photo not available while offline>" ));
+ else if ( !photoInfo->hBitmap )
+ SetDlgItemText( hwndDlg, IDC_CANVAS, TranslateT( "<No photo>" ));
+ else {
+ BITMAP bm;
+ POINT ptSize, ptOrg, pt, ptFitSize;
+ RECT rect;
+
+ SetDlgItemTextA( hwndDlg, IDC_CANVAS, "" );
+ HBITMAP hBitmap = photoInfo->hBitmap;
+ HWND hwndCanvas = GetDlgItem( hwndDlg, IDC_CANVAS );
+ HDC hdcCanvas = GetDC( hwndCanvas );
+ HDC hdcMem = CreateCompatibleDC( hdcCanvas );
+ SelectObject( hdcMem, hBitmap );
+ SetMapMode( hdcMem, GetMapMode( hdcCanvas ));
+ GetObject( hBitmap, sizeof( BITMAP ), ( LPVOID ) &bm );
+ ptSize.x = bm.bmWidth;
+ ptSize.y = bm.bmHeight;
+ DPtoLP( hdcCanvas, &ptSize, 1 );
+ ptOrg.x = ptOrg.y = 0;
+ DPtoLP( hdcMem, &ptOrg, 1 );
+ GetClientRect( hwndCanvas, &rect );
+ InvalidateRect( hwndCanvas, NULL, TRUE );
+ UpdateWindow( hwndCanvas );
+ if ( ptSize.x<=rect.right && ptSize.y<=rect.bottom ) {
+ pt.x = ( rect.right - ptSize.x )/2;
+ pt.y = ( rect.bottom - ptSize.y )/2;
+ ptFitSize = ptSize;
+ }
+ else {
+ if (( ( float )( ptSize.x-rect.right ))/ptSize.x > (( float )( ptSize.y-rect.bottom ))/ptSize.y ) {
+ ptFitSize.x = rect.right;
+ ptFitSize.y = ( ptSize.y*rect.right )/ptSize.x;
+ pt.x = 0;
+ pt.y = ( rect.bottom - ptFitSize.y )/2;
+ }
+ else {
+ ptFitSize.x = ( ptSize.x*rect.bottom )/ptSize.y;
+ ptFitSize.y = rect.bottom;
+ pt.x = ( rect.right - ptFitSize.x )/2;
+ pt.y = 0;
+ }
+ }
+
+ if (JabberIsThemeActive && JabberDrawThemeParentBackground && JabberIsThemeActive()) {
+ RECT rc; GetClientRect(hwndCanvas, &rc);
+ JabberDrawThemeParentBackground(hwndCanvas, hdcCanvas, &rc);
+ }
+ else {
+ RECT rc; GetClientRect(hwndCanvas, &rc);
+ FillRect(hdcCanvas, &rc, (HBRUSH)GetSysColorBrush(COLOR_BTNFACE));
+ }
+
+ if (JabberAlphaBlend && bm.bmBitsPixel == 32 ) {
+ BLENDFUNCTION bf = {0};
+ bf.AlphaFormat = AC_SRC_ALPHA;
+ bf.BlendOp = AC_SRC_OVER;
+ bf.SourceConstantAlpha = 255;
+ JabberAlphaBlend( hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, bf );
+ }
+ else {
+ SetStretchBltMode( hdcCanvas, COLORONCOLOR );
+ StretchBlt( hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY );
+ }
+
+ DeleteDC( hdcMem );
+ }
+ break;
+
+ case WM_DESTROY:
+ DestroyIcon(( HICON )SendDlgItemMessage( hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ if ( photoInfo->hBitmap ) {
+ photoInfo->ppro->Log( "Delete bitmap" );
+ DeleteObject( photoInfo->hBitmap );
+ }
+ if ( photoInfo ) mir_free( photoInfo );
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// OnInfoInit - initializes user info option dialogs
+
+int CJabberProto::OnUserInfoInit( WPARAM wParam, LPARAM lParam )
+{
+ if ( !CallService( MS_PROTO_ISPROTOCOLLOADED, 0, ( LPARAM )m_szModuleName ))
+ return 0;
+
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof( odp );
+ odp.hInstance = hInst;
+ odp.dwInitParam = ( LPARAM )this;
+
+ HANDLE hContact = ( HANDLE )lParam;
+ if ( hContact ) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto != NULL && !strcmp( szProto, m_szModuleName )) {
+ odp.pfnDlgProc = JabberUserInfoDlgProc;
+ odp.position = -2000000000;
+ odp.pszTemplate = MAKEINTRESOURCEA( IDD_INFO_JABBER );
+ odp.pszTitle = LPGEN("Account");
+ UserInfo_AddPage(wParam, &odp);
+
+ odp.pfnDlgProc = JabberUserPhotoDlgProc;
+ odp.position = 2000000000;
+ odp.pszTemplate = MAKEINTRESOURCEA( IDD_VCARD_PHOTO );
+ odp.pszTitle = LPGEN("Photo");
+ UserInfo_AddPage(wParam, &odp);
+ }
+ }
+ else {
+ // Show our vcard
+ OnUserInfoInit_VCard(wParam, lParam);
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserInfoUpdate
+
+void JabberUserInfoInit()
+{
+ hUserInfoList = ( HANDLE )CallService( MS_UTILS_ALLOCWINDOWLIST, 0, 0 );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserInfoUpdate
+
+void JabberUserInfoUpdate( HANDLE hContact )
+{
+ if ( !hContact )
+ WindowList_BroadcastAsync( hUserInfoList, WM_JABBER_REFRESH, 0, 0 );
+ else if ( HWND hwnd = WindowList_Find( hUserInfoList, hContact ))
+ PostMessage( hwnd, WM_JABBER_REFRESH, 0, 0 );
+}
diff --git a/protocols/JabberG/src/jabber_util.cpp b/protocols/JabberG/src/jabber_util.cpp new file mode 100644 index 0000000000..a7650c5cd0 --- /dev/null +++ b/protocols/JabberG/src/jabber_util.cpp @@ -0,0 +1,1752 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include <richedit.h>
+
+#include "jabber_list.h"
+#include "jabber_caps.h"
+
+#include "m_clistint.h"
+
+extern CRITICAL_SECTION mutex;
+
+extern int bSecureIM;
+
+void CJabberProto::SerialInit( void )
+{
+ InitializeCriticalSection( &m_csSerial );
+ m_nSerial = 0;
+}
+
+void CJabberProto::SerialUninit( void )
+{
+ DeleteCriticalSection( &m_csSerial );
+}
+
+int CJabberProto::SerialNext( void )
+{
+ unsigned int ret;
+
+ EnterCriticalSection( &m_csSerial );
+ ret = m_nSerial;
+ m_nSerial++;
+ LeaveCriticalSection( &m_csSerial );
+ return ret;
+}
+
+void CJabberProto::Log( const char* fmt, ... )
+{
+ va_list vararg;
+ va_start( vararg, fmt );
+ char* str = ( char* )alloca( 32000 );
+ mir_vsnprintf( str, 32000, fmt, vararg );
+ va_end( vararg );
+
+ CallService( MS_NETLIB_LOG, ( WPARAM )m_hNetlibUser, ( LPARAM )str );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberChatRoomHContactFromJID - looks for the char room HCONTACT with required JID
+
+HANDLE CJabberProto::ChatRoomHContactFromJID( const TCHAR* jid )
+{
+ if ( jid == NULL )
+ return ( HANDLE )NULL;
+
+ HANDLE hContactMatched = NULL;
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL ) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto != NULL && !strcmp( m_szModuleName, szProto )) {
+ DBVARIANT dbv;
+ int result = JGetStringT( hContact, "ChatRoomID", &dbv );
+ if ( result )
+ result = JGetStringT( hContact, "jid", &dbv );
+
+ if ( !result ) {
+ int result;
+ result = lstrcmpi( jid, dbv.ptszVal );
+ JFreeVariant( &dbv );
+ if ( !result && JGetByte( hContact, "ChatRoom", 0 ) != 0 ) {
+ hContactMatched = hContact;
+ break;
+ } } }
+
+ hContact = db_find_next(hContact);
+ }
+
+ return hContactMatched;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberHContactFromJID - looks for the HCONTACT with required JID
+
+HANDLE CJabberProto::HContactFromJID( const TCHAR* jid , BOOL bStripResource )
+{
+ if ( jid == NULL )
+ return ( HANDLE )NULL;
+
+ JABBER_LIST_ITEM* item = ListGetItemPtr( LIST_CHATROOM, jid );
+
+ HANDLE hContactMatched = NULL;
+ HANDLE hContact = ( HANDLE ) db_find_first();
+ while ( hContact != NULL ) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto != NULL && !strcmp( m_szModuleName, szProto )) {
+ DBVARIANT dbv;
+ int result;
+ //safer way to check UID (coz some contact have both setting from convert to chat)
+ if(DBGetContactSettingByte(hContact, szProto, "ChatRoom",0))
+ result = JGetStringT( hContact, "ChatRoomID", &dbv );
+ else
+ result = JGetStringT( hContact, "jid", &dbv );
+
+ if ( !result ) {
+ int result;
+ if ( item != NULL )
+ result = lstrcmpi( jid, dbv.ptszVal );
+ else {
+ if ( bStripResource == 3 ) {
+ if (JGetByte(hContact, "ChatRoom", 0))
+ result = lstrcmpi( jid, dbv.ptszVal ); // for chat room we have to have full contact matched
+ else if ( TRUE )
+ result = _tcsnicmp( jid, dbv.ptszVal, _tcslen(dbv.ptszVal));
+ else
+ result = JabberCompareJids( jid, dbv.ptszVal );
+ }
+ // most probably it should just look full matching contact
+ else
+ result = lstrcmpi( jid, dbv.ptszVal );
+
+ }
+ JFreeVariant( &dbv );
+ if ( !result ) {
+ hContactMatched = hContact;
+ break;
+ } } }
+
+ hContact = db_find_next(hContact);
+ }
+
+ return hContactMatched;
+}
+
+TCHAR* __stdcall JabberNickFromJID( const TCHAR* jid )
+{
+ if (!jid) return mir_tstrdup(_T(""));
+
+ const TCHAR* p;
+ TCHAR* nick;
+
+ if (( p = _tcschr( jid, '@' )) == NULL )
+ p = _tcschr( jid, '/' );
+
+ if ( p != NULL ) {
+ if (( nick=( TCHAR* )mir_alloc( sizeof(TCHAR)*( int( p-jid )+1 ))) != NULL ) {
+ _tcsncpy( nick, jid, p-jid );
+ nick[p-jid] = '\0';
+ }
+ }
+ else nick = mir_tstrdup( jid );
+ return nick;
+}
+
+JABBER_RESOURCE_STATUS* CJabberProto::ResourceInfoFromJID( const TCHAR* jid )
+{
+ if ( !jid )
+ return NULL;
+
+ JABBER_LIST_ITEM *item = NULL;
+ if (( item = ListGetItemPtr( LIST_VCARD_TEMP, jid )) == NULL)
+ item = ListGetItemPtr( LIST_ROSTER, jid );
+ if ( item == NULL ) return NULL;
+
+ const TCHAR* p = _tcschr( jid, '/' );
+ if ( p == NULL )
+ return &item->itemResource;
+ if ( *++p == '\0' ) return NULL;
+
+ JABBER_RESOURCE_STATUS *r = item->resource;
+ if ( r == NULL ) return NULL;
+
+ int i;
+ for ( i=0; i<item->resourceCount && _tcscmp( r->resourceName, p ); i++, r++ );
+ if ( i >= item->resourceCount )
+ return NULL;
+
+ return r;
+}
+
+TCHAR* JabberPrepareJid( LPCTSTR jid )
+{
+ if ( !jid ) return NULL;
+ TCHAR* szNewJid = mir_tstrdup( jid );
+ if ( !szNewJid ) return NULL;
+ TCHAR* pDelimiter = _tcschr( szNewJid, _T('/'));
+ if ( pDelimiter ) *pDelimiter = _T('\0');
+ CharLower( szNewJid );
+ if ( pDelimiter ) *pDelimiter = _T('/');
+ return szNewJid;
+}
+
+void strdel( char* parBuffer, int len )
+{
+ char* p;
+ for ( p = parBuffer+len; *p != 0; p++ )
+ p[ -len ] = *p;
+
+ p[ -len ] = '\0';
+}
+
+char* __stdcall JabberUrlDecode( char* str )
+{
+ char* p, *q;
+
+ if ( str == NULL )
+ return NULL;
+
+ for ( p=q=str; *p!='\0'; p++,q++ ) {
+ if ( *p == '<' ) {
+ // skip CDATA
+ if ( !strncmp( p, "<![CDATA[", 9 ))
+ {
+ p += 9;
+ char *tail = strstr(p, "]]>");
+ size_t count = tail ? (tail-p) : strlen(p);
+ memmove(q, p, count);
+ q += count-1;
+ p = (tail ? (tail+3) : (p+count)) - 1;
+ } else
+ {
+ *q = *p;
+ }
+ } else
+ if ( *p == '&' ) {
+ if ( !strncmp( p, "&", 5 )) { *q = '&'; p += 4; }
+ else if ( !strncmp( p, "'", 6 )) { *q = '\''; p += 5; }
+ else if ( !strncmp( p, ">", 4 )) { *q = '>'; p += 3; }
+ else if ( !strncmp( p, "<", 4 )) { *q = '<'; p += 3; }
+ else if ( !strncmp( p, """, 6 )) { *q = '"'; p += 5; }
+ else { *q = *p; }
+ } else
+ {
+ *q = *p;
+ }
+ }
+ *q = '\0';
+ return str;
+}
+
+void __stdcall JabberUrlDecodeW( WCHAR* str )
+{
+ if ( str == NULL )
+ return;
+
+ WCHAR* p, *q;
+ for ( p=q=str; *p!='\0'; p++,q++ ) {
+ if ( *p == '&' ) {
+ if ( !wcsncmp( p, L"&", 5 )) { *q = '&'; p += 4; }
+ else if ( !wcsncmp( p, L"'", 6 )) { *q = '\''; p += 5; }
+ else if ( !wcsncmp( p, L">", 4 )) { *q = '>'; p += 3; }
+ else if ( !wcsncmp( p, L"<", 4 )) { *q = '<'; p += 3; }
+ else if ( !wcsncmp( p, L""", 6 )) { *q = '"'; p += 5; }
+ else { *q = *p; }
+ }
+ else {
+ *q = *p;
+ }
+ }
+ *q = '\0';
+}
+
+char* __stdcall JabberUrlEncode( const char* str )
+{
+ char* s, *p, *q;
+ int c;
+
+ if ( str == NULL )
+ return NULL;
+
+ for ( c=0,p=( char* )str; *p!='\0'; p++ ) {
+ switch ( *p ) {
+ case '&': c += 5; break;
+ case '\'': c += 6; break;
+ case '>': c += 4; break;
+ case '<': c += 4; break;
+ case '"': c += 6; break;
+ default: c++; break;
+ }
+ }
+ if (( s=( char* )mir_alloc( c+1 )) != NULL ) {
+ for ( p=( char* )str,q=s; *p!='\0'; p++ ) {
+ switch ( *p ) {
+ case '&': strcpy( q, "&" ); q += 5; break;
+ case '\'': strcpy( q, "'" ); q += 6; break;
+ case '>': strcpy( q, ">" ); q += 4; break;
+ case '<': strcpy( q, "<" ); q += 4; break;
+ case '"': strcpy( q, """ ); q += 6; break;
+ default:
+ if ( *p > 0 && *p < 32 ) {
+ switch( *p ) {
+ case '\r':
+ case '\n':
+ case '\t':
+ *q = *p;
+ break;
+ default:
+ *q = '?';
+ }
+ }
+ else *q = *p;
+ q++;
+ break;
+ }
+ }
+ *q = '\0';
+ }
+
+ return s;
+}
+
+void __stdcall JabberUtfToTchar( const char* pszValue, size_t cbLen, LPTSTR& dest )
+{
+ char* pszCopy = NULL;
+ bool bNeedsFree = false;
+ __try
+ {
+ // this code can cause access violation when a stack overflow occurs
+ pszCopy = ( char* )alloca( cbLen+1 );
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER )
+ {
+ bNeedsFree = true;
+ pszCopy = ( char* )malloc( cbLen+1 );
+ }
+ if ( pszCopy == NULL )
+ return;
+
+ memcpy( pszCopy, pszValue, cbLen );
+ pszCopy[ cbLen ] = 0;
+
+ JabberUrlDecode( pszCopy );
+
+
+ mir_utf8decode( pszCopy, &dest );
+
+
+ if ( bNeedsFree )
+ free( pszCopy );
+}
+
+char* __stdcall JabberSha1( char* str )
+{
+ mir_sha1_ctx sha;
+ mir_sha1_byte_t digest[20];
+ char* result;
+ int i;
+
+ if ( str == NULL )
+ return NULL;
+
+ mir_sha1_init( &sha );
+ mir_sha1_append( &sha, (mir_sha1_byte_t* )str, (int)strlen( str ));
+ mir_sha1_finish( &sha, digest );
+ if (( result=( char* )mir_alloc( 41 )) == NULL )
+ return NULL;
+
+ for ( i=0; i<20; i++ )
+ sprintf( result+( i<<1 ), "%02x", digest[i] );
+ return result;
+}
+
+TCHAR* __stdcall JabberStrFixLines( const TCHAR* str )
+{
+ if (!str) return NULL;
+
+ const TCHAR *p;
+ int add = 0;
+ bool prev_r = false;
+ bool prev_n = false;
+
+ for (p = str; p && *p; ++p)
+ if (*p == _T('\r') || *p == _T('\n'))
+ ++add;
+
+ TCHAR *buf = (TCHAR *)mir_alloc((lstrlen(str) + add + 1) * sizeof(TCHAR));
+ TCHAR *res = buf;
+
+ for (p = str; p && *p; ++p)
+ {
+ if (*p == _T('\n') && !prev_r)
+ *res++ = _T('\r');
+ if (*p != _T('\r') && *p != _T('\n') && prev_r)
+ *res++ = _T('\n');
+ *res++ = *p;
+ prev_r = *p == _T('\r');
+ prev_n = *p == _T('\n');
+ }
+ *res = 0;
+
+ return buf;
+}
+
+char* __stdcall JabberUnixToDos( const char* str )
+{
+ char* p, *q, *res;
+ int extra;
+
+ if ( str==NULL || str[0]=='\0' )
+ return NULL;
+
+ extra = 0;
+ for ( p=( char* )str; *p!='\0'; p++ ) {
+ if ( *p == '\n' )
+ extra++;
+ }
+ if (( res=( char* )mir_alloc( strlen( str )+extra+1 )) != NULL ) {
+ for ( p=( char* )str,q=res; *p!='\0'; p++,q++ ) {
+ if ( *p == '\n' ) {
+ *q = '\r';
+ q++;
+ }
+ *q = *p;
+ }
+ *q = '\0';
+ }
+ return res;
+}
+
+WCHAR* __stdcall JabberUnixToDosW( const WCHAR* str )
+{
+ if ( str==NULL || str[0]=='\0' )
+ return NULL;
+
+ const WCHAR* p;
+ WCHAR* q, *res;
+ int extra = 0;
+
+ for ( p = str; *p!='\0'; p++ ) {
+ if ( *p == '\n' )
+ extra++;
+ }
+ if (( res = ( WCHAR* )mir_alloc( sizeof( WCHAR )*( wcslen( str ) + extra + 1 ))) != NULL ) {
+ for ( p = str,q=res; *p!='\0'; p++,q++ ) {
+ if ( *p == '\n' ) {
+ *q = '\r';
+ q++;
+ }
+ *q = *p;
+ }
+ *q = '\0';
+ }
+ return res;
+}
+
+TCHAR* __stdcall JabberHttpUrlEncode( const TCHAR* str )
+{
+ TCHAR* p, *q, *res;
+
+ if ( str == NULL ) return NULL;
+ res = ( TCHAR* ) mir_alloc( 3*_tcslen( str ) + 1 );
+ for ( p = ( TCHAR* )str, q = res; *p!='\0'; p++,q++ ) {
+ if (( *p>='A' && *p<='Z' ) || ( *p>='a' && *p<='z' ) || ( *p>='0' && *p<='9' ) || strchr( "$-_.+!*'(),", *p )!=NULL ) {
+ *q = *p;
+ }
+ else {
+ wsprintf( q, _T("%%%02X"), *p );
+ q += 2;
+ }
+ }
+ *q = '\0';
+ return res;
+}
+
+void __stdcall JabberHttpUrlDecode( TCHAR* str )
+{
+ TCHAR* p, *q;
+ unsigned int code;
+
+ if ( str == NULL ) return;
+ for ( p = q = ( TCHAR* )str; *p!='\0'; p++,q++ ) {
+ if ( *p=='%' && *( p+1 )!='\0' && isxdigit( *( p+1 )) && *( p+2 )!='\0' && isxdigit( *( p+2 ))) {
+ _stscanf(( TCHAR* )p+1, _T("%2x"), &code );
+ *q = ( unsigned char ) code;
+ p += 2;
+ }
+ else {
+ *q = *p;
+ } }
+
+ *q = '\0';
+}
+
+int __stdcall JabberCombineStatus( int status1, int status2 )
+{
+ // Combine according to the following priority ( high to low )
+ // ID_STATUS_FREECHAT
+ // ID_STATUS_ONLINE
+ // ID_STATUS_DND
+ // ID_STATUS_AWAY
+ // ID_STATUS_NA
+ // ID_STATUS_INVISIBLE ( valid only for TLEN_PLUGIN )
+ // ID_STATUS_OFFLINE
+ // other ID_STATUS in random order ( actually return status1 )
+ if ( status1==ID_STATUS_FREECHAT || status2==ID_STATUS_FREECHAT )
+ return ID_STATUS_FREECHAT;
+ if ( status1==ID_STATUS_ONLINE || status2==ID_STATUS_ONLINE )
+ return ID_STATUS_ONLINE;
+ if ( status1==ID_STATUS_DND || status2==ID_STATUS_DND )
+ return ID_STATUS_DND;
+ if ( status1==ID_STATUS_AWAY || status2==ID_STATUS_AWAY )
+ return ID_STATUS_AWAY;
+ if ( status1==ID_STATUS_NA || status2==ID_STATUS_NA )
+ return ID_STATUS_NA;
+ if ( status1==ID_STATUS_INVISIBLE || status2==ID_STATUS_INVISIBLE )
+ return ID_STATUS_INVISIBLE;
+ if ( status1==ID_STATUS_OFFLINE || status2==ID_STATUS_OFFLINE )
+ return ID_STATUS_OFFLINE;
+ return status1;
+}
+
+struct tagErrorCodeToStr {
+ int code;
+ TCHAR* str;
+}
+static JabberErrorCodeToStrMapping[] = {
+ { JABBER_ERROR_REDIRECT, _T("Redirect") },
+ { JABBER_ERROR_BAD_REQUEST, _T("Bad request") },
+ { JABBER_ERROR_UNAUTHORIZED, _T("Unauthorized") },
+ { JABBER_ERROR_PAYMENT_REQUIRED, _T("Payment required") },
+ { JABBER_ERROR_FORBIDDEN, _T("Forbidden") },
+ { JABBER_ERROR_NOT_FOUND, _T("Not found") },
+ { JABBER_ERROR_NOT_ALLOWED, _T("Not allowed") },
+ { JABBER_ERROR_NOT_ACCEPTABLE, _T("Not acceptable") },
+ { JABBER_ERROR_REGISTRATION_REQUIRED, _T("Registration required") },
+ { JABBER_ERROR_REQUEST_TIMEOUT, _T("Request timeout") },
+ { JABBER_ERROR_CONFLICT, _T("Conflict") },
+ { JABBER_ERROR_INTERNAL_SERVER_ERROR, _T("Internal server error") },
+ { JABBER_ERROR_NOT_IMPLEMENTED, _T("Not implemented") },
+ { JABBER_ERROR_REMOTE_SERVER_ERROR, _T("Remote server error") },
+ { JABBER_ERROR_SERVICE_UNAVAILABLE, _T("Service unavailable") },
+ { JABBER_ERROR_REMOTE_SERVER_TIMEOUT, _T("Remote server timeout") },
+ { -1, _T("Unknown error") }
+};
+
+TCHAR* __stdcall JabberErrorStr( int errorCode )
+{
+ int i;
+
+ for ( i=0; JabberErrorCodeToStrMapping[i].code!=-1 && JabberErrorCodeToStrMapping[i].code!=errorCode; i++ );
+ return JabberErrorCodeToStrMapping[i].str;
+}
+
+TCHAR* __stdcall JabberErrorMsg( HXML errorNode, int* pErrorCode )
+{
+ TCHAR* errorStr = ( TCHAR* )mir_alloc( 256 * sizeof( TCHAR ));
+ if ( errorNode == NULL ) {
+ if ( pErrorCode )
+ *pErrorCode = -1;
+ mir_sntprintf( errorStr, 256, _T("%s -1: %s"), TranslateT( "Error" ), TranslateT( "Unknown error message" ));
+ return errorStr;
+ }
+
+ int errorCode = -1;
+ const TCHAR *str = xmlGetAttrValue( errorNode, _T("code"));
+ if ( str != NULL )
+ errorCode = _ttoi( str );
+
+ str = xmlGetText( errorNode );
+ if ( str == NULL )
+ str = xmlGetText( xmlGetChild( errorNode, _T("text")));
+ if ( str == NULL ) {
+ for (int i = 0; ; ++i ) {
+ HXML c = xmlGetChild( errorNode, i );
+ if ( c == NULL ) break;
+ const TCHAR *attr = xmlGetAttrValue( c, _T("xmlns"));
+ if ( attr && !_tcscmp( attr, _T("urn:ietf:params:xml:ns:xmpp-stanzas"))) {
+ str = xmlGetName( c );
+ break;
+ }
+ }
+ }
+
+ if ( str != NULL )
+ mir_sntprintf( errorStr, 256, _T("%s %d: %s\r\n%s"), TranslateT( "Error" ), errorCode, TranslateTS( JabberErrorStr( errorCode )), str );
+ else
+ mir_sntprintf( errorStr, 256, _T("%s %d: %s"), TranslateT( "Error" ), errorCode, TranslateTS( JabberErrorStr( errorCode )));
+
+ if ( pErrorCode )
+ *pErrorCode = errorCode;
+ return errorStr;
+}
+
+void CJabberProto::SendVisibleInvisiblePresence( BOOL invisible )
+{
+ if ( !m_bJabberOnline ) return;
+
+ LISTFOREACH(i, this, LIST_ROSTER)
+ {
+ JABBER_LIST_ITEM *item = ListGetItemPtrFromIndex( i );
+ if ( item == NULL )
+ continue;
+
+ HANDLE hContact = HContactFromJID( item->jid );
+ if ( hContact == NULL )
+ continue;
+
+ WORD apparentMode = JGetWord( hContact, "ApparentMode", 0 );
+ if ( invisible==TRUE && apparentMode==ID_STATUS_OFFLINE )
+ m_ThreadInfo->send( XmlNode( _T("presence" )) << XATTR( _T("to"), item->jid ) << XATTR( _T("type"), _T("invisible")));
+ else if ( invisible==FALSE && apparentMode==ID_STATUS_ONLINE )
+ SendPresenceTo( m_iStatus, item->jid, NULL );
+} }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberBase64Encode
+
+static char b64table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+char* __stdcall JabberBase64Encode( const char* buffer, int bufferLen )
+{
+ if ( buffer==NULL || bufferLen<=0 )
+ return NULL;
+
+ char* res = (char*)mir_alloc(((( bufferLen+2 )*4 )/3 ) + 1);
+ if ( res == NULL )
+ return NULL;
+
+ unsigned char igroup[3];
+ char *r = res;
+ const char* peob = buffer + bufferLen;
+ for ( const char* p = buffer; p < peob; ) {
+ igroup[ 0 ] = igroup[ 1 ] = igroup[ 2 ] = 0;
+ int n;
+ for ( n=0; n<3; n++ ) {
+ if ( p >= peob ) break;
+ igroup[n] = ( unsigned char ) *p;
+ p++;
+ }
+
+ if ( n > 0 ) {
+ r[0] = b64table[ igroup[0]>>2 ];
+ r[1] = b64table[ (( igroup[0]&3 )<<4 ) | ( igroup[1]>>4 ) ];
+ r[2] = b64table[ (( igroup[1]&0xf )<<2 ) | ( igroup[2]>>6 ) ];
+ r[3] = b64table[ igroup[2]&0x3f ];
+
+ if ( n < 3 ) {
+ r[3] = '=';
+ if ( n < 2 )
+ r[2] = '=';
+ }
+ r += 4;
+ } }
+
+ *r = '\0';
+
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberBase64Decode
+
+static unsigned char b64rtable[256];
+
+char* __stdcall JabberBase64DecodeW( const WCHAR* str, int *resultLen )
+{
+ char *stra = mir_u2a(str);
+ char *res = JabberBase64Decode(stra, resultLen);
+ mir_free(stra);
+ return res;
+}
+
+char* __stdcall JabberBase64Decode( const char* str, int *resultLen )
+{
+ char* res;
+ unsigned char* r, igroup[4], a[4];
+ int n, num, count;
+
+ if ( str==NULL || resultLen==NULL ) return NULL;
+ if (( res=( char* )mir_alloc(( ( strlen( str )+3 )/4 )*3 )) == NULL ) return NULL;
+
+ for ( n=0; n<256; n++ )
+ b64rtable[n] = ( unsigned char ) 0x80;
+ for ( n=0; n<26; n++ )
+ b64rtable['A'+n] = n;
+ for ( n=0; n<26; n++ )
+ b64rtable['a'+n] = n + 26;
+ for ( n=0; n<10; n++ )
+ b64rtable['0'+n] = n + 52;
+ b64rtable['+'] = 62;
+ b64rtable['/'] = 63;
+ b64rtable['='] = 0;
+ count = 0;
+ for ( r=( unsigned char* )res; *str != '\0'; ) {
+ for ( n=0; n<4; n++ ) {
+ if ( BYTE(*str) == '\r' || BYTE(*str) == '\n' ) {
+ n--; str++;
+ continue;
+ }
+
+ if ( BYTE(*str)=='\0' ) {
+ if ( n == 0 )
+ goto LBL_Exit;
+ mir_free( res );
+ return NULL;
+ }
+
+ if ( b64rtable[BYTE(*str)]==0x80 ) {
+ mir_free( res );
+ return NULL;
+ }
+
+ a[n] = BYTE(*str);
+ igroup[n] = b64rtable[BYTE(*str)];
+ str++;
+ }
+ r[0] = igroup[0]<<2 | igroup[1]>>4;
+ r[1] = igroup[1]<<4 | igroup[2]>>2;
+ r[2] = igroup[2]<<6 | igroup[3];
+ r += 3;
+ num = ( a[2]=='='?1:( a[3]=='='?2:3 ));
+ count += num;
+ if ( num < 3 ) break;
+ }
+LBL_Exit:
+ *resultLen = count;
+ return res;
+}
+
+time_t __stdcall JabberIsoToUnixTime( const TCHAR* stamp )
+{
+ struct tm timestamp;
+ TCHAR date[9];
+ int i, y;
+ time_t t;
+
+ if ( stamp == NULL ) return ( time_t ) 0;
+
+ const TCHAR *p = stamp;
+
+ // Get the date part
+ for ( i=0; *p!='\0' && i<8 && isdigit( *p ); p++,i++ )
+ date[i] = *p;
+
+ // Parse year
+ if ( i == 6 ) {
+ // 2-digit year ( 1970-2069 )
+ y = ( date[0]-'0' )*10 + ( date[1]-'0' );
+ if ( y < 70 ) y += 100;
+ }
+ else if ( i == 8 ) {
+ // 4-digit year
+ y = ( date[0]-'0' )*1000 + ( date[1]-'0' )*100 + ( date[2]-'0' )*10 + date[3]-'0';
+ y -= 1900;
+ }
+ else
+ return ( time_t ) 0;
+ timestamp.tm_year = y;
+ // Parse month
+ timestamp.tm_mon = ( date[i-4]-'0' )*10 + date[i-3]-'0' - 1;
+ // Parse date
+ timestamp.tm_mday = ( date[i-2]-'0' )*10 + date[i-1]-'0';
+
+ // Skip any date/time delimiter
+ for ( ; *p!='\0' && !isdigit( *p ); p++ );
+
+ // Parse time
+ if ( _stscanf( p, _T("%d:%d:%d"), ×tamp.tm_hour, ×tamp.tm_min, ×tamp.tm_sec ) != 3 )
+ return ( time_t ) 0;
+
+ timestamp.tm_isdst = 0; // DST is already present in _timezone below
+ t = mktime( ×tamp );
+
+ _tzset();
+ t -= _timezone;
+
+ if ( t >= 0 )
+ return t;
+ else
+ return ( time_t ) 0;
+}
+
+void CJabberProto::SendPresenceTo( int status, TCHAR* to, HXML extra, const TCHAR *msg )
+{
+ if ( !m_bJabberOnline ) return;
+
+ // Send <presence/> update for status ( we won't handle ID_STATUS_OFFLINE here )
+ short iPriority = (short)JGetWord( NULL, "Priority", 0 );
+ UpdatePriorityMenu(iPriority);
+
+ TCHAR szPriority[40];
+ _itot( iPriority, szPriority, 10 );
+
+ XmlNode p( _T("presence")); p << XCHILD( _T("priority"), szPriority );
+ if ( to != NULL )
+ p << XATTR( _T("to"), to );
+
+ if ( extra )
+ xmlAddChild( p, extra );
+
+ // XEP-0115:Entity Capabilities
+ HXML c = p << XCHILDNS( _T("c"), _T(JABBER_FEAT_ENTITY_CAPS)) << XATTR( _T("node"), _T(JABBER_CAPS_MIRANDA_NODE))
+ << XATTR( _T("ver"), szCoreVersion);
+
+ TCHAR szExtCaps[ 512 ] = _T("");
+
+ if ( m_bGoogleTalk )
+ _tcscat( szExtCaps, _T(JABBER_EXT_GTALK_PMUC));
+
+ if ( bSecureIM ) {
+ if ( szExtCaps[0] )
+ _tcscat( szExtCaps, _T(" "));
+ _tcscat( szExtCaps, _T(JABBER_EXT_SECUREIM));
+ }
+
+ if ( m_options.EnableRemoteControl ) {
+ if ( szExtCaps[0] )
+ _tcscat( szExtCaps, _T(" "));
+ _tcscat( szExtCaps, _T(JABBER_EXT_COMMANDS));
+ }
+
+ if ( m_options.EnableUserMood ) {
+ if ( szExtCaps[0] )
+ _tcscat( szExtCaps, _T(" "));
+ _tcscat( szExtCaps, _T(JABBER_EXT_USER_MOOD));
+ }
+
+ if ( m_options.EnableUserTune ) {
+ if ( szExtCaps[0] )
+ _tcscat( szExtCaps, _T(" "));
+ _tcscat( szExtCaps, _T(JABBER_EXT_USER_TUNE));
+ }
+
+ if ( m_options.EnableUserActivity ) {
+ if ( szExtCaps[0] )
+ _tcscat( szExtCaps, _T(" "));
+ _tcscat( szExtCaps, _T(JABBER_EXT_USER_ACTIVITY));
+ }
+
+ if ( m_options.AcceptNotes ) {
+ if ( szExtCaps[0] )
+ _tcscat( szExtCaps, _T(" "));
+ _tcscat( szExtCaps, _T(JABBER_EXT_MIR_NOTES));
+ }
+
+ // add features enabled through IJabberNetInterface::AddFeatures()
+ for ( int i = 0; i < m_lstJabberFeatCapPairsDynamic.getCount(); i++ ) {
+ if ( m_uEnabledFeatCapsDynamic & m_lstJabberFeatCapPairsDynamic[i]->jcbCap ) {
+ if ( szExtCaps[0] )
+ _tcscat( szExtCaps, _T(" "));
+ _tcscat( szExtCaps, m_lstJabberFeatCapPairsDynamic[i]->szExt );
+ }
+ }
+
+ if ( szExtCaps[0] )
+ xmlAddAttr( c, _T("ext"), szExtCaps );
+
+ if ( m_options.EnableAvatars ) {
+ char hashValue[ 50 ];
+ if ( !JGetStaticString( "AvatarHash", NULL, hashValue, sizeof( hashValue ))) {
+ // XEP-0153: vCard-Based Avatars
+ HXML x = p << XCHILDNS( _T("x"), _T("vcard-temp:x:update"));
+ x << XCHILD( _T("photo"), _A2T(hashValue));
+ } else {
+ HXML x = p << XCHILDNS( _T("x"), _T("vcard-temp:x:update"));
+ x << XCHILD( _T("photo"));
+ }
+ }
+
+ EnterCriticalSection( &m_csModeMsgMutex );
+ switch ( status ) {
+ case ID_STATUS_ONLINE:
+ if ( !msg ) msg = m_modeMsgs.szOnline;
+ break;
+ case ID_STATUS_INVISIBLE:
+ if (!m_bGoogleSharedStatus) p << XATTR( _T("type"), _T("invisible"));
+ break;
+ case ID_STATUS_AWAY:
+ case ID_STATUS_ONTHEPHONE:
+ case ID_STATUS_OUTTOLUNCH:
+ p << XCHILD( _T("show"), _T("away"));
+ if ( !msg ) msg = m_modeMsgs.szAway;
+ break;
+ case ID_STATUS_NA:
+ p << XCHILD( _T("show"), _T("xa"));
+ if ( !msg ) msg = m_modeMsgs.szNa;
+ break;
+ case ID_STATUS_DND:
+ case ID_STATUS_OCCUPIED:
+ p << XCHILD( _T("show"), _T("dnd"));
+ if ( !msg ) msg = m_modeMsgs.szDnd;
+ break;
+ case ID_STATUS_FREECHAT:
+ p << XCHILD( _T("show"), _T("chat"));
+ if ( !msg ) msg = m_modeMsgs.szFreechat;
+ break;
+ default:
+ // Should not reach here
+ break;
+ }
+
+ if ( msg )
+ p << XCHILD( _T("status"), msg );
+
+ if ( m_bGoogleSharedStatus && !to )
+ SendIqGoogleSharedStatus( status, msg );
+
+ LeaveCriticalSection( &m_csModeMsgMutex );
+ m_ThreadInfo->send( p );
+}
+
+void CJabberProto::SendPresence( int status, bool bSendToAll )
+{
+ SendPresenceTo( status, NULL, NULL );
+ SendVisibleInvisiblePresence( status == ID_STATUS_INVISIBLE );
+
+ // Also update status in all chatrooms
+ if ( bSendToAll ) {
+ LISTFOREACH(i, this, LIST_CHATROOM)
+ {
+ JABBER_LIST_ITEM *item = ListGetItemPtrFromIndex( i );
+ if ( item != NULL ) {
+ TCHAR text[ 1024 ];
+ mir_sntprintf( text, SIZEOF( text ), _T("%s/%s"), item->jid, item->nick );
+ SendPresenceTo( status == ID_STATUS_INVISIBLE ? ID_STATUS_ONLINE : status, text, NULL );
+} } } }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Google Shared Status
+
+void CJabberProto::OnIqResultGoogleSharedStatus(HXML iqNode, CJabberIqInfo* pInfo) {
+ m_bGoogleSharedStatus = (JABBER_IQ_TYPE_RESULT == pInfo->GetIqType());
+ m_bGoogleSharedStatusLock = FALSE;
+}
+
+BOOL CJabberProto::OnIqSetGoogleSharedStatus(HXML iqNode, CJabberIqInfo* pInfo) {
+ if (JABBER_IQ_TYPE_SET != pInfo->GetIqType()) return FALSE;
+ if (m_bGoogleSharedStatusLock) return TRUE;
+
+ int status;
+ HXML query = xmlGetChild(iqNode, _T("query"));
+ HXML node = xmlGetChild(query, _T("invisible"));
+ if (0 == _tcsicmp(_T("true"), xmlGetAttrValue(node, _T("value"))))
+ status = ID_STATUS_INVISIBLE;
+ else {
+ LPCTSTR txt = xmlGetText(xmlGetChild(query, _T("show")));
+ if (txt && 0 == _tcsicmp(_T("dnd"), txt))
+ status = ID_STATUS_DND;
+ else if (m_iStatus == ID_STATUS_DND || m_iStatus == ID_STATUS_INVISIBLE)
+ status = ID_STATUS_ONLINE;
+ else
+ status = m_iStatus;
+ }
+
+ if (status != m_iStatus) SetStatus(status);
+
+ return TRUE;
+}
+
+void CJabberProto::SendIqGoogleSharedStatus(int status, const TCHAR *msg) {
+ XmlNodeIq iq(m_iqManager.AddHandler(&CJabberProto::OnIqResultGoogleSharedStatus, JABBER_IQ_TYPE_SET));
+ HXML query = iq << XQUERY(_T(JABBER_FEAT_GTALK_SHARED_STATUS)) << XATTR(_T("version"), _T("2"));
+ query << XCHILD(_T("status"), msg);
+ if (status == ID_STATUS_INVISIBLE) {
+ query << XCHILD(_T("show"), _T("default"));
+ query << XCHILD(_T("invisible")) << XATTR(_T("value"), _T("true"));
+ } else {
+ if (status == ID_STATUS_DND)
+ query << XCHILD(_T("show"), _T("dnd"));
+ else
+ query << XCHILD(_T("show"), _T("default"));
+
+ query << XCHILD(_T("invisible")) << XATTR(_T("value"), _T("false"));
+ }
+ m_bGoogleSharedStatusLock = TRUE;
+ m_ThreadInfo->send(iq);
+}
+
+void __stdcall JabberStringAppend( char* *str, int *sizeAlloced, const char* fmt, ... )
+{
+ va_list vararg;
+ char* p;
+ size_t size, len;
+
+ if ( str == NULL ) return;
+
+ if ( *str==NULL || *sizeAlloced<=0 ) {
+ *sizeAlloced = 2048;
+ size = 2048;
+ *str = ( char* )mir_alloc( size );
+ len = 0;
+ }
+ else {
+ len = strlen( *str );
+ size = *sizeAlloced - strlen( *str );
+ }
+
+ p = *str + len;
+ va_start( vararg, fmt );
+ while ( _vsnprintf( p, size, fmt, vararg ) == -1 ) {
+ size += 2048;
+ ( *sizeAlloced ) += 2048;
+ *str = ( char* )mir_realloc( *str, *sizeAlloced );
+ p = *str + len;
+ }
+ va_end( vararg );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberGetPacketID - converts the xml id attribute into an integer
+
+int __stdcall JabberGetPacketID( HXML n )
+{
+ int result = -1;
+
+ const TCHAR* str = xmlGetAttrValue( n, _T("id"));
+ if ( str )
+ if ( !_tcsncmp( str, _T(JABBER_IQID), SIZEOF( JABBER_IQID )-1 ))
+ result = _ttoi( str + SIZEOF( JABBER_IQID )-1 );
+
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberGetClientJID - adds a resource postfix to a JID
+
+TCHAR* CJabberProto::GetClientJID( const TCHAR* jid, TCHAR* dest, size_t destLen )
+{
+ if ( jid == NULL )
+ return NULL;
+
+ size_t len = _tcslen( jid );
+ if ( len >= destLen )
+ len = destLen-1;
+
+ _tcsncpy( dest, jid, len );
+ dest[ len ] = '\0';
+
+ TCHAR* p = _tcschr( dest, '/' );
+
+ JABBER_LIST_ITEM* LI = ListGetItemPtr( LIST_ROSTER, jid );
+ if ( LI && LI->resourceCount == 1 && LI->resource[ 0 ].szCapsNode &&
+ _tcsicmp( LI->resource[ 0 ].szCapsNode, _T( "http://talk.google.com/xmpp/bot/caps")) == 0)
+ {
+ if ( p ) *p = 0;
+ return dest;
+ }
+
+ if ( p == NULL ) {
+ TCHAR* resource = ListGetBestResourceNamePtr( jid );
+ if ( resource != NULL )
+ mir_sntprintf( dest+len, destLen-len-1, _T("/%s"), resource );
+ }
+
+ return dest;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JabberStripJid - strips a resource postfix from a JID
+
+TCHAR* __stdcall JabberStripJid( const TCHAR* jid, TCHAR* dest, size_t destLen )
+{
+ if ( jid == NULL )
+ *dest = 0;
+ else {
+ size_t len = _tcslen( jid );
+ if ( len >= destLen )
+ len = destLen-1;
+
+ memcpy( dest, jid, len * sizeof( TCHAR ));
+ dest[ len ] = 0;
+
+ TCHAR* p = _tcschr( dest, '/' );
+ if ( p != NULL )
+ *p = 0;
+ }
+
+ return dest;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetPictureType - tries to autodetect the picture type from the buffer
+
+int __stdcall JabberGetPictureType( const char* buf )
+{
+ if ( buf != NULL ) {
+ if ( memcmp( buf, "GIF8", 4 ) == 0 ) return PA_FORMAT_GIF;
+ if ( memcmp( buf, "\x89PNG", 4 ) == 0 ) return PA_FORMAT_PNG;
+ if ( memcmp( buf, "BM", 2 ) == 0 ) return PA_FORMAT_BMP;
+ if ( memcmp( buf, "\xFF\xD8", 2 ) == 0 ) return PA_FORMAT_JPEG;
+ }
+
+ return PA_FORMAT_UNKNOWN;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// TStringPairs class members
+
+TStringPairs::TStringPairs( char* buffer ) :
+ elems( NULL )
+{
+ TStringPairsElem tempElem[ 100 ];
+
+ char* token = strtok( buffer, "," );
+
+ for ( numElems=0; token != NULL; numElems++ ) {
+ char* p = strchr( token, '=' ), *p1;
+ if ( p == NULL )
+ break;
+
+ while( isspace( *token ))
+ token++;
+
+ tempElem[ numElems ].name = rtrim( token );
+ *p++ = 0;
+ if (( p1 = strchr( p, '\"' )) != NULL ) {
+ *p1 = 0;
+ p = p1+1;
+ }
+
+ if (( p1 = strrchr( p, '\"' )) != NULL )
+ *p1 = 0;
+
+ tempElem[ numElems ].value = rtrim( p );
+ token = strtok( NULL, "," );
+ }
+
+ if ( numElems ) {
+ elems = new TStringPairsElem[ numElems ];
+ memcpy( elems, tempElem, sizeof(tempElem[0]) * numElems );
+} }
+
+TStringPairs::~TStringPairs()
+{
+ delete[] elems;
+}
+
+const char* TStringPairs::operator[]( const char* key ) const
+{
+ for ( int i = 0; i < numElems; i++ )
+ if ( !strcmp( elems[i].name, key ))
+ return elems[i].value;
+
+ return "";
+}
+
+////////////////////////////////////////////////////////////////////////
+// Manage combo boxes with recent item list
+
+void CJabberProto::ComboLoadRecentStrings(HWND hwndDlg, UINT idcCombo, char *param, int recentCount)
+{
+ for (int i = 0; i < recentCount; ++i) {
+ DBVARIANT dbv;
+ char setting[MAXMODULELABELLENGTH];
+ mir_snprintf(setting, sizeof(setting), "%s%d", param, i);
+ if (!JGetStringT(NULL, setting, &dbv)) {
+ SendDlgItemMessage(hwndDlg, idcCombo, CB_ADDSTRING, 0, (LPARAM)dbv.ptszVal);
+ JFreeVariant(&dbv);
+ } }
+ if (!SendDlgItemMessage(hwndDlg, idcCombo, CB_GETCOUNT, 0, 0))
+ SendDlgItemMessage(hwndDlg, idcCombo, CB_ADDSTRING, 0, (LPARAM)_T(""));
+}
+
+void CJabberProto::ComboAddRecentString(HWND hwndDlg, UINT idcCombo, char *param, TCHAR *string, int recentCount)
+{
+ if (!string || !*string)
+ return;
+ if (SendDlgItemMessage(hwndDlg, idcCombo, CB_FINDSTRING, (WPARAM)-1, (LPARAM)string) != CB_ERR)
+ return;
+
+ int id;
+ SendDlgItemMessage(hwndDlg, idcCombo, CB_ADDSTRING, 0, (LPARAM)string);
+ if ((id = SendDlgItemMessage(hwndDlg, idcCombo, CB_FINDSTRING, (WPARAM)-1, (LPARAM)_T(""))) != CB_ERR)
+ SendDlgItemMessage(hwndDlg, idcCombo, CB_DELETESTRING, id, 0);
+
+ id = JGetByte(NULL, param, 0);
+ char setting[MAXMODULELABELLENGTH];
+ mir_snprintf(setting, sizeof(setting), "%s%d", param, id);
+ JSetStringT(NULL, setting, string);
+ JSetByte(NULL, param, (id+1)%recentCount);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// jabber frame maintenance code
+
+static VOID CALLBACK sttRebuildInfoFrameApcProc( void* param )
+{
+ CJabberProto *ppro = (CJabberProto *)param;
+ if (!ppro->m_pInfoFrame)
+ return;
+
+ ppro->m_pInfoFrame->LockUpdates();
+ if (!ppro->m_bJabberOnline)
+ {
+ ppro->m_pInfoFrame->RemoveInfoItem("$/PEP");
+ ppro->m_pInfoFrame->RemoveInfoItem("$/Transports");
+ ppro->m_pInfoFrame->UpdateInfoItem("$/JID", LoadSkinnedIconHandle(SKINICON_OTHER_USERDETAILS), TranslateT("Offline"));
+ } else
+ {
+ ppro->m_pInfoFrame->UpdateInfoItem("$/JID", LoadSkinnedIconHandle(SKINICON_OTHER_USERDETAILS), ppro->m_szJabberJID);
+
+ if (!ppro->m_bPepSupported)
+ {
+ ppro->m_pInfoFrame->RemoveInfoItem("$/PEP");
+ } else
+ {
+ ppro->m_pInfoFrame->RemoveInfoItem("$/PEP/");
+ ppro->m_pInfoFrame->CreateInfoItem("$/PEP", false);
+ ppro->m_pInfoFrame->UpdateInfoItem("$/PEP", ppro->GetIconHandle(IDI_PL_LIST_ANY), TranslateT("Advanced Status"));
+
+ ppro->m_pInfoFrame->CreateInfoItem("$/PEP/mood", true);
+ ppro->m_pInfoFrame->SetInfoItemCallback("$/PEP/mood", &CJabberProto::InfoFrame_OnUserMood);
+ ppro->m_pInfoFrame->UpdateInfoItem("$/PEP/mood", LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), TranslateT("Set mood..."));
+
+ ppro->m_pInfoFrame->CreateInfoItem("$/PEP/activity", true);
+ ppro->m_pInfoFrame->SetInfoItemCallback("$/PEP/activity", &CJabberProto::InfoFrame_OnUserActivity);
+ ppro->m_pInfoFrame->UpdateInfoItem("$/PEP/activity", LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), TranslateT("Set activity..."));
+ }
+
+ ppro->m_pInfoFrame->RemoveInfoItem("$/Transports/");
+ ppro->m_pInfoFrame->CreateInfoItem("$/Transports", false);
+ ppro->m_pInfoFrame->UpdateInfoItem("$/Transports", ppro->GetIconHandle(IDI_TRANSPORT), TranslateT("Transports"));
+
+ JABBER_LIST_ITEM *item = NULL;
+ LISTFOREACH(i, ppro, LIST_ROSTER)
+ {
+ if (( item=ppro->ListGetItemPtrFromIndex( i )) != NULL ) {
+ if ( _tcschr( item->jid, '@' )==NULL && _tcschr( item->jid, '/' )==NULL && item->subscription!=SUB_NONE ) {
+ HANDLE hContact = ppro->HContactFromJID( item->jid );
+ if ( hContact == NULL ) continue;
+
+ char name[128];
+ char *jid_copy = mir_t2a(item->jid);
+ mir_snprintf(name, SIZEOF(name), "$/Transports/%s", jid_copy);
+ ppro->m_pInfoFrame->CreateInfoItem(name, true, (LPARAM)hContact);
+ ppro->m_pInfoFrame->UpdateInfoItem(name, ppro->GetIconHandle(IDI_TRANSPORTL), (TCHAR *)item->jid);
+ ppro->m_pInfoFrame->SetInfoItemCallback(name, &CJabberProto::InfoFrame_OnTransport);
+ mir_free(jid_copy);
+ } }
+ }
+ }
+ ppro->m_pInfoFrame->Update();
+}
+
+void CJabberProto::RebuildInfoFrame()
+{
+ CallFunctionAsync(sttRebuildInfoFrameApcProc, this);
+}
+
+////////////////////////////////////////////////////////////////////////
+// case-insensitive _tcsstr
+const TCHAR *JabberStrIStr( const TCHAR *str, const TCHAR *substr)
+{
+ TCHAR *str_up = NEWTSTR_ALLOCA(str);
+ TCHAR *substr_up = NEWTSTR_ALLOCA(substr);
+
+ CharUpperBuff(str_up, lstrlen(str_up));
+ CharUpperBuff(substr_up, lstrlen(substr_up));
+
+ TCHAR* p = _tcsstr(str_up, substr_up);
+ return p ? (str + (p - str_up)) : NULL;
+}
+
+////////////////////////////////////////////////////////////////////////
+// clipboard processing
+void JabberCopyText(HWND hwnd, TCHAR *text)
+{
+ if (!hwnd || !text) return;
+
+ OpenClipboard(hwnd);
+ EmptyClipboard();
+ HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, sizeof(TCHAR)*(lstrlen(text)+1));
+ TCHAR *s = (TCHAR *)GlobalLock(hMem);
+ lstrcpy(s, text);
+ GlobalUnlock(hMem);
+ SetClipboardData(CF_UNICODETEXT, hMem);
+ CloseClipboard();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// One string entry dialog
+
+struct JabberEnterStringParams
+{
+ CJabberProto* ppro;
+
+ int type;
+ TCHAR* caption;
+ TCHAR* result;
+ size_t resultLen;
+ char *windowName;
+ int recentCount;
+ int timeout;
+
+ int idcControl;
+ int height;
+};
+
+static int sttEnterStringResizer(HWND, LPARAM, UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId)
+ {
+ case IDC_TXT_MULTILINE:
+ case IDC_TXT_COMBO:
+ case IDC_TXT_RICHEDIT:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ case IDOK:
+ case IDCANCEL:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP;
+}
+
+static INT_PTR CALLBACK sttEnterStringDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ JabberEnterStringParams *params = (JabberEnterStringParams *)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ {
+ //SetWindowPos( hwndDlg, HWND_TOPMOST ,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE );
+ TranslateDialogDefault( hwndDlg );
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIconBig(SKINICON_OTHER_RENAME));
+ SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinnedIcon(SKINICON_OTHER_RENAME));
+ JabberEnterStringParams *params = (JabberEnterStringParams *)lParam;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, ( LONG_PTR )params );
+ SetWindowText( hwndDlg, params->caption );
+
+ RECT rc; GetWindowRect(hwndDlg, &rc);
+ switch (params->type)
+ {
+ case JES_PASSWORD:
+ {
+ params->idcControl = IDC_TXT_PASSWORD;
+ params->height = rc.bottom-rc.top;
+ break;
+ }
+ case JES_MULTINE:
+ {
+ params->idcControl = IDC_TXT_MULTILINE;
+ params->height = 0;
+ rc.bottom += (rc.bottom-rc.top) * 2;
+ SetWindowPos(hwndDlg, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top, SWP_NOMOVE|SWP_NOREPOSITION);
+ break;
+ }
+ case JES_COMBO:
+ {
+ params->idcControl = IDC_TXT_COMBO;
+ params->height = rc.bottom-rc.top;
+ if (params->windowName && params->recentCount)
+ params->ppro->ComboLoadRecentStrings(hwndDlg, IDC_TXT_COMBO, params->windowName, params->recentCount);
+ break;
+ }
+ case JES_RICHEDIT:
+ {
+ params->idcControl = IDC_TXT_RICHEDIT;
+ SendDlgItemMessage(hwndDlg, IDC_TXT_RICHEDIT, EM_AUTOURLDETECT, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_TXT_RICHEDIT, EM_SETEVENTMASK, 0, ENM_LINK);
+ params->height = 0;
+ rc.bottom += (rc.bottom-rc.top) * 2;
+ SetWindowPos(hwndDlg, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top, SWP_NOMOVE|SWP_NOREPOSITION);
+ break;
+ }
+ }
+
+ ShowWindow(GetDlgItem(hwndDlg, params->idcControl), SW_SHOW);
+ SetDlgItemText( hwndDlg, params->idcControl, params->result );
+
+ if (params->windowName)
+ Utils_RestoreWindowPosition(hwndDlg, NULL, params->ppro->m_szModuleName, params->windowName);
+
+ SetTimer(hwndDlg, 1000, 50, NULL);
+
+ if (params->timeout > 0)
+ {
+ SetTimer(hwndDlg, 1001, 1000, NULL);
+ TCHAR buf[128];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s (%d)"), TranslateT("OK"), params->timeout);
+ SetDlgItemText(hwndDlg, IDOK, buf);
+ }
+
+ return TRUE;
+ }
+ case WM_DESTROY:
+ WindowFreeIcon( hwndDlg );
+ break;
+ case WM_TIMER:
+ {
+ switch (wParam)
+ {
+ case 1000:
+ KillTimer(hwndDlg,1000);
+ EnableWindow(GetParent(hwndDlg), TRUE);
+ return TRUE;
+
+ case 1001:
+ {
+ TCHAR buf[128];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s (%d)"), TranslateT("OK"), --params->timeout);
+ SetDlgItemText(hwndDlg, IDOK, buf);
+
+ if (params->timeout < 0)
+ {
+ KillTimer(hwndDlg, 1001);
+ UIEmulateBtnClick(hwndDlg, IDOK);
+ }
+
+ return TRUE;
+ }
+ }
+ }
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd = {0};
+ urd.cbSize = sizeof(urd);
+ urd.hInstance = hInst;
+ urd.hwndDlg = hwndDlg;
+ urd.lpTemplate = MAKEINTRESOURCEA(IDD_GROUPCHAT_INPUT);
+ urd.pfnResizer = sttEnterStringResizer;
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd);
+ break;
+ }
+ case WM_GETMINMAXINFO:
+ {
+ LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
+ if (params && params->height)
+ lpmmi->ptMaxSize.y = lpmmi->ptMaxTrackSize.y = params->height;
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ ENLINK *param = (ENLINK *)lParam;
+ if (param->nmhdr.idFrom != IDC_TXT_RICHEDIT) break;
+ if (param->nmhdr.code != EN_LINK) break;
+ if (param->msg != WM_LBUTTONUP) break;
+
+ CHARRANGE sel;
+ SendMessage(param->nmhdr.hwndFrom, EM_EXGETSEL, 0, (LPARAM) & sel);
+ if (sel.cpMin != sel.cpMax) break; // allow link selection
+
+ TEXTRANGE tr;
+ tr.chrg = param->chrg;
+ tr.lpstrText = (TCHAR *)mir_alloc(sizeof(TCHAR)*(tr.chrg.cpMax - tr.chrg.cpMin + 2));
+ SendMessage(param->nmhdr.hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM) & tr);
+
+ char *tmp = mir_t2a(tr.lpstrText);
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)tmp);
+ mir_free(tmp);
+ mir_free(tr.lpstrText);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDOK:
+ GetDlgItemText( hwndDlg, params->idcControl, params->result, (int)params->resultLen );
+ params->result[ params->resultLen-1 ] = 0;
+
+ if ((params->type == JES_COMBO) && params->windowName && params->recentCount)
+ params->ppro->ComboAddRecentString(hwndDlg, IDC_TXT_COMBO, params->windowName, params->result, params->recentCount);
+ if (params->windowName)
+ Utils_SaveWindowPosition(hwndDlg, NULL, params->ppro->m_szModuleName, params->windowName);
+
+ EndDialog( hwndDlg, 1 );
+ break;
+
+ case IDCANCEL:
+ if (params->windowName)
+ Utils_SaveWindowPosition(hwndDlg, NULL, params->ppro->m_szModuleName, params->windowName);
+
+ EndDialog( hwndDlg, 0 );
+ break;
+
+ case IDC_TXT_MULTILINE:
+ case IDC_TXT_RICHEDIT:
+ if ((HIWORD(wParam) != EN_SETFOCUS) && (HIWORD(wParam) != EN_KILLFOCUS))
+ {
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("OK"));
+ KillTimer(hwndDlg, 1001);
+ }
+ break;
+
+ case IDC_TXT_COMBO:
+ if ((HIWORD(wParam) != CBN_SETFOCUS) && (HIWORD(wParam) != CBN_KILLFOCUS))
+ {
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("OK"));
+ KillTimer(hwndDlg, 1001);
+ }
+ break;
+ } }
+
+ return FALSE;
+}
+
+BOOL CJabberProto::EnterString(TCHAR *result, size_t resultLen, TCHAR *caption, int type, char *windowName, int recentCount, int timeout)
+{
+ bool free_caption = false;
+ if (!caption || (caption==result))
+ {
+ free_caption = true;
+ caption = mir_tstrdup( result );
+ result[ 0 ] = _T('\0');
+ }
+
+ JabberEnterStringParams params = { this, type, caption, result, resultLen, windowName, recentCount, timeout };
+
+ BOOL bRetVal = DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_GROUPCHAT_INPUT ), GetForegroundWindow(), sttEnterStringDlgProc, LPARAM( ¶ms ));
+
+ if (free_caption) mir_free( caption );
+
+ return bRetVal;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Premultiply bitmap channels for 32-bit bitmaps
+void JabberBitmapPremultiplyChannels(HBITMAP hBitmap)
+{
+ BITMAP bmp;
+ DWORD dwLen;
+ BYTE *p;
+ int x, y;
+
+ GetObject(hBitmap, sizeof(bmp), &bmp);
+
+ if (bmp.bmBitsPixel != 32)
+ return;
+
+ dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
+ p = (BYTE *)malloc(dwLen);
+ if (p == NULL)
+ return;
+ memset(p, 0, dwLen);
+
+ GetBitmapBits(hBitmap, dwLen, p);
+
+ for (y = 0; y < bmp.bmHeight; ++y)
+ {
+ BYTE *px = p + bmp.bmWidth * 4 * y;
+
+ for (x = 0; x < bmp.bmWidth; ++x)
+ {
+ px[0] = px[0] * px[3] / 255;
+ px[1] = px[1] * px[3] / 255;
+ px[2] = px[2] * px[3] / 255;
+
+ px += 4;
+ }
+ }
+
+ SetBitmapBits(hBitmap, dwLen, p);
+
+ free(p);
+}
+
+// Last resource map
+void CJabberProto::CleanLastResourceMap()
+{
+ EnterCriticalSection( &m_csLastResourceMap );
+
+ m_dwResourceMapPointer = 0;
+ ZeroMemory( m_ulpResourceToDbEventMap, sizeof( m_ulpResourceToDbEventMap ));
+
+ while ( m_pLastResourceList ) {
+ void *pNext = (( void ** )m_pLastResourceList )[ 0 ];
+ mir_free( m_pLastResourceList );
+ m_pLastResourceList = pNext;
+ }
+
+ LeaveCriticalSection( &m_csLastResourceMap );
+}
+
+// lock CS before use
+BOOL CJabberProto::IsLastResourceExists( void *pResource )
+{
+ if ( !pResource )
+ return FALSE;
+
+ void *pOurResource = m_pLastResourceList;
+ while ( pOurResource ) {
+ if ( pOurResource == pResource )
+ return TRUE;
+ pOurResource = (( void ** )pOurResource)[ 0 ];
+ }
+ return FALSE;
+}
+
+// lock CS before use
+void* CJabberProto::AddToLastResourceMap( LPCTSTR szFullJid )
+{
+ // detach resource from full jid
+ const TCHAR* szResource = _tcschr( szFullJid, '/' );
+ if ( szResource == NULL )
+ return NULL;
+ if ( *++szResource == '\0' )
+ return NULL;
+
+ DWORD dwResourceCount = 0;
+
+ void *pNewTailResource = NULL;
+ void *pOurResource = m_pLastResourceList;
+ while ( pOurResource ) {
+ dwResourceCount++;
+
+ if ( !_tcscmp(( TCHAR * )(( BYTE * )pOurResource + sizeof( void * )), szResource ))
+ return pOurResource;
+
+ void *pTmp = (( void ** )pOurResource )[ 0 ];
+ if ( pTmp && !((( void ** )pTmp )[ 0 ]))
+ pNewTailResource = pOurResource;
+ pOurResource = pTmp;
+ }
+
+ if ( pNewTailResource && ( dwResourceCount > ( SIZEOF( m_ulpResourceToDbEventMap ) / 2 ))) {
+ void *pTmp = (( void ** )pNewTailResource )[ 0 ];
+ (( void ** )pNewTailResource )[ 0 ] = NULL;
+ mir_free( pTmp );
+ }
+
+ void *pNewResource = mir_alloc( sizeof( void * ) + sizeof( TCHAR ) * ( _tcslen( szResource ) + 1 ));
+ if ( !pNewResource )
+ return NULL;
+
+ (( void ** )pNewResource)[ 0 ] = m_pLastResourceList;
+ _tcscpy(( TCHAR * )(( BYTE * )pNewResource + sizeof( void * )), szResource );
+
+ m_pLastResourceList = pNewResource;
+
+ return pNewResource;
+}
+
+// lock CS before use
+TCHAR* CJabberProto::FindLastResourceByDbEvent( HANDLE hDbEvent )
+{
+ for ( int i = 0; i < SIZEOF( m_ulpResourceToDbEventMap ); i += 2 ) {
+ if ( m_ulpResourceToDbEventMap[ i ] == ( ULONG_PTR )hDbEvent ) {
+ TCHAR *szRetVal = ( TCHAR * )( m_ulpResourceToDbEventMap[ i + 1 ] + sizeof( void * ));
+ m_ulpResourceToDbEventMap[ i ] = 0;
+ m_ulpResourceToDbEventMap[ i + 1 ] = 0;
+ return szRetVal;
+ }
+ }
+ return NULL;
+}
+
+BOOL CJabberProto::IsMyOwnJID( LPCTSTR szJID )
+{
+ if ( !m_ThreadInfo )
+ return FALSE;
+
+ TCHAR* szFrom = JabberPrepareJid( szJID );
+ if ( !szFrom )
+ return FALSE;
+
+ TCHAR* szTo = JabberPrepareJid( m_ThreadInfo->fullJID );
+ if ( !szTo ) {
+ mir_free( szFrom );
+ return FALSE;
+ }
+
+ TCHAR* pDelimiter = _tcschr( szFrom, _T('/'));
+ if ( pDelimiter ) *pDelimiter = _T('\0');
+
+ pDelimiter = _tcschr( szTo, _T('/'));
+ if ( pDelimiter ) *pDelimiter = _T('\0');
+
+ BOOL bRetVal = _tcscmp( szFrom, szTo ) == 0;
+
+ mir_free( szFrom );
+ mir_free( szTo );
+
+ return bRetVal;
+}
+
+void __cdecl CJabberProto::LoadHttpAvatars(void* param)
+{
+ OBJLIST<JABBER_HTTP_AVATARS> &avs = *(OBJLIST<JABBER_HTTP_AVATARS>*)param;
+ HANDLE hHttpCon = NULL;
+ for (int i = 0; i < avs.getCount(); ++i)
+ {
+ NETLIBHTTPREQUEST nlhr = {0};
+ nlhr.cbSize = sizeof(nlhr);
+ nlhr.requestType = REQUEST_GET;
+ nlhr.flags = NLHRF_HTTP11 | NLHRF_REDIRECT | NLHRF_PERSISTENT;
+ nlhr.szUrl = avs[i].Url;
+ nlhr.nlc = hHttpCon;
+
+ NETLIBHTTPREQUEST * res = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)m_hNetlibUser, (LPARAM)&nlhr);
+ if (res)
+ {
+ hHttpCon = res->nlc;
+ if ( res->resultCode == 200 && res->dataLength )
+ {
+ int pictureType = JabberGetPictureType( res->pData );
+ if (pictureType != PA_FORMAT_UNKNOWN)
+ {
+ TCHAR tszFileName[ MAX_PATH ];
+
+ PROTO_AVATAR_INFORMATIONT AI;
+ AI.cbSize = sizeof(AI);
+ AI.format = pictureType;
+ AI.hContact = avs[i].hContact;
+
+ if ( JGetByte( AI.hContact, "AvatarType", PA_FORMAT_UNKNOWN ) != (unsigned char)pictureType ) {
+ GetAvatarFileName( AI.hContact, tszFileName, SIZEOF(tszFileName));
+ DeleteFile( tszFileName );
+ }
+
+ JSetByte( AI.hContact, "AvatarType", pictureType );
+
+ char cmpsha[ 41 ];
+ char buffer[ 41 ];
+ mir_sha1_byte_t digest[20];
+ mir_sha1_ctx sha;
+ mir_sha1_init( &sha );
+ mir_sha1_append( &sha, ( mir_sha1_byte_t* )res->pData, res->dataLength );
+ mir_sha1_finish( &sha, digest );
+ for ( int i=0; i<20; i++ )
+ sprintf( buffer+( i<<1 ), "%02x", digest[i] );
+
+ if (JGetStaticString("AvatarSaved", AI.hContact, cmpsha, sizeof(cmpsha)) || strnicmp(cmpsha, buffer, sizeof(buffer)))
+ {
+ GetAvatarFileName( AI.hContact, tszFileName, SIZEOF(tszFileName));
+ _tcsncpy(AI.filename, tszFileName, SIZEOF(AI.filename));
+ FILE* out = _tfopen( tszFileName, _T("wb"));
+ if ( out != NULL ) {
+ fwrite( res->pData, res->dataLength, 1, out );
+ fclose( out );
+ JSetString( AI.hContact, "AvatarSaved", buffer );
+ JSendBroadcast( AI.hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &AI, 0 );
+ Log("Broadcast new avatar: %s",AI.filename);
+ }
+ else JSendBroadcast( AI.hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, &AI, 0 );
+ }
+ }
+ }
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)res);
+ }
+ else
+ hHttpCon = NULL;
+ }
+ delete &avs;
+ if ( hHttpCon )
+ Netlib_CloseHandle(hHttpCon);
+}
\ No newline at end of file diff --git a/protocols/JabberG/src/jabber_vcard.cpp b/protocols/JabberG/src/jabber_vcard.cpp new file mode 100644 index 0000000000..691f3516ed --- /dev/null +++ b/protocols/JabberG/src/jabber_vcard.cpp @@ -0,0 +1,1250 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <io.h>
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CJabberProto::SendGetVcard( const TCHAR* jid )
+{
+ if (!m_bJabberOnline) return 0;
+
+ int iqId = SerialNext();
+ JABBER_IQ_PROCID procId = !lstrcmp( jid, m_szJabberJID ) ? IQ_PROC_GETVCARD : IQ_PROC_NONE;
+
+ IqAdd( iqId, procId, &CJabberProto::OnIqResultGetVcard );
+ m_ThreadInfo->send(
+ XmlNodeIq( _T("get"), iqId, jid ) << XCHILDNS( _T("vCard"), _T(JABBER_FEAT_VCARD_TEMP))
+ << XATTR( _T("prodid"), _T("-//HandGen//NONSGML vGen v1.0//EN")) << XATTR( _T("version"), _T("2.0")));
+
+ return iqId;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void SetDialogField( CJabberProto* ppro, HWND hwndDlg, int nDlgItem, char* key, bool bTranslate = false )
+{
+ DBVARIANT dbv;
+
+ if ( !DBGetContactSettingTString( NULL, ppro->m_szModuleName, key, &dbv )) {
+ SetDlgItemText( hwndDlg, nDlgItem, ( bTranslate ) ? TranslateTS(dbv.ptszVal) : dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else SetDlgItemTextA( hwndDlg, nDlgItem, "" );
+}
+
+static INT_PTR CALLBACK PersonalDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ const unsigned long iPageId = 0;
+ CJabberProto* ppro = ( CJabberProto* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ if ( lParam ) {
+ ppro = ( CJabberProto* )lParam;
+ TranslateDialogDefault( hwndDlg );
+ SendMessage( GetDlgItem( hwndDlg, IDC_GENDER ), CB_ADDSTRING, 0, ( LPARAM )TranslateT( "Male" ));
+ SendMessage( GetDlgItem( hwndDlg, IDC_GENDER ), CB_ADDSTRING, 0, ( LPARAM )TranslateT( "Female" ));
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+ SendMessage( hwndDlg, WM_JABBER_REFRESH_VCARD, 0, 0 );
+ ppro->WindowSubscribe(hwndDlg);
+ }
+ break;
+
+ case WM_JABBER_REFRESH_VCARD:
+ SetDialogField( ppro, hwndDlg, IDC_FULLNAME, "FullName" );
+ SetDialogField( ppro, hwndDlg, IDC_NICKNAME, "Nick" );
+ SetDialogField( ppro, hwndDlg, IDC_FIRSTNAME, "FirstName" );
+ SetDialogField( ppro, hwndDlg, IDC_MIDDLE, "MiddleName" );
+ SetDialogField( ppro, hwndDlg, IDC_LASTNAME, "LastName" );
+ SetDialogField( ppro, hwndDlg, IDC_BIRTH, "BirthDate" );
+ SetDialogField( ppro, hwndDlg, IDC_GENDER, "GenderString", true );
+ SetDialogField( ppro, hwndDlg, IDC_OCCUPATION, "Role" );
+ SetDialogField( ppro, hwndDlg, IDC_HOMEPAGE, "Homepage" );
+ break;
+
+ case WM_COMMAND:
+ if (( ( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE ) ||
+ (( HWND )lParam==GetDlgItem( hwndDlg, IDC_GENDER ) && ( HIWORD( wParam )==CBN_EDITCHANGE||HIWORD( wParam )==CBN_SELCHANGE )))
+ {
+ ppro->m_vCardUpdates |= (1UL<<iPageId);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->idFrom == 0) {
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_PARAMCHANGED:
+ SendMessage(hwndDlg, WM_INITDIALOG, 0, ((PSHNOTIFY*)lParam)->lParam);
+ break;
+ case PSN_APPLY:
+ ppro->m_vCardUpdates &= ~(1UL<<iPageId);
+ ppro->SaveVcardToDB( hwndDlg, iPageId );
+ if (!ppro->m_vCardUpdates)
+ ppro->SetServerVcard( ppro->m_bPhotoChanged, ppro->m_szPhotoFileName );
+ break;
+ } }
+ break;
+
+ case WM_DESTROY:
+ ppro->WindowUnsubscribe(hwndDlg);
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK HomeDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ const unsigned long iPageId = 1;
+ CJabberProto* ppro = ( CJabberProto* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ if ( lParam ) {
+ ppro = ( CJabberProto* )lParam;
+ TranslateDialogDefault( hwndDlg );
+ for (int i = 0; i < g_cbCountries; i++) {
+ if ( g_countries[i].id != 0xFFFF && g_countries[i].id != 0) {
+ TCHAR *country = mir_a2t(g_countries[i].szName);
+ SendMessage( GetDlgItem( hwndDlg, IDC_COUNTRY ), CB_ADDSTRING, 0, ( LPARAM )TranslateTS(country));
+ mir_free(country);
+ }
+ }
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+ SendMessage( hwndDlg, WM_JABBER_REFRESH_VCARD, 0, 0 );
+ ppro->WindowSubscribe(hwndDlg);
+ }
+ break;
+
+ case WM_JABBER_REFRESH_VCARD:
+ SetDialogField( ppro, hwndDlg, IDC_ADDRESS1, "Street" );
+ SetDialogField( ppro, hwndDlg, IDC_ADDRESS2, "Street2" );
+ SetDialogField( ppro, hwndDlg, IDC_CITY, "City" );
+ SetDialogField( ppro, hwndDlg, IDC_STATE, "State" );
+ SetDialogField( ppro, hwndDlg, IDC_ZIP, "ZIP" );
+ SetDialogField( ppro, hwndDlg, IDC_COUNTRY, "Country", true );
+ break;
+
+ case WM_COMMAND:
+ if ((( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE) ||
+ (( HWND )lParam==GetDlgItem( hwndDlg, IDC_COUNTRY ) && ( HIWORD( wParam )==CBN_EDITCHANGE||HIWORD( wParam )==CBN_SELCHANGE )))
+ {
+ ppro->m_vCardUpdates |= (1UL<<iPageId);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->idFrom == 0) {
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_PARAMCHANGED:
+ SendMessage(hwndDlg, WM_INITDIALOG, 0, ((PSHNOTIFY*)lParam)->lParam);
+ break;
+ case PSN_APPLY:
+ ppro->m_vCardUpdates &= ~(1UL<<iPageId);
+ ppro->SaveVcardToDB( hwndDlg, iPageId );
+ if (!ppro->m_vCardUpdates)
+ ppro->SetServerVcard( ppro->m_bPhotoChanged, ppro->m_szPhotoFileName );
+ break;
+ } }
+ break;
+
+ case WM_DESTROY:
+ ppro->WindowUnsubscribe(hwndDlg);
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK WorkDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ const unsigned long iPageId = 2;
+ CJabberProto* ppro = ( CJabberProto* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ if ( lParam ) { // proto info is available
+ ppro = ( CJabberProto* )lParam;
+ TranslateDialogDefault( hwndDlg );
+ for (int i = 0; i < g_cbCountries; i++) {
+ if ( g_countries[i].id != 0xFFFF && g_countries[i].id != 0 ) {
+ TCHAR *country = mir_a2t( g_countries[i].szName );
+ SendMessage( GetDlgItem( hwndDlg, IDC_COUNTRY ), CB_ADDSTRING, 0, ( LPARAM )TranslateTS(country));
+ mir_free(country);
+ }
+ }
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+ SendMessage( hwndDlg, WM_JABBER_REFRESH_VCARD, 0, 0 );
+ ppro->WindowSubscribe(hwndDlg);
+ }
+ break;
+
+ case WM_JABBER_REFRESH_VCARD:
+ SetDialogField( ppro, hwndDlg, IDC_COMPANY, "Company" );
+ SetDialogField( ppro, hwndDlg, IDC_DEPARTMENT, "CompanyDepartment" );
+ SetDialogField( ppro, hwndDlg, IDC_TITLE, "CompanyPosition" );
+ SetDialogField( ppro, hwndDlg, IDC_ADDRESS1, "CompanyStreet" );
+ SetDialogField( ppro, hwndDlg, IDC_ADDRESS2, "CompanyStreet2" );
+ SetDialogField( ppro, hwndDlg, IDC_CITY, "CompanyCity" );
+ SetDialogField( ppro, hwndDlg, IDC_STATE, "CompanyState" );
+ SetDialogField( ppro, hwndDlg, IDC_ZIP, "CompanyZIP" );
+ SetDialogField( ppro, hwndDlg, IDC_COUNTRY, "CompanyCountry", true );
+ break;
+
+ case WM_COMMAND:
+ if ((( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE) ||
+ (( HWND )lParam==GetDlgItem( hwndDlg, IDC_COUNTRY ) && ( HIWORD( wParam )==CBN_EDITCHANGE||HIWORD( wParam )==CBN_SELCHANGE )))
+ {
+ ppro->m_vCardUpdates |= (1UL<<iPageId);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->idFrom == 0) {
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_PARAMCHANGED:
+ SendMessage(hwndDlg, WM_INITDIALOG, 0, ((PSHNOTIFY*)lParam)->lParam);
+ break;
+ case PSN_APPLY:
+ ppro->m_vCardUpdates &= ~(1UL<<iPageId);
+ ppro->SaveVcardToDB( hwndDlg, iPageId );
+ if (!ppro->m_vCardUpdates)
+ ppro->SetServerVcard( ppro->m_bPhotoChanged, ppro->m_szPhotoFileName );
+ break;
+ } }
+ break;
+
+ case WM_DESTROY:
+ ppro->WindowUnsubscribe(hwndDlg);
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct PhotoDlgProcData
+{
+ CJabberProto* ppro;
+// char szPhotoFileName[MAX_PATH];
+// BOOL bPhotoChanged;
+ HBITMAP hBitmap;
+};
+
+static INT_PTR CALLBACK PhotoDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ const unsigned long iPageId = 3;
+
+ TCHAR szAvatarFileName[ MAX_PATH ], szTempPath[MAX_PATH], szTempFileName[MAX_PATH];
+ PhotoDlgProcData* dat = ( PhotoDlgProcData* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ if (!lParam) break; // Launched from userinfo
+ TranslateDialogDefault( hwndDlg );
+ SendDlgItemMessage( hwndDlg, IDC_LOAD, BM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadImage( hInst, MAKEINTRESOURCE( IDI_OPEN ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 ));
+ SendDlgItemMessage( hwndDlg, IDC_LOAD, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage( hwndDlg, IDC_DELETE, BM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadImage( hInst, MAKEINTRESOURCE( IDI_DELETE ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 ));
+ SendDlgItemMessage( hwndDlg, IDC_DELETE, BUTTONSETASFLATBTN, TRUE, 0);
+ ShowWindow( GetDlgItem( hwndDlg, IDC_SAVE ), SW_HIDE );
+ {
+ dat = new PhotoDlgProcData;
+ dat->ppro = ( CJabberProto* )lParam;
+ dat->hBitmap = NULL;
+ dat->ppro->m_bPhotoChanged = FALSE;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, ( LONG_PTR )dat );
+ dat->ppro->WindowSubscribe(hwndDlg);
+ }
+ SendMessage( hwndDlg, WM_JABBER_REFRESH_VCARD, 0, 0 );
+ break;
+
+ case WM_JABBER_REFRESH_VCARD:
+ if ( dat->hBitmap ) {
+ DeleteObject( dat->hBitmap );
+ dat->hBitmap = NULL;
+ DeleteFile( dat->ppro->m_szPhotoFileName );
+ dat->ppro->m_szPhotoFileName[0] = '\0';
+ }
+ EnableWindow( GetDlgItem( hwndDlg, IDC_DELETE ), FALSE );
+ dat->ppro->GetAvatarFileName( NULL, szAvatarFileName, SIZEOF( szAvatarFileName ));
+ if ( _taccess( szAvatarFileName, 0 ) == 0 ) {
+ if ( GetTempPath( SIZEOF( szTempPath ), szTempPath ) <= 0 )
+ _tcscpy( szTempPath, _T(".\\"));
+ if ( GetTempFileName( szTempPath, _T("jab"), 0, szTempFileName ) > 0 ) {
+ dat->ppro->Log( "Temp file = " TCHAR_STR_PARAM, szTempFileName );
+ if ( CopyFile( szAvatarFileName, szTempFileName, FALSE ) == TRUE ) {
+ char* p = mir_t2a( szTempFileName );
+ if (( dat->hBitmap=( HBITMAP ) CallService( MS_UTILS_LOADBITMAP, 0, ( LPARAM )p )) != NULL ) {
+ JabberBitmapPremultiplyChannels( dat->hBitmap );
+ _tcscpy( dat->ppro->m_szPhotoFileName, szTempFileName );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_DELETE ), TRUE );
+ }
+ else DeleteFile( szTempFileName );
+ mir_free(p);
+ }
+ else DeleteFile( szTempFileName );
+ } }
+
+ dat->ppro->m_bPhotoChanged = FALSE;
+ InvalidateRect( hwndDlg, NULL, TRUE );
+ UpdateWindow( hwndDlg );
+ break;
+
+ case WM_JABBER_CHANGED:
+ dat->ppro->SetServerVcard( dat->ppro->m_bPhotoChanged, dat->ppro->m_szPhotoFileName );
+ break;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDC_LOAD:
+ {
+ TCHAR szFilter[512];
+ TCHAR szFileName[MAX_PATH];
+
+ CallService( MS_UTILS_GETBITMAPFILTERSTRINGST, SIZEOF( szFilter ), ( LPARAM )szFilter );
+
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = hwndDlg;
+ ofn.lpstrFilter = szFilter;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.lpstrFile = szFileName;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_DONTADDTORECENT;
+ szFileName[0] = '\0';
+ if ( GetOpenFileName( &ofn )) {
+ struct _stat st;
+ HBITMAP hNewBitmap;
+
+ dat->ppro->Log( "File selected is " TCHAR_STR_PARAM, szFileName );
+ if ( _tstat( szFileName, &st )<0 || st.st_size>40*1024 ) {
+ MessageBox( hwndDlg, TranslateT( "Only JPG, GIF, and BMP image files smaller than 40 KB are supported." ), TranslateT( "Jabber vCard" ), MB_OK|MB_SETFOREGROUND );
+ break;
+ }
+ if ( GetTempPath( SIZEOF( szTempPath ), szTempPath ) <= 0 )
+ _tcscpy( szTempPath, _T(".\\"));
+ if ( GetTempFileName( szTempPath, _T("jab"), 0, szTempFileName ) > 0 ) {
+ dat->ppro->Log( "Temp file = " TCHAR_STR_PARAM, szTempFileName );
+ if ( CopyFile( szFileName, szTempFileName, FALSE ) == TRUE ) {
+ char* pszTemp = mir_t2a( szTempFileName );
+ if (( hNewBitmap=( HBITMAP ) CallService( MS_UTILS_LOADBITMAP, 0, ( LPARAM )pszTemp )) != NULL ) {
+ if ( dat->hBitmap ) {
+ DeleteObject( dat->hBitmap );
+ DeleteFile( dat->ppro->m_szPhotoFileName );
+ }
+
+ dat->hBitmap = hNewBitmap;
+ _tcscpy( dat->ppro->m_szPhotoFileName, szTempFileName );
+ dat->ppro->m_bPhotoChanged = TRUE;
+ EnableWindow( GetDlgItem( hwndDlg, IDC_DELETE ), TRUE );
+ InvalidateRect( hwndDlg, NULL, TRUE );
+ UpdateWindow( hwndDlg );
+ dat->ppro->m_vCardUpdates |= (1UL<<iPageId);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ else DeleteFile( szTempFileName );
+
+ mir_free( pszTemp );
+ }
+ else DeleteFile( szTempFileName );
+ }
+ }
+ }
+ break;
+ case IDC_DELETE:
+ if ( dat->hBitmap ) {
+ DeleteObject( dat->hBitmap );
+ dat->hBitmap = NULL;
+ DeleteFile( dat->ppro->m_szPhotoFileName );
+ dat->ppro->m_szPhotoFileName[0] = '\0';
+ dat->ppro->m_bPhotoChanged = TRUE;
+ EnableWindow( GetDlgItem( hwndDlg, IDC_DELETE ), FALSE );
+ InvalidateRect( hwndDlg, NULL, TRUE );
+ UpdateWindow( hwndDlg );
+ dat->ppro->m_vCardUpdates |= (1UL<<iPageId);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ break;
+ }
+ break;
+ case WM_PAINT:
+ if ( dat->hBitmap ) {
+ BITMAP bm;
+ HDC hdcMem;
+ HWND hwndCanvas;
+ HDC hdcCanvas;
+ POINT ptSize, ptOrg, pt, ptFitSize;
+ RECT rect;
+
+ hwndCanvas = GetDlgItem( hwndDlg, IDC_CANVAS );
+ hdcCanvas = GetDC( hwndCanvas );
+ hdcMem = CreateCompatibleDC( hdcCanvas );
+ SelectObject( hdcMem, dat->hBitmap );
+ SetMapMode( hdcMem, GetMapMode( hdcCanvas ));
+ GetObject( dat->hBitmap, sizeof( BITMAP ), ( LPVOID ) &bm );
+ ptSize.x = bm.bmWidth;
+ ptSize.y = bm.bmHeight;
+ DPtoLP( hdcCanvas, &ptSize, 1 );
+ ptOrg.x = ptOrg.y = 0;
+ DPtoLP( hdcMem, &ptOrg, 1 );
+ GetClientRect( hwndCanvas, &rect );
+ InvalidateRect( hwndCanvas, NULL, TRUE );
+ UpdateWindow( hwndCanvas );
+ if ( ptSize.x<=rect.right && ptSize.y<=rect.bottom ) {
+ pt.x = ( rect.right - ptSize.x )/2;
+ pt.y = ( rect.bottom - ptSize.y )/2;
+ ptFitSize = ptSize;
+ }
+ else {
+ if (( ( float )( ptSize.x-rect.right ))/ptSize.x > (( float )( ptSize.y-rect.bottom ))/ptSize.y ) {
+ ptFitSize.x = rect.right;
+ ptFitSize.y = ( ptSize.y*rect.right )/ptSize.x;
+ pt.x = 0;
+ pt.y = ( rect.bottom - ptFitSize.y )/2;
+ }
+ else {
+ ptFitSize.x = ( ptSize.x*rect.bottom )/ptSize.y;
+ ptFitSize.y = rect.bottom;
+ pt.x = ( rect.right - ptFitSize.x )/2;
+ pt.y = 0;
+ }
+ }
+
+ RECT rc;
+ GetClientRect(hwndCanvas, &rc);
+ if (JabberIsThemeActive && JabberDrawThemeParentBackground && JabberIsThemeActive())
+ JabberDrawThemeParentBackground(hwndCanvas, hdcCanvas, &rc);
+ else
+ FillRect(hdcCanvas, &rc, (HBRUSH)GetSysColorBrush(COLOR_BTNFACE));
+
+ if (JabberAlphaBlend && (bm.bmBitsPixel == 32)) {
+ BLENDFUNCTION bf = {0};
+ bf.AlphaFormat = AC_SRC_ALPHA;
+ bf.BlendOp = AC_SRC_OVER;
+ bf.SourceConstantAlpha = 255;
+ JabberAlphaBlend( hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, bf );
+ }
+ else {
+ SetStretchBltMode( hdcCanvas, COLORONCOLOR );
+ StretchBlt( hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY );
+ }
+
+ DeleteDC( hdcMem );
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->idFrom == 0) {
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_PARAMCHANGED:
+ SendMessage(hwndDlg, WM_INITDIALOG, 0, ((PSHNOTIFY*)lParam)->lParam);
+ break;
+ case PSN_APPLY:
+ dat->ppro->m_vCardUpdates &= ~(1UL<<iPageId);
+ dat->ppro->SaveVcardToDB( hwndDlg, iPageId );
+ if (!dat->ppro->m_vCardUpdates)
+ dat->ppro->SetServerVcard( dat->ppro->m_bPhotoChanged, dat->ppro->m_szPhotoFileName );
+ break;
+ } }
+ break;
+
+ case WM_DESTROY:
+ DestroyIcon(( HICON )SendDlgItemMessage( hwndDlg, IDC_LOAD, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ DestroyIcon(( HICON )SendDlgItemMessage( hwndDlg, IDC_DELETE, BM_SETIMAGE, IMAGE_ICON, 0 ));
+ dat->ppro->WindowUnsubscribe(hwndDlg);
+ if ( dat->hBitmap ) {
+ dat->ppro->Log( "Delete bitmap" );
+ DeleteObject( dat->hBitmap );
+ DeleteFile( dat->ppro->m_szPhotoFileName );
+ }
+ delete dat;
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR CALLBACK NoteDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ const unsigned long iPageId = 4;
+ CJabberProto* ppro = ( CJabberProto* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ if (!lParam) break; // Launched from userinfo
+ ppro = ( CJabberProto* )lParam;
+ TranslateDialogDefault( hwndDlg );
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+ SendMessage( hwndDlg, WM_JABBER_REFRESH_VCARD, 0, 0 );
+ ppro->WindowSubscribe(hwndDlg);
+ break;
+ case WM_JABBER_REFRESH_VCARD:
+ {
+ SetDialogField( ppro, hwndDlg, IDC_DESC, "About" );
+ break;
+ }
+ case WM_COMMAND:
+ if (( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE )
+ {
+ ppro->m_vCardUpdates |= (1UL<<iPageId);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ break;
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->idFrom == 0) {
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_PARAMCHANGED:
+ SendMessage(hwndDlg, WM_INITDIALOG, 0, ((PSHNOTIFY*)lParam)->lParam);
+ break;
+ case PSN_APPLY:
+ ppro->m_vCardUpdates &= ~(1UL<<iPageId);
+ ppro->SaveVcardToDB( hwndDlg, iPageId );
+ if (!ppro->m_vCardUpdates)
+ ppro->SetServerVcard( ppro->m_bPhotoChanged, ppro->m_szPhotoFileName );
+ break;
+ } }
+ break;
+ case WM_DESTROY:
+ ppro->WindowUnsubscribe(hwndDlg);
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct EditDlgParam
+{
+ int id;
+ CJabberProto* ppro;
+};
+
+static INT_PTR CALLBACK EditEmailDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ EditDlgParam* dat = ( EditDlgParam* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ {
+ EditDlgParam* dat = ( EditDlgParam* )lParam;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+
+ TranslateDialogDefault( hwndDlg );
+
+ if ( lParam >= 0 ) {
+ DBVARIANT dbv;
+ char idstr[33];
+ WORD nFlag;
+
+ SetWindowText( hwndDlg, TranslateT( "Jabber vCard: Edit Email Address" ));
+ wsprintfA( idstr, "e-mail%d", dat->id );
+ if ( !DBGetContactSettingString( NULL, dat->ppro->m_szModuleName, idstr, &dbv )) {
+ SetDlgItemTextA( hwndDlg, IDC_EMAIL, dbv.pszVal );
+ JFreeVariant( &dbv );
+ wsprintfA( idstr, "e-mailFlag%d", lParam );
+ nFlag = DBGetContactSettingWord( NULL, dat->ppro->m_szModuleName, idstr, 0 );
+ if ( nFlag & JABBER_VCEMAIL_HOME ) CheckDlgButton( hwndDlg, IDC_HOME, TRUE );
+ if ( nFlag & JABBER_VCEMAIL_WORK ) CheckDlgButton( hwndDlg, IDC_WORK, TRUE );
+ if ( nFlag & JABBER_VCEMAIL_INTERNET ) CheckDlgButton( hwndDlg, IDC_INTERNET, TRUE );
+ if ( nFlag & JABBER_VCEMAIL_X400 ) CheckDlgButton( hwndDlg, IDC_X400, TRUE );
+ } } }
+ break;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDOK:
+ {
+ TCHAR text[128];
+ char idstr[33];
+ DBVARIANT dbv;
+ WORD nFlag;
+
+ if ( dat->id < 0 ) {
+ for ( dat->id=0;;dat->id++ ) {
+ mir_snprintf( idstr, SIZEOF(idstr), "e-mail%d", dat->id );
+ if ( DBGetContactSettingString( NULL, dat->ppro->m_szModuleName, idstr, &dbv )) break;
+ JFreeVariant( &dbv );
+ } }
+ GetDlgItemText( hwndDlg, IDC_EMAIL, text, SIZEOF( text ));
+ mir_snprintf( idstr, SIZEOF(idstr), "e-mail%d", dat->id );
+ dat->ppro->JSetStringT( NULL, idstr, text );
+
+ nFlag = 0;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_HOME )) nFlag |= JABBER_VCEMAIL_HOME;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_WORK )) nFlag |= JABBER_VCEMAIL_WORK;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_INTERNET )) nFlag |= JABBER_VCEMAIL_INTERNET;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_X400 )) nFlag |= JABBER_VCEMAIL_X400;
+ mir_snprintf( idstr, SIZEOF(idstr), "e-mailFlag%d", dat->id );
+ dat->ppro->JSetWord( NULL, idstr, nFlag );
+ }
+ // fall through
+ case IDCANCEL:
+ EndDialog( hwndDlg, wParam );
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK EditPhoneDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ EditDlgParam* dat = ( EditDlgParam* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ {
+ EditDlgParam* dat = ( EditDlgParam* )lParam;
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+
+ TranslateDialogDefault( hwndDlg );
+ if ( dat->id >= 0 ) {
+ DBVARIANT dbv;
+ char idstr[33];
+ WORD nFlag;
+
+ SetWindowText( hwndDlg, TranslateT( "Jabber vCard: Edit Phone Number" ));
+ wsprintfA( idstr, "Phone%d", dat->id );
+ if ( !DBGetContactSettingString( NULL, dat->ppro->m_szModuleName, idstr, &dbv )) {
+ SetDlgItemTextA( hwndDlg, IDC_PHONE, dbv.pszVal );
+ JFreeVariant( &dbv );
+ wsprintfA( idstr, "PhoneFlag%d", dat->id );
+ nFlag = dat->ppro->JGetWord( NULL, idstr, 0 );
+ if ( nFlag & JABBER_VCTEL_HOME ) CheckDlgButton( hwndDlg, IDC_HOME, TRUE );
+ if ( nFlag & JABBER_VCTEL_WORK ) CheckDlgButton( hwndDlg, IDC_WORK, TRUE );
+ if ( nFlag & JABBER_VCTEL_VOICE ) CheckDlgButton( hwndDlg, IDC_VOICE, TRUE );
+ if ( nFlag & JABBER_VCTEL_FAX ) CheckDlgButton( hwndDlg, IDC_FAX, TRUE );
+ if ( nFlag & JABBER_VCTEL_PAGER ) CheckDlgButton( hwndDlg, IDC_PAGER, TRUE );
+ if ( nFlag & JABBER_VCTEL_MSG ) CheckDlgButton( hwndDlg, IDC_MSG, TRUE );
+ if ( nFlag & JABBER_VCTEL_CELL ) CheckDlgButton( hwndDlg, IDC_CELL, TRUE );
+ if ( nFlag & JABBER_VCTEL_VIDEO ) CheckDlgButton( hwndDlg, IDC_VIDEO, TRUE );
+ if ( nFlag & JABBER_VCTEL_BBS ) CheckDlgButton( hwndDlg, IDC_BBS, TRUE );
+ if ( nFlag & JABBER_VCTEL_MODEM ) CheckDlgButton( hwndDlg, IDC_MODEM, TRUE );
+ if ( nFlag & JABBER_VCTEL_ISDN ) CheckDlgButton( hwndDlg, IDC_ISDN, TRUE );
+ if ( nFlag & JABBER_VCTEL_PCS ) CheckDlgButton( hwndDlg, IDC_PCS, TRUE );
+ } } }
+ break;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDOK:
+ {
+ char text[128];
+ char idstr[33];
+ DBVARIANT dbv;
+ WORD nFlag;
+
+ if ( dat->id < 0 ) {
+ for ( dat->id=0;;dat->id++ ) {
+ wsprintfA( idstr, "Phone%d", dat->id );
+ if ( DBGetContactSettingString( NULL, dat->ppro->m_szModuleName, idstr, &dbv )) break;
+ JFreeVariant( &dbv );
+ }
+ }
+ GetDlgItemTextA( hwndDlg, IDC_PHONE, text, SIZEOF( text ));
+ wsprintfA( idstr, "Phone%d", dat->id );
+ dat->ppro->JSetString( NULL, idstr, text );
+ nFlag = 0;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_HOME )) nFlag |= JABBER_VCTEL_HOME;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_WORK )) nFlag |= JABBER_VCTEL_WORK;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_VOICE )) nFlag |= JABBER_VCTEL_VOICE;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_FAX )) nFlag |= JABBER_VCTEL_FAX;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_PAGER )) nFlag |= JABBER_VCTEL_PAGER;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_MSG )) nFlag |= JABBER_VCTEL_MSG;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_CELL )) nFlag |= JABBER_VCTEL_CELL;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_VIDEO )) nFlag |= JABBER_VCTEL_VIDEO;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_BBS )) nFlag |= JABBER_VCTEL_BBS;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_MODEM )) nFlag |= JABBER_VCTEL_MODEM;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_ISDN )) nFlag |= JABBER_VCTEL_ISDN;
+ if ( IsDlgButtonChecked( hwndDlg, IDC_PCS )) nFlag |= JABBER_VCTEL_PCS;
+ wsprintfA( idstr, "PhoneFlag%d", dat->id );
+ dat->ppro->JSetWord( NULL, idstr, nFlag );
+ }
+ // fall through
+ case IDCANCEL:
+ EndDialog( hwndDlg, wParam );
+ break;
+ }
+ }
+ return FALSE;
+}
+
+#define M_REMAKELISTS ( WM_USER+1 )
+static INT_PTR CALLBACK ContactDlgProc( HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam )
+{
+ const unsigned long iPageId = 5;
+ CJabberProto* ppro = ( CJabberProto* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch( msg ) {
+ case WM_INITDIALOG:
+ if (!lParam) break; // Launched from userinfo
+ ppro = ( CJabberProto* )lParam;
+ {
+ LVCOLUMN lvc;
+ RECT rc;
+
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+
+ TranslateDialogDefault( hwndDlg );
+ GetClientRect( GetDlgItem( hwndDlg,IDC_EMAILS ), &rc );
+ rc.right -= GetSystemMetrics( SM_CXVSCROLL );
+ lvc.mask = LVCF_WIDTH;
+ lvc.cx = 30;
+ ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_EMAILS ), 0, &lvc );
+ ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_PHONES ), 0, &lvc );
+ lvc.cx = rc.right - 30 - 40;
+ ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_EMAILS ), 1, &lvc );
+ ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_PHONES ), 1, &lvc );
+ lvc.cx = 20;
+ ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_EMAILS ), 2, &lvc );
+ ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_EMAILS ), 3, &lvc );
+ ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_PHONES ), 2, &lvc );
+ ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_PHONES ), 3, &lvc );
+ SendMessage( hwndDlg, M_REMAKELISTS, 0, 0 );
+
+ ppro->WindowSubscribe(hwndDlg);
+ }
+ break;
+ case M_REMAKELISTS:
+ {
+ LVITEM lvi;
+ int i;
+ char idstr[33];
+ TCHAR number[20];
+ DBVARIANT dbv;
+
+ //e-mails
+ ListView_DeleteAllItems( GetDlgItem( hwndDlg, IDC_EMAILS ));
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iSubItem = 0;
+ lvi.iItem = 0;
+ for ( i=0;;i++ ) {
+ wsprintfA( idstr, "e-mail%d", i );
+ if ( DBGetContactSettingTString( NULL, ppro->m_szModuleName, idstr, &dbv )) break;
+ wsprintf( number, _T("%d"), i+1 );
+ lvi.pszText = number;
+ lvi.lParam = ( LPARAM )i;
+ ListView_InsertItem( GetDlgItem( hwndDlg, IDC_EMAILS ), &lvi );
+ ListView_SetItemText( GetDlgItem( hwndDlg, IDC_EMAILS ), lvi.iItem, 1, dbv.ptszVal );
+ JFreeVariant( &dbv );
+ lvi.iItem++;
+ }
+ lvi.mask = LVIF_PARAM;
+ lvi.lParam = ( LPARAM )( -1 );
+ ListView_InsertItem( GetDlgItem( hwndDlg, IDC_EMAILS ), &lvi );
+ //phones
+ ListView_DeleteAllItems( GetDlgItem( hwndDlg, IDC_PHONES ));
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iSubItem = 0;
+ lvi.iItem = 0;
+ for ( i=0;;i++ ) {
+ wsprintfA( idstr, "Phone%d", i );
+ if ( DBGetContactSettingTString( NULL, ppro->m_szModuleName, idstr, &dbv )) break;
+ wsprintf( number, _T("%d"), i+1 );
+ lvi.pszText = number;
+ lvi.lParam = ( LPARAM )i;
+ ListView_InsertItem( GetDlgItem( hwndDlg, IDC_PHONES ), &lvi );
+ ListView_SetItemText( GetDlgItem( hwndDlg, IDC_PHONES ), lvi.iItem, 1, dbv.ptszVal );
+ JFreeVariant( &dbv );
+ lvi.iItem++;
+ }
+ lvi.mask = LVIF_PARAM;
+ lvi.lParam = ( LPARAM )( -1 );
+ ListView_InsertItem( GetDlgItem( hwndDlg, IDC_PHONES ), &lvi );
+ }
+ break;
+ case WM_NOTIFY:
+ switch (( ( LPNMHDR )lParam )->idFrom ) {
+ case 0: {
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_PARAMCHANGED:
+ SendMessage(hwndDlg, WM_INITDIALOG, 0, ((PSHNOTIFY*)lParam)->lParam);
+ break;
+ case PSN_APPLY:
+ ppro->m_vCardUpdates &= ~(1UL<<iPageId);
+ ppro->SaveVcardToDB( hwndDlg, iPageId );
+ if (!ppro->m_vCardUpdates)
+ ppro->SetServerVcard( ppro->m_bPhotoChanged, ppro->m_szPhotoFileName );
+ break;
+ } }
+ break;
+
+ case IDC_EMAILS:
+ case IDC_PHONES:
+ switch (( ( LPNMHDR )lParam )->code ) {
+ case NM_CUSTOMDRAW:
+ {
+ NMLVCUSTOMDRAW *nm = ( NMLVCUSTOMDRAW * ) lParam;
+
+ switch ( nm->nmcd.dwDrawStage ) {
+ case CDDS_PREPAINT:
+ case CDDS_ITEMPREPAINT:
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW );
+ return TRUE;
+ case CDDS_SUBITEM|CDDS_ITEMPREPAINT:
+ {
+ RECT rc;
+ HICON hIcon;
+
+ ListView_GetSubItemRect( nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, LVIR_LABEL, &rc );
+ if ( nm->nmcd.lItemlParam==( LPARAM )( -1 ) && nm->iSubItem==3 )
+ hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_ADDCONTACT ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 );
+ else if ( nm->iSubItem==2 && nm->nmcd.lItemlParam!=( LPARAM )( -1 ))
+ hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_EDIT ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 );
+ else if ( nm->iSubItem==3 && nm->nmcd.lItemlParam!=( LPARAM )( -1 ))
+ hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_DELETE ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 );
+ else break;
+ DrawIconEx( nm->nmcd.hdc, ( rc.left+rc.right-GetSystemMetrics( SM_CXSMICON ))/2, ( rc.top+rc.bottom-GetSystemMetrics( SM_CYSMICON ))/2,hIcon, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0, NULL, DI_NORMAL );
+ DestroyIcon( hIcon );
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT );
+ }
+ return TRUE;
+ }
+ }
+ break;
+ case NM_CLICK:
+ {
+ NMLISTVIEW *nm = ( NMLISTVIEW * ) lParam;
+ LVITEM lvi;
+ const char* szIdTemplate = nm->hdr.idFrom==IDC_PHONES?"Phone%d":"e-mail%d";
+ const char* szFlagTemplate = nm->hdr.idFrom==IDC_PHONES?"PhoneFlag%d":"e-mailFlag%d";
+ LVHITTESTINFO hti;
+
+ if ( nm->iSubItem < 2 ) break;
+ hti.pt.x = ( short ) LOWORD( GetMessagePos());
+ hti.pt.y = ( short ) HIWORD( GetMessagePos());
+ ScreenToClient( nm->hdr.hwndFrom, &hti.pt );
+ if ( ListView_SubItemHitTest( nm->hdr.hwndFrom, &hti ) == -1 ) break;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = hti.iItem;
+ lvi.iSubItem = 0;
+ ListView_GetItem( nm->hdr.hwndFrom, &lvi );
+ if ( lvi.lParam == ( LPARAM )( -1 )) {
+ if ( hti.iSubItem == 3 ) {
+ //add
+ EditDlgParam param = { -1, ppro };
+ int res;
+ if ( nm->hdr.idFrom == IDC_PHONES )
+ res = DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_VCARD_ADDPHONE ), hwndDlg, EditPhoneDlgProc, ( LPARAM )¶m );
+ else
+ res = DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_VCARD_ADDEMAIL ), hwndDlg, EditEmailDlgProc, ( LPARAM )¶m );
+ if ( res != IDOK )
+ break;
+ SendMessage( hwndDlg, M_REMAKELISTS, 0, 0 );
+ ppro->m_vCardUpdates |= (1UL<<iPageId);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ }
+ else {
+ if ( hti.iSubItem == 3 ) {
+ //delete
+ int i;
+ char idstr[33];
+ DBVARIANT dbv;
+
+ for ( i=lvi.lParam;;i++ ) {
+ WORD nFlag;
+
+ wsprintfA( idstr, szIdTemplate, i+1 );
+ if ( DBGetContactSettingString( NULL, ppro->m_szModuleName, idstr, &dbv )) break;
+ wsprintfA( idstr,szIdTemplate,i );
+ ppro->JSetString( NULL, idstr, dbv.pszVal );
+ wsprintfA( idstr, szFlagTemplate, i+1 );
+ JFreeVariant( &dbv );
+ nFlag = ppro->JGetWord( NULL, idstr, 0 );
+ wsprintfA( idstr, szFlagTemplate, i );
+ ppro->JSetWord( NULL, idstr, nFlag );
+ }
+ wsprintfA( idstr, szIdTemplate, i );
+ ppro->JDeleteSetting( NULL, idstr );
+ wsprintfA( idstr, szFlagTemplate, i );
+ ppro->JDeleteSetting( NULL, idstr );
+ SendMessage( hwndDlg, M_REMAKELISTS, 0, 0 );
+ ppro->m_vCardUpdates |= (1UL<<iPageId);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ else if ( hti.iSubItem == 2 ) {
+ EditDlgParam param = { lvi.lParam, ppro };
+ int res;
+ if ( nm->hdr.idFrom == IDC_PHONES )
+ res = DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_VCARD_ADDPHONE ), hwndDlg, EditPhoneDlgProc, ( LPARAM )¶m );
+ else
+ res = DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_VCARD_ADDEMAIL ), hwndDlg, EditEmailDlgProc, ( LPARAM )¶m );
+ if ( res != IDOK )
+ break;
+ SendMessage( hwndDlg,M_REMAKELISTS,0,0 );
+ ppro->m_vCardUpdates |= (1UL<<iPageId);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ }
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case WM_SETCURSOR:
+ if ( LOWORD( lParam ) != HTCLIENT ) break;
+ if ( GetForegroundWindow() == GetParent( hwndDlg )) {
+ POINT pt;
+ GetCursorPos( &pt );
+ ScreenToClient( hwndDlg,&pt );
+ SetFocus( ChildWindowFromPoint( hwndDlg,pt )); //ugly hack because listviews ignore their first click
+ }
+ break;
+ case WM_DESTROY:
+ ppro->WindowUnsubscribe(hwndDlg);
+ break;
+ }
+ return FALSE;
+}
+
+void CJabberProto::SaveVcardToDB( HWND hwndPage, int iPage )
+{
+ TCHAR text[2048];
+
+ // Page 0: Personal
+ switch (iPage) {
+ case 0:
+ GetDlgItemText( hwndPage, IDC_FULLNAME, text, SIZEOF( text ));
+ JSetStringT( NULL, "FullName", text );
+ GetDlgItemText( hwndPage, IDC_NICKNAME, text, SIZEOF( text ));
+ JSetStringT( NULL, "Nick", text );
+ GetDlgItemText( hwndPage, IDC_FIRSTNAME, text, SIZEOF( text ));
+ JSetStringT( NULL, "FirstName", text );
+ GetDlgItemText( hwndPage, IDC_MIDDLE, text, SIZEOF( text ));
+ JSetStringT( NULL, "MiddleName", text );
+ GetDlgItemText( hwndPage, IDC_LASTNAME, text, SIZEOF( text ));
+ JSetStringT( NULL, "LastName", text );
+ GetDlgItemText( hwndPage, IDC_BIRTH, text, SIZEOF( text ));
+ JSetStringT( NULL, "BirthDate", text );
+ switch( SendMessage( GetDlgItem( hwndPage, IDC_GENDER ), CB_GETCURSEL, 0, 0 )) {
+ case 0: JSetString( NULL, "GenderString", "Male" ); break;
+ case 1: JSetString( NULL, "GenderString", "Female" ); break;
+ default: JSetString( NULL, "GenderString", "" ); break;
+ }
+ GetDlgItemText( hwndPage, IDC_OCCUPATION, text, SIZEOF( text ));
+ JSetStringT( NULL, "Role", text );
+ GetDlgItemText( hwndPage, IDC_HOMEPAGE, text, SIZEOF( text ));
+ JSetStringT( NULL, "Homepage", text );
+ break;
+
+ // Page 1: Home
+ case 1:
+ GetDlgItemText( hwndPage, IDC_ADDRESS1, text, SIZEOF( text ));
+ JSetStringT( NULL, "Street", text );
+ GetDlgItemText( hwndPage, IDC_ADDRESS2, text, SIZEOF( text ));
+ JSetStringT( NULL, "Street2", text );
+ GetDlgItemText( hwndPage, IDC_CITY, text, SIZEOF( text ));
+ JSetStringT( NULL, "City", text );
+ GetDlgItemText( hwndPage, IDC_STATE, text, SIZEOF( text ));
+ JSetStringT( NULL, "State", text );
+ GetDlgItemText( hwndPage, IDC_ZIP, text, SIZEOF( text ));
+ JSetStringT( NULL, "ZIP", text );
+ {
+ int i = SendMessage( GetDlgItem( hwndPage, IDC_COUNTRY ), CB_GETCURSEL, 0, 0 );
+ TCHAR *country = mir_a2t((i) ? g_countries[i+2].szName : g_countries[1].szName);
+ JSetStringT( NULL, "Country", country );
+ mir_free(country);
+ }
+ break;
+
+ // Page 2: Work
+ case 2:
+ GetDlgItemText( hwndPage, IDC_COMPANY, text, SIZEOF( text ));
+ JSetStringT( NULL, "Company", text );
+ GetDlgItemText( hwndPage, IDC_DEPARTMENT, text, SIZEOF( text ));
+ JSetStringT( NULL, "CompanyDepartment", text );
+ GetDlgItemText( hwndPage, IDC_TITLE, text, SIZEOF( text ));
+ JSetStringT( NULL, "CompanyPosition", text );
+ GetDlgItemText( hwndPage, IDC_ADDRESS1, text, SIZEOF( text ));
+ JSetStringT( NULL, "CompanyStreet", text );
+ GetDlgItemText( hwndPage, IDC_ADDRESS2, text, SIZEOF( text ));
+ JSetStringT( NULL, "CompanyStreet2", text );
+ GetDlgItemText( hwndPage, IDC_CITY, text, SIZEOF( text ));
+ JSetStringT( NULL, "CompanyCity", text );
+ GetDlgItemText( hwndPage, IDC_STATE, text, SIZEOF( text ));
+ JSetStringT( NULL, "CompanyState", text );
+ GetDlgItemText( hwndPage, IDC_ZIP, text, SIZEOF( text ));
+ JSetStringT( NULL, "CompanyZIP", text );
+ {
+ int i = SendMessage( GetDlgItem( hwndPage, IDC_COUNTRY ), CB_GETCURSEL, 0, 0 );
+ TCHAR *country = mir_a2t((i) ? g_countries[i+2].szName : g_countries[1].szName);
+ JSetStringT( NULL, "CompanyCountry", country );
+ mir_free(country);
+ }
+ break;
+
+ // Page 3: Photo
+ // not needed to be saved into db
+
+ // Page 4: Note
+ case 4:
+ GetDlgItemText( hwndPage, IDC_DESC, text, SIZEOF( text ));
+ JSetStringT( NULL, "About", text );
+ break;
+
+ // Page 5: Contacts
+ // is always synced with db
+} }
+
+void CJabberProto::AppendVcardFromDB( HXML n, char* tag, char* key )
+{
+ if ( n == NULL || tag == NULL || key == NULL )
+ return;
+
+ DBVARIANT dbv;
+ if ( DBGetContactSettingTString( NULL, m_szModuleName, key, &dbv ))
+ n << XCHILD( _A2T(tag));
+ else {
+ n << XCHILD( _A2T(tag), dbv.ptszVal );
+ JFreeVariant( &dbv );
+} }
+
+void CJabberProto::SetServerVcard( BOOL bPhotoChanged, TCHAR* szPhotoFileName )
+{
+ if (!m_bJabberOnline) return;
+
+ DBVARIANT dbv;
+ int iqId;
+ TCHAR *szFileName;
+ int i;
+ char idstr[33];
+ WORD nFlag;
+
+ iqId = SerialNext();
+ IqAdd( iqId, IQ_PROC_SETVCARD, &CJabberProto::OnIqResultSetVcard );
+
+ XmlNodeIq iq( _T("set"), iqId );
+ HXML v = iq << XCHILDNS( _T("vCard"), _T(JABBER_FEAT_VCARD_TEMP));
+
+ AppendVcardFromDB( v, "FN", "FullName" );
+
+ HXML n = v << XCHILD( _T("N"));
+ AppendVcardFromDB( n, "GIVEN", "FirstName" );
+ AppendVcardFromDB( n, "MIDDLE", "MiddleName" );
+ AppendVcardFromDB( n, "FAMILY", "LastName" );
+
+ AppendVcardFromDB( v, "NICKNAME", "Nick" );
+ AppendVcardFromDB( v, "BDAY", "BirthDate" );
+ AppendVcardFromDB( v, "GENDER", "GenderString" );
+
+ for ( i=0;;i++ ) {
+ wsprintfA( idstr, "e-mail%d", i );
+ if ( DBGetContactSettingTString( NULL, m_szModuleName, idstr, &dbv ))
+ break;
+
+ HXML e = v << XCHILD( _T("EMAIL"), dbv.ptszVal );
+ JFreeVariant( &dbv );
+ AppendVcardFromDB( e, "USERID", idstr );
+
+ wsprintfA( idstr, "e-mailFlag%d", i );
+ nFlag = DBGetContactSettingWord( NULL, m_szModuleName, idstr, 0 );
+ if ( nFlag & JABBER_VCEMAIL_HOME ) e << XCHILD( _T("HOME"));
+ if ( nFlag & JABBER_VCEMAIL_WORK ) e << XCHILD( _T("WORK"));
+ if ( nFlag & JABBER_VCEMAIL_INTERNET ) e << XCHILD( _T("INTERNET"));
+ if ( nFlag & JABBER_VCEMAIL_X400 ) e << XCHILD( _T("X400"));
+ }
+
+ n = v << XCHILD( _T("ADR"));
+ n << XCHILD( _T("HOME"));
+ AppendVcardFromDB( n, "STREET", "Street" );
+ AppendVcardFromDB( n, "EXTADR", "Street2" );
+ AppendVcardFromDB( n, "EXTADD", "Street2" ); // for compatibility with client using old vcard format
+ AppendVcardFromDB( n, "LOCALITY", "City" );
+ AppendVcardFromDB( n, "REGION", "State" );
+ AppendVcardFromDB( n, "PCODE", "ZIP" );
+ AppendVcardFromDB( n, "CTRY", "Country" );
+ AppendVcardFromDB( n, "COUNTRY", "Country" ); // for compatibility with client using old vcard format
+
+ n = v << XCHILD( _T("ADR"));
+ n << XCHILD( _T("WORK"));
+ AppendVcardFromDB( n, "STREET", "CompanyStreet" );
+ AppendVcardFromDB( n, "EXTADR", "CompanyStreet2" );
+ AppendVcardFromDB( n, "EXTADD", "CompanyStreet2" ); // for compatibility with client using old vcard format
+ AppendVcardFromDB( n, "LOCALITY", "CompanyCity" );
+ AppendVcardFromDB( n, "REGION", "CompanyState" );
+ AppendVcardFromDB( n, "PCODE", "CompanyZIP" );
+ AppendVcardFromDB( n, "CTRY", "CompanyCountry" );
+ AppendVcardFromDB( n, "COUNTRY", "CompanyCountry" ); // for compatibility with client using old vcard format
+
+ n = v << XCHILD( _T("ORG"));
+ AppendVcardFromDB( n, "ORGNAME", "Company" );
+ AppendVcardFromDB( n, "ORGUNIT", "CompanyDepartment" );
+
+ AppendVcardFromDB( v, "TITLE", "CompanyPosition" );
+ AppendVcardFromDB( v, "ROLE", "Role" );
+ AppendVcardFromDB( v, "URL", "Homepage" );
+ AppendVcardFromDB( v, "DESC", "About" );
+
+ for ( i=0;;i++ ) {
+ wsprintfA( idstr, "Phone%d", i );
+ if ( DBGetContactSettingTString( NULL, m_szModuleName, idstr, &dbv )) break;
+ JFreeVariant( &dbv );
+
+ n = v << XCHILD( _T("TEL"));
+ AppendVcardFromDB( n, "NUMBER", idstr );
+
+ wsprintfA( idstr, "PhoneFlag%d", i );
+ nFlag = JGetWord( NULL, idstr, 0 );
+ if ( nFlag & JABBER_VCTEL_HOME ) n << XCHILD( _T("HOME"));
+ if ( nFlag & JABBER_VCTEL_WORK ) n << XCHILD( _T("WORK"));
+ if ( nFlag & JABBER_VCTEL_VOICE ) n << XCHILD( _T("VOICE"));
+ if ( nFlag & JABBER_VCTEL_FAX ) n << XCHILD( _T("FAX"));
+ if ( nFlag & JABBER_VCTEL_PAGER ) n << XCHILD( _T("PAGER"));
+ if ( nFlag & JABBER_VCTEL_MSG ) n << XCHILD( _T("MSG"));
+ if ( nFlag & JABBER_VCTEL_CELL ) n << XCHILD( _T("CELL"));
+ if ( nFlag & JABBER_VCTEL_VIDEO ) n << XCHILD( _T("VIDEO"));
+ if ( nFlag & JABBER_VCTEL_BBS ) n << XCHILD( _T("BBS"));
+ if ( nFlag & JABBER_VCTEL_MODEM ) n << XCHILD( _T("MODEM"));
+ if ( nFlag & JABBER_VCTEL_ISDN ) n << XCHILD( _T("ISDN"));
+ if ( nFlag & JABBER_VCTEL_PCS ) n << XCHILD( _T("PCS"));
+ }
+
+ TCHAR szAvatarName[ MAX_PATH ];
+ GetAvatarFileName( NULL, szAvatarName, SIZEOF( szAvatarName ));
+ if ( bPhotoChanged )
+ szFileName = szPhotoFileName;
+ else
+ szFileName = szAvatarName;
+
+ // Set photo element, also update the global jabberVcardPhotoFileName to reflect the update
+ Log( "Before update, file name = " TCHAR_STR_PARAM, szFileName );
+ if ( szFileName == NULL || szFileName[0] == 0 ) {
+ v << XCHILD( _T("PHOTO"));
+ DeleteFile( szAvatarName );
+ JDeleteSetting( NULL, "AvatarSaved" );
+ JDeleteSetting( NULL, "AvatarHash" );
+ }
+ else {
+ HANDLE hFile;
+ struct _stat st;
+ char* buffer, *str;
+ DWORD nRead;
+
+ Log( "Saving picture from " TCHAR_STR_PARAM, szFileName );
+ if ( _tstat( szFileName, &st ) >= 0 ) {
+ // Note the FILE_SHARE_READ attribute so that the CopyFile can succeed
+ if (( hFile=CreateFile( szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) != INVALID_HANDLE_VALUE ) {
+ if (( buffer=( char* )mir_alloc( st.st_size )) != NULL ) {
+ if ( ReadFile( hFile, buffer, st.st_size, &nRead, NULL )) {
+ if (( str=JabberBase64Encode( buffer, nRead )) != NULL ) {
+ n = v << XCHILD( _T("PHOTO"));
+ TCHAR* szFileType;
+ switch( JabberGetPictureType( buffer )) {
+ case PA_FORMAT_PNG: szFileType = _T("image/png"); break;
+ case PA_FORMAT_GIF: szFileType = _T("image/gif"); break;
+ case PA_FORMAT_BMP: szFileType = _T("image/bmp"); break;
+ default: szFileType = _T("image/jpeg"); break;
+ }
+ n << XCHILD( _T("TYPE"), szFileType );
+
+ n << XCHILD( _T("BINVAL"), _A2T(str));
+ mir_free( str );
+
+ // NEED TO UPDATE OUR AVATAR HASH:
+
+ mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE];
+ mir_sha1_ctx sha1ctx;
+ mir_sha1_init( &sha1ctx );
+ mir_sha1_append( &sha1ctx, (mir_sha1_byte_t*)buffer, nRead );
+ mir_sha1_finish( &sha1ctx, digest );
+
+ char buf[MIR_SHA1_HASH_SIZE*2+1];
+ for ( int j=0; j<MIR_SHA1_HASH_SIZE; j++ )
+ sprintf( buf+( j<<1 ), "%02x", digest[j] );
+
+ m_options.AvatarType = JabberGetPictureType( buffer );
+
+ if ( bPhotoChanged ) {
+ DeleteFile( szAvatarName );
+
+ GetAvatarFileName( NULL, szAvatarName, SIZEOF( szAvatarName ));
+
+ CopyFile( szFileName, szAvatarName, FALSE );
+ }
+
+ JSetString( NULL, "AvatarHash", buf );
+ JSetString( NULL, "AvatarSaved", buf );
+ } }
+ mir_free( buffer );
+ }
+ CloseHandle( hFile );
+ } } }
+
+ m_ThreadInfo->send( iq );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CJabberProto::OnUserInfoInit_VCard( WPARAM wParam, LPARAM )
+{
+ m_vCardUpdates = 0;
+ m_bPhotoChanged = FALSE;
+ m_szPhotoFileName[0] = 0;
+
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.dwInitParam = (LPARAM)this;
+ odp.flags = ODPF_TCHAR|ODPF_USERINFOTAB|ODPF_DONTTRANSLATE;
+ odp.ptszTitle = m_tszUserName;
+
+ odp.pfnDlgProc = PersonalDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_VCARD_PERSONAL);
+ odp.ptszTab = LPGENT("General");
+ UserInfo_AddPage(wParam, &odp);
+
+ odp.pfnDlgProc = ContactDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_VCARD_CONTACT);
+ odp.ptszTab = LPGENT("Contacts");
+ UserInfo_AddPage(wParam, &odp);
+
+ odp.pfnDlgProc = HomeDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_VCARD_HOME);
+ odp.ptszTab = LPGENT("Home");
+ UserInfo_AddPage(wParam, &odp);
+
+ odp.pfnDlgProc = WorkDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_VCARD_WORK);
+ odp.ptszTab = LPGENT("Work");
+ UserInfo_AddPage(wParam, &odp);
+
+ odp.pfnDlgProc = PhotoDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_VCARD_PHOTO);
+ odp.ptszTab = LPGENT("Photo");
+ UserInfo_AddPage(wParam, &odp);
+
+ odp.pfnDlgProc = NoteDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_VCARD_NOTE);
+ odp.ptszTab = LPGENT("Note");
+ UserInfo_AddPage(wParam, &odp);
+
+ SendGetVcard( m_szJabberJID );
+}
diff --git a/protocols/JabberG/src/jabber_ws.cpp b/protocols/JabberG/src/jabber_ws.cpp new file mode 100644 index 0000000000..494dcf3454 --- /dev/null +++ b/protocols/JabberG/src/jabber_ws.cpp @@ -0,0 +1,90 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+BOOL CJabberProto::WsInit( void )
+{
+ m_lastTicks = ::GetTickCount();
+
+ TCHAR name[128];
+ mir_sntprintf( name, SIZEOF(name), TranslateT("%s connection"), m_tszUserName);
+
+ NETLIBUSER nlu = {0};
+ nlu.cbSize = sizeof( nlu );
+ nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS | NUF_TCHAR; // | NUF_HTTPGATEWAY;
+ nlu.ptszDescriptiveName = name;
+ nlu.szSettingsModule = m_szModuleName;
+ //nlu.szHttpGatewayHello = "http://http.proxy.icq.com/hello";
+ //nlu.szHttpGatewayUserAgent = "Mozilla/4.08 [en] ( WinNT; U ;Nav )";
+ //nlu.pfnHttpGatewayInit = JabberHttpGatewayInit;
+ //nlu.pfnHttpGatewayBegin = JabberHttpGatewayBegin;
+ //nlu.pfnHttpGatewayWrapSend = JabberHttpGatewayWrapSend;
+ //nlu.pfnHttpGatewayUnwrapRecv = JabberHttpGatewayUnwrapRecv;
+ m_hNetlibUser = ( HANDLE ) CallService( MS_NETLIB_REGISTERUSER, 0, ( LPARAM )&nlu );
+
+ return m_hNetlibUser != NULL;
+}
+
+void CJabberProto::WsUninit( void )
+{
+ Netlib_CloseHandle( m_hNetlibUser );
+ m_hNetlibUser = NULL;
+}
+
+JABBER_SOCKET CJabberProto::WsConnect( char* host, WORD port )
+{
+ NETLIBOPENCONNECTION nloc = { 0 };
+ nloc.cbSize = sizeof( nloc );
+ nloc.szHost = host;
+ nloc.wPort = port;
+ nloc.timeout = 6;
+ return ( HANDLE )CallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nloc );
+}
+
+int CJabberProto::WsSend( JABBER_SOCKET hConn, char* data, int datalen, int flags )
+{
+ m_lastTicks = ::GetTickCount();
+ int len;
+
+ if (( len = Netlib_Send( hConn, data, datalen, flags )) == SOCKET_ERROR || len != datalen ) {
+ Log( "Netlib_Send() failed, error=%d", WSAGetLastError());
+ return SOCKET_ERROR;
+ }
+ return len;
+}
+
+int CJabberProto::WsRecv( JABBER_SOCKET hConn, char* data, long datalen, int flags )
+{
+ int ret;
+
+ ret = Netlib_Recv( hConn, data, datalen, flags );
+ if ( ret == SOCKET_ERROR ) {
+ Log( "Netlib_Recv() failed, error=%d", WSAGetLastError());
+ return 0;
+ }
+ if ( ret == 0 ) {
+ Log( "Connection closed gracefully" );
+ return 0;
+ }
+ return ret;
+}
diff --git a/protocols/JabberG/src/jabber_xml.cpp b/protocols/JabberG/src/jabber_xml.cpp new file mode 100644 index 0000000000..287a335243 --- /dev/null +++ b/protocols/JabberG/src/jabber_xml.cpp @@ -0,0 +1,524 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+#define TAG_MAX_LEN 128
+#define ATTR_MAX_LEN 8192
+
+#define T2UTF(A) A
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// XmlNodeIq class members
+
+XmlNodeIq::XmlNodeIq( const TCHAR* type, int id, LPCTSTR to ) :
+ XmlNode( _T( "iq" ))
+{
+ if ( type != NULL ) *this << XATTR( _T("type"), type );
+ if ( to != NULL ) *this << XATTR( _T("to"), to );
+ if ( id != -1 ) *this << XATTRID( id );
+}
+
+XmlNodeIq::XmlNodeIq( const TCHAR* type, LPCTSTR idStr, LPCTSTR to ) :
+ XmlNode( _T( "iq" ))
+{
+ if ( type != NULL ) *this << XATTR( _T("type"), type );
+ if ( to != NULL ) *this << XATTR( _T("to"), to );
+ if ( idStr != NULL ) *this << XATTR( _T("id"), idStr );
+}
+
+XmlNodeIq::XmlNodeIq( const TCHAR* type, HXML node, LPCTSTR to ) :
+ XmlNode( _T( "iq" ))
+{
+ if ( type != NULL ) *this << XATTR( _T("type"), type );
+ if ( to != NULL ) *this << XATTR( _T("to"), to );
+ if ( node != NULL ) {
+ const TCHAR *iqId = xmlGetAttrValue( *this, _T( "id" ));
+ if ( iqId != NULL ) *this << XATTR( _T("id"), iqId );
+ }
+}
+
+XmlNodeIq::XmlNodeIq( CJabberIqInfo* pInfo ) :
+ XmlNode( _T( "iq" ))
+{
+ if ( pInfo ) {
+ if ( pInfo->GetCharIqType() != NULL ) *this << XATTR( _T("type"), _A2T(pInfo->GetCharIqType()));
+ if ( pInfo->GetReceiver() != NULL ) *this << XATTR( _T("to"), pInfo->GetReceiver());
+ if ( pInfo->GetIqId() != -1 ) *this << XATTRID( pInfo->GetIqId());
+ }
+}
+
+XmlNodeIq::XmlNodeIq( const TCHAR* type, CJabberIqInfo* pInfo ) :
+ XmlNode( _T( "iq" ))
+{
+ if ( type != NULL ) *this << XATTR( _T("type"), type );
+ if ( pInfo ) {
+ if ( pInfo->GetFrom() != NULL ) *this << XATTR( _T("to"), pInfo->GetFrom());
+ if ( pInfo->GetIdStr() != NULL ) *this << XATTR( _T("id"), pInfo->GetIdStr());
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// XmlNode class members
+
+XmlNode::XmlNode( LPCTSTR pszName )
+{
+ m_hXml = xi.createNode( T2UTF(pszName), NULL, 0 );
+}
+
+XmlNode::XmlNode( LPCTSTR pszName, LPCTSTR ptszText )
+{
+ m_hXml = xi.createNode( T2UTF(pszName), ptszText, 0 );
+}
+
+XmlNode::XmlNode( const XmlNode& n )
+{
+ m_hXml = xi.copyNode( n );
+}
+
+XmlNode& XmlNode::operator =( const XmlNode& n )
+{
+ if ( m_hXml )
+ xi.destroyNode( m_hXml );
+ m_hXml = xi.copyNode( n );
+ return *this;
+}
+
+XmlNode::~XmlNode()
+{
+ if ( m_hXml ) {
+ xi.destroyNode( m_hXml );
+ m_hXml = NULL;
+} }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HXML __fastcall operator<<( HXML node, const XCHILDNS& child )
+{
+ HXML res = xmlAddChild( node, child.name );
+ xmlAddAttr( res, _T("xmlns"), child.ns );
+ return res;
+}
+
+HXML __fastcall operator<<( HXML node, const XQUERY& child )
+{
+ HXML n = xmlAddChild( node, _T("query"));
+ if ( n )
+ xmlAddAttr( n, _T("xmlns"), child.ns );
+ return n;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void __fastcall xmlAddAttr( HXML hXml, LPCTSTR name, LPCTSTR value )
+{
+ if ( value )
+ xi.addAttr( hXml, name, T2UTF(value));
+}
+
+void __fastcall xmlAddAttr( HXML hXml, LPCTSTR pszName, int value )
+{
+ xi.addAttrInt( hXml, T2UTF(pszName), value );
+}
+
+void __fastcall xmlAddAttr( HXML hXml, LPCTSTR pszName, unsigned __int64 value )
+{
+ TCHAR buf[60];
+ _ui64tot( value, buf, 10 );
+
+ xi.addAttr( hXml, T2UTF(pszName), T2UTF(buf));
+}
+
+void __fastcall xmlAddAttrID( HXML hXml, int id )
+{
+ TCHAR text[ 100 ];
+ mir_sntprintf( text, SIZEOF(text), _T("mir_%d"), id );
+ xmlAddAttr( hXml, _T("id"), text );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+LPCTSTR __fastcall xmlGetAttr( HXML hXml, int n )
+{
+ return xi.getAttr( hXml, n );
+}
+
+int __fastcall xmlGetAttrCount( HXML hXml )
+{
+ return xi.getAttrCount( hXml );
+}
+
+LPCTSTR __fastcall xmlGetAttrName( HXML hXml, int n )
+{
+ return xi.getAttrName( hXml, n );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void __fastcall xmlAddChild( HXML hXml, HXML n )
+{
+ xi.addChild2( n, hXml );
+}
+
+HXML __fastcall xmlAddChild( HXML hXml, LPCTSTR name )
+{
+ return xi.addChild( hXml, T2UTF(name), NULL );
+}
+
+HXML __fastcall xmlAddChild( HXML hXml, LPCTSTR name, LPCTSTR value )
+{
+ return xi.addChild( hXml, T2UTF(name), T2UTF(value));
+}
+
+HXML __fastcall xmlAddChild( HXML hXml, LPCTSTR name, int value )
+{
+ TCHAR buf[40];
+ _itot( value, buf, 10 );
+ return xi.addChild( hXml, T2UTF(name), buf );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+LPCTSTR __fastcall xmlGetAttrValue( HXML hXml, LPCTSTR key )
+{
+ return xi.getAttrValue( hXml, key );
+}
+
+HXML __fastcall xmlGetChild( HXML hXml, int n )
+{
+ return xi.getChild( hXml, n );
+}
+
+HXML __fastcall xmlGetChild( HXML hXml, LPCTSTR key )
+{
+ return xi.getNthChild( hXml, key, 0 );
+}
+
+HXML __fastcall xmlGetChild( HXML hXml, LPCSTR key )
+{
+ LPTSTR wszKey = mir_a2t( key );
+ HXML result = xi.getNthChild( hXml, wszKey, 0 );
+ mir_free( wszKey );
+ return result;
+}
+
+HXML __fastcall xmlGetChildByTag( HXML hXml, LPCTSTR key, LPCTSTR attrName, LPCTSTR attrValue )
+{
+ return xi.getChildByAttrValue( hXml, key, attrName, attrValue );
+}
+
+HXML __fastcall xmlGetChildByTag( HXML hXml, LPCSTR key, LPCSTR attrName, LPCTSTR attrValue )
+{
+ LPTSTR wszKey = mir_a2t( key ), wszName = mir_a2t( attrName );
+ HXML result = xi.getChildByAttrValue( hXml, wszKey, wszName, attrValue );
+ mir_free( wszKey ), mir_free( wszName );
+ return result;
+}
+
+int __fastcall xmlGetChildCount( HXML hXml )
+{
+ return xi.getChildCount( hXml );
+}
+
+HXML __fastcall xmlGetNthChild( HXML hXml, LPCTSTR tag, int nth )
+{
+ int i, num;
+
+ if ( !hXml || tag == NULL || _tcslen( tag ) <= 0 || nth < 1 )
+ return NULL;
+
+ num = 1;
+ for ( i=0; ; i++ ) {
+ HXML n = xi.getChild( hXml, i );
+ if ( !n )
+ break;
+ if ( !lstrcmp( tag, xmlGetName( n ))) {
+ if ( num == nth )
+ return n;
+
+ num++;
+ } }
+
+ return NULL;
+}
+
+LPCTSTR __fastcall xmlGetName( HXML xml )
+{
+ return xi.getName( xml );
+}
+
+LPCTSTR __fastcall xmlGetText( HXML xml )
+{
+ return xi.getText( xml );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void XPath::ProcessPath(LookupInfo &info, bool bCreate)
+{
+ if (!info.nodeName) return;
+
+ TCHAR *nodeName = (TCHAR *)alloca(sizeof(TCHAR) * (info.nodeName.length+1));
+ lstrcpyn(nodeName, info.nodeName.p, info.nodeName.length+1);
+
+ if (info.attrName && info.attrValue)
+ {
+ TCHAR *attrName = (TCHAR *)alloca(sizeof(TCHAR) * (info.attrName.length+1));
+ lstrcpyn(attrName, info.attrName.p, info.attrName.length+1);
+ TCHAR *attrValue = (TCHAR *)alloca(sizeof(TCHAR) * (info.attrValue.length+1));
+ lstrcpyn(attrValue, info.attrValue.p, info.attrValue.length+1);
+ HXML hXml = xmlGetChildByTag(m_hXml, nodeName, attrName, attrValue);
+
+ m_hXml = (hXml || !bCreate) ? hXml : (m_hXml << XCHILD(nodeName) << XATTR(attrName, attrValue));
+ } else
+ if (info.nodeIndex)
+ {
+ int idx = _ttoi(info.nodeIndex.p);
+ m_hXml = lstrcmp(nodeName, _T("*")) ? xmlGetNthChild(m_hXml, nodeName, idx) : xmlGetChild(m_hXml, idx-1);
+
+ // no support for such creation mode
+ } else
+ {
+ HXML hXml = xmlGetChild(m_hXml, nodeName);
+ m_hXml = (hXml || !bCreate) ? hXml : (m_hXml << XCHILD(nodeName));
+ }
+
+ info.Reset();
+}
+
+XPath::PathType XPath::LookupImpl(bool bCreate)
+{
+ LookupState state = S_START;
+ LookupInfo info = {0};
+
+ for (LPCTSTR p = m_szPath; state < S_FINAL; ++p)
+ {
+ switch (state)
+ {
+ case S_START:
+ {
+ ProcessPath(info, bCreate);
+ if (!m_hXml)
+ {
+ state = S_FINAL_ERROR;
+ break;
+ }
+
+ switch (*p)
+ {
+ case 0:
+ state = S_FINAL_ERROR;
+ break;
+ case _T('@'):
+ info.attrName.Begin(p+1);
+ state = S_ATTR_STEP;
+ break;
+ case _T('/'):
+ break;
+ default:
+ info.nodeName.Begin(p);
+ state = S_NODE_NAME;
+ break;
+ };
+ break;
+ }
+ case S_ATTR_STEP:
+ {
+ switch (*p)
+ {
+ case 0:
+ info.attrName.End(p);
+ state = S_FINAL_ATTR;
+ break;
+ default:
+ break;
+ };
+ break;
+ }
+ case S_NODE_NAME:
+ {
+ switch (*p)
+ {
+ case 0:
+ info.nodeName.End(p);
+ state = S_FINAL_NODESET;
+ break;
+ case _T('['):
+ info.nodeName.End(p);
+ state = S_NODE_OPENBRACKET;
+ break;
+ case _T('/'):
+ info.nodeName.End(p);
+ state = S_START;
+ break;
+ default:
+ break;
+ };
+ break;
+ }
+ case S_NODE_OPENBRACKET:
+ {
+ switch (*p)
+ {
+ case 0:
+ state = S_FINAL_ERROR;
+ break;
+ case _T('@'):
+ info.attrName.Begin(p+1);
+ state = S_NODE_ATTRNAME;
+ break;
+ case _T('0'): case _T('1'): case _T('2'): case _T('3'): case _T('4'):
+ case _T('5'): case _T('6'): case _T('7'): case _T('8'): case _T('9'):
+ info.nodeIndex.Begin(p);
+ state = S_NODE_INDEX;
+ break;
+ default:
+ state = S_FINAL_ERROR;
+ break;
+ };
+ break;
+ }
+ case S_NODE_INDEX:
+ {
+ switch (*p)
+ {
+ case 0:
+ state = S_FINAL_ERROR;
+ break;
+ case _T(']'):
+ info.nodeIndex.End(p);
+ state = S_NODE_CLOSEBRACKET;
+ break;
+ case _T('0'): case _T('1'): case _T('2'): case _T('3'): case _T('4'):
+ case _T('5'): case _T('6'): case _T('7'): case _T('8'): case _T('9'):
+ break;
+ default:
+ state = S_FINAL_ERROR;
+ break;
+ };
+ break;
+ }
+ case S_NODE_ATTRNAME:
+ {
+ switch (*p)
+ {
+ case 0:
+ state = S_FINAL_ERROR;
+ break;
+ case _T('='):
+ info.attrName.End(p);
+ state = S_NODE_ATTREQUALS;
+ break;
+ default:
+ break;
+ };
+ break;
+ }
+ case S_NODE_ATTREQUALS:
+ {
+ switch (*p)
+ {
+ case 0:
+ state = S_FINAL_ERROR;
+ break;
+ case _T('\''):
+ info.attrValue.Begin(p+1);
+ state = S_NODE_ATTRVALUE;
+ break;
+ default:
+ state = S_FINAL_ERROR;
+ break;
+ };
+ break;
+ }
+ case S_NODE_ATTRVALUE:
+ {
+ switch (*p)
+ {
+ case 0:
+ state = S_FINAL_ERROR;
+ break;
+ case _T('\''):
+ info.attrValue.End(p);
+ state = S_NODE_ATTRCLOSEVALUE;
+ break;
+ default:
+ break;
+ };
+ break;
+ }
+ case S_NODE_ATTRCLOSEVALUE:
+ {
+ switch (*p)
+ {
+ case 0:
+ state = S_FINAL_ERROR;
+ break;
+ case _T(']'):
+ state = S_NODE_CLOSEBRACKET;
+ break;
+ default:
+ state = S_FINAL_ERROR;
+ break;
+ };
+ break;
+ }
+ case S_NODE_CLOSEBRACKET:
+ {
+ switch (*p)
+ {
+ case 0:
+ state = S_FINAL_NODE;
+ break;
+ case _T('/'):
+ state = S_START;
+ break;
+ default:
+ state = S_FINAL_ERROR;
+ break;
+ };
+ break;
+ }
+ }
+
+ if (!*p && (state < S_FINAL))
+ {
+ state = S_FINAL_ERROR;
+ }
+ }
+
+ switch (state)
+ {
+ case S_FINAL_ATTR:
+ m_szParam = info.attrName.p;
+ return T_ATTRIBUTE;
+ case S_FINAL_NODE:
+ ProcessPath(info, bCreate);
+ return T_NODE;
+ case S_FINAL_NODESET:
+ m_szParam = info.nodeName.p;
+ return T_NODESET;
+ }
+
+ return T_ERROR;
+}
diff --git a/protocols/JabberG/src/jabber_xml.h b/protocols/JabberG/src/jabber_xml.h new file mode 100644 index 0000000000..0a1b41e511 --- /dev/null +++ b/protocols/JabberG/src/jabber_xml.h @@ -0,0 +1,382 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_XML_H_
+#define _JABBER_XML_H_
+
+#include <m_xml.h>
+
+void __fastcall xmlAddChild( HXML, HXML );
+HXML __fastcall xmlAddChild( HXML, LPCTSTR pszName );
+HXML __fastcall xmlAddChild( HXML, LPCTSTR pszName, LPCTSTR ptszValue );
+HXML __fastcall xmlAddChild( HXML, LPCTSTR pszName, int iValue );
+
+LPCTSTR __fastcall xmlGetAttrValue( HXML, LPCTSTR key );
+HXML __fastcall xmlGetChild( HXML, int n = 0 );
+HXML __fastcall xmlGetChild( HXML, LPCSTR key );
+HXML __fastcall xmlGetChild( HXML, LPCTSTR key );
+int __fastcall xmlGetChildCount( HXML );
+HXML __fastcall xmlGetChildByTag( HXML, LPCTSTR key, LPCTSTR attrName, LPCTSTR attrValue );
+HXML __fastcall xmlGetChildByTag( HXML, LPCSTR key, LPCSTR attrName, LPCTSTR attrValue );
+HXML __fastcall xmlGetNthChild( HXML, LPCTSTR key, int n = 0 );
+
+LPCTSTR __fastcall xmlGetName( HXML );
+LPCTSTR __fastcall xmlGetText( HXML );
+
+void __fastcall xmlAddAttr( HXML, LPCTSTR pszName, LPCTSTR ptszValue );
+void __fastcall xmlAddAttr( HXML, LPCTSTR pszName, int value );
+void __fastcall xmlAddAttr( HXML hXml, LPCTSTR pszName, unsigned __int64 value );
+void __fastcall xmlAddAttrID( HXML, int id );
+
+int __fastcall xmlGetAttrCount( HXML );
+LPCTSTR __fastcall xmlGetAttr( HXML, int n );
+LPCTSTR __fastcall xmlGetAttrName( HXML, int n );
+LPCTSTR __fastcall xmlGetAttrValue( HXML, LPCTSTR key );
+
+struct XmlNode
+{
+ __forceinline XmlNode() { m_hXml = NULL; }
+
+ __forceinline XmlNode( LPCTSTR pszString, int* numBytes, LPCTSTR ptszTag )
+ {
+ m_hXml = xi.parseString( pszString, numBytes, ptszTag );
+ }
+
+ XmlNode( const XmlNode& n );
+ XmlNode( LPCTSTR name );
+ XmlNode( LPCTSTR pszName, LPCTSTR ptszText );
+ ~XmlNode();
+
+ XmlNode& operator =( const XmlNode& n );
+
+ __forceinline operator HXML() const
+ { return m_hXml;
+ }
+
+private:
+ HXML m_hXml;
+};
+
+class CJabberIqInfo;
+
+struct XmlNodeIq : public XmlNode
+{
+ XmlNodeIq( const TCHAR* type, int id = -1, const TCHAR* to = NULL );
+ XmlNodeIq( const TCHAR* type, const TCHAR* idStr, const TCHAR* to );
+ XmlNodeIq( const TCHAR* type, HXML node, const TCHAR* to );
+ // new request
+ XmlNodeIq( CJabberIqInfo* pInfo );
+ // answer to request
+ XmlNodeIq( const TCHAR* type, CJabberIqInfo* pInfo );
+};
+
+typedef void ( *JABBER_XML_CALLBACK )( HXML, void* );
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct XATTR
+{
+ LPCTSTR name, value;
+
+ __forceinline XATTR( LPCTSTR _name, LPCTSTR _value ) :
+ name( _name ),
+ value( _value )
+ {}
+};
+
+HXML __forceinline operator<<( HXML node, const XATTR& attr )
+{ xmlAddAttr( node, attr.name, attr.value );
+ return node;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct XATTRI
+{
+ LPCTSTR name;
+ int value;
+
+ __forceinline XATTRI( LPCTSTR _name, int _value ) :
+ name( _name ),
+ value( _value )
+ {}
+};
+
+HXML __forceinline operator<<( HXML node, const XATTRI& attr )
+{ xmlAddAttr( node, attr.name, attr.value );
+ return node;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct XATTRI64
+{
+ LPCTSTR name;
+ unsigned __int64 value;
+
+ __forceinline XATTRI64( LPCTSTR _name, unsigned __int64 _value ) :
+ name( _name ),
+ value( _value )
+ {}
+};
+
+HXML __forceinline operator<<( HXML node, const XATTRI64& attr )
+{ xmlAddAttr( node, attr.name, attr.value );
+ return node;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct XATTRID
+{
+ int id;
+
+ __forceinline XATTRID( int _value ) :
+ id( _value )
+ {}
+};
+
+HXML __forceinline operator<<( HXML node, const XATTRID& attr )
+{ xmlAddAttrID( node, attr.id );
+ return node;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct XCHILD
+{
+ LPCTSTR name, value;
+
+ __forceinline XCHILD( LPCTSTR _name, LPCTSTR _value = NULL ) :
+ name( _name ),
+ value( _value )
+ {}
+};
+
+HXML __forceinline operator<<( HXML node, const XCHILD& child )
+{ return xmlAddChild( node, child.name, child.value );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct XCHILDNS
+{
+ LPCTSTR name, ns;
+
+ __forceinline XCHILDNS( LPCTSTR _name, LPCTSTR _ns = NULL ) :
+ name( _name ),
+ ns( _ns )
+ {}
+};
+
+HXML __fastcall operator<<( HXML node, const XCHILDNS& child );
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct XQUERY
+{
+ LPCTSTR ns;
+
+ __forceinline XQUERY( LPCTSTR _ns ) :
+ ns( _ns )
+ {}
+};
+
+HXML __fastcall operator<<( HXML node, const XQUERY& child );
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Limited XPath support
+// path should look like: "node-spec/node-spec/.../result-spec"
+// where "node-spec" can be "node-name", "node-name[@attr-name='attr-value']" or "node-name[node-index]"
+// result may be either "node-spec", or "@attr-name"
+//
+// Samples:
+// LPCTSTR s = XPathT(node, "child/subchild[@attr='value']"); // get node text
+// LPCTSTR s = XPathT(node, "child/subchild[2]/@attr"); // get attribute value
+// XPathT(node, "child/subchild[@name='test']/@attr") = _T("Hello"); // create path if needed and set attribute value
+//
+// XPathT(node, "child/subchild[@name='test']") = _T("Hello"); // TODO: create path if needed and set node text
+
+#define XPathT(a,b) XPath(a, _T(b))
+
+class XPath
+{
+public:
+ __forceinline XPath(HXML hXml, TCHAR *path):
+ m_type(T_UNKNOWN),
+ m_hXml(hXml),
+ m_szPath(path),
+ m_szParam(NULL)
+ {}
+
+ // Read data
+ operator HXML()
+ {
+ switch (Lookup())
+ {
+ case T_NODE: return m_hXml;
+ case T_NODESET: return xmlGetNthChild(m_hXml, m_szParam, 1);
+ }
+ return NULL;
+ }
+ operator LPTSTR()
+ {
+ switch (Lookup())
+ {
+ case T_ATTRIBUTE: return (TCHAR *)xmlGetAttrValue(m_hXml, m_szParam);
+ case T_NODE: return (TCHAR *)xmlGetText(m_hXml);
+ case T_NODESET: return (TCHAR *)xmlGetText(xmlGetNthChild(m_hXml, m_szParam, 1));
+ }
+ return NULL;
+ }
+ operator int()
+ {
+ if (TCHAR *s = *this) return _ttoi(s);
+ return 0;
+ }
+ __forceinline bool operator== (TCHAR *str)
+ {
+ return !lstrcmp((LPCTSTR)*this, str);
+ }
+ __forceinline bool operator!= (TCHAR *str)
+ {
+ return lstrcmp((LPCTSTR)*this, str) ? true : false;
+ }
+ HXML operator[] (int idx)
+ {
+ return (Lookup() == T_NODESET) ? xmlGetNthChild(m_hXml, m_szParam, idx) : NULL;
+ }
+
+ // Write data
+ void operator= (LPCTSTR value)
+ {
+ switch (Lookup(true))
+ {
+ case T_ATTRIBUTE: xmlAddAttr(m_hXml, m_szParam, value); break;
+ case T_NODE: break; // TODO: set node text
+ }
+ }
+ void operator= (int value)
+ {
+ TCHAR buf[16];
+ _itot(value, buf, 10);
+ *this = buf;
+ }
+
+private:
+ enum PathType
+ {
+ T_UNKNOWN,
+ T_ERROR,
+ T_NODE,
+ T_ATTRIBUTE,
+ T_NODESET
+ };
+
+ __forceinline PathType Lookup(bool bCreate=false)
+ {
+ return (m_type == T_UNKNOWN) ? LookupImpl(bCreate) : m_type;
+ }
+
+ enum LookupState
+ {
+ S_START,
+ S_ATTR_STEP,
+ S_NODE_NAME,
+ S_NODE_OPENBRACKET,
+ S_NODE_INDEX,
+ S_NODE_ATTRNAME,
+ S_NODE_ATTREQUALS,
+ S_NODE_ATTRVALUE,
+ S_NODE_ATTRCLOSEVALUE,
+ S_NODE_CLOSEBRACKET,
+
+ S_FINAL,
+ S_FINAL_ERROR,
+ S_FINAL_ATTR,
+ S_FINAL_NODESET,
+ S_FINAL_NODE
+ };
+
+ struct LookupString
+ {
+ void Begin(const TCHAR *p_) { p = p_; }
+ void End(const TCHAR *p_) { length = p_ - p; }
+ operator bool() { return p ? true : false; }
+
+ const TCHAR *p;
+ int length;
+
+ };
+
+ struct LookupInfo
+ {
+ void Reset() { memset(this, 0, sizeof(*this)); }
+ LookupString nodeName;
+ LookupString attrName;
+ LookupString attrValue;
+ LookupString nodeIndex;
+ };
+
+ void ProcessPath(LookupInfo &info, bool bCreate);
+ PathType LookupImpl(bool bCreate);
+
+ PathType m_type;
+ HXML m_hXml;
+ LPCTSTR m_szPath;
+ LPCTSTR m_szParam;
+};
+
+class XPathFmt: public XPath
+{
+public:
+ enum { BUFSIZE = 512 };
+ XPathFmt(HXML hXml, TCHAR *path, ...): XPath(hXml, m_buf)
+ {
+ *m_buf = 0;
+
+ va_list args;
+ va_start(args, path);
+ _vsntprintf(m_buf, BUFSIZE, path, args);
+ m_buf[BUFSIZE-1] = 0;
+ va_end(args);
+ }
+
+ XPathFmt(HXML hXml, char *path, ...): XPath(hXml, m_buf)
+ {
+ *m_buf = 0;
+ char buf[BUFSIZE];
+
+ va_list args;
+ va_start(args, path);
+ _vsnprintf(buf, BUFSIZE, path, args);
+ buf[BUFSIZE-1] = 0;
+ MultiByteToWideChar(CP_ACP, 0, buf, -1, m_buf, BUFSIZE);
+ va_end(args);
+ }
+
+private:
+ TCHAR m_buf[BUFSIZE];
+};
+
+#endif
diff --git a/protocols/JabberG/src/jabber_xstatus.cpp b/protocols/JabberG/src/jabber_xstatus.cpp new file mode 100644 index 0000000000..d15cae2bcd --- /dev/null +++ b/protocols/JabberG/src/jabber_xstatus.cpp @@ -0,0 +1,2138 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Maxim Mluhov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+#include "jabber_caps.h"
+
+#include <m_genmenu.h>
+#include <m_icolib.h>
+#include <m_fontservice.h>
+
+#include <m_cluiframes.h>
+
+#include "m_proto_listeningto.h"
+#include "m_skin_eng.h"
+#include "m_extraicons.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Simple dialog with timer and ok/cancel buttons
+class CJabberDlgPepBase: public CJabberDlgBase
+{
+ typedef CJabberDlgBase CSuper;
+public:
+ CJabberDlgPepBase(CJabberProto *proto, int id);
+
+protected:
+ CPepService *m_pepService;
+
+ CCtrlButton m_btnOk;
+ CCtrlButton m_btnCancel;
+
+ void OnInitDialog();
+ int Resizer(UTILRESIZECONTROL *urc);
+ virtual INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ void StopTimer();
+
+private:
+ int m_time;
+};
+
+CJabberDlgPepBase::CJabberDlgPepBase(CJabberProto *proto, int id):
+ CJabberDlgBase(proto, id, NULL),
+ m_btnOk(this, IDOK),
+ m_btnCancel(this, IDCANCEL)
+{
+}
+
+void CJabberDlgPepBase::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+
+ m_time = 5;
+ SetTimer(m_hwnd, 1, 1000, NULL);
+
+ TCHAR buf[128];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s (%d)"), TranslateT("OK"), m_time);
+ m_btnOk.SetText(buf);
+}
+
+int CJabberDlgPepBase::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId)
+ {
+ case IDOK:
+ case IDCANCEL:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ }
+
+ return CSuper::Resizer(urc);
+}
+
+INT_PTR CJabberDlgPepBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_TIMER:
+ if (wParam == 1) {
+ TCHAR buf[128];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s (%d)"), TranslateT("OK"), --m_time);
+ m_btnOk.SetText(buf);
+
+ if (m_time < 0) {
+ KillTimer(m_hwnd, 1);
+ UIEmulateBtnClick(m_hwnd, IDOK);
+ }
+
+ return TRUE;
+ }
+
+ break;
+ }
+
+ return CSuper::DlgProc(msg, wParam, lParam);
+}
+
+void CJabberDlgPepBase::StopTimer()
+{
+ KillTimer(m_hwnd, 1);
+ m_btnOk.SetText(TranslateT("OK"));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Simple PEP status
+class CJabberDlgPepSimple: public CJabberDlgPepBase
+{
+ typedef CJabberDlgPepBase CSuper;
+public:
+ CJabberDlgPepSimple(CJabberProto *proto, TCHAR *title);
+ ~CJabberDlgPepSimple();
+
+ bool OkClicked() { return m_bOkClicked; }
+ void AddStatusMode(LPARAM id, char *name, HICON hIcon, TCHAR *title, bool subitem = false);
+ void SetActiveStatus(LPARAM id, TCHAR *text);
+ LPARAM GetStatusMode();
+ TCHAR *GetStatusText();
+
+protected:
+ CCtrlCombo m_cbModes;
+ CCtrlEdit m_txtDescription;
+
+ void OnInitDialog();
+ int Resizer(UTILRESIZECONTROL *urc);
+
+ UI_MESSAGE_MAP(CJabberDlgPepSimple, CSuper);
+ UI_MESSAGE(WM_MEASUREITEM, OnWmMeasureItem);
+ UI_MESSAGE(WM_DRAWITEM, OnWmDrawItem);
+ UI_MESSAGE(WM_GETMINMAXINFO, OnWmGetMinMaxInfo);
+ UI_MESSAGE_MAP_END();
+
+ BOOL OnWmMeasureItem(UINT msg, WPARAM wParam, LPARAM lParam);
+ BOOL OnWmDrawItem(UINT msg, WPARAM wParam, LPARAM lParam);
+ BOOL OnWmGetMinMaxInfo(UINT msg, WPARAM wParam, LPARAM lParam);
+
+private:
+ struct CStatusMode
+ {
+ LPARAM m_id;
+ char *m_name;
+ HICON m_hIcon;
+ TCHAR *m_title;
+ bool m_subitem;
+
+ CStatusMode(LPARAM id, char *name, HICON hIcon, TCHAR *title, bool subitem): m_id(id), m_name(name), m_hIcon(hIcon), m_title(title), m_subitem(subitem) {}
+ ~CStatusMode() { g_ReleaseIcon( m_hIcon ); }
+ };
+
+ OBJLIST<CStatusMode> m_modes;
+ TCHAR *m_text;
+ TCHAR *m_title;
+ int m_time;
+ int m_prevSelected;
+ int m_selected;
+ bool m_bOkClicked;
+
+ LPARAM m_active;
+ TCHAR *m_activeText;
+
+ void btnOk_OnClick(CCtrlButton *btn);
+ void global_OnChange(CCtrlData *);
+ void cbModes_OnChange(CCtrlData *);
+};
+
+CJabberDlgPepSimple::CJabberDlgPepSimple(CJabberProto *proto, TCHAR *title):
+ CJabberDlgPepBase(proto, IDD_PEP_SIMPLE),
+ m_cbModes(this, IDC_CB_MODES),
+ m_txtDescription(this, IDC_TXT_DESCRIPTION),
+ m_modes(10),
+ m_text(NULL),
+ m_selected(0),
+ m_prevSelected(-1),
+ m_active(-1),
+ m_bOkClicked(false),
+ m_title(title)
+{
+ m_btnOk.OnClick = Callback(this, &CJabberDlgPepSimple::btnOk_OnClick);
+ m_cbModes.OnChange = Callback(this, &CJabberDlgPepSimple::cbModes_OnChange);
+ m_cbModes.OnDropdown =
+ m_txtDescription.OnChange = Callback(this, &CJabberDlgPepSimple::global_OnChange);
+
+ m_modes.insert(new CStatusMode(-1, "<none>", LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT), TranslateT("None"), false));
+}
+
+CJabberDlgPepSimple::~CJabberDlgPepSimple()
+{
+ mir_free(m_text);
+}
+
+void CJabberDlgPepSimple::AddStatusMode(LPARAM id, char *name, HICON hIcon, TCHAR *title, bool subitem)
+{
+ m_modes.insert(new CStatusMode(id, name, hIcon, title, subitem));
+}
+
+void CJabberDlgPepSimple::SetActiveStatus(LPARAM id, TCHAR *text)
+{
+ m_active = id;
+ m_activeText = text;
+}
+
+LPARAM CJabberDlgPepSimple::GetStatusMode()
+{
+ return m_modes[m_selected].m_id;
+}
+
+TCHAR *CJabberDlgPepSimple::GetStatusText()
+{
+ return m_text;
+}
+
+void CJabberDlgPepSimple::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+
+ WindowSetIcon( m_hwnd, m_proto, "main" );
+ SetWindowText(m_hwnd, m_title);
+
+ m_txtDescription.Enable(false);
+ for (int i = 0; i < m_modes.getCount(); ++i)
+ {
+ int idx = m_cbModes.AddString(m_modes[i].m_title, i);
+ if ((m_modes[i].m_id == m_active) || !idx)
+ {
+ m_prevSelected = idx;
+ m_cbModes.SetCurSel(idx);
+ if (idx) m_txtDescription.Enable();
+ }
+ }
+ if (m_activeText)
+ {
+ m_txtDescription.SetText(m_activeText);
+ }
+}
+
+int CJabberDlgPepSimple::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId)
+ {
+ case IDC_CB_MODES:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_TOP;
+ case IDC_TXT_DESCRIPTION:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ }
+
+ return CSuper::Resizer(urc);
+}
+
+void CJabberDlgPepSimple::btnOk_OnClick(CCtrlButton*)
+{
+ m_text = m_txtDescription.GetText();
+ m_selected = m_cbModes.GetCurSel();
+ m_bOkClicked = true;
+}
+
+void CJabberDlgPepSimple::global_OnChange(CCtrlData *)
+{
+ StopTimer();
+}
+
+void CJabberDlgPepSimple::cbModes_OnChange(CCtrlData *)
+{
+ StopTimer();
+
+ if (m_prevSelected == m_cbModes.GetCurSel())
+ return;
+
+ char szSetting[128];
+
+ if ((m_prevSelected >= 0) && (m_modes[m_cbModes.GetItemData(m_prevSelected)].m_id >= 0))
+ {
+ TCHAR *txt = m_txtDescription.GetText();
+ mir_snprintf(szSetting, SIZEOF(szSetting), "PepMsg_%s", m_modes[m_cbModes.GetItemData(m_prevSelected)].m_name);
+ m_proto->JSetStringT(NULL, szSetting, txt);
+ mir_free(txt);
+ }
+
+ m_prevSelected = m_cbModes.GetCurSel();
+ if ((m_prevSelected >= 0) && (m_modes[m_cbModes.GetItemData(m_prevSelected)].m_id >= 0))
+ {
+ mir_snprintf(szSetting, SIZEOF(szSetting), "PepMsg_%s", m_modes[m_cbModes.GetItemData(m_prevSelected)].m_name);
+
+ DBVARIANT dbv;
+ if (!m_proto->JGetStringT(NULL, szSetting, &dbv))
+ {
+ m_txtDescription.SetText(dbv.ptszVal);
+ JFreeVariant(&dbv);
+ } else
+ {
+ m_txtDescription.SetTextA("");
+ }
+ m_txtDescription.Enable(true);
+ } else
+ {
+ m_txtDescription.SetTextA("");
+ m_txtDescription.Enable(false);
+ }
+}
+
+BOOL CJabberDlgPepSimple::OnWmMeasureItem(UINT, WPARAM, LPARAM lParam)
+{
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+ if (lpmis->CtlID != IDC_CB_MODES)
+ return FALSE;
+
+ TEXTMETRIC tm = {0};
+ HDC hdc = GetDC(m_cbModes.GetHwnd());
+ GetTextMetrics(hdc, &tm);
+ ReleaseDC(m_cbModes.GetHwnd(), hdc);
+
+ lpmis->itemHeight = max(tm.tmHeight, 18);
+ if (lpmis->itemHeight < 18) lpmis->itemHeight = 18;
+
+ return TRUE;
+}
+
+BOOL CJabberDlgPepSimple::OnWmDrawItem(UINT, WPARAM, LPARAM lParam)
+{
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ if (lpdis->CtlID != IDC_CB_MODES)
+ return FALSE;
+
+ if (lpdis->itemData == -1) return FALSE;
+
+ CStatusMode *mode = &m_modes[lpdis->itemData];
+
+ TEXTMETRIC tm = {0};
+ GetTextMetrics(lpdis->hDC, &tm);
+
+ SetBkMode(lpdis->hDC, TRANSPARENT);
+ if (lpdis->itemState & ODS_SELECTED)
+ {
+ SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ } else
+ {
+ SetTextColor(lpdis->hDC, GetSysColor(COLOR_WINDOWTEXT));
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_WINDOW));
+ }
+
+ if (!mode->m_subitem || (lpdis->itemState & ODS_COMBOBOXEDIT))
+ {
+ TCHAR text[128];
+ if (mode->m_subitem)
+ {
+ for (int i = lpdis->itemData; i >= 0; --i)
+ if (!m_modes[i].m_subitem)
+ {
+ mir_sntprintf(text, SIZEOF(text), _T("%s [%s]"), m_modes[i].m_title, mode->m_title);
+ break;
+ }
+ } else
+ {
+ lstrcpyn(text, mode->m_title, SIZEOF(text));
+ }
+
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.left+2, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2, mode->m_hIcon, 16, 16, 0, NULL, DI_NORMAL);
+ TextOut(lpdis->hDC, lpdis->rcItem.left + 23, (lpdis->rcItem.top+lpdis->rcItem.bottom-tm.tmHeight)/2, text, lstrlen(text));
+ } else
+ {
+ TCHAR text[128];
+ mir_sntprintf(text, SIZEOF(text), _T("...%s"), mode->m_title);
+ DrawIconEx(lpdis->hDC, lpdis->rcItem.left+23, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2, mode->m_hIcon, 16, 16, 0, NULL, DI_NORMAL);
+ TextOut(lpdis->hDC, lpdis->rcItem.left + 44, (lpdis->rcItem.top+lpdis->rcItem.bottom-tm.tmHeight)/2, text, lstrlen(text));
+ }
+
+ return TRUE;
+}
+
+BOOL CJabberDlgPepSimple::OnWmGetMinMaxInfo(UINT, WPARAM, LPARAM lParam)
+{
+ LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
+ lpmmi->ptMinTrackSize.x = 200;
+ lpmmi->ptMinTrackSize.y = 200;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CPepService base class
+CPepService::CPepService(CJabberProto *proto, char *name, TCHAR *node):
+ m_proto(proto),
+ m_name(name),
+ m_node(node),
+ m_hMenuItem(NULL)
+{
+}
+
+CPepService::~CPepService()
+{
+}
+
+void CPepService::Publish()
+{
+ XmlNodeIq iq( _T("set"), m_proto->SerialNext());
+ CreateData(
+ iq << XCHILDNS( _T("pubsub"), _T(JABBER_FEAT_PUBSUB))
+ << XCHILD( _T("publish")) << XATTR( _T("node"), m_node )
+ << XCHILD( _T("item")) << XATTR( _T("id"), _T("current")));
+ m_proto->m_ThreadInfo->send( iq );
+
+ m_wasPublished = TRUE;
+}
+
+void CPepService::Retract()
+{
+ TCHAR* tempName = mir_a2t( m_name );
+ _tcslwr( tempName );
+
+ m_proto->m_ThreadInfo->send(
+ XmlNodeIq( _T("set"), m_proto->SerialNext())
+ << XCHILDNS( _T("pubsub"), _T(JABBER_FEAT_PUBSUB))
+ << XCHILD( _T("publish")) << XATTR( _T("node"), m_node )
+ << XCHILD( _T("item"))
+ << XCHILDNS( tempName, m_node ));
+
+ mir_free( tempName );
+}
+
+void CPepService::ResetPublish()
+{
+ m_wasPublished = FALSE;
+}
+
+void CPepService::ForceRepublishOnLogin()
+{
+ if (!m_wasPublished)
+ Publish();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CPepGuiService base class
+
+CPepGuiService::CPepGuiService(CJabberProto *proto, char *name, TCHAR *node):
+ CPepService(proto, name, node),
+ m_bGuiOpen(false),
+ m_hIcolibItem(NULL),
+ m_szText(NULL),
+ m_hMenuService(NULL)
+{
+}
+
+CPepGuiService::~CPepGuiService()
+{
+ if (m_hMenuService)
+ {
+ DestroyServiceFunction(m_hMenuService);
+ m_hMenuService = NULL;
+ }
+
+ if (m_szText) mir_free(m_szText);
+}
+
+void CPepGuiService::InitGui()
+{
+ char szService[128];
+ mir_snprintf(szService, SIZEOF(szService), "%s/AdvStatusSet/%s", m_proto->m_szModuleName, m_name);
+
+ int (__cdecl CPepGuiService::*serviceProc)(WPARAM, LPARAM);
+ serviceProc = &CPepGuiService::OnMenuItemClick;
+ m_hMenuService = CreateServiceFunctionObj(szService, (MIRANDASERVICEOBJ)*(void **)&serviceProc, this);
+
+ RebuildMenu();
+}
+
+void CPepGuiService::RebuildMenu()
+{
+ HGENMENU hJabberRoot = MO_GetProtoRootMenu( m_proto->m_szModuleName );
+ if ( hJabberRoot ) {
+ char szService[128];
+ mir_snprintf(szService, SIZEOF(szService), "%s/AdvStatusSet/%s", m_proto->m_szModuleName, m_name);
+
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.hParentMenu = hJabberRoot;
+ mi.pszService = szService;
+ mi.position = 200010;
+ mi.flags = CMIF_TCHAR | CMIF_ICONFROMICOLIB | CMIF_HIDDEN | CMIF_ROOTHANDLE;
+
+ mi.icolibItem = m_hIcolibItem;
+ mi.ptszName = m_szText ? m_szText : _T("<advanced status slot>");
+ m_hMenuItem = Menu_AddProtoMenuItem(&mi);
+} }
+
+bool CPepGuiService::LaunchSetGui(BYTE bQuiet)
+{
+ if (m_bGuiOpen) return false;
+
+ m_bGuiOpen = true;
+ ShowSetDialog(bQuiet);
+ m_bGuiOpen = false;
+
+ return true;
+}
+
+void CPepGuiService::UpdateMenuItem(HANDLE hIcolibIcon, TCHAR *text)
+{
+ m_hIcolibItem = hIcolibIcon;
+ if (m_szText) mir_free(m_szText);
+ m_szText = text ? mir_tstrdup(text) : NULL;
+
+ if (!m_hMenuItem) return;
+
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_TCHAR|CMIF_ICONFROMICOLIB|CMIM_ICON|CMIM_NAME;
+ mi.icolibItem = m_hIcolibItem;
+ mi.ptszName = m_szText ? m_szText : _T("<advanced status slot>");
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)m_hMenuItem, (LPARAM)&mi);
+}
+
+int CPepGuiService::OnMenuItemClick(WPARAM, LPARAM)
+{
+ LaunchSetGui(0);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CPepMood
+
+struct
+{
+ TCHAR *szName;
+ char* szTag;
+} static g_arrMoods[] =
+{
+ { LPGENT("None"), NULL },
+ { LPGENT("Afraid"), "afraid" },
+ { LPGENT("Amazed"), "amazed" },
+ { LPGENT("Amorous"), "amorous" },
+ { LPGENT("Angry"), "angry" },
+ { LPGENT("Annoyed"), "annoyed" },
+ { LPGENT("Anxious"), "anxious" },
+ { LPGENT("Aroused"), "aroused" },
+ { LPGENT("Ashamed"), "ashamed" },
+ { LPGENT("Bored"), "bored" },
+ { LPGENT("Brave"), "brave" },
+ { LPGENT("Calm"), "calm" },
+ { LPGENT("Cautious"), "cautious" },
+ { LPGENT("Cold"), "cold" },
+ { LPGENT("Confident"), "confident" },
+ { LPGENT("Confused"), "confused" },
+ { LPGENT("Contemplative"),"contemplative" },
+ { LPGENT("Contented"), "contented" },
+ { LPGENT("Cranky"), "cranky" },
+ { LPGENT("Crazy"), "crazy" },
+ { LPGENT("Creative"), "creative" },
+ { LPGENT("Curious"), "curious" },
+ { LPGENT("Dejected"), "dejected" },
+ { LPGENT("Depressed"), "depressed" },
+ { LPGENT("Disappointed"), "disappointed" },
+ { LPGENT("Disgusted"), "disgusted" },
+ { LPGENT("Dismayed"), "dismayed" },
+ { LPGENT("Distracted"), "distracted" },
+ { LPGENT("Embarrassed"), "embarrassed" },
+ { LPGENT("Envious"), "envious" },
+ { LPGENT("Excited"), "excited" },
+ { LPGENT("Flirtatious"), "flirtatious" },
+ { LPGENT("Frustrated"), "frustrated" },
+ { LPGENT("Grateful"), "grateful" },
+ { LPGENT("Grieving"), "grieving" },
+ { LPGENT("Grumpy"), "grumpy" },
+ { LPGENT("Guilty"), "guilty" },
+ { LPGENT("Happy"), "happy" },
+ { LPGENT("Hopeful"), "hopeful" },
+ { LPGENT("Hot"), "hot" },
+ { LPGENT("Humbled"), "humbled" },
+ { LPGENT("Humiliated"), "humiliated" },
+ { LPGENT("Hungry"), "hungry" },
+ { LPGENT("Hurt"), "hurt" },
+ { LPGENT("Impressed"), "impressed" },
+ { LPGENT("In awe"), "in_awe" },
+ { LPGENT("In love"), "in_love" },
+ { LPGENT("Indignant"), "indignant" },
+ { LPGENT("Interested"), "interested" },
+ { LPGENT("Intoxicated"), "intoxicated" },
+ { LPGENT("Invincible"), "invincible" },
+ { LPGENT("Jealous"), "jealous" },
+ { LPGENT("Lonely"), "lonely" },
+ { LPGENT("Lost"), "lost" },
+ { LPGENT("Lucky"), "lucky" },
+ { LPGENT("Mean"), "mean" },
+ { LPGENT("Moody"), "moody" },
+ { LPGENT("Nervous"), "nervous" },
+ { LPGENT("Neutral"), "neutral" },
+ { LPGENT("Offended"), "offended" },
+ { LPGENT("Outraged"), "outraged" },
+ { LPGENT("Playful"), "playful" },
+ { LPGENT("Proud"), "proud" },
+ { LPGENT("Relaxed"), "relaxed" },
+ { LPGENT("Relieved"), "relieved" },
+ { LPGENT("Remorseful"), "remorseful" },
+ { LPGENT("Restless"), "restless" },
+ { LPGENT("Sad"), "sad" },
+ { LPGENT("Sarcastic"), "sarcastic" },
+ { LPGENT("Satisfied"), "satisfied" },
+ { LPGENT("Serious"), "serious" },
+ { LPGENT("Shocked"), "shocked" },
+ { LPGENT("Shy"), "shy" },
+ { LPGENT("Sick"), "sick" },
+ { LPGENT("Sleepy"), "sleepy" },
+ { LPGENT("Spontaneous"), "spontaneous" },
+ { LPGENT("Stressed"), "stressed" },
+ { LPGENT("Strong"), "strong" },
+ { LPGENT("Surprised"), "surprised" },
+ { LPGENT("Thankful"), "thankful" },
+ { LPGENT("Thirsty"), "thirsty" },
+ { LPGENT("Tired"), "tired" },
+ { LPGENT("Undefined"), "undefined" },
+ { LPGENT("Weak"), "weak" },
+ { LPGENT("Worried"), "worried" },
+};
+
+CPepMood::CPepMood(CJabberProto *proto):
+ CPepGuiService(proto, "Mood", _T(JABBER_FEAT_USER_MOOD)),
+ m_icons(proto),
+ m_text(NULL),
+ m_mode(-1)
+{
+ UpdateMenuItem(LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), LPGENT("Set mood..."));
+}
+
+CPepMood::~CPepMood()
+{
+ if (m_text) mir_free(m_text);
+}
+
+void CPepMood::InitGui()
+{
+ CSuper::InitGui();
+
+ char szFile[MAX_PATH];
+ GetModuleFileNameA(hInst, szFile, MAX_PATH);
+ if (char *p = strrchr(szFile, '\\'))
+ strcpy( p+1, "..\\Icons\\xstatus_jabber.dll" );
+
+ TCHAR szSection[100];
+
+ mir_sntprintf(szSection, SIZEOF(szSection), _T("Status Icons/%s/Moods"), m_proto->m_tszUserName);
+ for (int i = 1; i < SIZEOF(g_arrMoods); i++)
+ m_icons.RegisterIcon( g_arrMoods[i].szTag, szFile, -(200+i), szSection, TranslateTS(g_arrMoods[i].szName));
+}
+
+void CPepMood::ProcessItems(const TCHAR *from, HXML itemsNode)
+{
+ HANDLE hContact = NULL, hSelfContact = NULL;
+ if ( !m_proto->IsMyOwnJID( from ))
+ {
+ hContact = m_proto->HContactFromJID(from);
+ if (!hContact) return;
+ }
+ else
+ hSelfContact = m_proto->HContactFromJID(from);
+
+ if ( xmlGetChild( itemsNode, _T("retract")))
+ {
+ if (hSelfContact)
+ SetMood(hSelfContact, NULL, NULL);
+ SetMood(hContact, NULL, NULL);
+ return;
+ }
+
+ HXML n, moodNode = XPath( itemsNode, _T("item/mood[@xmlns='") _T(JABBER_FEAT_USER_MOOD) _T("']"));
+ if ( !moodNode ) return;
+
+ LPCTSTR moodType = NULL, moodText = NULL;
+ for ( int i = 0; n = xmlGetChild( moodNode, i ); i++ ) {
+ if ( !_tcscmp( xmlGetName( n ), _T("text")))
+ moodText = xmlGetText( n );
+ else
+ moodType = xmlGetName( n );
+ }
+
+ TCHAR *fixedText = JabberStrFixLines( moodText );
+ if (hSelfContact)
+ SetMood(hSelfContact, moodType, fixedText);
+ SetMood(hContact, moodType, fixedText);
+ mir_free( fixedText );
+
+ if (!hContact && m_mode >= 0)
+ ForceRepublishOnLogin();
+}
+
+void CPepMood::CreateData( HXML n )
+{
+ HXML moodNode = n << XCHILDNS( _T("mood"), _T(JABBER_FEAT_USER_MOOD));
+ moodNode << XCHILD( _A2T(g_arrMoods[m_mode].szTag));
+ if ( m_text )
+ moodNode << XCHILD( _T("text"), m_text );
+}
+
+void CPepMood::ResetExtraIcon(HANDLE hContact)
+{
+ char *szMood = m_proto->ReadAdvStatusA(hContact, ADVSTATUS_MOOD, "id");
+ SetExtraIcon(hContact, szMood);
+ mir_free(szMood);
+}
+
+void CPepMood::SetExtraIcon(HANDLE hContact, char *szMood)
+{
+ if (hExtraMood != NULL)
+ {
+ ExtraIcon_SetIcon(hExtraMood, hContact, szMood == NULL ? NULL : m_icons.GetIcolibName(szMood));
+ }
+ else
+ {
+ IconExtraColumn iec;
+ iec.cbSize = sizeof(iec);
+ iec.hImage = m_icons.GetClistHandle(szMood);
+ iec.ColumnType = EXTRA_ICON_ADV1;
+ CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+ }
+}
+
+void CPepMood::SetMood(HANDLE hContact, const TCHAR *szMood, const TCHAR *szText)
+{
+ int mood = -1;
+ if (szMood)
+ {
+ char* p = mir_t2a( szMood );
+
+ for (int i = 1; i < SIZEOF(g_arrMoods); ++i)
+ if (!lstrcmpA(g_arrMoods[i].szTag, p ))
+ {
+ mood = i;
+ break;
+ }
+
+ mir_free( p );
+
+ if (mood < 0)
+ return;
+ }
+
+ if (!hContact)
+ {
+ m_mode = mood;
+ replaceStrT(m_text, szText);
+
+ HANDLE hIcon = (mood >= 0) ? m_icons.GetIcolibHandle(g_arrMoods[mood].szTag) : LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT);
+ TCHAR title[128];
+
+ if (mood >= 0)
+ {
+ mir_sntprintf(title, SIZEOF(title), TranslateT("Mood: %s"), TranslateTS(g_arrMoods[mood].szName));
+ m_proto->m_pInfoFrame->UpdateInfoItem("$/PEP/mood", m_icons.GetIcolibHandle(g_arrMoods[mood].szTag), TranslateTS(g_arrMoods[mood].szName));
+ } else
+ {
+ lstrcpy(title, LPGENT("Set mood..."));
+ m_proto->m_pInfoFrame->UpdateInfoItem("$/PEP/mood", LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), TranslateT("Set mood..."));
+ }
+
+ UpdateMenuItem(hIcon, title);
+ } else
+ {
+ SetExtraIcon(hContact, mood < 0 ? NULL : g_arrMoods[mood].szTag);
+ }
+
+ if (szMood)
+ {
+ m_proto->JSetByte(hContact, DBSETTING_XSTATUSID, mood);
+ m_proto->JSetStringT(hContact, DBSETTING_XSTATUSNAME, TranslateTS(g_arrMoods[mood].szName));
+ if (szText)
+ m_proto->JSetStringT(hContact, DBSETTING_XSTATUSMSG, szText);
+ else
+ m_proto->JDeleteSetting(hContact, DBSETTING_XSTATUSMSG);
+
+ m_proto->WriteAdvStatus(hContact, ADVSTATUS_MOOD, szMood, m_icons.GetIcolibName(g_arrMoods[mood].szTag), TranslateTS(g_arrMoods[mood].szName), szText);
+ } else
+ {
+ m_proto->JDeleteSetting(hContact, DBSETTING_XSTATUSID);
+ m_proto->JDeleteSetting(hContact, DBSETTING_XSTATUSNAME);
+ m_proto->JDeleteSetting(hContact, DBSETTING_XSTATUSMSG);
+
+ m_proto->ResetAdvStatus(hContact, ADVSTATUS_MOOD);
+ }
+
+ NotifyEventHooks(m_proto->m_hEventXStatusChanged, (WPARAM)hContact, 0);
+}
+
+void CPepMood::ShowSetDialog(BYTE bQuiet)
+{
+ if ( !bQuiet ) {
+ CJabberDlgPepSimple dlg(m_proto, TranslateT("Set Mood"));
+ for (int i = 1; i < SIZEOF(g_arrMoods); ++i)
+ dlg.AddStatusMode(i, g_arrMoods[i].szTag, m_icons.GetIcon(g_arrMoods[i].szTag), TranslateTS(g_arrMoods[i].szName));
+ dlg.SetActiveStatus(m_mode, m_text);
+ dlg.DoModal();
+ if (!dlg.OkClicked())
+ return;
+
+ m_mode = dlg.GetStatusMode();
+ replaceStrT(m_text, dlg.GetStatusText());
+ }
+
+ if (m_mode >= 0)
+ {
+ Publish();
+
+ UpdateMenuItem(m_icons.GetIcolibHandle(g_arrMoods[m_mode].szTag), g_arrMoods[m_mode].szName);
+ m_proto->m_pInfoFrame->UpdateInfoItem("$/PEP/mood", m_icons.GetIcolibHandle(g_arrMoods[m_mode].szTag), TranslateTS(g_arrMoods[m_mode].szName));
+ } else
+ {
+ Retract();
+ UpdateMenuItem(LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), LPGENT("Set mood..."));
+ m_proto->m_pInfoFrame->UpdateInfoItem("$/PEP/mood", LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), TranslateT("Set mood..."));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CPepActivity
+#define ACTIVITY_ICON(section, item) -(300 + (section) * 20 + (item))
+
+struct
+{
+ char *szFirst;
+ char *szSecond;
+ TCHAR *szTitle;
+ int iconid;
+} g_arrActivities[] =
+{
+ { "doing_chores", NULL, _T("Doing chores"), ACTIVITY_ICON( 0, 0) },
+ { NULL, "buying_groceries", _T("buying groceries"), ACTIVITY_ICON( 0, 1) },
+ { NULL, "cleaning", _T("cleaning"), ACTIVITY_ICON( 0, 2) },
+ { NULL, "cooking", _T("cooking"), ACTIVITY_ICON( 0, 3) },
+ { NULL, "doing_maintenance", _T("doing maintenance"), ACTIVITY_ICON( 0, 4) },
+ { NULL, "doing_the_dishes", _T("doing the dishes"), ACTIVITY_ICON( 0, 5) },
+ { NULL, "doing_the_laundry", _T("doing the laundry"), ACTIVITY_ICON( 0, 6) },
+ { NULL, "gardening", _T("gardening"), ACTIVITY_ICON( 0, 7) },
+ { NULL, "running_an_errand", _T("running an errand"), ACTIVITY_ICON( 0, 8) },
+ { NULL, "walking_the_dog", _T("walking the dog"), ACTIVITY_ICON( 0, 9) },
+ { "drinking", NULL, _T("Drinking"), ACTIVITY_ICON( 1, 0) },
+ { NULL, "having_a_beer", _T("having a beer"), ACTIVITY_ICON( 1, 1) },
+ { NULL, "having_coffee", _T("having coffee"), ACTIVITY_ICON( 1, 2) },
+ { NULL, "having_tea", _T("having tea"), ACTIVITY_ICON( 1, 3) },
+ { "eating", NULL, _T("Eating"), ACTIVITY_ICON( 2, 0) },
+ { NULL, "having_a_snack", _T("having a snack"), ACTIVITY_ICON( 2, 1) },
+ { NULL, "having_breakfast", _T("having breakfast"), ACTIVITY_ICON( 2, 2) },
+ { NULL, "having_dinner", _T("having dinner"), ACTIVITY_ICON( 2, 3) },
+ { NULL, "having_lunch", _T("having lunch"), ACTIVITY_ICON( 2, 4) },
+ { "exercising", NULL, _T("Exercising"), ACTIVITY_ICON( 3, 0) },
+ { NULL, "cycling", _T("cycling"), ACTIVITY_ICON( 3, 1) },
+ { NULL, "dancing", _T("dancing"), ACTIVITY_ICON( 3, 2) },
+ { NULL, "hiking", _T("hiking"), ACTIVITY_ICON( 3, 3) },
+ { NULL, "jogging", _T("jogging"), ACTIVITY_ICON( 3, 4) },
+ { NULL, "playing_sports", _T("playing sports"), ACTIVITY_ICON( 3, 5) },
+ { NULL, "running", _T("running"), ACTIVITY_ICON( 3, 6) },
+ { NULL, "skiing", _T("skiing"), ACTIVITY_ICON( 3, 7) },
+ { NULL, "swimming", _T("swimming"), ACTIVITY_ICON( 3, 8) },
+ { NULL, "working_out", _T("working out"), ACTIVITY_ICON( 3, 9) },
+ { "grooming", NULL, _T("Grooming"), ACTIVITY_ICON( 4, 0) },
+ { NULL, "at_the_spa", _T("at the spa"), ACTIVITY_ICON( 4, 1) },
+ { NULL, "brushing_teeth", _T("brushing teeth"), ACTIVITY_ICON( 4, 2) },
+ { NULL, "getting_a_haircut", _T("getting a haircut"), ACTIVITY_ICON( 4, 3) },
+ { NULL, "shaving", _T("shaving"), ACTIVITY_ICON( 4, 4) },
+ { NULL, "taking_a_bath", _T("taking a bath"), ACTIVITY_ICON( 4, 5) },
+ { NULL, "taking_a_shower", _T("taking a shower"), ACTIVITY_ICON( 4, 6) },
+ { "having_appointment", NULL, _T("Having appointment"), ACTIVITY_ICON( 5, 0) },
+ { "inactive", NULL, _T("Inactive"), ACTIVITY_ICON( 6, 0) },
+ { NULL, "day_off", _T("day off"), ACTIVITY_ICON( 6, 1) },
+ { NULL, "hanging_out", _T("hanging out"), ACTIVITY_ICON( 6, 2) },
+ { NULL, "hiding", _T("hiding"), ACTIVITY_ICON( 6, 3) },
+ { NULL, "on_vacation", _T("on vacation"), ACTIVITY_ICON( 6, 4) },
+ { NULL, "praying", _T("praying"), ACTIVITY_ICON( 6, 5) },
+ { NULL, "scheduled_holiday", _T("scheduled holiday"), ACTIVITY_ICON( 6, 6) },
+ { NULL, "sleeping", _T("sleeping"), ACTIVITY_ICON( 6, 7) },
+ { NULL, "thinking", _T("thinking"), ACTIVITY_ICON( 6, 8) },
+ { "relaxing", NULL, _T("Relaxing"), ACTIVITY_ICON( 7, 0) },
+ { NULL, "fishing", _T("fishing"), ACTIVITY_ICON( 7, 1) },
+ { NULL, "gaming", _T("gaming"), ACTIVITY_ICON( 7, 2) },
+ { NULL, "going_out", _T("going out"), ACTIVITY_ICON( 7, 3) },
+ { NULL, "partying", _T("partying"), ACTIVITY_ICON( 7, 4) },
+ { NULL, "reading", _T("reading"), ACTIVITY_ICON( 7, 5) },
+ { NULL, "rehearsing", _T("rehearsing"), ACTIVITY_ICON( 7, 6) },
+ { NULL, "shopping", _T("shopping"), ACTIVITY_ICON( 7, 7) },
+ { NULL, "smoking", _T("smoking"), ACTIVITY_ICON( 7, 8) },
+ { NULL, "socializing", _T("socializing"), ACTIVITY_ICON( 7, 9) },
+ { NULL, "sunbathing", _T("sunbathing"), ACTIVITY_ICON( 7, 10) },
+ { NULL, "watching_tv", _T("watching TV"), ACTIVITY_ICON( 7, 11) },
+ { NULL, "watching_a_movie", _T("watching a movie"), ACTIVITY_ICON( 7, 12) },
+ { "talking", NULL, _T("Talking"), ACTIVITY_ICON( 8, 0) },
+ { NULL, "in_real_life", _T("in real life"), ACTIVITY_ICON( 8, 1) },
+ { NULL, "on_the_phone", _T("on the phone"), ACTIVITY_ICON( 8, 2) },
+ { NULL, "on_video_phone", _T("on video phone"), ACTIVITY_ICON( 8, 3) },
+ { "traveling", NULL, _T("Traveling"), ACTIVITY_ICON( 9, 0) },
+ { NULL, "commuting", _T("commuting"), ACTIVITY_ICON( 9, 1) },
+ { NULL, "cycling", _T("cycling"), ACTIVITY_ICON( 9, 2) },
+ { NULL, "driving", _T("driving"), ACTIVITY_ICON( 9, 3) },
+ { NULL, "in_a_car", _T("in a car"), ACTIVITY_ICON( 9, 4) },
+ { NULL, "on_a_bus", _T("on a bus"), ACTIVITY_ICON( 9, 5) },
+ { NULL, "on_a_plane", _T("on a plane"), ACTIVITY_ICON( 9, 6) },
+ { NULL, "on_a_train", _T("on a train"), ACTIVITY_ICON( 9, 7) },
+ { NULL, "on_a_trip", _T("on a trip"), ACTIVITY_ICON( 9, 8) },
+ { NULL, "walking", _T("walking"), ACTIVITY_ICON( 9, 9) },
+ { "working", NULL, _T("Working"), ACTIVITY_ICON(10, 0) },
+ { NULL, "coding", _T("coding"), ACTIVITY_ICON(10, 1) },
+ { NULL, "in_a_meeting", _T("in a meeting"), ACTIVITY_ICON(10, 2) },
+ { NULL, "studying", _T("studying"), ACTIVITY_ICON(10, 3) },
+ { NULL, "writing", _T("writing"), ACTIVITY_ICON(10, 4) },
+ { NULL, NULL, NULL } // the end, don't delete this
+};
+
+inline char *ActivityGetId(int id)
+{
+ return g_arrActivities[id].szSecond ? g_arrActivities[id].szSecond : g_arrActivities[id].szFirst;
+}
+
+// -1 if not found, otherwise activity number
+static int ActivityCheck( LPCTSTR szFirstNode, LPCTSTR szSecondNode )
+{
+ if (!szFirstNode) return 0;
+
+ char *s1 = mir_t2a( szFirstNode ), *s2 = mir_t2a( szSecondNode );
+
+ int i = 0, nFirst = -1, nSecond = -1;
+ while ( g_arrActivities[i].szFirst || g_arrActivities[i].szSecond ) {
+ // check first node
+ if ( g_arrActivities[i].szFirst && !strcmp( s1, g_arrActivities[i].szFirst )) {
+ // first part found
+ nFirst = i;
+ if ( !s2 ) {
+ nSecond = i;
+ break;
+ }
+ i++; // move to next
+ while ( g_arrActivities[i].szSecond ) {
+ if ( !strcmp( g_arrActivities[i].szSecond, s2 )) {
+ nSecond = i;
+ break;
+ }
+ i++;
+ }
+ break;
+ }
+ i++;
+ }
+
+ mir_free( s1 );
+ mir_free( s2 );
+
+ if ( nSecond != -1 )
+ return nSecond;
+
+ return nFirst;
+}
+
+char *returnActivity (int id){
+ if (g_arrActivities[id].szFirst)
+ return g_arrActivities[id].szFirst;
+ if (g_arrActivities[id].szSecond)
+ return g_arrActivities[id].szSecond;
+ return NULL;}
+
+char *ActivityGetFirst(int id)
+{
+ if (id >= SIZEOF(g_arrActivities) - 1)
+ return NULL;
+
+ while (id >= 0)
+ {
+ if (g_arrActivities[id].szFirst)
+ return g_arrActivities[id].szFirst;
+ --id;
+ }
+
+ return NULL;
+}
+
+char *ActivityGetFirst(char *szId)
+{
+ if (!szId) return NULL;
+
+ int id = SIZEOF(g_arrActivities) - 1;
+ bool found_second = false;
+
+ while (id >= 0)
+ {
+ if (g_arrActivities[id].szFirst && (found_second || !lstrcmpA(g_arrActivities[id].szFirst, szId)))
+ return g_arrActivities[id].szFirst;
+ if (g_arrActivities[id].szSecond && !found_second && !lstrcmpA(g_arrActivities[id].szSecond, szId))
+ found_second = true;
+ --id;
+ }
+
+ return NULL;
+}
+
+char *ActivityGetSecond(int id)
+{
+ return (id >= 0) ? g_arrActivities[id].szSecond : NULL;
+}
+
+TCHAR *ActivityGetFirstTitle(int id)
+{
+ if (id >= SIZEOF(g_arrActivities) - 1)
+ return NULL;
+
+ while (id >= 0)
+ {
+ if (g_arrActivities[id].szFirst)
+ return g_arrActivities[id].szTitle;
+ --id;
+ }
+
+ return NULL;
+}
+
+TCHAR *ActivityGetSecondTitle(int id)
+{
+ return ((id >= 0) && g_arrActivities[id].szSecond) ? g_arrActivities[id].szTitle : NULL;
+}
+
+void ActivityBuildTitle(int id, TCHAR *buf, int size)
+{
+ TCHAR *szFirst = ActivityGetFirstTitle(id);
+ TCHAR *szSecond = ActivityGetSecondTitle(id);
+
+ if (szFirst)
+ {
+ if (szSecond)
+ mir_sntprintf(buf, size, _T("%s [%s]"), TranslateTS(szFirst), TranslateTS(szSecond));
+ else
+ lstrcpyn(buf, TranslateTS(szFirst), size);
+ } else
+ *buf = 0;
+}
+
+CPepActivity::CPepActivity(CJabberProto *proto):
+ CPepGuiService(proto, "Activity", _T(JABBER_FEAT_USER_ACTIVITY)),
+ m_icons(proto),
+ m_text(NULL),
+ m_mode(-1)
+{
+ UpdateMenuItem(LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), LPGENT("Set activity..."));
+}
+
+CPepActivity::~CPepActivity()
+{
+ if (m_text) mir_free(m_text);
+}
+
+void CPepActivity::InitGui()
+{
+ CSuper::InitGui();
+
+ char szFile[MAX_PATH];
+ GetModuleFileNameA(hInst, szFile, MAX_PATH);
+ if (char *p = strrchr(szFile, '\\'))
+ strcpy( p+1, "..\\Icons\\xstatus_jabber.dll" );
+
+ TCHAR szSection[100];
+
+ mir_sntprintf(szSection, SIZEOF(szSection), _T("Status Icons/%s/Activities"), m_proto->m_tszUserName);
+ for (int i = 0; i < SIZEOF(g_arrActivities); i++) {
+ if (g_arrActivities[i].szFirst)
+ m_icons.RegisterIcon(g_arrActivities[i].szFirst, szFile, g_arrActivities[i].iconid, szSection, TranslateTS(g_arrActivities[i].szTitle));
+ if (g_arrActivities[i].szSecond)
+ m_icons.RegisterIcon(g_arrActivities[i].szSecond, szFile, g_arrActivities[i].iconid, szSection, TranslateTS(g_arrActivities[i].szTitle));}
+
+}
+
+void CPepActivity::ProcessItems(const TCHAR *from, HXML itemsNode)
+{
+ HANDLE hContact = NULL, hSelfContact = NULL;
+ if ( !m_proto->IsMyOwnJID( from ))
+ {
+ hContact = m_proto->HContactFromJID(from);
+ if (!hContact) return;
+ }
+ else
+ hSelfContact = m_proto->HContactFromJID(from);
+
+ if ( xmlGetChild( itemsNode, "retract"))
+ {
+ if (hSelfContact)
+ SetActivity(hSelfContact, NULL, NULL, NULL);
+ SetActivity(hContact, NULL, NULL, NULL);
+ return;
+ }
+
+ HXML actNode = XPath( itemsNode, _T("item/activity[@xmlns='") _T(JABBER_FEAT_USER_ACTIVITY) _T("']"));
+ if ( !actNode ) return;
+
+ LPCTSTR szText = XPathT( actNode, "text" );
+ LPCTSTR szFirstNode = NULL, szSecondNode = NULL;
+
+ HXML n;
+ for ( int i = 0; n = xmlGetChild( actNode, i ); i++ ) {
+ if ( lstrcmp( xmlGetName( n ), _T("text")))
+ {
+ szFirstNode = xmlGetName( n );
+ HXML secondNode = xmlGetChild( n, 0 );
+ if (szFirstNode && secondNode && xmlGetName( secondNode ))
+ szSecondNode = xmlGetName( secondNode );
+ break;
+ }
+ }
+
+ TCHAR *fixedText = JabberStrFixLines( szText );
+ if (hSelfContact)
+ SetActivity(hSelfContact, szFirstNode, szSecondNode, fixedText);
+ SetActivity(hContact, szFirstNode, szSecondNode, fixedText);
+ mir_free( fixedText );
+
+ if (!hContact && m_mode >= 0)
+ ForceRepublishOnLogin();
+}
+
+void CPepActivity::CreateData( HXML n )
+{
+ char *szFirstNode = ActivityGetFirst(m_mode);
+ char *szSecondNode = ActivityGetSecond(m_mode);
+
+ HXML activityNode = n << XCHILDNS( _T("activity"), _T(JABBER_FEAT_USER_ACTIVITY));
+ HXML firstNode = activityNode << XCHILD( _A2T( szFirstNode ));
+
+ if (firstNode && szSecondNode)
+ firstNode << XCHILD( _A2T(szSecondNode));
+
+ if (m_text)
+ activityNode << XCHILD( _T("text"), m_text);
+}
+
+void CPepActivity::ResetExtraIcon(HANDLE hContact)
+{
+ char *szActivity = m_proto->ReadAdvStatusA(hContact, ADVSTATUS_ACTIVITY, "id");
+ SetExtraIcon(hContact, szActivity);
+ mir_free(szActivity);
+}
+
+void CPepActivity::SetExtraIcon(HANDLE hContact, char *szActivity)
+{
+ if (hExtraActivity != NULL)
+ {
+ ExtraIcon_SetIcon(hExtraActivity, hContact,
+ szActivity == NULL ? NULL : m_icons.GetIcolibName(szActivity));
+ }
+ else
+ {
+ IconExtraColumn iec;
+ iec.cbSize = sizeof(iec);
+ iec.hImage = m_icons.GetClistHandle(szActivity);
+ iec.ColumnType = EXTRA_ICON_ADV2;
+ CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+ }
+}
+
+void CPepActivity::SetActivity(HANDLE hContact, LPCTSTR szFirst, LPCTSTR szSecond, LPCTSTR szText)
+{
+ int activity = -1;
+ if (szFirst || szSecond)
+ {
+ activity = ActivityCheck(szFirst, szSecond);
+
+ if (activity < 0)
+ return;
+ }
+
+ TCHAR activityTitle[128];
+ ActivityBuildTitle(activity, activityTitle, SIZEOF(activityTitle));
+
+ if (!hContact)
+ {
+ m_mode = activity;
+ replaceStrT(m_text, szText);
+
+ HANDLE hIcon = (activity >= 0) ? m_icons.GetIcolibHandle(returnActivity(activity)) : LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT);
+ TCHAR title[128];
+
+ if (activity >= 0)
+ {
+ mir_sntprintf(title, SIZEOF(title), TranslateT("Activity: %s"), activityTitle);
+ m_proto->m_pInfoFrame->UpdateInfoItem("$/PEP/activity", m_icons.GetIcolibHandle(returnActivity(activity)), activityTitle);
+ } else
+ {
+ lstrcpy(title, LPGENT("Set activity..."));
+ m_proto->m_pInfoFrame->UpdateInfoItem("$/PEP/activity", LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), TranslateT("Set activity..."));
+ }
+
+ UpdateMenuItem(hIcon, title);
+ } else
+ {
+ SetExtraIcon(hContact, activity < 0 ? NULL : returnActivity(activity));
+ }
+
+
+ if (activity >= 0) {
+ TCHAR* p = mir_a2t( ActivityGetId(activity));
+ m_proto->WriteAdvStatus(hContact, ADVSTATUS_ACTIVITY, p, m_icons.GetIcolibName(returnActivity(activity)), activityTitle, szText);
+ mir_free( p );
+ }
+ else
+ m_proto->ResetAdvStatus(hContact, ADVSTATUS_ACTIVITY);
+}
+
+void CPepActivity::ShowSetDialog(BYTE bQuiet)
+{
+ CJabberDlgPepSimple dlg(m_proto, TranslateT("Set Activity"));
+ for (int i = 0; i < SIZEOF(g_arrActivities); ++i)
+ if (g_arrActivities[i].szFirst || g_arrActivities[i].szSecond)
+ dlg.AddStatusMode(i, ActivityGetId(i), m_icons.GetIcon(returnActivity(i)), TranslateTS(g_arrActivities[i].szTitle), (g_arrActivities[i].szSecond != NULL));
+ dlg.SetActiveStatus(m_mode, m_text);
+ dlg.DoModal();
+
+ if (!dlg.OkClicked()) return;
+
+ m_mode = dlg.GetStatusMode();
+ if (m_mode >= 0)
+ {
+ replaceStrT(m_text, dlg.GetStatusText());
+ Publish();
+
+ UpdateMenuItem(m_icons.GetIcolibHandle(returnActivity(m_mode)), g_arrActivities[m_mode].szTitle);
+ m_proto->m_pInfoFrame->UpdateInfoItem("$/PEP/activity", m_icons.GetIcolibHandle(returnActivity(m_mode)), TranslateTS(g_arrActivities[m_mode].szTitle));
+ } else
+ {
+ Retract();
+ UpdateMenuItem(LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), LPGENT("Set activity..."));
+ m_proto->m_pInfoFrame->UpdateInfoItem("$/PEP/activity", LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), TranslateT("Set activity..."));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// icq api emulation
+
+HICON CJabberProto::GetXStatusIcon(int bStatus, UINT flags)
+{
+ CPepMood *pepMood = (CPepMood *)m_pepServices.Find(_T(JABBER_FEAT_USER_MOOD));
+ HICON icon = pepMood->m_icons.GetIcon(g_arrMoods[bStatus].szTag, (flags & LR_BIGICON) != 0);
+ return ( flags & LR_SHARED ) ? icon : CopyIcon( icon );
+}
+
+int CJabberProto::CListMW_ExtraIconsApply( WPARAM wParam, LPARAM )
+{
+ if (m_bJabberOnline && m_bPepSupported && ServiceExists(MS_CLIST_EXTRA_SET_ICON))
+ {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, wParam, 0 );
+ if ( szProto==NULL || strcmp( szProto, m_szModuleName ))
+ return 0; // only apply icons to our contacts, do not mess others
+
+ m_pepServices.ResetExtraIcon((HANDLE)wParam);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetXStatus - gets the extended status info (mood)
+
+INT_PTR __cdecl CJabberProto::OnGetXStatus( WPARAM wParam, LPARAM lParam )
+{
+ if ( !m_bJabberOnline || !m_bPepSupported )
+ return 0;
+
+ if ( wParam ) *(( char** )wParam ) = DBSETTING_XSTATUSNAME;
+ if ( lParam ) *(( char** )lParam ) = DBSETTING_XSTATUSMSG;
+ return ((CPepMood *)m_pepServices.Find(_T(JABBER_FEAT_USER_MOOD)))->m_mode;
+}
+
+// not needed anymore and therefore commented out
+
+/*INT_PTR __cdecl CJabberProto::OnGetXStatusEx( WPARAM wParam, LPARAM lParam )
+{
+ JABBER_CUSTOM_STATUS *pData = (JABBER_CUSTOM_STATUS*)lParam;
+ HANDLE hContact = (HANDLE)wParam;
+
+ if ( !m_bJabberOnline || !m_bPepSupported )
+ return 1;
+
+ if (pData->cbSize < sizeof(JABBER_CUSTOM_STATUS)) return 1; // Failure
+
+
+ if ( wParam ) *(( char** )wParam ) = DBSETTING_XSTATUSNAME;
+ if ( lParam ) *(( char** )lParam ) = DBSETTING_XSTATUSMSG;
+ return ((CPepMood *)m_pepServices.Find(_T(JABBER_FEAT_USER_MOOD)))->m_mode;
+}*/
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetXStatusIcon - Retrieves specified custom status icon
+//wParam = (int)N // custom status id, 0 = my current custom status
+//lParam = flags // use LR_SHARED for shared HICON
+//return = HICON // custom status icon (use DestroyIcon to release resources if not LR_SHARED)
+
+INT_PTR __cdecl CJabberProto::OnGetXStatusIcon( WPARAM wParam, LPARAM lParam )
+{
+ if ( !m_bJabberOnline )
+ return 0;
+
+ if ( !wParam )
+ wParam = ((CPepMood *)m_pepServices.Find(_T(JABBER_FEAT_USER_MOOD)))->m_mode;
+
+ if ( wParam < 1 || wParam >= SIZEOF(g_arrMoods))
+ return 0;
+
+ int flags = 0;
+ if ( lParam & LR_SHARED ) flags |= LR_SHARED;
+ if ( lParam & LR_BIGICON ) flags |= LR_BIGICON;
+
+ return (INT_PTR)GetXStatusIcon( wParam, flags );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SendPepMood - sends mood
+
+BOOL CJabberProto::SendPepTune( TCHAR* szArtist, TCHAR* szLength, TCHAR* szSource, TCHAR* szTitle, TCHAR* szTrack, TCHAR* szUri )
+{
+ if ( !m_bJabberOnline || !m_bPepSupported )
+ return FALSE;
+
+ XmlNodeIq iq( _T("set"), SerialNext());
+ HXML tuneNode = iq << XCHILDNS( _T("pubsub"), _T(JABBER_FEAT_PUBSUB))
+ << XCHILD( _T("publish")) << XATTR( _T("node"), _T(JABBER_FEAT_USER_TUNE))
+ << XCHILD( _T("item")) << XCHILDNS( _T("tune"), _T(JABBER_FEAT_USER_TUNE));
+
+ if ( szArtist || szLength || szSource || szTitle || szUri ) {
+ if ( szArtist ) tuneNode << XCHILD( _T("artist"), szArtist );
+ if ( szLength ) tuneNode << XCHILD( _T("length"), szLength );
+ if ( szSource ) tuneNode << XCHILD( _T("source"), szSource );
+ if ( szTitle ) tuneNode << XCHILD( _T("title"), szTitle );
+ if ( szTrack ) tuneNode << XCHILD( _T("track"), szTrack );
+ if ( szUri ) tuneNode << XCHILD( _T("uri"), szUri );
+ }
+ m_ThreadInfo->send( iq );
+
+ return TRUE;
+}
+
+void CJabberProto::SetContactTune( HANDLE hContact, LPCTSTR szArtist, LPCTSTR szLength, LPCTSTR szSource, LPCTSTR szTitle, LPCTSTR szTrack )
+{
+ if ( !szArtist && !szTitle ) {
+ JDeleteSetting( hContact, "ListeningTo" );
+ ResetAdvStatus( hContact, ADVSTATUS_TUNE );
+ return;
+ }
+
+ TCHAR *szListeningTo;
+ if ( ServiceExists( MS_LISTENINGTO_GETPARSEDTEXT )) {
+ LISTENINGTOINFO li;
+ ZeroMemory( &li, sizeof( li ));
+ li.cbSize = sizeof( li );
+ li.dwFlags = LTI_TCHAR;
+ li.ptszArtist = ( TCHAR* )szArtist;
+ li.ptszLength = ( TCHAR* )szLength;
+ li.ptszAlbum = ( TCHAR* )szSource;
+ li.ptszTitle = ( TCHAR* )szTitle;
+ li.ptszTrack = ( TCHAR* )szTrack;
+ szListeningTo = (TCHAR *)CallService( MS_LISTENINGTO_GETPARSEDTEXT, (WPARAM)_T("%title% - %artist%"), (LPARAM)&li );
+ }
+ else {
+ szListeningTo = (TCHAR *) mir_alloc( 2048 * sizeof( TCHAR ));
+ mir_sntprintf( szListeningTo, 2047, _T("%s - %s"), szTitle ? szTitle : _T(""), szArtist ? szArtist : _T(""));
+ }
+
+ JSetStringT( hContact, "ListeningTo", szListeningTo );
+
+ char tuneIcon[128];
+ mir_snprintf(tuneIcon, SIZEOF(tuneIcon), "%s_%s", m_szModuleName, "main");
+ WriteAdvStatus( hContact, ADVSTATUS_TUNE, _T("listening_to"), tuneIcon, TranslateT("Listening To"), szListeningTo );
+
+ mir_free( szListeningTo );
+}
+
+TCHAR* a2tf( const TCHAR* str, BOOL unicode )
+{
+ if ( str == NULL )
+ return NULL;
+
+ return ( unicode ) ? mir_tstrdup( str ) : mir_a2t(( char* )str );
+}
+
+void overrideStr( TCHAR*& dest, const TCHAR* src, BOOL unicode, const TCHAR* def = NULL )
+{
+ if ( dest != NULL )
+ {
+ mir_free( dest );
+ dest = NULL;
+ }
+
+ if ( src != NULL )
+ dest = a2tf( src, unicode );
+ else if ( def != NULL )
+ dest = mir_tstrdup( def );
+}
+
+INT_PTR __cdecl CJabberProto::OnSetListeningTo( WPARAM, LPARAM lParam )
+{
+ LISTENINGTOINFO *cm = (LISTENINGTOINFO *)lParam;
+ if ( !cm || cm->cbSize != sizeof(LISTENINGTOINFO)) {
+ SendPepTune( NULL, NULL, NULL, NULL, NULL, NULL );
+ JDeleteSetting( NULL, "ListeningTo" );
+ }
+ else {
+ TCHAR *szArtist = NULL, *szLength = NULL, *szSource = NULL;
+ TCHAR *szTitle = NULL, *szTrack = NULL;
+
+ BOOL unicode = cm->dwFlags & LTI_UNICODE;
+
+ overrideStr( szArtist, cm->ptszArtist, unicode );
+ overrideStr( szSource, cm->ptszAlbum, unicode );
+ overrideStr( szTitle, cm->ptszTitle, unicode );
+ overrideStr( szTrack, cm->ptszTrack, unicode );
+ overrideStr( szLength, cm->ptszLength, unicode );
+
+ TCHAR szLengthInSec[ 32 ];
+ szLengthInSec[ 0 ] = _T('\0');
+ if ( szLength ) {
+ unsigned int multiplier = 1, result = 0;
+ for ( TCHAR *p = szLength; *p; p++ ) {
+ if ( *p == _T(':')) multiplier *= 60;
+ }
+ if ( multiplier <= 3600 ) {
+ TCHAR *szTmp = szLength;
+ while ( szTmp[0] ) {
+ result += ( _ttoi( szTmp ) * multiplier );
+ multiplier /= 60;
+ szTmp = _tcschr( szTmp, _T(':'));
+ if ( !szTmp )
+ break;
+ szTmp++;
+ }
+ }
+ mir_sntprintf( szLengthInSec, SIZEOF( szLengthInSec ), _T("%d"), result );
+ }
+
+ SendPepTune( szArtist, szLength ? szLengthInSec : NULL, szSource, szTitle, szTrack, NULL );
+ SetContactTune( NULL, szArtist, szLength, szSource, szTitle, szTrack );
+
+ mir_free( szArtist );
+ mir_free( szLength );
+ mir_free( szSource );
+ mir_free( szTitle );
+ mir_free( szTrack );
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// process InfoFrame clicks
+
+void CJabberProto::InfoFrame_OnUserMood(CJabberInfoFrame_Event*)
+{
+ m_pepServices.Find(_T(JABBER_FEAT_USER_MOOD))->LaunchSetGui();
+}
+
+void CJabberProto::InfoFrame_OnUserActivity(CJabberInfoFrame_Event*)
+{
+ m_pepServices.Find(_T(JABBER_FEAT_USER_ACTIVITY))->LaunchSetGui();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// builds xstatus menu
+
+void CJabberProto::XStatusInit()
+{
+ if (hExtraMood == NULL)
+ JHookEvent( ME_CLIST_EXTRA_IMAGE_APPLY, &CJabberProto::CListMW_ExtraIconsApply );
+
+ RegisterAdvStatusSlot( ADVSTATUS_MOOD );
+ RegisterAdvStatusSlot( ADVSTATUS_TUNE );
+ RegisterAdvStatusSlot( ADVSTATUS_ACTIVITY );
+}
+
+void CJabberProto::XStatusUninit()
+{
+ if ( m_hHookExtraIconsRebuild )
+ UnhookEvent( m_hHookExtraIconsRebuild );
+
+ if ( m_hHookExtraIconsApply )
+ UnhookEvent( m_hHookExtraIconsApply );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberSetXStatus - sets the extended status info (mood)
+
+INT_PTR __cdecl CJabberProto::OnSetXStatus( WPARAM wParam, LPARAM )
+{
+ if ( !m_bPepSupported || !m_bJabberOnline )
+ return 0;
+
+ CPepMood *pepMood = (CPepMood *)m_pepServices.Find(_T(JABBER_FEAT_USER_MOOD));
+ if ( !wParam ) {
+ pepMood->m_mode = -1;
+ pepMood->Retract();
+ return 0;
+ }
+
+ if ( wParam > 0 && wParam < SIZEOF(g_arrMoods)) {
+ pepMood->m_mode = wParam;
+ pepMood->LaunchSetGui( 0 );
+ return wParam;
+ }
+
+ return 0;
+}
+
+INT_PTR __cdecl CJabberProto::OnSetXStatusEx( WPARAM wParam, LPARAM lParam)
+{
+ JABBER_CUSTOM_STATUS *pData = (JABBER_CUSTOM_STATUS*)lParam;
+
+ if ( !m_bPepSupported || !m_bJabberOnline )
+ return 1;
+
+ if (pData->cbSize < sizeof(JABBER_CUSTOM_STATUS)) return 1; // Failure
+
+ CPepMood *pepMood = (CPepMood *)m_pepServices.Find(_T(JABBER_FEAT_USER_MOOD));
+
+ int status = *pData->status;
+ if (status > 0 && status < SIZEOF(g_arrMoods)) {
+ pepMood->m_mode = status;
+ pepMood->m_text = JabberStrFixLines( pData->ptszMessage );
+ pepMood->LaunchSetGui( 1 );
+ return 0;
+ }
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Advanced status slots
+// DB data format:
+// Contact / AdvStatus / Proto/Status/id = mode_id
+// Contact / AdvStatus / Proto/Status/icon = icon
+// Contact / AdvStatus / Proto/Status/title = title
+// Contact / AdvStatus / Proto/Status/text = title
+
+void CJabberProto::RegisterAdvStatusSlot(const char *pszSlot)
+{
+ char szSetting[256];
+ mir_snprintf(szSetting, SIZEOF(szSetting), "AdvStatus/%s/%s/id", m_szModuleName, pszSlot);
+ CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)szSetting);
+ mir_snprintf(szSetting, SIZEOF(szSetting), "AdvStatus/%s/%s/icon", m_szModuleName, pszSlot);
+ CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)szSetting);
+ mir_snprintf(szSetting, SIZEOF(szSetting), "AdvStatus/%s/%s/title", m_szModuleName, pszSlot);
+ CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)szSetting);
+ mir_snprintf(szSetting, SIZEOF(szSetting), "AdvStatus/%s/%s/text", m_szModuleName, pszSlot);
+ CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)szSetting);
+}
+
+void CJabberProto::ResetAdvStatus(HANDLE hContact, const char *pszSlot)
+{ // set empty text before DBDeleteContactSetting to make resident setting manager happy
+ char szSetting[128];
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/id", m_szModuleName, pszSlot);
+ DBWriteContactSettingString(hContact, "AdvStatus", szSetting, "");
+ DBDeleteContactSetting(hContact, "AdvStatus", szSetting);
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/icon", m_szModuleName, pszSlot);
+ DBWriteContactSettingString(hContact, "AdvStatus", szSetting, "");
+ DBDeleteContactSetting(hContact, "AdvStatus", szSetting);
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/title", m_szModuleName, pszSlot);
+ DBWriteContactSettingString(hContact, "AdvStatus", szSetting, "");
+ DBDeleteContactSetting(hContact, "AdvStatus", szSetting);
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/text", m_szModuleName, pszSlot);
+ DBWriteContactSettingString(hContact, "AdvStatus", szSetting, "");
+ DBDeleteContactSetting(hContact, "AdvStatus", szSetting);
+}
+
+void CJabberProto::WriteAdvStatus(HANDLE hContact, const char *pszSlot, const TCHAR *pszMode, const char *pszIcon, const TCHAR *pszTitle, const TCHAR *pszText)
+{
+ char szSetting[128];
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/id", m_szModuleName, pszSlot);
+ DBWriteContactSettingTString(hContact, "AdvStatus", szSetting, pszMode);
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/icon", m_szModuleName, pszSlot);
+ DBWriteContactSettingString(hContact, "AdvStatus", szSetting, pszIcon);
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/title", m_szModuleName, pszSlot);
+ DBWriteContactSettingTString(hContact, "AdvStatus", szSetting, pszTitle);
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/text", m_szModuleName, pszSlot);
+ if (pszText) {
+ DBWriteContactSettingTString(hContact, "AdvStatus", szSetting, pszText);
+ } else
+ {
+ // set empty text before DBDeleteContactSetting to make resident setting manager happy
+ DBWriteContactSettingString(hContact, "AdvStatus", szSetting, "");
+ DBDeleteContactSetting(hContact, "AdvStatus", szSetting);
+ }
+}
+
+char *CJabberProto::ReadAdvStatusA(HANDLE hContact, const char *pszSlot, const char *pszValue)
+{
+ char szSetting[128];
+ DBVARIANT dbv = {0};
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/%s", m_szModuleName, pszSlot, pszValue);
+ if (DBGetContactSettingString(hContact, "AdvStatus", szSetting, &dbv))
+ return NULL;
+
+ char *res = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ return res;
+}
+
+TCHAR *CJabberProto::ReadAdvStatusT(HANDLE hContact, const char *pszSlot, const char *pszValue)
+{
+ char szSetting[128];
+ DBVARIANT dbv = {0};
+
+ mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/%s", m_szModuleName, pszSlot, pszValue);
+ if (DBGetContactSettingTString(hContact, "AdvStatus", szSetting, &dbv))
+ return NULL;
+
+ TCHAR *res = mir_tstrdup(dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CJabberInfoFrame
+
+class CJabberInfoFrameItem
+{
+public:
+ char *m_pszName;
+ HANDLE m_hIcolibIcon;
+ TCHAR *m_pszText;
+ LPARAM m_pUserData;
+ bool m_bCompact;
+ bool m_bShow;
+ void (CJabberProto::*m_onEvent)(CJabberInfoFrame_Event *);
+ RECT m_rcItem;
+ int m_tooltipId;
+
+public:
+/*
+ CJabberInfoFrameItem():
+ m_pszName(NULL), m_hIcolibIcon(NULL), m_pszText(NULL)
+ {
+ }
+*/
+ CJabberInfoFrameItem(char *pszName, bool bCompact=false, LPARAM pUserData=0):
+ m_pszName(NULL), m_hIcolibIcon(NULL), m_pszText(NULL), m_bShow(true), m_bCompact(bCompact), m_pUserData(pUserData), m_onEvent(NULL)
+ {
+ m_pszName = mir_strdup(pszName);
+ }
+ ~CJabberInfoFrameItem()
+ {
+ mir_free(m_pszName);
+ mir_free(m_pszText);
+ }
+
+ void SetInfo(HANDLE hIcolibIcon, TCHAR *pszText)
+ {
+ mir_free(m_pszText);
+ m_pszText = pszText ? mir_tstrdup(pszText) : NULL;
+ m_hIcolibIcon = hIcolibIcon;
+ }
+
+ static int cmp(const CJabberInfoFrameItem *p1, const CJabberInfoFrameItem *p2)
+ {
+ return lstrcmpA(p1->m_pszName, p2->m_pszName);
+ }
+};
+
+CJabberInfoFrame::CJabberInfoFrame(CJabberProto *proto):
+ m_pItems(3, CJabberInfoFrameItem::cmp), m_compact(false)
+{
+ m_proto = proto;
+ m_hwnd = m_hwndToolTip = NULL;
+ m_clickedItem = -1;
+ m_hiddenItemCount = 0;
+ m_bLocked = false;
+ m_nextTooltipId = 0;
+ m_hhkFontsChanged = 0;
+
+ if (!proto->m_options.DisableFrame && ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ {
+ InitClass();
+
+ CLISTFrame frame = {0};
+ frame.cbSize = sizeof(frame);
+ HWND hwndClist = (HWND)CallService(MS_CLUI_GETHWND, 0, 0);
+ frame.hWnd = CreateWindowEx(0, _T("JabberInfoFrameClass"), NULL, WS_CHILD|WS_VISIBLE, 0, 0, 100, 100, hwndClist, NULL, hInst, this);
+ frame.align = alBottom;
+ frame.height = 2 * SZ_FRAMEPADDING + GetSystemMetrics(SM_CYSMICON) + SZ_LINEPADDING; // compact height by default
+ frame.Flags = F_VISIBLE|F_LOCKED|F_NOBORDER|F_TCHAR;
+ frame.tname = mir_a2t(proto->m_szModuleName);
+ frame.TBtname = proto->m_tszUserName;
+ m_frameId = CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)&frame, 0);
+ mir_free(frame.tname);
+ if (m_frameId == -1) {
+ DestroyWindow(frame.hWnd);
+ return;
+ }
+
+ m_hhkFontsChanged = HookEventMessage(ME_FONT_RELOAD, m_hwnd, WM_APP);
+ ReloadFonts();
+
+ m_hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
+ WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ m_hwnd, NULL, hInst, NULL);
+ SetWindowPos(m_hwndToolTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+ CreateInfoItem("$", true);
+ UpdateInfoItem("$", proto->GetIconHandle(IDI_JABBER), proto->m_tszUserName);
+
+ CreateInfoItem("$/JID", true);
+ UpdateInfoItem("$/JID", LoadSkinnedIconHandle(SKINICON_OTHER_USERDETAILS), _T("Offline"));
+ SetInfoItemCallback("$/JID", &CJabberProto::InfoFrame_OnSetup);
+ }
+}
+
+CJabberInfoFrame::~CJabberInfoFrame()
+{
+ if (!m_hwnd) return;
+
+ if (m_hhkFontsChanged) UnhookEvent(m_hhkFontsChanged);
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)m_frameId, 0);
+ SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0);
+ DestroyWindow(m_hwnd);
+ DestroyWindow(m_hwndToolTip);
+ DeleteObject(m_hfntText);
+ DeleteObject(m_hfntTitle);
+ m_hwnd = NULL;
+}
+
+void CJabberInfoFrame::InitClass()
+{
+ static bool bClassRegistered = false;
+ if (bClassRegistered) return;
+
+ WNDCLASSEX wcx = {0};
+ wcx.cbSize = sizeof(wcx);
+ wcx.style = CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
+ wcx.lpfnWndProc = GlobalWndProc;
+ wcx.hInstance = hInst;
+ wcx.lpszClassName = _T("JabberInfoFrameClass");
+ wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
+ RegisterClassEx(&wcx);
+ bClassRegistered = true;
+}
+
+LRESULT CALLBACK CJabberInfoFrame::GlobalWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CJabberInfoFrame *pFrame;
+
+ if (msg == WM_CREATE)
+ {
+ CREATESTRUCT *pcs = (CREATESTRUCT *)lParam;
+ pFrame = (CJabberInfoFrame *)pcs->lpCreateParams;
+ if (pFrame) pFrame->m_hwnd = hwnd;
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pFrame);
+ } else
+ {
+ pFrame = (CJabberInfoFrame *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ }
+
+ return pFrame ? pFrame->WndProc(msg, wParam, lParam) : DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+LRESULT CJabberInfoFrame::WndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_APP:
+ {
+ ReloadFonts();
+ return 0;
+ }
+
+ case WM_PAINT:
+ {
+ RECT rc; GetClientRect(m_hwnd, &rc);
+ m_compact = rc.bottom < (2 * (GetSystemMetrics(SM_CYSMICON) + SZ_LINEPADDING) + SZ_LINESPACING + 2 * SZ_FRAMEPADDING);
+
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(m_hwnd, &ps);
+ m_compact ? PaintCompact(hdc) : PaintNormal(hdc);
+ EndPaint(m_hwnd, &ps);
+ return 0;
+ }
+
+ case WM_RBUTTONUP:
+ {
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ MapWindowPoints(m_hwnd, NULL, &pt, 1);
+ HMENU hMenu = (HMENU)CallService(MS_CLIST_MENUBUILDFRAMECONTEXT, m_frameId, 0);
+ int res = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, (HWND)CallService(MS_CLUI_GETHWND, 0, 0), NULL);
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(res, 0), m_frameId);
+ return 0;
+ }
+
+ case WM_LBUTTONDOWN:
+ {
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ for (int i = 0; i < m_pItems.getCount(); ++i)
+ if (m_pItems[i].m_onEvent && PtInRect(&m_pItems[i].m_rcItem, pt))
+ {
+ m_clickedItem = i;
+ return 0;
+ }
+
+ return 0;
+ }
+
+ case WM_LBUTTONUP:
+ {
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ if ((m_clickedItem >= 0) && (m_clickedItem < m_pItems.getCount()) && m_pItems[m_clickedItem].m_onEvent && PtInRect(&m_pItems[m_clickedItem].m_rcItem, pt))
+ {
+ CJabberInfoFrame_Event evt;
+ evt.m_event = CJabberInfoFrame_Event::CLICK;
+ evt.m_pszName = m_pItems[m_clickedItem].m_pszName;
+ evt.m_pUserData = m_pItems[m_clickedItem].m_pUserData;
+ (m_proto->*m_pItems[m_clickedItem].m_onEvent)(&evt);
+ return 0;
+ }
+
+ m_clickedItem = -1;
+
+ return 0;
+ }
+
+ case WM_LBUTTONDBLCLK:
+ {
+ m_compact = !m_compact;
+ UpdateSize();
+ return 0;
+ }
+ }
+
+ return DefWindowProc(m_hwnd, msg, wParam, lParam);
+}
+
+void CJabberInfoFrame::LockUpdates()
+{
+ m_bLocked = true;
+}
+
+void CJabberInfoFrame::Update()
+{
+ m_bLocked = false;
+ UpdateSize();
+}
+
+void CJabberInfoFrame::ReloadFonts()
+{
+ LOGFONT lfFont;
+
+ FontID fontid = {0};
+ fontid.cbSize = sizeof(fontid);
+ lstrcpyA(fontid.group, "Jabber");
+ lstrcpyA(fontid.name, "Frame title");
+ m_clTitle = CallService(MS_FONT_GET, (WPARAM)&fontid, (LPARAM)&lfFont);
+ DeleteObject(m_hfntTitle);
+ m_hfntTitle = CreateFontIndirect(&lfFont);
+ lstrcpyA(fontid.name, "Frame text");
+ m_clText = CallService(MS_FONT_GET, (WPARAM)&fontid, (LPARAM)&lfFont);
+ DeleteObject(m_hfntText);
+ m_hfntText = CreateFontIndirect(&lfFont);
+
+ ColourID colourid = {0};
+ colourid.cbSize = sizeof(colourid);
+ lstrcpyA(colourid.group, "Jabber");
+ lstrcpyA(colourid.name, "Background");
+ m_clBack = CallService(MS_COLOUR_GET, (WPARAM)&colourid, 0);
+
+ UpdateSize();
+}
+
+void CJabberInfoFrame::UpdateSize()
+{
+ if (!m_hwnd) return;
+ if (m_bLocked) return;
+
+ int line_count = m_compact ? 1 : (m_pItems.getCount() - m_hiddenItemCount);
+ int height = 2 * SZ_FRAMEPADDING + line_count * (GetSystemMetrics(SM_CYSMICON) + SZ_LINEPADDING) + (line_count - 1) * SZ_LINESPACING;
+
+ if (CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, m_frameId), 0) & F_VISIBLE)
+ {
+ if (!ServiceExists(MS_SKIN_DRAWGLYPH))
+ {
+ // crazy resizing for clist_nicer...
+ CallService(MS_CLIST_FRAMES_SHFRAME, m_frameId, 0);
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_HEIGHT, m_frameId), height);
+ CallService(MS_CLIST_FRAMES_SHFRAME, m_frameId, 0);
+ } else
+ {
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_HEIGHT, m_frameId), height);
+ RedrawWindow(m_hwnd, NULL, NULL, RDW_INVALIDATE);
+ }
+ } else
+ {
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_HEIGHT, m_frameId), height);
+ }
+}
+
+void CJabberInfoFrame::RemoveTooltip(int id)
+{
+ TOOLINFO ti = {0};
+ ti.cbSize = sizeof(TOOLINFO);
+
+ ti.hwnd = m_hwnd;
+ ti.uId = id;
+ SendMessage(m_hwndToolTip, TTM_DELTOOLW, 0, (LPARAM)&ti);
+}
+
+void CJabberInfoFrame::SetToolTip(int id, RECT *rc, TCHAR *pszText)
+{
+ TOOLINFO ti = {0};
+ ti.cbSize = sizeof(TOOLINFO);
+
+ ti.hwnd = m_hwnd;
+ ti.uId = id;
+ SendMessage(m_hwndToolTip, TTM_DELTOOLW, 0, (LPARAM)&ti);
+
+ ti.uFlags = TTF_SUBCLASS;
+ ti.hwnd = m_hwnd;
+ ti.uId = id;
+ ti.hinst = hInst;
+ ti.lpszText = pszText;
+ ti.rect = *rc;
+ SendMessage(m_hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+}
+
+void CJabberInfoFrame::PaintSkinGlyph(HDC hdc, RECT *rc, char **glyphs, COLORREF fallback)
+{
+ if (ServiceExists(MS_SKIN_DRAWGLYPH))
+ {
+ SKINDRAWREQUEST rq = {0};
+ rq.hDC = hdc;
+ rq.rcDestRect = *rc;
+ rq.rcClipRect = *rc;
+
+ for ( ; *glyphs; ++glyphs)
+ {
+ strncpy(rq.szObjectID, *glyphs, sizeof(rq.szObjectID));
+ if (!CallService(MS_SKIN_DRAWGLYPH, (WPARAM)&rq, 0))
+ return;
+ }
+ }
+
+ if (fallback != 0xFFFFFFFF)
+ {
+ HBRUSH hbr = CreateSolidBrush(fallback);
+ FillRect(hdc, rc, hbr);
+ DeleteObject(hbr);
+ }
+}
+
+void CJabberInfoFrame::PaintCompact(HDC hdc)
+{
+ RECT rc; GetClientRect(m_hwnd, &rc);
+ char *glyphs[] = { "Main,ID=ProtoInfo", "Main,ID=EventArea", "Main,ID=StatusBar", NULL };
+ PaintSkinGlyph(hdc, &rc, glyphs, m_clBack);
+
+ HFONT hfntSave = (HFONT)SelectObject(hdc, m_hfntTitle);
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextColor(hdc, m_clTitle);
+
+ int cx_icon = GetSystemMetrics(SM_CXSMICON);
+ int cy_icon = GetSystemMetrics(SM_CYSMICON);
+
+ int cx = rc.right - cx_icon - SZ_FRAMEPADDING;
+ for (int i = m_pItems.getCount(); i--; )
+ {
+ CJabberInfoFrameItem &item = m_pItems[i];
+
+ SetRect(&item.m_rcItem, 0, 0, 0, 0);
+ if (!item.m_bShow) continue;
+ if (!item.m_bCompact) continue;
+
+ int depth = 0;
+ for (char *p = item.m_pszName; p = strchr(p+1, '/'); ++depth) ;
+
+ if (depth == 0)
+ {
+ if (item.m_hIcolibIcon)
+ {
+ HICON hIcon = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)item.m_hIcolibIcon);
+ if (hIcon)
+ {
+ DrawIconEx(hdc, SZ_FRAMEPADDING, (rc.bottom-cy_icon)/2, hIcon, cx_icon, cy_icon, 0, NULL, DI_NORMAL);
+ g_ReleaseIcon(hIcon);
+ }
+ }
+
+ RECT rcText; SetRect(&rcText, cx_icon + SZ_FRAMEPADDING + SZ_ICONSPACING, 0, rc.right - SZ_FRAMEPADDING, rc.bottom);
+ DrawText(hdc, item.m_pszText, lstrlen(item.m_pszText), &rcText, DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);
+ } else
+ {
+ if (item.m_hIcolibIcon)
+ {
+ HICON hIcon = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)item.m_hIcolibIcon);
+ if (hIcon)
+ {
+ SetRect(&item.m_rcItem, cx, (rc.bottom-cy_icon)/2, cx+cx_icon, (rc.bottom-cy_icon)/2+cy_icon);
+ DrawIconEx(hdc, cx, (rc.bottom-cy_icon)/2, hIcon, cx_icon, cy_icon, 0, NULL, DI_NORMAL);
+ cx -= cx_icon;
+
+ g_ReleaseIcon(hIcon);
+
+ SetToolTip(item.m_tooltipId, &item.m_rcItem, item.m_pszText);
+ }
+ }
+ }
+ }
+
+ SelectObject(hdc, hfntSave);
+}
+
+void CJabberInfoFrame::PaintNormal(HDC hdc)
+{
+ RECT rc; GetClientRect(m_hwnd, &rc);
+ char *glyphs[] = { "Main,ID=ProtoInfo", "Main,ID=EventArea", "Main,ID=StatusBar", NULL };
+ PaintSkinGlyph(hdc, &rc, glyphs, m_clBack);
+
+ HFONT hfntSave = (HFONT)SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
+ SetBkMode(hdc, TRANSPARENT);
+
+ int cx_icon = GetSystemMetrics(SM_CXSMICON);
+ int cy_icon = GetSystemMetrics(SM_CYSMICON);
+ int line_height = cy_icon + SZ_LINEPADDING;
+ int cy = SZ_FRAMEPADDING;
+
+ for (int i = 0; i < m_pItems.getCount(); ++i)
+ {
+ CJabberInfoFrameItem &item = m_pItems[i];
+
+ if (!item.m_bShow)
+ {
+ SetRect(&item.m_rcItem, 0, 0, 0, 0);
+ continue;
+ }
+
+ int cx = SZ_FRAMEPADDING;
+ int depth = 0;
+ for (char *p = item.m_pszName; p = strchr(p+1, '/'); cx += cx_icon) ++depth;
+
+ SetRect(&item.m_rcItem, cx, cy, rc.right - SZ_FRAMEPADDING, cy + line_height);
+
+ if (item.m_hIcolibIcon)
+ {
+ HICON hIcon = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)item.m_hIcolibIcon);
+ if (hIcon)
+ {
+ DrawIconEx(hdc, cx, cy + (line_height-cy_icon)/2, hIcon, cx_icon, cy_icon, 0, NULL, DI_NORMAL);
+ cx += cx_icon + SZ_ICONSPACING;
+
+ g_ReleaseIcon(hIcon);
+ }
+ }
+
+ SelectObject(hdc, depth ? m_hfntText : m_hfntTitle);
+ SetTextColor(hdc, depth ? m_clText : m_clTitle);
+
+ RECT rcText; SetRect(&rcText, cx, cy, rc.right - SZ_FRAMEPADDING, cy + line_height);
+ DrawText(hdc, item.m_pszText, lstrlen(item.m_pszText), &rcText, DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);
+
+ RemoveTooltip(item.m_tooltipId);
+
+ cy += line_height + SZ_LINESPACING;
+ }
+
+ SelectObject(hdc, hfntSave);
+}
+
+void CJabberInfoFrame::CreateInfoItem(char *pszName, bool bCompact, LPARAM pUserData)
+{
+ CJabberInfoFrameItem item(pszName);
+ if (CJabberInfoFrameItem *pItem = m_pItems.find(&item))
+ return;
+
+ CJabberInfoFrameItem *newItem = new CJabberInfoFrameItem(pszName, bCompact, pUserData);
+ newItem->m_tooltipId = m_nextTooltipId++;
+ m_pItems.insert(newItem);
+ UpdateSize();
+}
+
+void CJabberInfoFrame::SetInfoItemCallback(char *pszName, void (CJabberProto::*onEvent)(CJabberInfoFrame_Event *))
+{
+ CJabberInfoFrameItem item(pszName);
+ if (CJabberInfoFrameItem *pItem = m_pItems.find(&item))
+ {
+ pItem->m_onEvent = onEvent;
+ }
+}
+
+void CJabberInfoFrame::UpdateInfoItem(char *pszName, HANDLE hIcolibIcon, TCHAR *pszText)
+{
+ CJabberInfoFrameItem item(pszName);
+ if (CJabberInfoFrameItem *pItem = m_pItems.find(&item))
+ pItem->SetInfo(hIcolibIcon, pszText);
+ if (m_hwnd)
+ RedrawWindow(m_hwnd, NULL, NULL, RDW_INVALIDATE);
+}
+
+void CJabberInfoFrame::ShowInfoItem(char *pszName, bool bShow)
+{
+ bool bUpdate = false;
+ size_t length = strlen(pszName);
+ for (int i = 0; i < m_pItems.getCount(); ++i)
+ if ((m_pItems[i].m_bShow != bShow) && !strncmp(m_pItems[i].m_pszName, pszName, length))
+ {
+ m_pItems[i].m_bShow = bShow;
+ m_hiddenItemCount += bShow ? -1 : 1;
+ bUpdate = true;
+ }
+
+ if (bUpdate)
+ UpdateSize();
+}
+
+void CJabberInfoFrame::RemoveInfoItem(char *pszName)
+{
+ bool bUpdate = false;
+ size_t length = strlen(pszName);
+ for (int i = 0; i < m_pItems.getCount(); ++i)
+ if (!strncmp(m_pItems[i].m_pszName, pszName, length))
+ {
+ if (!m_pItems[i].m_bShow) --m_hiddenItemCount;
+ RemoveTooltip(m_pItems[i].m_tooltipId);
+ m_pItems.remove(i);
+ bUpdate = true;
+ --i;
+ }
+
+ if (bUpdate)
+ UpdateSize();
+}
diff --git a/protocols/JabberG/src/jabber_xstatus.h b/protocols/JabberG/src/jabber_xstatus.h new file mode 100644 index 0000000000..ae12ebae86 --- /dev/null +++ b/protocols/JabberG/src/jabber_xstatus.h @@ -0,0 +1,191 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007-09 Maxim Mluhov
+Copyright ( C ) 2007-09 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _JABBER_XSTATUS_H_
+#define _JABBER_XSTATUS_H_
+
+struct CJabberProto;
+
+class CPepService
+{
+public:
+ CPepService(CJabberProto *proto, char *name, TCHAR *node);
+ virtual ~CPepService();
+
+ HANDLE GetMenu() { return m_hMenuItem; }
+ TCHAR *GetNode() { return m_node; }
+ virtual void ProcessItems(const TCHAR *from, HXML items) = 0;
+
+ void Publish();
+ void Retract();
+ void ResetPublish();
+
+ virtual void InitGui() {}
+ virtual void RebuildMenu() {}
+ virtual void ResetExtraIcon(HANDLE /*hContact*/) {}
+ virtual bool LaunchSetGui() { return false; }
+
+protected:
+ CJabberProto *m_proto;
+ char *m_name;
+ TCHAR *m_node;
+ HANDLE m_hMenuItem;
+
+ int m_wasPublished;
+
+ virtual void CreateData( HXML ) = 0;
+ void ForceRepublishOnLogin();
+};
+
+class CPepServiceList: public OBJLIST<CPepService>
+{
+public:
+ CPepServiceList(): OBJLIST<CPepService>(1) {}
+
+ void ProcessEvent(const TCHAR *from, HXML eventNode)
+ {
+ for (int i = 0; i < getCount(); ++i)
+ {
+ CPepService &pepSvc = (*this)[i];
+ HXML itemsNode = xmlGetChildByTag( eventNode, _T("items"), _T("node"), pepSvc.GetNode());
+ if ( itemsNode )
+ pepSvc.ProcessItems(from, itemsNode);
+ }
+ }
+
+ void InitGui()
+ {
+ for (int i = 0; i < getCount(); ++i)
+ (*this)[i].InitGui();
+ }
+
+ void RebuildMenu()
+ {
+ for (int i = 0; i < getCount(); ++i)
+ (*this)[i].RebuildMenu();
+ }
+
+ void ResetExtraIcon(HANDLE hContact)
+ {
+ for (int i = 0; i < getCount(); ++i)
+ (*this)[i].ResetExtraIcon(hContact);
+ }
+
+ void PublishAll()
+ {
+ for (int i = 0; i < getCount(); ++i)
+ (*this)[i].Publish();
+ }
+
+ void RetractAll()
+ {
+ for (int i = 0; i < getCount(); ++i)
+ (*this)[i].Retract();
+ }
+
+ void ResetPublishAll()
+ {
+ for(int i = 0; i < getCount(); ++i)
+ (*this)[i].ResetPublish();
+ }
+
+ CPepService *Find(TCHAR *node)
+ {
+ for (int i = 0; i < getCount(); ++i)
+ if (!lstrcmp((*this)[i].GetNode(), node))
+ return &((*this)[i]);
+ return NULL;
+ }
+};
+
+class CPepGuiService: public CPepService
+{
+ typedef CPepService CSuper;
+public:
+ CPepGuiService(CJabberProto *proto, char *name, TCHAR *node);
+ ~CPepGuiService();
+ void InitGui();
+ void RebuildMenu();
+ bool LaunchSetGui(BYTE bQuiet);
+
+protected:
+ void UpdateMenuItem(HANDLE hIcolibIcon, TCHAR *text);
+ virtual void ShowSetDialog(BYTE bQuiet) = 0;
+
+private:
+ HANDLE m_hMenuService;
+ HANDLE m_hIcolibItem;
+ TCHAR *m_szText;
+
+ bool m_bGuiOpen;
+
+ int __cdecl OnMenuItemClick(WPARAM, LPARAM);
+};
+
+class CPepMood: public CPepGuiService
+{
+ typedef CPepGuiService CSuper;
+public:
+ CPepMood(CJabberProto *proto);
+ ~CPepMood();
+ void InitGui();
+ void ProcessItems(const TCHAR *from, HXML items);
+ void ResetExtraIcon(HANDLE hContact);
+
+public: // FIXME: ugly hack
+ CIconPool m_icons;
+ TCHAR *m_text;
+ int m_mode;
+
+protected:
+ void CreateData( HXML );
+ void ShowSetDialog(BYTE bQuiet);
+ void SetExtraIcon(HANDLE hContact, char *szMood);
+
+ void SetMood(HANDLE hContact, const TCHAR *szMood, const TCHAR *szText);
+};
+
+class CPepActivity: public CPepGuiService
+{
+ typedef CPepGuiService CSuper;
+public:
+ CPepActivity(CJabberProto *proto);
+ ~CPepActivity();
+ void InitGui();
+ void ProcessItems(const TCHAR *from, HXML items);
+ void ResetExtraIcon(HANDLE hContact);
+
+protected:
+ CIconPool m_icons;
+ TCHAR *m_text;
+ int m_mode;
+
+ void CreateData( HXML );
+ void ShowSetDialog(BYTE bQuiet);
+ void SetExtraIcon(HANDLE hContact, char *szActivity);
+
+ void SetActivity(HANDLE hContact, LPCTSTR szFirst, LPCTSTR szSecond, LPCTSTR szText);
+};
+
+#endif // _JABBER_XSTATUS_H_
diff --git a/protocols/JabberG/src/jabber_zstream.cpp b/protocols/JabberG/src/jabber_zstream.cpp new file mode 100644 index 0000000000..11829bf401 --- /dev/null +++ b/protocols/JabberG/src/jabber_zstream.cpp @@ -0,0 +1,128 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+XEP-0138 (Stream Compression) implementation
+
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007 Kostya Chukavin, Taras Zackrepa
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+BOOL ThreadData::zlibInit( void )
+{
+ proto->Log( "Zlib init..." );
+ zStreamIn.zalloc = Z_NULL;
+ zStreamIn.zfree = Z_NULL;
+ zStreamIn.opaque = Z_NULL;
+ zStreamIn.next_in = Z_NULL;
+ zStreamIn.avail_in = 0;
+
+ zStreamOut.zalloc = Z_NULL;
+ zStreamOut.zfree = Z_NULL;
+ zStreamOut.opaque = Z_NULL;
+
+ if ( deflateInit( &zStreamOut, Z_BEST_COMPRESSION) != Z_OK ) return FALSE;
+ if ( inflateInit( &zStreamIn ) != Z_OK ) return FALSE;
+
+ zRecvReady = true;
+ return TRUE;
+}
+
+void ThreadData::zlibUninit( void )
+{
+ deflateEnd( &zStreamOut );
+ inflateEnd( &zStreamIn );
+}
+
+int ThreadData::zlibSend( char* data, int datalen )
+{
+ char send_data[ ZLIB_CHUNK_SIZE ];
+ int bytesOut = 0;
+
+ zStreamOut.avail_in = datalen;
+ zStreamOut.next_in = ( unsigned char* )data;
+
+ do {
+ zStreamOut.avail_out = ZLIB_CHUNK_SIZE;
+ zStreamOut.next_out = ( unsigned char* )send_data;
+
+ switch ( deflate( &zStreamOut, Z_SYNC_FLUSH )) {
+ case Z_OK: proto->Log( "Deflate: Z_OK" ); break;
+ case Z_BUF_ERROR: proto->Log( "Deflate: Z_BUF_ERROR" ); break;
+ case Z_DATA_ERROR: proto->Log( "Deflate: Z_DATA_ERROR" ); break;
+ case Z_MEM_ERROR: proto->Log( "Deflate: Z_MEM_ERROR" ); break;
+ }
+
+ int len, send_datalen = ZLIB_CHUNK_SIZE - zStreamOut.avail_out;
+
+ if (( len = sendws( send_data, send_datalen, MSG_NODUMP )) == SOCKET_ERROR || len != send_datalen ) {
+ proto->Log( "Netlib_Send() failed, error=%d", WSAGetLastError());
+ return FALSE;
+ }
+
+ bytesOut += len;
+ }
+ while ( zStreamOut.avail_out == 0 );
+
+ if ( DBGetContactSettingByte( NULL, "Netlib", "DumpSent", TRUE ) == TRUE )
+ proto->Log( "(ZLIB) Data sent\n%s\n===OUT: %d(%d) bytes", data, datalen, bytesOut );
+
+ return TRUE;
+}
+
+int ThreadData::zlibRecv( char* data, long datalen )
+{
+ if ( zRecvReady ) {
+retry:
+ zRecvDatalen = recvws( zRecvData, ZLIB_CHUNK_SIZE, MSG_NODUMP );
+ if ( zRecvDatalen == SOCKET_ERROR ) {
+ proto->Log( "Netlib_Recv() failed, error=%d", WSAGetLastError());
+ return SOCKET_ERROR;
+ }
+ if ( zRecvDatalen == 0 )
+ return 0;
+
+ zStreamIn.avail_in = zRecvDatalen;
+ zStreamIn.next_in = ( Bytef* )zRecvData;
+ }
+
+ zStreamIn.avail_out = datalen;
+ zStreamIn.next_out = ( BYTE* )data;
+
+ switch ( inflate( &zStreamIn, Z_NO_FLUSH )) {
+ case Z_OK: proto->Log( "Inflate: Z_OK" ); break;
+ case Z_BUF_ERROR: proto->Log( "Inflate: Z_BUF_ERROR" ); break;
+ case Z_DATA_ERROR: proto->Log( "Inflate: Z_DATA_ERROR" ); break;
+ case Z_MEM_ERROR: proto->Log( "Inflate: Z_MEM_ERROR" ); break;
+ }
+
+ int len = datalen - zStreamIn.avail_out;
+ if ( DBGetContactSettingByte( NULL, "Netlib", "DumpRecv", TRUE ) == TRUE ) {
+ char* szLogBuffer = ( char* )alloca( len+32 );
+ memcpy( szLogBuffer, data, len );
+ szLogBuffer[ len ]='\0';
+ proto->Log( "(ZLIB) Data received\n%s\n===IN: %d(%d) bytes", szLogBuffer, len, zRecvDatalen );
+ }
+
+ if ( len == 0 )
+ goto retry;
+
+ zRecvReady = ( zStreamIn.avail_out != 0 );
+ return len;
+}
diff --git a/protocols/JabberG/src/resource.h b/protocols/JabberG/src/resource.h new file mode 100644 index 0000000000..5cbc583aa5 --- /dev/null +++ b/protocols/JabberG/src/resource.h @@ -0,0 +1,358 @@ +//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by jabber.rc
+//
+#define IDD_OPT_JABBER 101
+#define IDI_JABBER 102
+#define IDD_INFO_JABBER 103
+#define IDD_OPT_REGISTER 105
+#define IDD_AGENTS 106
+#define IDD_FORM 107
+#define IDI_ADDROSTER 108
+#define IDI_USER2ROOM 109
+#define IDI_REFRESH 110
+#define IDD_PASSWORD 111
+#define IDI_ADDCONTACT 122
+#define IDI_DELETE 123
+#define IDI_EDIT 124
+#define IDD_DATAFORM_TEST 125
+#define IDD_VCARD_HOME 126
+#define IDD_VCARD_PERSONAL 127
+#define IDD_VCARD_WORK 128
+#define IDD_VCARD_CONTACT 129
+#define IDD_VCARD_ADDEMAIL 130
+#define IDD_VCARD_ADDPHONE 131
+#define IDI_OPEN 131
+#define IDD_VCARD_PHOTO 132
+#define IDD_VCARD_NOTE 133
+#define IDD_CHANGEPASSWORD 136
+#define IDD_SEARCHUSER 138
+#define IDD_OPT_JABBER2 140
+#define IDI_REQUEST 141
+#define IDD_GROUPCHAT 141
+#define IDI_GRANT 142
+#define IDI_KEYS 144
+#define IDI_GROUP 147
+#define IDD_GROUPCHAT_JOIN 148
+#define IDI_AGENTS 154
+#define IDI_VCARD 155
+#define IDI_SAVE 166
+#define IDD_GROUPCHAT_INPUT 167
+#define IDD_JIDLIST 171
+#define IDD_AGENT_MANUAL_REGISTER 182
+#define IDD_GROUPCHAT_INVITE 183
+#define IDD_GROUPCHAT_INVITE_ACCEPT 184
+#define IDD_OPT_JABBER3 185
+#define IDI_LOGIN 186
+#define IDI_AUTHREVOKE 187
+#define IDI_COMMAND 188
+#define IDI_DISCO_OK 190
+#define IDI_DISCO_FAIL 191
+#define IDI_VIEW_LIST 192
+#define IDI_VIEW_TREE 193
+#define IDI_FILTER_APPLY 194
+#define IDI_BROWSE 195
+#define IDI_NAV_HOME 196
+#define IDI_NAV_REFRESH 197
+#define IDI_FILTER_RESET 198
+#define IDI_DISCO_PROGRESS 199
+#define IDI_NODE_RSS 200
+#define IDI_NODE_SERVER 201
+#define IDI_NODE_STORE 202
+#define IDI_NODE_WEATHER 203
+#define IDD_CONSOLE 205
+#define IDI_CONSOLE 207
+#define IDD_DATAFORM_PAGE 208
+#define IDI_PL_MSG_ALLOW 209
+#define IDI_PL_MSG_DENY 210
+#define IDI_PL_PRIN_ALLOW 211
+#define IDI_PL_PRIN_DENY 212
+#define IDI_PL_PROUT_ALLOW 213
+#define IDI_PL_PROUT_DENY 214
+#define IDI_PL_QUERY_ALLOW 215
+#define IDD_SETMOODMSG 216
+#define IDI_PL_QUERY_DENY 216
+#define IDI_PL_LIST_ACTIVE 217
+#define IDI_PL_LIST_ANY 218
+#define IDI_PL_LIST_DEFAULT 219
+#define IDI_ARROW_DOWN 220
+#define IDI_ARROW_UP 221
+#define IDI_TRANSPORT 222
+#define IDI_TRANSPORTL 223
+#define IDD_GROUPCHAT_INFO 224
+#define IDD_OPT_JABBER4 226
+#define IDD_ACCMGRUI 227
+#define IDD_HTTP_AUTH 228
+#define IDI_HTTP_AUTH 229
+#define IDD_PEP_SIMPLE 230
+#define IDD_NOTEBOOK 231
+#define IDD_NOTE_EDIT 232
+#define IDI_NOTES 233
+#define IDI_SEND_NOTE 234
+#define IDC_STATUSBAR 999
+#define IDC_EDIT_USERNAME 1000
+#define IDC_SAVE 1000
+#define IDC_EDIT_PASSWORD 1001
+#define IDC_BUTTON_REGISTER 1002
+#define IDC_EDIT_LOGIN_SERVER 1003
+#define IDC_PORT 1004
+#define IDC_BUTTON_CHANGE_PASSWORD 1005
+#define IDC_LINK_PUBLIC_SERVER 1009
+#define IDC_NAME 1009
+#define IDC_PROGRESS_REG 1011
+#define IDC_AGENT_TRANSPORT 1015
+#define IDC_AGENT_REGISTER 1016
+#define IDC_AGENT_LOGON 1017
+#define IDC_AGENT_UNREGISTER 1018
+#define IDC_AGENT_SERVER 1019
+#define IDC_AGENT_LOGOFF 1020
+#define IDC_AGENT_LIST 1021
+#define IDC_AGENT_SEARCH 1022
+#define IDC_SUBMIT 1023
+#define IDC_NEXT 1025
+#define IDC_PREV 1026
+#define IDC_COMPLETE 1027
+#define IDC_AGENT_BROWSE 1029
+#define IDC_INSTRUCTION 1030
+#define IDC_FRAME 1037
+#define IDC_FRAME_TEXT 1038
+#define IDC_SIMPLE 1041
+#define IDC_KEEPALIVE 1042
+#define IDC_HOST 1043
+#define IDC_HOSTPORT 1044
+#define IDC_USE_SSL 1045
+#define IDC_MANUAL 1046
+#define IDC_RESOURCE_T 1047
+#define IDC_SAVEPASSWORD 1048
+#define IDC_MSGLANG 1049
+#define IDC_PASSWORD 1050
+#define IDC_JID 1051
+#define IDC_NEWPASSWD2 1052
+#define IDC_ROSTER_SYNC 1052
+#define IDC_OLDPASSWD 1053
+#define IDC_ADDRESS1 1056
+#define IDC_ADDRESS2 1057
+#define IDC_CITY 1058
+#define IDC_STATE 1059
+#define IDC_COUNTRY 1060
+#define IDC_FULLNAME 1061
+#define IDC_ZIP 1061
+#define IDC_NICKNAME 1062
+#define IDC_COMPANY 1062
+#define IDC_FIRSTNAME 1063
+#define IDC_DEPARTMENT 1063
+#define IDC_LASTNAME 1064
+#define IDC_BIRTH 1065
+#define IDC_OCCUPATION 1066
+#define IDC_HOMEPAGE 1067
+#define IDC_MIDDLE 1072
+#define IDC_EMAIL 1073
+#define IDC_HOME 1074
+#define IDC_INTERNET 1075
+#define IDC_X400 1076
+#define IDC_WORK 1077
+#define IDC_PHONE 1078
+#define IDC_CELL 1079
+#define IDC_VIDEO 1080
+#define IDC_BBS 1081
+#define IDC_MODEM 1082
+#define IDC_PAGER 1083
+#define IDC_MSG 1084
+#define IDC_ISDN 1085
+#define IDC_PCS 1086
+#define IDC_VOICE 1087
+#define IDC_FAX 1088
+#define IDC_TITLE 1089
+#define IDC_DESC 1090
+#define IDC_DELETE 1092
+#define IDC_LOAD 1093
+#define IDC_CANVAS 1094
+#define IDC_GENDER 1096
+#define IDC_JUD 1097
+#define IDC_JUD_LABEL 1098
+#define IDC_MSGLANG_LABEL 1099
+#define IDC_PRIORITY_SPIN 1104
+#define IDC_PRIORITY 1105
+#define IDC_PRIORITY_LABEL 1106
+#define IDC_NEWPASSWD 1107
+#define IDC_COMBO_ACCTYPE 1108
+#define IDD_MODERNOPT 1110
+#define IDC_PROXY_ADDR 1112
+#define IDC_DIRECT_ADDR 1114
+#define IDC_DIRECT_MANUAL 1121
+#define IDC_REG_STATUS 1122
+#define IDC_JOIN 1123
+#define IDC_DIRECT 1123
+#define IDC_ROOM 1124
+#define IDC_PROXY_MANUAL 1124
+#define IDC_SERVER 1125
+#define IDC_BROWSE 1126
+#define IDC_VSCROLL 1128
+#define IDC_NICK 1129
+#define IDC_EDIT 1131
+#define IDC_LIST 1133
+#define IDC_TABS 1141
+#define IDC_TXT_MULTILINE 1141
+#define IDC_TXT_PASSWORD 1142
+#define IDC_MANUAL_REGISTER 1167
+#define IDC_REASON 1171
+#define IDC_CLIST 1172
+#define IDC_NEWJID 1173
+#define IDC_ADDJID 1174
+#define IDC_INVITE 1175
+#define IDC_BTN_ADVANCED 1175
+#define IDC_ACCEPT 1176
+#define IDC_BTN_SIMPLE 1176
+#define IDC_FROM 1177
+#define IDC_USE_TLS 1180
+#define IDC_UNREGISTER 1183
+#define IDC_GO 1196
+#define IDC_HOSTNAME_AS_RESOURCE 1214
+#define IDC_COMMANDS1 1216
+#define IDC_COMMANDS2 1217
+#define IDC_COMBO_RESOURCE 1218
+#define IDC_PL_RULES_LIST 1219
+#define IDC_WHITERECT 1220
+#define IDC_ADD_RULE 1221
+#define IDC_EDIT_RULE 1222
+#define IDC_REMOVE_RULE 1223
+#define IDC_ADD_LIST 1225
+#define IDC_REMOVE_LIST 1226
+#define IDC_COMBO_TYPE 1230
+#define IDC_EDIT_VALUE 1231
+#define IDC_COMBO_ACTION 1232
+#define IDC_CHECK_MESSAGES 1233
+#define IDC_CHECK_QUERIES 1234
+#define IDC_CHECK_PRESENCE_OUT 1235
+#define IDC_CHECK_PRESENCE_IN 1236
+#define IDC_COMBO_VALUE 1237
+#define IDC_EDIT_NAME 1238
+#define IDC_TREE_DISCO 1240
+#define IDC_FRAME1 1241
+#define IDC_FRAME2 1242
+#define IDC_COMBO_JID 1243
+#define IDC_TXT_JID 1243
+#define IDC_COMBO_NODE 1244
+#define IDC_BUTTON_BROWSE 1245
+#define IDC_DOWNLOAD 1245
+#define IDC_ACTIVATE 1245
+#define IDC_BTN_NAVHOME 1246
+#define IDC_UPLOAD 1246
+#define IDC_TXT_FILTERTEXT 1247
+#define IDC_IMPORT 1247
+#define IDC_BTN_FILTERRESET 1248
+#define IDC_EXPORT 1248
+#define IDC_TXT_FILTER 1249
+#define IDC_TXT_NODELABEL 1250
+#define IDC_BTN_FAVORITE 1251
+#define IDC_BTN_REFRESH 1253
+#define IDC_BTN_VIEWTREE 1254
+#define IDC_BTN_VIEWLIST 1255
+#define IDC_BTN_FILTERAPPLY 1258
+#define IDC_ROSTER 1261
+#define IDC_MSG_MOOD 1262
+#define IDC_OPTTREE 1263
+#define IDC_LB_LISTS 1264
+#define IDC_LST_NOTES 1264
+#define IDC_TITLE1 1265
+#define IDC_CONSOLE 1266
+#define IDC_CONSOLEIN 1267
+#define IDC_RESET 1268
+#define IDC_BOOKMARKS 1270
+#define IDC_BTN_MSG 1270
+#define IDC_BTN_ROLE 1270
+#define IDC_DATAFORM 1270
+#define IDC_BTN_PRESENCE 1271
+#define IDC_BTN_AFFILIATION 1271
+#define IDC_SET_DEFAULT 1272
+#define IDC_BTN_IQ 1272
+#define IDC_TXT_RULES 1273
+#define IDC_TXT_LISTS 1274
+#define IDC_TV_INFO 1276
+#define IDC_TXT_OTHERJID 1277
+#define IDC_TXT_MESSAGE 1278
+#define IDC_TXT_QUERY 1279
+#define IDC_TXT_INPRESENCE 1280
+#define IDC_TXT_OUTPRESENCE 1281
+#define IDC_ICO_OUTPRESENCE 1282
+#define IDC_ICO_INPRESENCE 1283
+#define IDC_ICO_QUERY 1284
+#define IDC_ICO_MESSAGE 1285
+#define IDC_ICO_PRESENCEIN 1286
+#define IDC_ICO_PRESENCEOUT 1287
+#define IDC_RECENT1 1288
+#define IDC_RECENT2 1289
+#define IDC_RECENT3 1290
+#define IDC_RECENT4 1291
+#define IDC_RECENT5 1292
+#define IDC_TXT_RECENT 1293
+#define IDC_FILTER 1294
+#define IDC_TXT_NICK 1294
+#define IDC_TXT_QUIT 1294
+#define IDC_EDIT_HTTP_AUTH_INFO 1294
+#define IDC_TXT_URL 1294
+#define IDC_TXT_DESCRIPTION 1294
+#define IDC_TXT_TITLE 1294
+#define IDC_BTN_FILTER 1295
+#define IDC_TXT_ID 1295
+#define IDC_CB_FILTER 1296
+#define IDC_TXT_ROLE 1296
+#define IDC_TXT_COMBO 1296
+#define IDC_CB_TYPE 1296
+#define IDC_TXT_METHOD 1296
+#define IDC_CB_MODES 1296
+#define IDC_BTN_FILTER_REFRESH 1297
+#define IDC_TXT_STATUS 1298
+#define IDC_TXT_FROM 1298
+#define IDC_TXT_AFFILIATION 1299
+#define IDC_ICO_STATUS 1300
+#define IDC_TXT_RICHEDIT 1302
+#define IDC_TXT_SLAP 1304
+#define IDC_TXT_TAGS 1304
+#define IDC_EMAILS 1306
+#define IDC_TV_FILTER 1307
+#define IDC_PHONES 1308
+#define IDC_TXT_TEXT 1308
+#define IDC_ST_TAGS 1309
+#define IDC_INSTRUCTIONS 1315
+#define IDC_COMBO_VALUES 1319
+#define IDC_HEADERBAR 1320
+#define IDC_USEDOMAINLOGIN 1323
+#define IDC_TXT_ALTNICK 1323
+#define IDI_BOOKMARKS 3000
+#define IDD_BOOKMARKS 3001
+#define IDC_BM_LIST 3002
+#define IDC_ADD 3004
+#define IDC_REMOVE 3005
+#define IDD_BOOKMARK_ADD 3006
+#define IDC_UP_RULE 3006
+#define IDC_ROOM_JID 3007
+#define IDD_PRIVACY_LISTS 3007
+#define IDC_DOWN_RULE 3007
+#define IDC_APPLY 3008
+#define IDD_PRIVACY_RULE 3008
+#define IDC_ROOM_RADIO 3009
+#define IDD_PRIVACY_ADD_LIST 3009
+#define IDD_SERVICE_DISCOVERY 3010
+#define IDC_URL_RADIO 3011
+#define IDD_GROUPCHAT_INFO_TABS 3011
+#define IDC_AGENT_RADIO 3012
+#define IDD_GROUPCHAT_ADMLIST 3012
+#define IDC_BOOKMARK_TYPE 3013
+#define IDC_CHECK_BM_AUTOJOIN 3014
+#define IDI_PRIVACY_LISTS 3016
+#define IDI_SERVICE_DISCOVERY 3017
+#define IDD_CAPTCHAFORM 3018
+#define IDC_VALUE 3019
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 235
+#define _APS_NEXT_COMMAND_VALUE 40017
+#define _APS_NEXT_CONTROL_VALUE 1324
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/protocols/JabberG/src/ui_utils.cpp b/protocols/JabberG/src/ui_utils.cpp new file mode 100644 index 0000000000..39f63ecae7 --- /dev/null +++ b/protocols/JabberG/src/ui_utils.cpp @@ -0,0 +1,2574 @@ +/*
+
+Object UI extensions
+Copyright (C) 2008 Victor Pavlychko, George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "jabber.h"
+
+extern HINSTANCE hInst;
+
+CDlgBase::CDlgBase(int idDialog, HWND hwndParent) :
+ m_controls(1, CCtrlBase::cmp)
+{
+ m_idDialog = idDialog;
+ m_hwndParent = hwndParent;
+ m_hwnd = NULL;
+ m_first = NULL;
+ m_isModal = false;
+ m_initialized = false;
+ m_autoClose = CLOSE_ON_OK|CLOSE_ON_CANCEL;
+ m_forceResizable = false;
+}
+
+CDlgBase::~CDlgBase()
+{
+ // remove handlers
+ m_controls.destroy();
+
+ if (m_hwnd)
+ DestroyWindow(m_hwnd);
+}
+
+void CDlgBase::Create()
+{
+ ShowWindow(CreateDialogParam(hInst, MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)(CDlgBase *)this), SW_HIDE);
+}
+
+void CDlgBase::Show(int nCmdShow)
+{
+ ShowWindow(CreateDialogParam(hInst, MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)(CDlgBase *)this), nCmdShow);
+}
+
+int CDlgBase::DoModal()
+{
+ m_isModal = true;
+ return DialogBoxParam(hInst, MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)(CDlgBase *)this);
+}
+
+int CDlgBase::Resizer(UTILRESIZECONTROL*)
+{
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP;
+}
+
+INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ m_initialized = false;
+ TranslateDialogDefault(m_hwnd);
+
+ for ( CCtrlBase* p = m_first; p != NULL; p = p->m_next )
+ AddControl( p );
+
+ NotifyControls(&CCtrlBase::OnInit);
+ OnInitDialog();
+
+ m_initialized = true;
+ return TRUE;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ MEASUREITEMSTRUCT *param = (MEASUREITEMSTRUCT *)lParam;
+ if (param && param->CtlID)
+ if (CCtrlBase *ctrl = FindControl(param->CtlID))
+ return ctrl->OnMeasureItem(param);
+ return FALSE;
+ }
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *param = (DRAWITEMSTRUCT *)lParam;
+ if (param && param->CtlID)
+ if (CCtrlBase *ctrl = FindControl(param->CtlID))
+ return ctrl->OnDrawItem(param);
+ return FALSE;
+ }
+
+ case WM_DELETEITEM:
+ {
+ DELETEITEMSTRUCT *param = (DELETEITEMSTRUCT *)lParam;
+ if (param && param->CtlID)
+ if (CCtrlBase *ctrl = FindControl(param->CtlID))
+ return ctrl->OnDeleteItem(param);
+ return FALSE;
+ }
+
+ case WM_COMMAND:
+ {
+ HWND hwndCtrl = (HWND)lParam;
+ WORD idCtrl = LOWORD(wParam);
+ WORD idCode = HIWORD(wParam);
+ if (CCtrlBase *ctrl = FindControl(idCtrl)) {
+ BOOL result = ctrl->OnCommand(hwndCtrl, idCtrl, idCode);
+ if ( result != FALSE )
+ return result;
+ }
+
+ if (idCode == BN_CLICKED &&
+ ((idCtrl == IDOK) && (m_autoClose & CLOSE_ON_OK) ||
+ (idCtrl == IDCANCEL) && (m_autoClose & CLOSE_ON_CANCEL)))
+ {
+ PostMessage( m_hwnd, WM_CLOSE, 0, 0 );
+ }
+ return FALSE;
+ }
+
+ case WM_NOTIFY:
+ {
+ int idCtrl = wParam;
+ NMHDR *pnmh = (NMHDR *)lParam;
+
+ if (pnmh->idFrom == 0)
+ {
+ if (pnmh->code == PSN_APPLY)
+ {
+ NotifyControls(&CCtrlBase::OnApply);
+ OnApply();
+ }
+ else if (pnmh->code == PSN_RESET)
+ {
+ NotifyControls(&CCtrlBase::OnReset);
+ OnReset();
+ }
+ }
+
+ if (CCtrlBase *ctrl = FindControl(pnmh->idFrom))
+ return ctrl->OnNotify(idCtrl, pnmh);
+ return FALSE;
+ }
+
+ case WM_SIZE:
+ {
+ if (m_forceResizable || (GetWindowLongPtr(m_hwnd, GWL_STYLE) & WS_SIZEBOX))
+ {
+ UTILRESIZEDIALOG urd;
+ urd.cbSize = sizeof(urd);
+ urd.hwndDlg = m_hwnd;
+ urd.hInstance = hInst;
+ urd.lpTemplate = MAKEINTRESOURCEA(m_idDialog);
+ urd.lParam = 0;
+ urd.pfnResizer = GlobalDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd);
+ }
+ return TRUE;
+ }
+
+ case WM_CLOSE:
+ {
+ m_lresult = FALSE;
+ OnClose();
+ if ( !m_lresult )
+ {
+ if (m_isModal)
+ EndDialog(m_hwnd, 0);
+ else
+ DestroyWindow(m_hwnd);
+ }
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ {
+ OnDestroy();
+ NotifyControls(&CCtrlBase::OnDestroy);
+
+ SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0);
+ m_hwnd = NULL;
+ if (m_isModal)
+ {
+ m_isModal = false;
+ } else
+ { // modeless dialogs MUST be allocated with 'new'
+ delete this;
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK CDlgBase::GlobalDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CDlgBase *wnd = NULL;
+ if (msg == WM_INITDIALOG)
+ {
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
+ wnd = (CDlgBase *)lParam;
+ wnd->m_hwnd = hwnd;
+ } else
+ {
+ wnd = (CDlgBase *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ }
+
+ if (!wnd) return FALSE;
+
+ wnd->m_msg.hwnd = hwnd;
+ wnd->m_msg.message = msg;
+ wnd->m_msg.wParam = wParam;
+ wnd->m_msg.lParam = lParam;
+ return wnd->DlgProc(msg, wParam, lParam);
+}
+
+int CDlgBase::GlobalDlgResizer(HWND hwnd, LPARAM, UTILRESIZECONTROL *urc)
+{
+ CDlgBase *wnd = (CDlgBase *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (!wnd) return 0;
+
+ return wnd->Resizer(urc);
+}
+
+CDlgBase::pfnEnableThemeDialogTexture CDlgBase::MyEnableThemeDialogTexture = NULL;
+void CDlgBase::ThemeDialogBackground(BOOL tabbed)
+{
+ if (!MyEnableThemeDialogTexture && IsWinVerXPPlus())
+ {
+ HMODULE hThemeAPI = GetModuleHandleA("uxtheme");
+ if (hThemeAPI)
+ MyEnableThemeDialogTexture = (pfnEnableThemeDialogTexture)GetProcAddress(hThemeAPI,"EnableThemeDialogTexture");
+ }
+
+ if (MyEnableThemeDialogTexture)
+ {
+ MyEnableThemeDialogTexture(m_hwnd,(tabbed?0x00000002:0x00000001)|0x00000004); //0x00000002|0x00000004=ETDT_ENABLETAB
+ }
+}
+
+void CDlgBase::AddControl(CCtrlBase *ctrl)
+{
+ m_controls.insert(ctrl);
+}
+
+void CDlgBase::NotifyControls(void (CCtrlBase::*fn)())
+{
+ for (int i = 0; i < m_controls.getCount(); ++i)
+ (m_controls[i]->*fn)();
+}
+
+CCtrlBase* CDlgBase::FindControl(int idCtrl)
+{
+ CCtrlBase search(NULL, idCtrl);
+ return m_controls.find(&search);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCombo class
+
+CCtrlCombo::CCtrlCombo( CDlgBase* dlg, int ctrlId ) :
+ CCtrlData( dlg, ctrlId )
+{
+}
+
+int CCtrlCombo::AddString(const TCHAR *text, LPARAM data)
+{
+ int iItem = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text);
+ if ( data )
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+int CCtrlCombo::AddStringA(const char *text, LPARAM data)
+{
+ int iItem = SendMessageA(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text);
+ if ( data )
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+void CCtrlCombo::DeleteString(int index)
+{ SendMessage(m_hwnd, CB_DELETESTRING, index, 0);
+}
+
+int CCtrlCombo::FindString(const TCHAR *str, int index, bool exact )
+{ return SendMessage(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlCombo::FindStringA(const char *str, int index, bool exact )
+{ return SendMessageA(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlCombo::GetCount()
+{ return SendMessage(m_hwnd, CB_GETCOUNT, 0, 0);
+}
+
+int CCtrlCombo::GetCurSel()
+{ return SendMessage(m_hwnd, CB_GETCURSEL, 0, 0);
+}
+
+bool CCtrlCombo::GetDroppedState()
+{ return SendMessage(m_hwnd, CB_GETDROPPEDSTATE, 0, 0) ? true : false;
+}
+
+LPARAM CCtrlCombo::GetItemData(int index)
+{ return SendMessage(m_hwnd, CB_GETITEMDATA, index, 0);
+}
+
+TCHAR* CCtrlCombo::GetItemText(int index)
+{
+ TCHAR *result = (TCHAR *)mir_alloc(sizeof(TCHAR) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result);
+ return result;
+}
+
+TCHAR* CCtrlCombo::GetItemText(int index, TCHAR *buf, int size)
+{
+ TCHAR *result = (TCHAR *)_alloca(sizeof(TCHAR) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result);
+ lstrcpyn(buf, result, size);
+ return buf;
+}
+
+int CCtrlCombo::InsertString(TCHAR *text, int pos, LPARAM data)
+{
+ int iItem = SendMessage(m_hwnd, CB_INSERTSTRING, pos, (LPARAM)text);
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+void CCtrlCombo::ResetContent()
+{ SendMessage(m_hwnd, CB_RESETCONTENT, 0, 0);
+}
+
+int CCtrlCombo::SelectString(TCHAR *str)
+{ return SendMessage(m_hwnd, CB_SELECTSTRING, 0, (LPARAM)str);
+}
+
+int CCtrlCombo::SetCurSel(int index)
+{ return SendMessage(m_hwnd, CB_SETCURSEL, index, 0);
+}
+
+void CCtrlCombo::SetItemData(int index, LPARAM data)
+{ SendMessage(m_hwnd, CB_SETITEMDATA, index, data);
+}
+
+void CCtrlCombo::ShowDropdown(bool show)
+{ SendMessage(m_hwnd, CB_SHOWDROPDOWN, show ? TRUE : FALSE, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListBox class
+
+CCtrlListBox::CCtrlListBox( CDlgBase* dlg, int ctrlId ) :
+ CCtrlBase( dlg, ctrlId )
+{
+}
+
+BOOL CCtrlListBox::OnCommand(HWND, WORD, WORD idCode)
+{
+ switch (idCode)
+ {
+ case LBN_DBLCLK: OnDblClick(this); break;
+ case LBN_SELCANCEL: OnSelCancel(this); break;
+ case LBN_SELCHANGE: OnSelChange(this); break;
+ }
+ return TRUE;
+}
+
+int CCtrlListBox::AddString(TCHAR *text, LPARAM data)
+{
+ int iItem = SendMessage(m_hwnd, LB_ADDSTRING, 0, (LPARAM)text);
+ SendMessage(m_hwnd, LB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::DeleteString(int index)
+{ SendMessage(m_hwnd, LB_DELETESTRING, index, 0);
+}
+
+int CCtrlListBox::FindString( TCHAR *str, int index, bool exact)
+{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlListBox::GetCount()
+{ return SendMessage(m_hwnd, LB_GETCOUNT, 0, 0);
+}
+
+int CCtrlListBox::GetCurSel()
+{ return SendMessage(m_hwnd, LB_GETCURSEL, 0, 0);
+}
+
+LPARAM CCtrlListBox::GetItemData(int index)
+{ return SendMessage(m_hwnd, LB_GETITEMDATA, index, 0);
+}
+
+TCHAR* CCtrlListBox::GetItemText(int index)
+{
+ TCHAR *result = (TCHAR *)mir_alloc(sizeof(TCHAR) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ return result;
+}
+
+TCHAR* CCtrlListBox::GetItemText(int index, TCHAR *buf, int size)
+{
+ TCHAR *result = (TCHAR *)_alloca(sizeof(TCHAR) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ lstrcpyn(buf, result, size);
+ return buf;
+}
+
+bool CCtrlListBox::GetSel(int index)
+{ return SendMessage(m_hwnd, LB_GETSEL, index, 0) ? true : false;
+}
+
+int CCtrlListBox::GetSelCount()
+{ return SendMessage(m_hwnd, LB_GETSELCOUNT, 0, 0);
+}
+
+int* CCtrlListBox::GetSelItems(int *items, int count)
+{
+ SendMessage(m_hwnd, LB_GETSELITEMS, count, (LPARAM)items);
+ return items;
+}
+
+int* CCtrlListBox::GetSelItems()
+{
+ int count = GetSelCount() + 1;
+ int *result = (int *)mir_alloc(sizeof(int) * count);
+ SendMessage(m_hwnd, LB_GETSELITEMS, count, (LPARAM)result);
+ result[count-1] = -1;
+ return result;
+}
+
+int CCtrlListBox::InsertString(TCHAR *text, int pos, LPARAM data)
+{
+ int iItem = SendMessage(m_hwnd, CB_INSERTSTRING, pos, (LPARAM)text);
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::ResetContent()
+{ SendMessage(m_hwnd, LB_RESETCONTENT, 0, 0);
+}
+
+int CCtrlListBox::SelectString(TCHAR *str)
+{ return SendMessage(m_hwnd, LB_SELECTSTRING, 0, (LPARAM)str);
+}
+
+int CCtrlListBox::SetCurSel(int index)
+{ return SendMessage(m_hwnd, LB_SETCURSEL, index, 0);
+}
+
+void CCtrlListBox::SetItemData(int index, LPARAM data)
+{ SendMessage(m_hwnd, LB_SETITEMDATA, index, data);
+}
+
+void CCtrlListBox::SetSel(int index, bool sel)
+{ SendMessage(m_hwnd, LB_SETSEL, sel ? TRUE : FALSE, index);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCheck class
+
+CCtrlCheck::CCtrlCheck( CDlgBase* dlg, int ctrlId ) :
+ CCtrlData( dlg, ctrlId )
+{
+}
+
+int CCtrlCheck::GetState()
+{
+ return SendMessage(m_hwnd, BM_GETCHECK, 0, 0);
+}
+
+void CCtrlCheck::SetState(int state)
+{
+ SendMessage(m_hwnd, BM_SETCHECK, state, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlEdit class
+
+CCtrlEdit::CCtrlEdit( CDlgBase* dlg, int ctrlId ) :
+ CCtrlData( dlg, ctrlId )
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlData class
+
+CCtrlData::CCtrlData( CDlgBase *wnd, int idCtrl ) :
+ CCtrlBase( wnd, idCtrl ),
+ m_dbLink( NULL )
+{
+}
+
+void CCtrlData::OnInit()
+{
+ CCtrlBase::OnInit();
+ m_changed = false;
+}
+
+void CCtrlData::NotifyChange()
+{
+ if (!m_parentWnd || m_parentWnd->IsInitialized()) m_changed = true;
+ if ( m_parentWnd ) {
+ m_parentWnd->OnChange(this);
+ if ( m_parentWnd->IsInitialized())
+ ::SendMessage( ::GetParent( m_parentWnd->GetHwnd()), PSM_CHANGED, 0, 0 );
+ }
+
+ OnChange(this);
+}
+
+void CCtrlData::CreateDbLink( const char* szModuleName, const char* szSetting, BYTE type, DWORD iValue, bool bSigned )
+{
+ m_dbLink = new CDbLink( szModuleName, szSetting, type, iValue, bSigned );
+}
+
+void CCtrlData::CreateDbLink( const char* szModuleName, const char* szSetting, TCHAR* szValue )
+{
+ m_dbLink = new CDbLink( szModuleName, szSetting, DBVT_TCHAR, szValue );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlMButton
+
+CCtrlMButton::CCtrlMButton( CDlgBase* dlg, int ctrlId, HICON hIcon, const char* tooltip ) :
+ CCtrlButton( dlg, ctrlId ),
+ m_hIcon( hIcon ),
+ m_toolTip( tooltip )
+{
+}
+
+CCtrlMButton::CCtrlMButton( CDlgBase* dlg, int ctrlId, int iCoreIcon, const char* tooltip ) :
+ CCtrlButton( dlg, ctrlId ),
+ m_hIcon( LoadSkinnedIcon(iCoreIcon)),
+ m_toolTip( tooltip )
+{
+}
+
+CCtrlMButton::~CCtrlMButton()
+{
+ g_ReleaseIcon( m_hIcon );
+}
+
+void CCtrlMButton::OnInit()
+{
+ CCtrlButton::OnInit();
+
+ SendMessage( m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon );
+ SendMessage( m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0);
+ SendMessage( m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0);
+}
+
+void CCtrlMButton::MakeFlat()
+{
+ SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0);
+}
+
+void CCtrlMButton::MakePush()
+{
+ SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlButton
+
+CCtrlButton::CCtrlButton( CDlgBase* wnd, int idCtrl ) :
+ CCtrlBase( wnd, idCtrl )
+{
+}
+
+BOOL CCtrlButton::OnCommand(HWND, WORD, WORD idCode)
+{
+ if ( idCode == BN_CLICKED || idCode == STN_CLICKED )
+ OnClick(this);
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlHyperlink
+
+CCtrlHyperlink::CCtrlHyperlink( CDlgBase* wnd, int idCtrl, const char* url ) :
+ CCtrlBase( wnd, idCtrl ),
+ m_url(url)
+{
+}
+
+BOOL CCtrlHyperlink::OnCommand(HWND, WORD, WORD)
+{
+ ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW);
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlClc
+CCtrlClc::CCtrlClc( CDlgBase* dlg, int ctrlId ):
+ CCtrlBase(dlg, ctrlId)
+{
+}
+
+BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh };
+ switch (pnmh->code)
+ {
+ case CLN_EXPANDED: OnExpanded(&evt); break;
+ case CLN_LISTREBUILT: OnListRebuilt(&evt); break;
+ case CLN_ITEMCHECKED: OnItemChecked(&evt); break;
+ case CLN_DRAGGING: OnDragging(&evt); break;
+ case CLN_DROPPED: OnDropped(&evt); break;
+ case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break;
+ case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break;
+ case CLN_DRAGSTOP: OnDragStop(&evt); break;
+ case CLN_NEWCONTACT: OnNewContact(&evt); break;
+ case CLN_CONTACTMOVED: OnContactMoved(&evt); break;
+ case CLN_CHECKCHANGED: OnCheckChanged(&evt); break;
+ case NM_CLICK: OnClick(&evt); break;
+ }
+ return FALSE;
+}
+
+void CCtrlClc::AddContact(HANDLE hContact)
+{ SendMessage(m_hwnd, CLM_ADDCONTACT, (WPARAM)hContact, 0);
+}
+
+void CCtrlClc::AddGroup(HANDLE hGroup)
+{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0);
+}
+
+void CCtrlClc::AutoRebuild()
+{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0);
+}
+
+void CCtrlClc::DeleteItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EditLabel(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EndEditLabel(bool save)
+{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0);
+}
+
+void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk)
+{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE);
+}
+
+void CCtrlClc::Expand(HANDLE hItem, DWORD flags)
+{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags);
+}
+
+HANDLE CCtrlClc::FindContact(HANDLE hContact)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, (WPARAM)hContact, 0);
+}
+
+HANDLE CCtrlClc::FindGroup(HANDLE hGroup)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, (WPARAM)hGroup, 0);
+}
+
+COLORREF CCtrlClc::GetBkColor()
+{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0);
+}
+
+bool CCtrlClc::GetCheck(HANDLE hItem)
+{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false;
+}
+
+int CCtrlClc::GetCount()
+{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0);
+}
+
+HWND CCtrlClc::GetEditControl()
+{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0);
+}
+
+DWORD CCtrlClc::GetExpand(HANDLE hItem)
+{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0);
+}
+
+int CCtrlClc::GetExtraColumns()
+{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0);
+}
+
+BYTE CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn)
+{ return (BYTE)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFF);
+}
+
+HIMAGELIST CCtrlClc::GetExtraImageList()
+{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0);
+}
+
+HFONT CCtrlClc::GetFont(int iFontId)
+{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0);
+}
+
+HANDLE CCtrlClc::GetSelection()
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0);
+}
+
+HANDLE CCtrlClc::HitTest(int x, int y, DWORD *hitTest)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y));
+}
+
+void CCtrlClc::SelectItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::SetBkBitmap(DWORD mode, HBITMAP hBitmap)
+{ SendMessage(m_hwnd, CLM_SETBKBITMAP, mode, (LPARAM)hBitmap);
+}
+
+void CCtrlClc::SetBkColor(COLORREF clBack)
+{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0);
+}
+
+void CCtrlClc::SetCheck(HANDLE hItem, bool check)
+{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0);
+}
+
+void CCtrlClc::SetExtraColumns(int iColumns)
+{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0);
+}
+
+void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage));
+}
+
+void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList);
+}
+
+void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw)
+{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId));
+}
+
+void CCtrlClc::SetIndent(int iIndent)
+{ SendMessage(m_hwnd, CLM_SETINDENT, (WPARAM)iIndent, 0);
+}
+
+void CCtrlClc::SetItemText(HANDLE hItem, char *szText)
+{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText);
+}
+
+void CCtrlClc::SetHideEmptyGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0);
+}
+
+void CCtrlClc::SetGreyoutFlags(DWORD flags)
+{ SendMessage(m_hwnd, CLM_SETGREYOUTFLAGS, (WPARAM)flags, 0);
+}
+
+bool CCtrlClc::GetHideOfflineRoot()
+{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false;
+}
+
+void CCtrlClc::SetHideOfflineRoot(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9);
+}
+
+void CCtrlClc::SetUseGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0);
+}
+
+void CCtrlClc::SetOfflineModes(DWORD modes)
+{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0);
+}
+
+DWORD CCtrlClc::GetExStyle()
+{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0);
+}
+
+void CCtrlClc::SetExStyle(DWORD exStyle)
+{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0);
+}
+
+int CCtrlClc::GetLefrMargin()
+{ return SendMessage(m_hwnd, CLM_GETLEFTMARGIN, 0, 0);
+}
+
+void CCtrlClc::SetLeftMargin(int iMargin)
+{ SendMessage(m_hwnd, CLM_SETLEFTMARGIN, (WPARAM)iMargin, 0);
+}
+
+HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii);
+}
+
+int CCtrlClc::GetItemType(HANDLE hItem)
+{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+}
+
+HANDLE CCtrlClc::GetNextItem(HANDLE hItem, DWORD flags)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem);
+}
+
+COLORREF CCtrlClc::GetTextColot(int iFontId)
+{ return (COLORREF)SendMessage(m_hwnd, CLM_GETTEXTCOLOR, (WPARAM)iFontId, 0);
+}
+
+void CCtrlClc::SetTextColor(int iFontId, COLORREF clText)
+{ SendMessage(m_hwnd, CLM_SETTEXTCOLOR, (WPARAM)iFontId, (LPARAM)clText);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListView
+
+CCtrlListView::CCtrlListView( CDlgBase* dlg, int ctrlId ) :
+ CCtrlBase(dlg, ctrlId)
+{
+}
+
+BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, pnmh };
+
+ switch (pnmh->code) {
+ case NM_DBLCLK: OnDoubleClick(&evt); return TRUE;
+ case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE;
+ case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE;
+ case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE;
+ case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE;
+ case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE;
+ case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE;
+ case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE;
+ case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE;
+ case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE;
+ case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE;
+ case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE;
+ case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE;
+ //case LVN_INCREMENTALSEARCH: OnIncrementalSearch(&evt); return TRUE;
+ case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE;
+ case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE;
+ case LVN_ITEMCHANGED: OnItemChanged(&evt); return TRUE;
+ case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE;
+ case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE;
+ case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE;
+ case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE;
+ }
+
+ return FALSE;
+}
+
+// additional api
+HIMAGELIST CCtrlListView::CreateImageList(int iImageList)
+{
+ HIMAGELIST hIml;
+ if (hIml = GetImageList(iImageList))
+ return hIml;
+
+ hIml = ImageList_Create(16, 16, IsWinVerXPPlus() ? ILC_COLOR32|ILC_MASK : ILC_COLOR16|ILC_MASK, 0, 1);
+ SetImageList(hIml, iImageList);
+ return hIml;
+}
+
+void CCtrlListView::AddColumn(int iSubItem, TCHAR *name, int cx)
+{
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM;
+ lvc.iImage = 0;
+ lvc.pszText = name;
+ lvc.cx = cx;
+ lvc.iSubItem = iSubItem;
+ InsertColumn(iSubItem, &lvc);
+}
+
+void CCtrlListView::AddGroup(int iGroupId, TCHAR *name)
+{
+ if (IsWinVerXPPlus())
+ {
+ LVGROUP lvg = {0};
+ lvg.cbSize = sizeof(lvg);
+ lvg.mask = LVGF_HEADER|LVGF_GROUPID;
+ lvg.pszHeader = name;
+ lvg.cchHeader = lstrlen(lvg.pszHeader);
+ lvg.iGroupId = iGroupId;
+ InsertGroup(-1, &lvg);
+ }
+}
+
+int CCtrlListView::AddItem(TCHAR *text, int iIcon, LPARAM lParam, int iGroupId)
+{
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_PARAM|LVIF_TEXT|LVIF_IMAGE;
+ lvi.iSubItem = 0;
+ lvi.pszText = text;
+ lvi.iImage = iIcon;
+ lvi.lParam = lParam;
+
+
+ if ((iGroupId >= 0) && IsWinVerXPPlus())
+ {
+ lvi.mask |= LVIF_GROUPID;
+ lvi.iGroupId = iGroupId;
+ }
+
+
+ return InsertItem(&lvi);
+}
+
+void CCtrlListView::SetItem(int iItem, int iSubItem, TCHAR *text, int iIcon)
+{
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+ lvi.pszText = text;
+
+ if (iIcon >= 0)
+ {
+ lvi.mask |= LVIF_IMAGE;
+ lvi.iImage = iIcon;
+ }
+
+ SetItem(&lvi);
+}
+
+LPARAM CCtrlListView::GetItemData(int iItem)
+{
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+ GetItem(&lvi);
+ return lvi.lParam;
+}
+
+// classic api
+DWORD CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount)
+{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount);
+}
+void CCtrlListView::Arrange(UINT code)
+{ ListView_Arrange(m_hwnd, code);
+}
+void CCtrlListView::CancelEditLabel()
+{ ListView_CancelEditLabel(m_hwnd);
+}
+HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft)
+{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft);
+}
+void CCtrlListView::DeleteAllItems()
+{ ListView_DeleteAllItems(m_hwnd);
+}
+void CCtrlListView::DeleteColumn(int iCol)
+{ ListView_DeleteColumn(m_hwnd, iCol);
+}
+void CCtrlListView::DeleteItem(int iItem)
+{ ListView_DeleteItem(m_hwnd, iItem);
+}
+HWND CCtrlListView::EditLabel(int iItem)
+{ return ListView_EditLabel(m_hwnd, iItem);
+}
+int CCtrlListView::EnableGroupView(BOOL fEnable)
+{ return ListView_EnableGroupView(m_hwnd, fEnable);
+}
+BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK)
+{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK);
+}
+int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi)
+{ return ListView_FindItem(m_hwnd, iStart, plvfi);
+}
+COLORREF CCtrlListView::GetBkColor()
+{ return ListView_GetBkColor(m_hwnd);
+}
+void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki)
+{ ListView_GetBkImage(m_hwnd, plvbki);
+}
+UINT CCtrlListView::GetCallbackMask()
+{ return ListView_GetCallbackMask(m_hwnd);
+}
+BOOL CCtrlListView::GetCheckState(UINT iIndex)
+{ return ListView_GetCheckState(m_hwnd, iIndex);
+}
+void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol)
+{ ListView_GetColumn(m_hwnd, iCol, pcol);
+}
+void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray)
+{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+int CCtrlListView::GetColumnWidth(int iCol)
+{ return ListView_GetColumnWidth(m_hwnd, iCol);
+}
+int CCtrlListView::GetCountPerPage()
+{ return ListView_GetCountPerPage(m_hwnd);
+}
+HWND CCtrlListView::GetEditControl()
+{ return ListView_GetEditControl(m_hwnd);
+}
+//void CCtrlListView::GetEmptyText(PWSTR pszText, UINT cchText)
+//{ ListView_GetEmptyText(m_hwnd, pszText, cchText);
+//}
+DWORD CCtrlListView::GetExtendedListViewStyle()
+{ return ListView_GetExtendedListViewStyle(m_hwnd);
+}
+//INT CCtrlListView::GetFocusedGroup()
+//{ return ListView_GetFocusedGroup(m_hwnd);
+//}
+//void CCtrlListView::GetFooterInfo(LPLVFOOTERINFO plvfi)
+//{ ListView_GetFooterInfo(m_hwnd, plvfi);
+//}
+//void CCtrlListView::GetFooterItem(UINT iItem, LVFOOTERITEM *pfi)
+//{ ListView_GetFooterItem(m_hwnd, iItem, pfi);
+//}
+//void CCtrlListView::GetFooterItemRect(UINT iItem, RECT *prc)
+//{ ListView_GetFooterItemRect(m_hwnd, iItem, prc);
+//}
+//void CCtrlListView::GetFooterRect(RECT *prc)
+//{ ListView_GetFooterRect(m_hwnd, prc);
+//}
+//int CCtrlListView::GetGroupCount()
+//{ return ListView_GetGroupCount(m_hwnd);
+//}
+//HIMAGELIST CCtrlListView::GetGroupHeaderImageList()
+//{ return ListView_GetGroupHeaderImageList(m_hwnd);
+//}
+//void CCtrlListView::GetGroupInfo(int iGroupId, PLVGROUP pgrp)
+//{ ListView_GetGroupInfo(m_hwnd, iGroupId, pgrp);
+//}
+//void CCtrlListView::GetGroupInfoByIndex(int iIndex, PLVGROUP pgrp)
+//{ ListView_GetGroupInfoByIndex(m_hwnd, iIndex, pgrp);
+//}
+void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics)
+{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+//BOOL CCtrlListView::GetGroupRect(int iGroupId, RECT *prc)
+//{ return ListView_GetGroupRect(m_hwnd, iGroupId, prc);
+//}
+//UINT CCtrlListView::GetGroupState(UINT dwGroupId, UINT dwMask)
+//{ return ListView_GetGroupState(m_hwnd, dwGroupId, dwMask);
+//}
+HWND CCtrlListView::GetHeader()
+{ return ListView_GetHeader(m_hwnd);
+}
+HCURSOR CCtrlListView::GetHotCursor()
+{ return ListView_GetHotCursor(m_hwnd);
+}
+INT CCtrlListView::GetHotItem()
+{ return ListView_GetHotItem(m_hwnd);
+}
+DWORD CCtrlListView::GetHoverTime()
+{ return ListView_GetHoverTime(m_hwnd);
+}
+HIMAGELIST CCtrlListView::GetImageList(int iImageList)
+{ return ListView_GetImageList(m_hwnd, iImageList);
+}
+BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim)
+{ return ListView_GetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::GetInsertMarkColor()
+{ return ListView_GetInsertMarkColor(m_hwnd);
+}
+int CCtrlListView::GetInsertMarkRect(LPRECT prc)
+{ return ListView_GetInsertMarkRect(m_hwnd, prc);
+}
+BOOL CCtrlListView::GetISearchString(LPSTR lpsz)
+{ return ListView_GetISearchString(m_hwnd, lpsz);
+}
+void CCtrlListView::GetItem(LPLVITEM pitem)
+{ ListView_GetItem(m_hwnd, pitem);
+}
+int CCtrlListView::GetItemCount()
+{ return ListView_GetItemCount(m_hwnd);
+}
+//void CCtrlListView::GetItemIndexRect(LVITEMINDEX *plvii, LONG iSubItem, LONG code, LPRECT prc)
+//{ ListView_GetItemIndexRect(m_hwnd, plvii, iSubItem, code, prc);
+//}
+void CCtrlListView::GetItemPosition(int i, POINT *ppt)
+{ ListView_GetItemPosition(m_hwnd, i, ppt);
+}
+void CCtrlListView::GetItemRect(int i, RECT *prc, int code)
+{ ListView_GetItemRect(m_hwnd, i, prc, code);
+}
+DWORD CCtrlListView::GetItemSpacing(BOOL fSmall)
+{ return ListView_GetItemSpacing(m_hwnd, fSmall);
+}
+UINT CCtrlListView::GetItemState(int i, UINT mask)
+{ return ListView_GetItemState(m_hwnd, i, mask);
+}
+void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax)
+{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax);
+}
+int CCtrlListView::GetNextItem(int iStart, UINT flags)
+{ return ListView_GetNextItem(m_hwnd, iStart, flags);
+}
+//BOOL CCtrlListView::GetNextItemIndex(LVITEMINDEX *plvii, LPARAM flags)
+//{ return ListView_GetNextItemIndex(m_hwnd, plvii, flags);
+//}
+BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas)
+{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas);
+}
+BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg)
+{ return ListView_GetOrigin(m_hwnd, lpptOrg);
+}
+COLORREF CCtrlListView::GetOutlineColor()
+{ return ListView_GetOutlineColor(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedColumn()
+{ return ListView_GetSelectedColumn(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedCount()
+{ return ListView_GetSelectedCount(m_hwnd);
+}
+INT CCtrlListView::GetSelectionMark()
+{ return ListView_GetSelectionMark(m_hwnd);
+}
+int CCtrlListView::GetStringWidth(LPCSTR psz)
+{ return ListView_GetStringWidth(m_hwnd, psz);
+}
+BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect)
+{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect);
+}
+COLORREF CCtrlListView::GetTextBkColor()
+{ return ListView_GetTextBkColor(m_hwnd);
+}
+COLORREF CCtrlListView::GetTextColor()
+{ return ListView_GetTextColor(m_hwnd);
+}
+void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo)
+{ ListView_GetTileInfo(m_hwnd, plvtinfo);
+}
+void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo)
+{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::GetToolTips()
+{ return ListView_GetToolTips(m_hwnd);
+}
+int CCtrlListView::GetTopIndex()
+{ return ListView_GetTopIndex(m_hwnd);
+}
+BOOL CCtrlListView::GetUnicodeFormat()
+{ return ListView_GetUnicodeFormat(m_hwnd);
+}
+DWORD CCtrlListView::GetView()
+{ return ListView_GetView(m_hwnd);
+}
+BOOL CCtrlListView::GetViewRect(RECT *prc)
+{ return ListView_GetViewRect(m_hwnd, prc);
+}
+void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc)
+{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+BOOL CCtrlListView::HasGroup(int dwGroupId)
+{ return ListView_HasGroup(m_hwnd, dwGroupId);
+}
+int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo)
+{ return ListView_HitTest(m_hwnd, pinfo);
+}
+//int CCtrlListView::HitTestEx(LPLVHITTESTINFO pinfo)
+//{ return ListView_HitTestEx(m_hwnd, pinfo);
+//}
+int CCtrlListView::InsertColumn(int iCol, const LPLVCOLUMN pcol)
+{ return ListView_InsertColumn(m_hwnd, iCol, pcol);
+}
+int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp)
+{ return ListView_InsertGroup(m_hwnd, index, pgrp);
+}
+void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert)
+{ ListView_InsertGroupSorted(m_hwnd, structInsert);
+}
+int CCtrlListView::InsertItem(const LPLVITEM pitem)
+{ return ListView_InsertItem(m_hwnd, pitem);
+}
+BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim)
+{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim);
+}
+BOOL CCtrlListView::IsGroupViewEnabled()
+{ return ListView_IsGroupViewEnabled(m_hwnd);
+}
+//UINT CCtrlListView::IsItemVisible(UINT index)
+//{ return ListView_IsItemVisible(m_hwnd, index);
+//}
+UINT CCtrlListView::MapIDToIndex(UINT id)
+{ return ListView_MapIDToIndex(m_hwnd, id);
+}
+UINT CCtrlListView::MapIndexToID(UINT index)
+{ return ListView_MapIndexToID(m_hwnd, index);
+}
+BOOL CCtrlListView::RedrawItems(int iFirst, int iLast)
+{ return ListView_RedrawItems(m_hwnd, iFirst, iLast);
+}
+void CCtrlListView::RemoveAllGroups()
+{ ListView_RemoveAllGroups(m_hwnd);
+}
+int CCtrlListView::RemoveGroup(int iGroupId)
+{ return ListView_RemoveGroup(m_hwnd, iGroupId);
+}
+BOOL CCtrlListView::Scroll(int dx, int dy)
+{ return ListView_Scroll(m_hwnd, dx, dy);
+}
+BOOL CCtrlListView::SetBkColor(COLORREF clrBk)
+{ return ListView_SetBkColor(m_hwnd, clrBk);
+}
+BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki)
+{ return ListView_SetBkImage(m_hwnd, plvbki);
+}
+BOOL CCtrlListView::SetCallbackMask(UINT mask)
+{ return ListView_SetCallbackMask(m_hwnd, mask);
+}
+void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck)
+{ ListView_SetCheckState(m_hwnd, iIndex, fCheck);
+}
+BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol)
+{ return ListView_SetColumn(m_hwnd, iCol, pcol);
+}
+BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray)
+{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+BOOL CCtrlListView::SetColumnWidth(int iCol, int cx)
+{ return ListView_SetColumnWidth(m_hwnd, iCol, cx);
+}
+void CCtrlListView::SetExtendedListViewStyle(DWORD dwExStyle)
+{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle);
+}
+void CCtrlListView::SetExtendedListViewStyleEx(DWORD dwExMask, DWORD dwExStyle)
+{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle);
+}
+//HIMAGELIST CCtrlListView::SetGroupHeaderImageList(HIMAGELIST himl)
+//{ return ListView_SetGroupHeaderImageList(m_hwnd, himl);
+//}
+int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp)
+{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp);
+}
+void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics)
+{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+//void CCtrlListView::SetGroupState(UINT dwGroupId, UINT dwMask, UINT dwState)
+//{ ListView_SetGroupState(m_hwnd, dwGroupId, dwMask, dwState);
+//}
+HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor)
+{ return ListView_SetHotCursor(m_hwnd, hCursor);
+}
+INT CCtrlListView::SetHotItem(INT iIndex)
+{ return ListView_SetHotItem(m_hwnd, iIndex);
+}
+void CCtrlListView::SetHoverTime(DWORD dwHoverTime)
+{ ListView_SetHoverTime(m_hwnd, dwHoverTime);
+}
+DWORD CCtrlListView::SetIconSpacing(int cx, int cy)
+{ return ListView_SetIconSpacing(m_hwnd, cx, cy);
+}
+HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList)
+{ return ListView_SetImageList(m_hwnd, himl, iImageList);
+}
+BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip)
+{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip);
+}
+BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim)
+{ return ListView_SetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color)
+{ return ListView_SetInsertMarkColor(m_hwnd, color);
+}
+BOOL CCtrlListView::SetItem(const LPLVITEM pitem)
+{ return ListView_SetItem(m_hwnd, pitem);
+}
+void CCtrlListView::SetItemCount(int cItems)
+{ ListView_SetItemCount(m_hwnd, cItems);
+}
+void CCtrlListView::SetItemCountEx(int cItems, DWORD dwFlags)
+{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags);
+}
+//HRESULT CCtrlListView::SetItemIndexState(LVITEMINDEX *plvii, UINT data, UINT mask)
+//{ return ListView_SetItemIndexState(m_hwnd, plvii, data, mask);
+//}
+BOOL CCtrlListView::SetItemPosition(int i, int x, int y)
+{ return ListView_SetItemPosition(m_hwnd, i, x, y);
+}
+void CCtrlListView::SetItemPosition32(int iItem, int x, int y)
+{ ListView_SetItemPosition32(m_hwnd, iItem, x, y);
+}
+void CCtrlListView::SetItemState(int i, UINT state, UINT mask)
+{ ListView_SetItemState(m_hwnd, i, state, mask);
+}
+void CCtrlListView::SetItemText(int i, int iSubItem, TCHAR *pszText)
+{ ListView_SetItemText(m_hwnd, i, iSubItem, pszText);
+}
+COLORREF CCtrlListView::SetOutlineColor(COLORREF color)
+{ return ListView_SetOutlineColor(m_hwnd, color);
+}
+void CCtrlListView::SetSelectedColumn(int iCol)
+{ ListView_SetSelectedColumn(m_hwnd, iCol);
+}
+INT CCtrlListView::SetSelectionMark(INT iIndex)
+{ return ListView_SetSelectionMark(m_hwnd, iIndex);
+}
+BOOL CCtrlListView::SetTextBkColor(COLORREF clrText)
+{ return ListView_SetTextBkColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTextColor(COLORREF clrText)
+{ return ListView_SetTextColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo)
+{ return ListView_SetTileInfo(m_hwnd, plvtinfo);
+}
+BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo)
+{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::SetToolTips(HWND ToolTip)
+{ return ListView_SetToolTips(m_hwnd, ToolTip);
+}
+BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode)
+{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode);
+}
+int CCtrlListView::SetView(DWORD iView)
+{ return ListView_SetView(m_hwnd, iView);
+}
+void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc)
+{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv)
+{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv);
+}
+BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort);
+}
+BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort);
+}
+INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo)
+{ return ListView_SubItemHitTest(m_hwnd, pInfo);
+}
+//INT CCtrlListView::SubItemHitTestEx(LPLVHITTESTINFO plvhti)
+//{ return ListView_SubItemHitTestEx(m_hwnd, plvhti);
+//}
+BOOL CCtrlListView::Update(int iItem)
+{ return ListView_Update(m_hwnd, iItem);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlFilterListView
+
+#define FILTER_BOX_HEIGHT 21
+
+struct CFilterData : public MZeroedObject
+{
+ HFONT m_hfntNormal;
+ HFONT m_hfntEmpty;
+ COLORREF m_clGray;
+ TCHAR *m_filterText;
+
+ RECT m_rcButtonClear;
+ RECT m_rcEditBox;
+
+ WNDPROC m_oldWndProc;
+ HWND m_hwndOwner;
+ HWND m_hwndEditBox;
+
+ void ReleaseFilterData()
+ {
+ //DeleteObject(m_hfntNormal); m_hfntNormal = NULL; // managed by system
+ DeleteObject(m_hfntEmpty); m_hfntEmpty = NULL;
+ }
+
+ ~CFilterData()
+ {
+ ReleaseFilterData();
+ }
+};
+
+CCtrlFilterListView::CCtrlFilterListView(CDlgBase* dlg, int ctrlId, bool trackFilter, bool keepHiglight):
+ CCtrlListView(dlg, ctrlId),
+ m_trackFilter(trackFilter),
+ m_keepHiglight(keepHiglight)
+{
+ fdat = new CFilterData;
+}
+
+CCtrlFilterListView::~CCtrlFilterListView()
+{
+ if (fdat->m_filterText) mir_free(fdat->m_filterText);
+ delete fdat;
+}
+
+TCHAR *CCtrlFilterListView::GetFilterText()
+{
+ return fdat->m_filterText;
+}
+
+void CCtrlFilterListView::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+}
+
+static LRESULT CALLBACK sttEditBoxSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CFilterData *fdat = (CFilterData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (!fdat) return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ switch (msg)
+ {
+ case WM_GETDLGCODE:
+ if ((wParam == VK_RETURN) || (wParam == VK_ESCAPE))
+ return DLGC_WANTMESSAGE;
+ break;
+
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ {
+ if (wParam == VK_RETURN)
+ {
+ if (fdat->m_filterText) mir_free(fdat->m_filterText);
+ int length = GetWindowTextLength(hwnd) + 1;
+ if (length == 1)
+ {
+ fdat->m_filterText = 0;
+ } else
+ {
+ fdat->m_filterText = (TCHAR *)mir_alloc(sizeof(TCHAR) * length);
+ GetWindowText(hwnd, fdat->m_filterText, length);
+ }
+
+ DestroyWindow(hwnd);
+ RedrawWindow(fdat->m_hwndOwner, NULL, NULL, RDW_INVALIDATE|RDW_FRAME);
+ PostMessage(fdat->m_hwndOwner, WM_APP, 0, 0);
+ } else
+ if (wParam == VK_ESCAPE)
+ {
+ DestroyWindow(hwnd);
+ return 0;
+ } else
+ {
+ PostMessage(fdat->m_hwndOwner, WM_APP, 1, 0);
+ }
+
+ break;
+ }
+
+ case WM_KILLFOCUS:
+ {
+ DestroyWindow(hwnd);
+ return 0;
+ }
+
+ case WM_DESTROY:
+ {
+ fdat->m_hwndEditBox = NULL;
+ }
+ }
+
+ return CallWindowProc(fdat->m_oldWndProc, hwnd, msg, wParam, lParam);
+}
+
+void CCtrlFilterListView::FilterHighlight(TCHAR *str)
+{
+ TCHAR buf[256];
+ int count = GetItemCount();
+ for (int i = 0; i < count; ++i)
+ {
+ bool found = false;
+
+ if (str)
+ {
+ for (int j = 0; j < 10; ++j)
+ {
+ GetItemText(i, j, buf, SIZEOF(buf));
+ if (!*buf)
+ break;
+
+ if (_tcsstr(buf, str))
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ SetItemState(i, found ? LVIS_DROPHILITED : 0, LVIS_DROPHILITED);
+ }
+}
+
+LRESULT CCtrlFilterListView::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_APP:
+ {
+ switch (wParam)
+ {
+ case 0:
+ {
+ OnFilterChanged(this);
+ if (!m_keepHiglight) FilterHighlight(NULL);
+ break;
+ }
+
+ case 1:
+ {
+ if (m_trackFilter && fdat->m_hwndEditBox)
+ {
+ TCHAR *str = 0;
+ int length = GetWindowTextLength(fdat->m_hwndEditBox) + 1;
+ if (length == 1)
+ {
+ str = 0;
+ } else
+ {
+ str = (TCHAR *)mir_alloc(sizeof(TCHAR) * length);
+ GetWindowText(fdat->m_hwndEditBox, str, length);
+ }
+ FilterHighlight(str);
+ if (str) mir_free(str);
+ }
+ break;
+ }
+
+ case 2:
+ {
+ fdat->m_hwndOwner = m_hwnd;
+ fdat->m_hwndEditBox = CreateWindow(_T("edit"), fdat->m_filterText,
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL,
+ 0, 0, 0, 0,
+ ::GetParent(m_hwnd), (HMENU)-1, hInst, NULL);
+
+ SendMessage(fdat->m_hwndEditBox, WM_SETFONT, (WPARAM)fdat->m_hfntNormal, 0);
+
+ RECT rc = fdat->m_rcEditBox;
+ MapWindowPoints(m_hwnd, ::GetParent(m_hwnd), (LPPOINT)&rc, 2);
+ SetWindowPos(fdat->m_hwndEditBox, HWND_TOP, rc.left-5, rc.top+2, rc.right-rc.left, rc.bottom-rc.top-4, SWP_SHOWWINDOW);
+ SendMessage(fdat->m_hwndEditBox, EM_SETSEL, 0, -1);
+
+ fdat->m_oldWndProc = (WNDPROC)GetWindowLongPtr(fdat->m_hwndEditBox, GWLP_WNDPROC);
+ SetWindowLongPtr(fdat->m_hwndEditBox, GWLP_USERDATA, (LONG_PTR)fdat);
+ SetWindowLongPtr(fdat->m_hwndEditBox, GWLP_WNDPROC, (LONG_PTR)sttEditBoxSubclassProc);
+
+ SetFocus(m_hwnd); // hack to avoid popping of list over the box...
+ SetFocus(fdat->m_hwndEditBox);
+ break;
+ }
+ }
+ break;
+ }
+
+ case WM_NCCALCSIZE:
+ {
+ RECT *prect = (RECT *)lParam;
+
+ CSuper::CustomWndProc(msg, wParam, lParam);
+ prect->bottom -= FILTER_BOX_HEIGHT;
+
+ fdat->ReleaseFilterData();
+
+ fdat->m_hfntNormal = (HFONT)SendMessage(m_hwnd, WM_GETFONT, 0, 0);
+ if (!fdat->m_hfntNormal) fdat->m_hfntNormal = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+
+ LOGFONT lf;
+ GetObject(fdat->m_hfntNormal, sizeof(lf), &lf);
+ lf.lfItalic = TRUE;
+ fdat->m_hfntEmpty = CreateFontIndirect(&lf);
+
+ COLORREF clText = GetSysColor(COLOR_WINDOWTEXT);
+ COLORREF clBack = GetSysColor(COLOR_WINDOW);
+ fdat->m_clGray = RGB(
+ (GetRValue(clBack) + 2*GetRValue(clText)) / 3,
+ (GetGValue(clBack) + 2*GetGValue(clText)) / 3,
+ (GetBValue(clBack) + 2*GetBValue(clText)) / 3
+ );
+
+ if (fdat->m_hwndEditBox) DestroyWindow(fdat->m_hwndEditBox);
+
+ return 0;
+ }
+
+ case WM_NCPAINT:
+ {
+/*
+ {
+ HRGN hrgnUpdate, hrgnTmp;
+ RECT rc;
+ GetWindowRect(m_hwnd, &rc);
+ OffsetRect(&rc, -rc.left, -rc.top);
+
+ RECT rcClient;
+ GetClientRect(m_hwnd, &rcClient);
+
+ hrgnTmp = CreateRectRgn(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
+ if (wParam == 1)
+ {
+ hrgnUpdate = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
+ CombineRgn(hrgnUpdate, hrgnUpdate, hrgnTmp, RGN_DIFF);
+ } else
+ {
+ hrgnUpdate = CreateRectRgn(0, 0, 0, 0);
+ CombineRgn(hrgnUpdate, (HRGN)wParam, hrgnTmp, RGN_DIFF);
+ }
+ DeleteObject(hrgnTmp);
+
+ InflateRect(&rc, -1, -1);
+ rc.top = rc.bottom - FILTER_BOX_HEIGHT;
+ hrgnTmp = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
+ CombineRgn(hrgnUpdate, hrgnUpdate, hrgnTmp, RGN_DIFF);
+ DeleteObject(hrgnTmp);
+
+ CSuper::CustomWndProc(msg, (WPARAM)hrgnUpdate, lParam);
+ DeleteObject(hrgnUpdate);
+ }
+*/
+ CSuper::CustomWndProc(msg, wParam, lParam);
+
+ RECT rc;
+ GetWindowRect(m_hwnd, &rc);
+ OffsetRect(&rc, -rc.left, -rc.top);
+ InflateRect(&rc, -1, -1);
+ rc.top = rc.bottom - FILTER_BOX_HEIGHT;
+
+ POINT pts[] = {
+ {rc.left, rc.top},
+ {rc.left+FILTER_BOX_HEIGHT, rc.top},
+ {rc.left+FILTER_BOX_HEIGHT+FILTER_BOX_HEIGHT/2+1, rc.top+FILTER_BOX_HEIGHT/2+1},
+ {rc.left+FILTER_BOX_HEIGHT, rc.top+FILTER_BOX_HEIGHT},
+ {rc.left, rc.top+FILTER_BOX_HEIGHT},
+ };
+ HRGN hrgnFilter = CreatePolygonRgn(pts, SIZEOF(pts), ALTERNATE);
+
+ HDC hdc = GetWindowDC(m_hwnd);
+
+ FillRect(hdc, &rc, GetSysColorBrush(COLOR_WINDOW));
+ FillRgn(hdc, hrgnFilter, GetSysColorBrush(COLOR_BTNFACE));
+
+ SetBkMode(hdc, TRANSPARENT);
+
+ if (fdat->m_filterText)
+ {
+ SetRect(&fdat->m_rcButtonClear,
+ rc.right - FILTER_BOX_HEIGHT + (FILTER_BOX_HEIGHT-16)/2, rc.top + (FILTER_BOX_HEIGHT-16)/2,
+ rc.right - FILTER_BOX_HEIGHT + (FILTER_BOX_HEIGHT-16)/2 + 16, rc.top + (FILTER_BOX_HEIGHT-16)/2 + 16);
+
+ DrawIconEx(hdc, rc.left + (FILTER_BOX_HEIGHT-16)/2, rc.top + (FILTER_BOX_HEIGHT-16)/2, g_LoadIconEx("sd_filter_apply"), 16, 16, 0, NULL, DI_NORMAL);
+ DrawIconEx(hdc, rc.right - FILTER_BOX_HEIGHT + (FILTER_BOX_HEIGHT-16)/2, rc.top + (FILTER_BOX_HEIGHT-16)/2, LoadSkinnedIcon(SKINICON_OTHER_DELETE), 16, 16, 0, NULL, DI_NORMAL);
+
+ rc.left += 5*FILTER_BOX_HEIGHT/3;
+ rc.right -= 5*FILTER_BOX_HEIGHT/3;
+
+ fdat->m_rcEditBox = rc;
+
+ SelectObject(hdc, fdat->m_hfntNormal);
+ ::SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
+ DrawText(hdc, fdat->m_filterText, -1, &rc, DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_END_ELLIPSIS);
+ } else
+ {
+ SetRect(&fdat->m_rcButtonClear, 0, 0, 0, 0);
+
+ DrawIconEx(hdc, rc.left + (FILTER_BOX_HEIGHT-16)/2, rc.top + (FILTER_BOX_HEIGHT-16)/2, g_LoadIconEx("sd_filter_reset"), 16, 16, 0, NULL, DI_NORMAL);
+
+ rc.left += 5*FILTER_BOX_HEIGHT/3;
+ rc.right -= 5;
+
+ fdat->m_rcEditBox = rc;
+
+ SelectObject(hdc, fdat->m_hfntEmpty);
+ ::SetTextColor(hdc, fdat->m_clGray);
+ DrawText(hdc, TranslateT("Set filter..."), -1, &rc, DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_END_ELLIPSIS);
+ }
+
+ ReleaseDC(m_hwnd, hdc);
+
+ DeleteObject(hrgnFilter);
+
+ return 0;
+ }
+
+ case WM_NCHITTEST:
+ {
+ POINT pt;
+ pt.x = LOWORD(lParam);
+ pt.y = HIWORD(lParam);
+ MapWindowPoints(NULL, m_hwnd, &pt, 1);
+
+ if(PtInRect(&fdat->m_rcButtonClear, pt))
+ return HTBORDER;
+ if(PtInRect(&fdat->m_rcEditBox, pt))
+ return HTBORDER;
+
+ break;
+ }
+
+ case WM_NCLBUTTONUP:
+ {
+ POINT pt;
+ pt.x = LOWORD(lParam);
+ pt.y = HIWORD(lParam);
+ MapWindowPoints(NULL, m_hwnd, &pt, 1);
+
+ if(PtInRect(&fdat->m_rcButtonClear, pt))
+ {
+ SetFocus(m_hwnd);
+ if (fdat->m_filterText) mir_free(fdat->m_filterText);
+ fdat->m_filterText = NULL;
+ RedrawWindow(m_hwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME);
+ OnFilterChanged(this);
+ FilterHighlight(NULL);
+ } else
+ if(PtInRect(&fdat->m_rcEditBox, pt))
+ {
+ PostMessage(m_hwnd, WM_APP, 2, 0);
+ }
+
+ break;
+ }
+
+ case WM_KEYDOWN:
+ {
+ if (wParam == 'F' && GetAsyncKeyState(VK_CONTROL))
+ PostMessage(m_hwnd, WM_APP, 2, 0);
+ break;
+ }
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlTreeView
+
+CCtrlTreeView::CCtrlTreeView( CDlgBase* dlg, int ctrlId ):
+ CCtrlBase(dlg, ctrlId)
+{
+}
+
+BOOL CCtrlTreeView::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, pnmh };
+
+ switch (pnmh->code)
+ {
+ case TVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE;
+ case TVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE;
+ case TVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE;
+ case TVN_DELETEITEM: OnDeleteItem(&evt); return TRUE;
+ case TVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE;
+ case TVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE;
+ case TVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE;
+ case TVN_ITEMEXPANDED: OnItemExpanded(&evt); return TRUE;
+ case TVN_ITEMEXPANDING: OnItemExpanding(&evt); return TRUE;
+ case TVN_KEYDOWN: OnKeyDown(&evt); return TRUE;
+ case TVN_SELCHANGED: OnSelChanged(&evt); return TRUE;
+ case TVN_SELCHANGING: OnSelChanging(&evt); return TRUE;
+ case TVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE;
+ case TVN_SINGLEEXPAND: OnSingleExpand(&evt); return TRUE;
+ }
+
+ return FALSE;
+}
+
+void CCtrlTreeView::TranslateItem(HTREEITEM hItem)
+{
+ TCHAR buf[128];
+ TVITEMEX tvi;
+
+ GetItem(hItem, &tvi, buf, SIZEOF(buf));
+ tvi.pszText = TranslateTS(tvi.pszText);
+ tvi.cchTextMax = lstrlen(tvi.pszText);
+ SetItem(&tvi);
+}
+
+void CCtrlTreeView::TranslateTree()
+{
+ HTREEITEM hItem = GetRoot();
+ while (hItem)
+ {
+ TranslateItem(hItem);
+
+ HTREEITEM hItemTmp = 0;
+ if (hItemTmp = GetChild(hItem))
+ hItem = hItemTmp;
+ else if (hItemTmp = GetNextSibling(hItem))
+ hItem = hItemTmp;
+ else
+ {
+ while (1)
+ {
+ if (!(hItem = GetParent(hItem))) break;
+ if (hItemTmp = GetNextSibling(hItem))
+ {
+ hItem = hItemTmp;
+ break;
+ }
+ }
+ }
+ }
+}
+
+HTREEITEM CCtrlTreeView::FindNamedItem(HTREEITEM hItem, const TCHAR *name)
+{
+ TVITEMEX tvi = {0};
+ TCHAR str[MAX_PATH];
+
+ if (hItem)
+ tvi.hItem = GetChild(hItem);
+ else
+ tvi.hItem = GetRoot();
+
+ if (!name)
+ return tvi.hItem;
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = str;
+ tvi.cchTextMax = SIZEOF(str);
+
+ while (tvi.hItem)
+ {
+ GetItem(&tvi);
+
+ if (!lstrcmp(tvi.pszText, name))
+ return tvi.hItem;
+
+ tvi.hItem = GetNextSibling(tvi.hItem);
+ }
+ return NULL;
+}
+
+void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi)
+{
+ ZeroMemory(tvi, sizeof(*tvi));
+ tvi->mask = TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_INTEGRAL|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_STATE;
+ tvi->hItem = hItem;
+ GetItem(tvi);
+}
+
+void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi, TCHAR *szText, int iTextLength)
+{
+ ZeroMemory(tvi, sizeof(*tvi));
+ tvi->mask = TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_INTEGRAL|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_STATE|TVIF_TEXT;
+ tvi->hItem = hItem;
+ tvi->pszText = szText;
+ tvi->cchTextMax = iTextLength;
+ GetItem(tvi);
+}
+
+HIMAGELIST CCtrlTreeView::CreateDragImage(HTREEITEM hItem)
+{ return TreeView_CreateDragImage(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::DeleteAllItems()
+{ TreeView_DeleteAllItems(m_hwnd);
+}
+
+void CCtrlTreeView::DeleteItem(HTREEITEM hItem)
+{ TreeView_DeleteItem(m_hwnd, hItem);
+}
+
+HWND CCtrlTreeView::EditLabel(HTREEITEM hItem)
+{ return TreeView_EditLabel(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::EndEditLabelNow(BOOL cancel)
+{ TreeView_EndEditLabelNow(m_hwnd, cancel);
+}
+
+void CCtrlTreeView::EnsureVisible(HTREEITEM hItem)
+{ TreeView_EnsureVisible(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::Expand(HTREEITEM hItem, DWORD flag)
+{ TreeView_Expand(m_hwnd, hItem, flag);
+}
+
+COLORREF CCtrlTreeView::GetBkColor()
+{ return TreeView_GetBkColor(m_hwnd);
+}
+
+DWORD CCtrlTreeView::GetCheckState(HTREEITEM hItem)
+{ return TreeView_GetCheckState(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetChild(HTREEITEM hItem)
+{ return TreeView_GetChild(m_hwnd, hItem);
+}
+
+int CCtrlTreeView::GetCount()
+{ return TreeView_GetCount(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetDropHilight()
+{ return TreeView_GetDropHilight(m_hwnd);
+}
+
+HWND CCtrlTreeView::GetEditControl()
+{ return TreeView_GetEditControl(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetFirstVisible()
+{ return TreeView_GetFirstVisible(m_hwnd);
+}
+
+HIMAGELIST CCtrlTreeView::GetImageList(int iImage)
+{ return TreeView_GetImageList(m_hwnd, iImage);
+}
+
+int CCtrlTreeView::GetIndent()
+{ return TreeView_GetIndent(m_hwnd);
+}
+
+COLORREF CCtrlTreeView::GetInsertMarkColor()
+{ return TreeView_GetInsertMarkColor(m_hwnd);
+}
+
+void CCtrlTreeView::GetItem(TVITEMEX *tvi)
+{ TreeView_GetItem(m_hwnd, tvi);
+}
+
+int CCtrlTreeView::GetItemHeight()
+{ return TreeView_GetItemHeight(m_hwnd);
+}
+
+void CCtrlTreeView::GetItemRect(HTREEITEM hItem, RECT *rcItem, BOOL fItemRect)
+{ TreeView_GetItemRect(m_hwnd, hItem, rcItem, fItemRect);
+}
+
+DWORD CCtrlTreeView::GetItemState(HTREEITEM hItem, DWORD stateMask)
+{ return TreeView_GetItemState(m_hwnd, hItem, stateMask);
+}
+
+HTREEITEM CCtrlTreeView::GetLastVisible()
+{ return TreeView_GetLastVisible(m_hwnd);
+}
+
+COLORREF CCtrlTreeView::GetLineColor()
+{ return TreeView_GetLineColor(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetNextItem(HTREEITEM hItem, DWORD flag)
+{ return TreeView_GetNextItem(m_hwnd, hItem, flag);
+}
+
+HTREEITEM CCtrlTreeView::GetNextSibling(HTREEITEM hItem)
+{ return TreeView_GetNextSibling(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetNextVisible(HTREEITEM hItem)
+{ return TreeView_GetNextVisible(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetParent(HTREEITEM hItem)
+{ return TreeView_GetParent(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetPrevSibling(HTREEITEM hItem)
+{ return TreeView_GetPrevSibling(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetPrevVisible(HTREEITEM hItem)
+{ return TreeView_GetPrevVisible(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetRoot()
+{ return TreeView_GetRoot(m_hwnd);
+}
+
+DWORD CCtrlTreeView::GetScrollTime()
+{ return TreeView_GetScrollTime(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetSelection()
+{ return TreeView_GetSelection(m_hwnd);
+}
+
+COLORREF CCtrlTreeView::GetTextColor()
+{ return TreeView_GetTextColor(m_hwnd);
+}
+
+HWND CCtrlTreeView::GetToolTips()
+{ return TreeView_GetToolTips(m_hwnd);
+}
+
+BOOL CCtrlTreeView::GetUnicodeFormat()
+{ return TreeView_GetUnicodeFormat(m_hwnd);
+}
+
+unsigned CCtrlTreeView::GetVisibleCount()
+{ return TreeView_GetVisibleCount(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::HitTest(TVHITTESTINFO *hti)
+{ return TreeView_HitTest(m_hwnd, hti);
+}
+
+HTREEITEM CCtrlTreeView::InsertItem(TVINSERTSTRUCT *tvis)
+{ return TreeView_InsertItem(m_hwnd, tvis);
+}
+
+/*
+HTREEITEM CCtrlTreeView::MapAccIDToHTREEITEM(UINT id)
+{ return TreeView_MapAccIDToHTREEITEM(m_hwnd, id);
+}
+
+UINT CCtrlTreeView::MapHTREEITEMtoAccID(HTREEITEM hItem)
+{ return TreeView_MapHTREEITEMtoAccID(m_hwnd, hItem);
+}
+
+*/
+void CCtrlTreeView::Select(HTREEITEM hItem, DWORD flag)
+{ TreeView_Select(m_hwnd, hItem, flag);
+}
+
+void CCtrlTreeView::SelectDropTarget(HTREEITEM hItem)
+{ TreeView_SelectDropTarget(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::SelectItem(HTREEITEM hItem)
+{ TreeView_SelectItem(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::SelectSetFirstVisible(HTREEITEM hItem)
+{ TreeView_SelectSetFirstVisible(m_hwnd, hItem);
+}
+
+COLORREF CCtrlTreeView::SetBkColor(COLORREF clBack)
+{ return TreeView_SetBkColor(m_hwnd, clBack);
+}
+
+void CCtrlTreeView::SetCheckState(HTREEITEM hItem, DWORD state)
+{ TreeView_SetCheckState(m_hwnd, hItem, state);
+}
+
+void CCtrlTreeView::SetImageList(HIMAGELIST hIml, int iImage)
+{ TreeView_SetImageList(m_hwnd, hIml, iImage);
+}
+
+void CCtrlTreeView::SetIndent(int iIndent)
+{ TreeView_SetIndent(m_hwnd, iIndent);
+}
+
+void CCtrlTreeView::SetInsertMark(HTREEITEM hItem, BOOL fAfter)
+{ TreeView_SetInsertMark(m_hwnd, hItem, fAfter);
+}
+
+COLORREF CCtrlTreeView::SetInsertMarkColor(COLORREF clMark)
+{ return TreeView_SetInsertMarkColor(m_hwnd, clMark);
+}
+
+void CCtrlTreeView::SetItem(TVITEMEX *tvi)
+{ TreeView_SetItem(m_hwnd, tvi);
+}
+
+void CCtrlTreeView::SetItemHeight(short cyItem)
+{ TreeView_SetItemHeight(m_hwnd, cyItem);
+}
+
+void CCtrlTreeView::SetItemState(HTREEITEM hItem, DWORD state, DWORD stateMask)
+{ TreeView_SetItemState(m_hwnd, hItem, state, stateMask);
+}
+
+COLORREF CCtrlTreeView::SetLineColor(COLORREF clLine)
+{ return TreeView_SetLineColor(m_hwnd, clLine);
+}
+
+void CCtrlTreeView::SetScrollTime(UINT uMaxScrollTime)
+{ TreeView_SetScrollTime(m_hwnd, uMaxScrollTime);
+}
+
+COLORREF CCtrlTreeView::SetTextColor(COLORREF clText)
+{ return TreeView_SetTextColor(m_hwnd, clText);
+}
+
+HWND CCtrlTreeView::SetToolTips(HWND hwndToolTips)
+{ return TreeView_SetToolTips(m_hwnd, hwndToolTips);
+}
+
+BOOL CCtrlTreeView::SetUnicodeFormat(BOOL fUnicode)
+{ return TreeView_SetUnicodeFormat(m_hwnd, fUnicode);
+}
+
+void CCtrlTreeView::SortChildren(HTREEITEM hItem, BOOL fRecurse)
+{ TreeView_SortChildren(m_hwnd, hItem, fRecurse);
+}
+
+void CCtrlTreeView::SortChildrenCB(TVSORTCB *cb, BOOL fRecurse)
+{ TreeView_SortChildrenCB(m_hwnd, cb, fRecurse);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlPages
+
+CCtrlPages::CCtrlPages( CDlgBase* dlg, int ctrlId ):
+ CCtrlBase(dlg, ctrlId), m_hIml(NULL), m_pActivePage(NULL)
+{
+}
+
+void CCtrlPages::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+}
+
+LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_SIZE)
+ {
+ ShowPage(m_pActivePage);
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
+
+void CCtrlPages::AddPage( TCHAR *ptszName, HICON hIcon, CCallback<void> onCreate, void *param )
+{
+ TPageInfo *info = new TPageInfo;
+ info->m_onCreate = onCreate;
+ info->m_param = param;
+ info->m_pDlg = NULL;
+
+ TCITEM tci = {0};
+ tci.mask = TCIF_PARAM|TCIF_TEXT;
+ tci.lParam = (LPARAM)info;
+ tci.pszText = ptszName;
+ if (hIcon)
+ {
+ if (!m_hIml)
+ {
+ m_hIml = ImageList_Create(16, 16, IsWinVerXPPlus() ? ILC_COLOR32|ILC_MASK : ILC_COLOR16|ILC_MASK, 0, 1);
+ TabCtrl_SetImageList(m_hwnd, m_hIml);
+ }
+
+ tci.mask |= TCIF_IMAGE;
+ tci.iImage = ImageList_AddIcon(m_hIml, hIcon);
+ }
+
+ TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci);
+}
+
+void CCtrlPages::AttachDialog( int iPage, CDlgBase *pDlg )
+{
+ if ((iPage < 0) || (iPage >= TabCtrl_GetItemCount(m_hwnd)))
+ return;
+
+ TCITEM tci = {0};
+ tci.mask = TCIF_PARAM;
+ TabCtrl_GetItem(m_hwnd, iPage, &tci);
+
+ if (TPageInfo *info = (TPageInfo *)tci.lParam)
+ {
+ if (info->m_pDlg)
+ info->m_pDlg->Close();
+
+ info->m_pDlg = pDlg;
+ //SetParent(info->m_pDlg->GetHwnd(), m_hwnd);
+
+ if (iPage == TabCtrl_GetCurSel(m_hwnd))
+ {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(info->m_pDlg);
+ }
+ }
+}
+
+void CCtrlPages::ShowPage(CDlgBase *pDlg)
+{
+ if (!pDlg) return;
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ MapWindowPoints(m_hwnd, ::GetParent(m_hwnd), (LPPOINT)&rc, 2);
+ SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, SWP_SHOWWINDOW);
+}
+
+void CCtrlPages::ActivatePage( int iPage )
+{
+ TabCtrl_SetCurSel(m_hwnd, iPage);
+ //ShowPage(iPage);
+}
+
+BOOL CCtrlPages::OnNotify( int /*idCtrl*/, NMHDR *pnmh )
+{
+ switch (pnmh->code)
+ {
+ case TCN_SELCHANGING:
+ {
+ TCITEM tci = {0};
+ tci.mask = TCIF_PARAM;
+ TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci);
+
+ if (TPageInfo *info = (TPageInfo *)tci.lParam)
+ {
+ if (info->m_pDlg)
+ {
+ m_pActivePage = NULL;
+ ShowWindow(info->m_pDlg->GetHwnd(), SW_HIDE);
+ }
+ }
+
+ return TRUE;
+ }
+
+ case TCN_SELCHANGE:
+ {
+ TCITEM tci = {0};
+ tci.mask = TCIF_PARAM;
+ TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci);
+
+ if (TPageInfo *info = (TPageInfo *)tci.lParam)
+ {
+ if (info->m_pDlg)
+ {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(info->m_pDlg);
+ } else
+ {
+ m_pActivePage = NULL;
+ info->m_onCreate(info->m_param);
+ }
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void CCtrlPages::OnDestroy()
+{
+ int count = TabCtrl_GetItemCount(m_hwnd);
+ for (int i = 0; i < count ; ++i)
+ {
+ TCITEM tci = {0};
+ tci.mask = TCIF_PARAM;
+ TabCtrl_GetItem(m_hwnd, i, &tci);
+
+ if (TPageInfo *info = (TPageInfo *)tci.lParam)
+ {
+ if (info->m_pDlg)
+ info->m_pDlg->Close();
+
+ delete info;
+ }
+ }
+
+ TabCtrl_DeleteAllItems(m_hwnd);
+
+ if (m_hIml)
+ {
+ TabCtrl_SetImageList(m_hwnd, NULL);
+ ImageList_Destroy(m_hIml);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlBase
+
+CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) :
+ m_parentWnd( wnd ),
+ m_idCtrl( idCtrl ),
+ m_hwnd( NULL ),
+ m_wndproc( NULL )
+{
+ if ( wnd ) {
+ m_next = wnd->m_first;
+ wnd->m_first = this;
+} }
+
+void CCtrlBase::OnInit()
+{
+ m_hwnd = (m_idCtrl && m_parentWnd && m_parentWnd->GetHwnd()) ? GetDlgItem(m_parentWnd->GetHwnd(), m_idCtrl) : NULL;
+}
+
+void CCtrlBase::OnDestroy()
+{
+ Unsubclass();
+ m_hwnd = NULL;
+}
+
+void CCtrlBase::Enable( int bIsEnable )
+{
+ ::EnableWindow( m_hwnd, bIsEnable );
+}
+
+BOOL CCtrlBase::Enabled() const
+{
+ return ( m_hwnd ) ? IsWindowEnabled( m_hwnd ) : FALSE;
+}
+
+LRESULT CCtrlBase::SendMsg( UINT Msg, WPARAM wParam, LPARAM lParam )
+{
+ return ::SendMessage( m_hwnd, Msg, wParam, lParam );
+}
+
+void CCtrlBase::SetText(const TCHAR *text)
+{
+ ::SetWindowText( m_hwnd, text );
+}
+
+void CCtrlBase::SetTextA(const char *text)
+{
+ ::SetWindowTextA( m_hwnd, text );
+}
+
+void CCtrlBase::SetInt(int value)
+{
+ TCHAR buf[32] = {0};
+ mir_sntprintf(buf, SIZEOF(buf), _T("%d"), value);
+ SetWindowText(m_hwnd, buf);
+}
+
+TCHAR* CCtrlBase::GetText()
+{
+ int length = GetWindowTextLength(m_hwnd) + 1;
+ TCHAR *result = (TCHAR *)mir_alloc(length * sizeof(TCHAR));
+ GetWindowText(m_hwnd, result, length);
+ return result;
+}
+
+char* CCtrlBase::GetTextA()
+{
+ int length = GetWindowTextLength(m_hwnd) + 1;
+ char *result = (char *)mir_alloc(length * sizeof(char));
+ GetWindowTextA(m_hwnd, result, length);
+ return result;
+}
+
+TCHAR* CCtrlBase::GetText(TCHAR *buf, int size)
+{
+ GetWindowText(m_hwnd, buf, size);
+ buf[size-1] = 0;
+ return buf;
+}
+
+char* CCtrlBase::GetTextA(char *buf, int size)
+{
+ GetWindowTextA(m_hwnd, buf, size);
+ buf[size-1] = 0;
+ return buf;
+}
+
+int CCtrlBase::GetInt()
+{
+ int length = GetWindowTextLength(m_hwnd) + 1;
+ TCHAR *result = (TCHAR *)_alloca(length * sizeof(TCHAR));
+ GetWindowText(m_hwnd, result, length);
+ return _ttoi(result);
+}
+
+LRESULT CCtrlBase::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_DESTROY) Unsubclass();
+ return CallWindowProc(m_wndproc, m_hwnd, msg, wParam, lParam);
+}
+
+void CCtrlBase::Subclass()
+{
+ SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this);
+ m_wndproc = (WNDPROC)SetWindowLongPtr(m_hwnd, GWLP_WNDPROC, (LONG_PTR)GlobalSubclassWndProc);
+}
+
+void CCtrlBase::Unsubclass()
+{
+ if (m_wndproc)
+ {
+ SetWindowLongPtr(m_hwnd, GWLP_WNDPROC, (LONG_PTR)m_wndproc);
+ SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)0);
+ m_wndproc = 0;
+} }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDbLink class
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, BYTE type, DWORD iValue, bool bSigned): CDataLink(type, bSigned)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_iDefault = iValue;
+ m_szDefault = 0;
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, BYTE type, TCHAR *szValue): CDataLink(type, false)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_szDefault = mir_tstrdup(szValue);
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::~CDbLink()
+{
+ mir_free(m_szModule);
+ mir_free(m_szSetting);
+ mir_free(m_szDefault);
+ if (dbv.type != DBVT_DELETED)
+ DBFreeVariant(&dbv);
+}
+
+DWORD CDbLink::LoadUnsigned()
+{
+ switch (m_type) {
+ case DBVT_BYTE: return DBGetContactSettingByte(NULL, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_WORD: return DBGetContactSettingWord(NULL, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_DWORD: return DBGetContactSettingDword(NULL, m_szModule, m_szSetting, m_iDefault);
+ default: return m_iDefault;
+ }
+}
+
+int CDbLink::LoadSigned()
+{
+ switch (m_type) {
+ case DBVT_BYTE: return (signed char)DBGetContactSettingByte(NULL, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_WORD: return (signed short)DBGetContactSettingWord(NULL, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_DWORD: return (signed int)DBGetContactSettingDword(NULL, m_szModule, m_szSetting, m_iDefault);
+ default: return m_iDefault;
+ }
+}
+
+void CDbLink::SaveInt(DWORD value)
+{
+ switch (m_type) {
+ case DBVT_BYTE: DBWriteContactSettingByte(NULL, m_szModule, m_szSetting, (BYTE)value); break;
+ case DBVT_WORD: DBWriteContactSettingWord(NULL, m_szModule, m_szSetting, (WORD)value); break;
+ case DBVT_DWORD: DBWriteContactSettingDword(NULL, m_szModule, m_szSetting, value); break;
+ }
+}
+
+TCHAR* CDbLink::LoadText()
+{
+ if (dbv.type != DBVT_DELETED) DBFreeVariant(&dbv);
+ if (!DBGetContactSettingTString(NULL, m_szModule, m_szSetting, &dbv))
+ {
+ if (dbv.type == DBVT_TCHAR)
+ return dbv.ptszVal;
+ return m_szDefault;
+ }
+
+ dbv.type = DBVT_DELETED;
+ return m_szDefault;
+}
+
+void CDbLink::SaveText(TCHAR *value)
+{
+ DBWriteContactSettingTString(NULL, m_szModule, m_szSetting, value);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Base protocol dialog
+
+void CProtoIntDlgBase::SetStatusText(TCHAR *statusText)
+{
+ if (m_hwndStatus)
+ SendMessage(m_hwndStatus, SB_SETTEXT, 0, (LPARAM)statusText);
+}
+
+INT_PTR CProtoIntDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ { // call inherited init code first
+ INT_PTR result = CSuper::DlgProc(msg, wParam, lParam);
+ if (m_show_label)
+ {
+ m_hwndStatus = CreateStatusWindow(WS_CHILD|WS_VISIBLE, NULL, m_hwnd, IDC_STATUSBAR);
+ SetWindowPos(m_hwndStatus, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ UpdateStatusBar();
+ UpdateProtoTitle();
+ }
+ return result;
+ }
+
+ case WM_SETTEXT:
+ if (m_show_label && IsWindowUnicode(m_hwnd))
+
+ {
+ TCHAR *szTitle = (TCHAR *)lParam;
+ if (!_tcsstr(szTitle, m_proto_interface->m_tszUserName))
+ {
+ UpdateProtoTitle(szTitle);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_SIZE:
+ if (m_hwndStatus)
+ {
+ RECT rcStatus; GetWindowRect(m_hwndStatus, &rcStatus);
+ RECT rcClient; GetClientRect(m_hwnd, &rcClient);
+ SetWindowPos(m_hwndStatus, NULL, 0, rcClient.bottom-(rcStatus.bottom-rcStatus.top), rcClient.right, (rcStatus.bottom-rcStatus.top), SWP_NOZORDER);
+ UpdateStatusBar();
+ }
+ break;
+
+ // Protocol events
+ case WM_PROTO_ACTIVATE:
+ OnProtoActivate(wParam, lParam);
+ return m_lresult;
+ case WM_PROTO_CHECK_ONLINE:
+ if (m_hwndStatus)
+ UpdateStatusBar();
+ OnProtoCheckOnline(wParam, lParam);
+ return m_lresult;
+ case WM_PROTO_REFRESH:
+ OnProtoRefresh(wParam, lParam);
+ return m_lresult;
+ }
+
+ return CSuper::DlgProc(msg, wParam, lParam);
+}
+
+void CProtoIntDlgBase::UpdateProtoTitle(TCHAR *szText)
+{
+ if (!m_show_label) return;
+
+ int curLength;
+ TCHAR *curText;
+
+ if (szText)
+ {
+ curText = szText;
+ curLength = lstrlen(curText);;
+ } else
+ {
+ curLength = GetWindowTextLength(m_hwnd) + 1;
+ curText = (TCHAR *)_alloca(curLength * sizeof(TCHAR));
+ GetWindowText(m_hwnd, curText, curLength);
+ }
+
+ if (!_tcsstr(curText, m_proto_interface->m_tszUserName))
+ {
+ int length = curLength + lstrlen(m_proto_interface->m_tszUserName) + 256;
+ TCHAR *text = (TCHAR *)_alloca(length * sizeof(TCHAR));
+ mir_sntprintf(text, length, _T("%s [%s: %s]"), curText, TranslateT("Account"), m_proto_interface->m_tszUserName);
+ SetWindowText(m_hwnd, text);
+ }
+}
+
+void CProtoIntDlgBase::UpdateStatusBar()
+{
+ SIZE sz;
+
+ HDC hdc = GetDC(m_hwndStatus);
+ HFONT hFntSave = (HFONT)SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
+ GetTextExtentPoint32(hdc, m_proto_interface->m_tszUserName, lstrlen(m_proto_interface->m_tszUserName), &sz);
+ sz.cx += GetSystemMetrics(SM_CXSMICON) * 3;
+ SelectObject(hdc, hFntSave);
+ ReleaseDC(m_hwndStatus, hdc);
+
+ RECT rcStatus; GetWindowRect(m_hwndStatus, &rcStatus);
+ int parts[] = { rcStatus.right-rcStatus.left - sz.cx, -1 };
+ SendMessage(m_hwndStatus, SB_SETPARTS, 2, (LPARAM)parts);
+ SendMessage(m_hwndStatus, SB_SETICON, 1, (LPARAM)LoadSkinnedProtoIcon(m_proto_interface->m_szModuleName, m_proto_interface->m_iStatus));
+ SendMessage(m_hwndStatus, SB_SETTEXT, 1, (LPARAM)m_proto_interface->m_tszUserName);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Misc utilities
+
+int UIEmulateBtnClick(HWND hwndDlg, UINT idcButton)
+{
+ if (IsWindowEnabled(GetDlgItem(hwndDlg, idcButton)))
+ PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(idcButton, BN_CLICKED), (LPARAM)GetDlgItem(hwndDlg, idcButton));
+ return 0;
+}
+
+void UIShowControls(HWND hwndDlg, int *idList, int nCmdShow)
+{
+ for (; *idList; ++idList)
+ ShowWindow(GetDlgItem(hwndDlg, *idList), nCmdShow);
+}
diff --git a/protocols/JabberG/src/ui_utils.h b/protocols/JabberG/src/ui_utils.h new file mode 100644 index 0000000000..9af9e8ba36 --- /dev/null +++ b/protocols/JabberG/src/ui_utils.h @@ -0,0 +1,1392 @@ +/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-12 George Hazan
+Copyright ( C ) 2007-09 Maxim Mluhov
+Copyright ( C ) 2007-09 Victor Pavlychko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jabber_ui_utils_h__
+#define __jabber_ui_utils_h__
+
+#pragma warning(disable:4355)
+
+#ifndef LPLVCOLUMN
+typedef struct tagNMLVSCROLL
+{
+ NMHDR hdr;
+ int dx;
+ int dy;
+} NMLVSCROLL;
+typedef struct tagLVG
+{
+ UINT cbSize;
+ UINT mask;
+ LPWSTR pszHeader;
+ int cchHeader;
+ LPWSTR pszFooter;
+ int cchFooter;
+ int iGroupId;
+ UINT stateMask;
+ UINT state;
+ UINT uAlign;
+} LVGROUP, *PLVGROUP;
+typedef struct tagLVGROUPMETRICS
+{
+ UINT cbSize;
+ UINT mask;
+ UINT Left;
+ UINT Top;
+ UINT Right;
+ UINT Bottom;
+ COLORREF crLeft;
+ COLORREF crTop;
+ COLORREF crRight;
+ COLORREF crBottom;
+ COLORREF crHeader;
+ COLORREF crFooter;
+} LVGROUPMETRICS, *PLVGROUPMETRICS;
+typedef struct tagLVTILEVIEWINFO
+{
+ UINT cbSize;
+ DWORD dwMask;
+ DWORD dwFlags;
+ SIZE sizeTile;
+ int cLines;
+ RECT rcLabelMargin;
+} LVTILEVIEWINFO, *PLVTILEVIEWINFO;
+typedef struct tagLVTILEINFO
+{
+ UINT cbSize;
+ int iItem;
+ UINT cColumns;
+ PUINT puColumns;
+} LVTILEINFO, *PLVTILEINFO;
+typedef struct
+{
+ UINT cbSize;
+ DWORD dwFlags;
+ int iItem;
+ DWORD dwReserved;
+} LVINSERTMARK, * LPLVINSERTMARK;
+typedef int (CALLBACK *PFNLVGROUPCOMPARE)(int, int, void *);
+typedef struct tagLVINSERTGROUPSORTED
+{
+ PFNLVGROUPCOMPARE pfnGroupCompare;
+ void *pvData;
+ LVGROUP lvGroup;
+} LVINSERTGROUPSORTED, *PLVINSERTGROUPSORTED;
+typedef struct tagLVSETINFOTIP
+{
+ UINT cbSize;
+ DWORD dwFlags;
+ LPWSTR pszText;
+ int iItem;
+ int iSubItem;
+} LVSETINFOTIP, *PLVSETINFOTIP;
+#define LPLVCOLUMN LPLVCOLUMNA
+#define LPLVITEM LPLVITEMA
+#define LVN_BEGINSCROLL (LVN_FIRST-80)
+#define LVN_ENDSCROLL (LVN_FIRST-81)
+#define LVN_HOTTRACK (LVN_FIRST-21)
+#define LVN_MARQUEEBEGIN (LVN_FIRST-56)
+#define LVM_MAPINDEXTOID (LVM_FIRST + 180)
+#define LVGF_HEADER 0x00000001
+#define LVGF_GROUPID 0x00000010
+#define ListView_MapIndexToID(hwnd, index) \
+ (UINT)SendMessage((hwnd), LVM_MAPINDEXTOID, (WPARAM)index, (LPARAM)0)
+#define TreeView_GetLineColor(hwnd) \
+ (COLORREF)SendMessage((hwnd), TVM_GETLINECOLOR, 0, 0)
+#define TreeView_SetLineColor(hwnd, clr) \
+ (COLORREF)SendMessage((hwnd), TVM_SETLINECOLOR, 0, (LPARAM)(clr))
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Callbacks
+
+struct CCallbackImp
+{
+ struct CDummy
+ { int foo;
+ };
+
+public:
+ __inline CCallbackImp(): m_object(NULL), m_func(NULL) {}
+
+ __inline CCallbackImp(const CCallbackImp &other): m_object(other.m_object), m_func(other.m_func) {}
+ __inline CCallbackImp &operator=(const CCallbackImp &other) { m_object = other.m_object; m_func = other.m_func; return *this; }
+
+ __inline bool operator==(const CCallbackImp &other) const { return (m_object == other.m_object) && (m_func == other.m_func); }
+ __inline bool operator!=(const CCallbackImp &other) const { return (m_object != other.m_object) || (m_func != other.m_func); }
+
+ __inline operator bool() const { return m_object && m_func; }
+
+ __inline bool CheckObject(void *object) const { return (object == m_object) ? true : false; }
+
+protected:
+ template<typename TClass, typename TArgument>
+ __inline CCallbackImp(TClass *object, void ( TClass::*func)(TArgument *argument)): m_object(( CDummy* )object), m_func((TFnCallback)func) {}
+
+ __inline void Invoke(void *argument) const { if (m_func && m_object) (m_object->*m_func)(argument); }
+
+private:
+ typedef void ( CDummy::*TFnCallback)( void *argument );
+
+ CDummy* m_object;
+ TFnCallback m_func;
+};
+
+template<typename TArgument>
+struct CCallback: public CCallbackImp
+{
+ typedef CCallbackImp CSuper;
+
+public:
+ __inline CCallback() {}
+
+ template<typename TClass>
+ __inline CCallback(TClass *object, void ( TClass::*func)(TArgument *argument)): CCallbackImp(object, func) {}
+
+ __inline CCallback& operator=( const CCallbackImp& x ) { CSuper::operator =( x ); return *this; }
+
+ __inline void operator()(TArgument *argument) const { Invoke((void *)argument); }
+};
+
+template<typename TClass, typename TArgument>
+__inline CCallback<TArgument> Callback(TClass *object, void (TClass::*func)(TArgument *argument))
+ { return CCallback<TArgument>(object, func); }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDbLink
+
+class CDataLink
+{
+protected:
+ BYTE m_type;
+ bool m_bSigned;
+
+public:
+ CDataLink(BYTE type, bool bSigned): m_type(type), m_bSigned(bSigned) {}
+ virtual ~CDataLink() {}
+
+ __inline BYTE GetDataType() { return m_type; }
+ __inline BYTE GetDataSigned() { return m_bSigned; }
+
+ virtual DWORD LoadUnsigned() = 0;
+ virtual int LoadSigned() = 0;
+ virtual void SaveInt(DWORD value) = 0;
+
+ virtual TCHAR *LoadText() = 0;
+ virtual void SaveText(TCHAR *value) = 0;
+};
+
+class CDbLink: public CDataLink
+{
+ char *m_szModule;
+ char *m_szSetting;
+ bool m_bSigned;
+
+ DWORD m_iDefault;
+ TCHAR *m_szDefault;
+
+ DBVARIANT dbv;
+
+public:
+ CDbLink(const char *szModule, const char *szSetting, BYTE type, DWORD iValue, bool bSigned = false);
+ CDbLink(const char *szModule, const char *szSetting, BYTE type, TCHAR *szValue);
+ ~CDbLink();
+
+ DWORD LoadUnsigned();
+ int LoadSigned();
+ void SaveInt(DWORD value);
+
+ TCHAR *LoadText();
+ void SaveText(TCHAR *value);
+};
+
+template<class T>
+class CMOptionLink: public CDataLink
+{
+private:
+ CMOption<T> *m_option;
+
+public:
+ CMOptionLink(CMOption<T> &option): CDataLink(CMDBTraits<sizeof(T)>::DBTypeId, CMIntTraits<T>::IsSigned()), m_option(&option) {}
+
+ DWORD LoadUnsigned() { return (DWORD)(T)*m_option; }
+ int LoadSigned() { return (int)(T)*m_option; }
+ void SaveInt(DWORD value) { *m_option = (T)value; }
+
+ TCHAR *LoadText() { return NULL; }
+ void SaveText(TCHAR*) {}
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDlgBase - base dialog class
+
+class CDlgBase
+{
+ friend class CCtrlBase;
+ friend class CCtrlData;
+
+public:
+ CDlgBase(int idDialog, HWND hwndParent);
+ virtual ~CDlgBase();
+
+ // general utilities
+ void Create();
+ void Show(int nCmdShow = SW_SHOW);
+ int DoModal();
+
+ __inline HWND GetHwnd() const { return m_hwnd; }
+ __inline bool IsInitialized() const { return m_initialized; }
+ __inline void Close() { SendMessage(m_hwnd, WM_CLOSE, 0, 0); }
+ __inline const MSG *ActiveMessage() const { return &m_msg; }
+
+ // dynamic creation support (mainly to avoid leaks in options)
+ struct CreateParam
+ {
+ CDlgBase *(*create)(void *param);
+ void *param;
+ };
+ static INT_PTR CALLBACK DynamicDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ if (msg == WM_INITDIALOG)
+ {
+ CreateParam *param = (CreateParam *)lParam;
+ CDlgBase *wnd = param->create(param->param);
+ SetWindowLongPtr(hwnd, DWLP_DLGPROC, (LONG_PTR)GlobalDlgProc);
+ return GlobalDlgProc(hwnd, msg, wParam, (LPARAM)wnd);
+ }
+
+ return FALSE;
+ }
+
+ LRESULT m_lresult;
+
+protected:
+ HWND m_hwnd;
+ HWND m_hwndParent;
+ int m_idDialog;
+ MSG m_msg;
+ bool m_isModal;
+ bool m_initialized;
+ bool m_forceResizable;
+
+ enum { CLOSE_ON_OK = 0x1, CLOSE_ON_CANCEL = 0x2 };
+ BYTE m_autoClose; // automatically close dialog on IDOK/CANCEL commands. default: CLOSE_ON_OK|CLOSE_ON_CANCEL
+
+ CCtrlBase* m_first;
+
+ // override this handlers to provide custom functionality
+ // general messages
+ virtual void OnInitDialog() { }
+ virtual void OnClose() { }
+ virtual void OnDestroy() { }
+
+ // miranda-related stuff
+ virtual int Resizer(UTILRESIZECONTROL *urc);
+ virtual void OnApply() {}
+ virtual void OnReset() {}
+ virtual void OnChange(CCtrlBase*) {}
+
+ // main dialog procedure
+ virtual INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ // resister controls
+ void AddControl(CCtrlBase *ctrl);
+
+ // win32 stuff
+ void ThemeDialogBackground(BOOL tabbed);
+
+private:
+ LIST<CCtrlBase> m_controls;
+
+ void NotifyControls(void (CCtrlBase::*fn)());
+ CCtrlBase *FindControl(int idCtrl);
+
+ static INT_PTR CALLBACK GlobalDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ static int GlobalDlgResizer(HWND hwnd, LPARAM lParam, UTILRESIZECONTROL *urc);
+
+ typedef HRESULT (STDAPICALLTYPE *pfnEnableThemeDialogTexture)(HWND,DWORD);
+ static pfnEnableThemeDialogTexture MyEnableThemeDialogTexture;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlBase
+
+class CCtrlBase
+{
+ friend class CDlgBase;
+
+public:
+ CCtrlBase(CDlgBase *wnd, int idCtrl );
+ virtual ~CCtrlBase() { Unsubclass(); }
+
+ __inline HWND GetHwnd() const { return m_hwnd; }
+ __inline CDlgBase *GetParent() { return m_parentWnd; }
+
+ void Enable( int bIsEnable = true );
+ __inline void Disable() { Enable( false ); }
+ BOOL Enabled( void ) const;
+
+ LRESULT SendMsg( UINT Msg, WPARAM wParam, LPARAM lParam );
+
+ void SetText(const TCHAR *text);
+ void SetTextA(const char *text);
+ void SetInt(int value);
+
+ TCHAR *GetText();
+ char *GetTextA();
+
+ TCHAR *GetText(TCHAR *buf, int size);
+ char *GetTextA(char *buf, int size);
+
+ int GetInt();
+
+ virtual BOOL OnCommand(HWND /*hwndCtrl*/, WORD /*idCtrl*/, WORD /*idCode*/) { return FALSE; }
+ virtual BOOL OnNotify(int /*idCtrl*/, NMHDR* /*pnmh*/) { return FALSE; }
+
+ virtual BOOL OnMeasureItem(MEASUREITEMSTRUCT*) { return FALSE; }
+ virtual BOOL OnDrawItem(DRAWITEMSTRUCT*) { return FALSE; }
+ virtual BOOL OnDeleteItem(DELETEITEMSTRUCT*) { return FALSE; }
+
+ virtual void OnInit();
+ virtual void OnDestroy();
+
+ virtual void OnApply() {}
+ virtual void OnReset() {}
+
+ static int cmp(const CCtrlBase *c1, const CCtrlBase *c2)
+ {
+ if (c1->m_idCtrl < c2->m_idCtrl) return -1;
+ if (c1->m_idCtrl > c2->m_idCtrl) return +1;
+ return 0;
+ }
+
+protected:
+ HWND m_hwnd;
+ int m_idCtrl;
+ CCtrlBase* m_next;
+ CDlgBase* m_parentWnd;
+
+ virtual LRESULT CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam);
+ void Subclass();
+ void Unsubclass();
+
+private:
+ WNDPROC m_wndproc;
+ static LRESULT CALLBACK GlobalSubclassWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ if (CCtrlBase *ctrl = (CCtrlBase*)GetWindowLongPtr(hwnd, GWLP_USERDATA))
+ if (ctrl)
+ return ctrl->CustomWndProc(msg, wParam, lParam);
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlButton
+
+class CCtrlButton : public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+public:
+ CCtrlButton( CDlgBase* dlg, int ctrlId );
+
+ virtual BOOL OnCommand(HWND hwndCtrl, WORD idCtrl, WORD idCode);
+
+ CCallback<CCtrlButton> OnClick;
+};
+
+class CCtrlMButton : public CCtrlButton
+{
+ typedef CCtrlButton CSuper;
+
+public:
+ CCtrlMButton( CDlgBase* dlg, int ctrlId, HICON hIcon, const char* tooltip );
+ CCtrlMButton( CDlgBase* dlg, int ctrlId, int iCoreIcon, const char* tooltip );
+ ~CCtrlMButton();
+
+ void MakeFlat();
+ void MakePush();
+
+ virtual void OnInit();
+
+protected:
+ char m_flags;
+ HICON m_hIcon;
+ const char* m_toolTip;
+};
+
+class CCtrlHyperlink : public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+public:
+ CCtrlHyperlink( CDlgBase* dlg, int ctrlId, const char* url );
+
+ virtual BOOL OnCommand(HWND hwndCtrl, WORD idCtrl, WORD idCode);
+
+protected:
+ const char* m_url;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlClc
+class CCtrlClc: public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+public:
+ CCtrlClc( CDlgBase* dlg, int ctrlId );
+
+ void AddContact(HANDLE hContact);
+ void AddGroup(HANDLE hGroup);
+ void AutoRebuild();
+ void DeleteItem(HANDLE hItem);
+ void EditLabel(HANDLE hItem);
+ void EndEditLabel(bool save);
+ void EnsureVisible(HANDLE hItem, bool partialOk);
+ void Expand(HANDLE hItem, DWORD flags);
+ HANDLE FindContact(HANDLE hContact);
+ HANDLE FindGroup(HANDLE hGroup);
+ COLORREF GetBkColor();
+ bool GetCheck(HANDLE hItem);
+ int GetCount();
+ HWND GetEditControl();
+ DWORD GetExpand(HANDLE hItem);
+ int GetExtraColumns();
+ BYTE GetExtraImage(HANDLE hItem, int iColumn);
+ HIMAGELIST GetExtraImageList();
+ HFONT GetFont(int iFontId);
+ HANDLE GetSelection();
+ HANDLE HitTest(int x, int y, DWORD *hitTest);
+ void SelectItem(HANDLE hItem);
+ void SetBkBitmap(DWORD mode, HBITMAP hBitmap);
+ void SetBkColor(COLORREF clBack);
+ void SetCheck(HANDLE hItem, bool check);
+ void SetExtraColumns(int iColumns);
+ void SetExtraImage(HANDLE hItem, int iColumn, int iImage);
+ void SetExtraImageList(HIMAGELIST hImgList);
+ void SetFont(int iFontId, HANDLE hFont, bool bRedraw);
+ void SetIndent(int iIndent);
+ void SetItemText(HANDLE hItem, char *szText);
+ void SetHideEmptyGroups(bool state);
+ void SetGreyoutFlags(DWORD flags);
+ bool GetHideOfflineRoot();
+ void SetHideOfflineRoot(bool state);
+ void SetUseGroups(bool state);
+ void SetOfflineModes(DWORD modes);
+ DWORD GetExStyle();
+ void SetExStyle(DWORD exStyle);
+ int GetLefrMargin();
+ void SetLeftMargin(int iMargin);
+ HANDLE AddInfoItem(CLCINFOITEM *cii);
+ int GetItemType(HANDLE hItem);
+ HANDLE GetNextItem(HANDLE hItem, DWORD flags);
+ COLORREF GetTextColot(int iFontId);
+ void SetTextColor(int iFontId, COLORREF clText);
+
+ struct TEventInfo
+ {
+ CCtrlClc *ctrl;
+ NMCLISTCONTROL *info;
+ };
+
+ CCallback<TEventInfo> OnExpanded;
+ CCallback<TEventInfo> OnListRebuilt;
+ CCallback<TEventInfo> OnItemChecked;
+ CCallback<TEventInfo> OnDragging;
+ CCallback<TEventInfo> OnDropped;
+ CCallback<TEventInfo> OnListSizeChange;
+ CCallback<TEventInfo> OnOptionsChanged;
+ CCallback<TEventInfo> OnDragStop;
+ CCallback<TEventInfo> OnNewContact;
+ CCallback<TEventInfo> OnContactMoved;
+ CCallback<TEventInfo> OnCheckChanged;
+ CCallback<TEventInfo> OnClick;
+
+protected:
+ BOOL OnNotify(int idCtrl, NMHDR *pnmh);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlData - data access controls base class
+
+class CCtrlData : public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+public:
+ CCtrlData( CDlgBase* dlg, int ctrlId );
+
+ virtual ~CCtrlData()
+ {
+ if (m_dbLink) delete m_dbLink;
+ }
+
+ __inline bool IsChanged() const { return m_changed; }
+
+ void CreateDbLink( const char* szModuleName, const char* szSetting, BYTE type, DWORD iValue, bool bSigned = false );
+ void CreateDbLink( const char* szModuleName, const char* szSetting, TCHAR* szValue );
+ void CreateDbLink( CDataLink *link ) { m_dbLink = link; }
+
+ virtual void OnInit();
+
+ // Events
+ CCallback<CCtrlData> OnChange;
+
+protected:
+ CDataLink *m_dbLink;
+ bool m_changed;
+
+ void NotifyChange();
+
+ __inline BYTE GetDataType() { return m_dbLink ? m_dbLink->GetDataType() : DBVT_DELETED; }
+ __inline bool GetDataSigned() { return m_dbLink ? m_dbLink->GetDataSigned() ? true : false : false; }
+ __inline DWORD LoadUnsigned() { return m_dbLink ? m_dbLink->LoadUnsigned() : 0; }
+ __inline int LoadSigned() { return m_dbLink ? m_dbLink->LoadSigned() : 0; }
+ __inline void SaveInt(DWORD value) { if (m_dbLink) m_dbLink->SaveInt(value); }
+ __inline const TCHAR *LoadText() { return m_dbLink ? m_dbLink->LoadText() : _T(""); }
+ __inline void SaveText(TCHAR *value) { if (m_dbLink) m_dbLink->SaveText(value); }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCheck
+
+class CCtrlCheck : public CCtrlData
+{
+ typedef CCtrlData CSuper;
+
+public:
+ CCtrlCheck( CDlgBase* dlg, int ctrlId );
+ virtual BOOL OnCommand(HWND /*hwndCtrl*/, WORD /*idCtrl*/, WORD /*idCode*/) { NotifyChange(); return TRUE; }
+ virtual void OnInit()
+ {
+ CSuper::OnInit();
+ OnReset();
+ }
+ virtual void OnApply()
+ {
+ SaveInt(GetState());
+ }
+ virtual void OnReset()
+ {
+ SetState(LoadUnsigned());
+ }
+
+ int GetState();
+ void SetState(int state);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlEdit
+
+class CCtrlEdit : public CCtrlData
+{
+ typedef CCtrlData CSuper;
+
+public:
+ CCtrlEdit( CDlgBase* dlg, int ctrlId );
+ virtual BOOL OnCommand(HWND /*hwndCtrl*/, WORD /*idCtrl*/, WORD idCode)
+ {
+ if (idCode == EN_CHANGE)
+ NotifyChange();
+ return TRUE;
+ }
+ virtual void OnInit()
+ {
+ CSuper::OnInit();
+ OnReset();
+ }
+ virtual void OnApply()
+ {
+ if (GetDataType() == DBVT_TCHAR)
+ {
+ int len = GetWindowTextLength(m_hwnd) + 1;
+ TCHAR *buf = (TCHAR *)_alloca(sizeof(TCHAR) * len);
+ GetWindowText(m_hwnd, buf, len);
+ SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED)
+ {
+ SaveInt(GetInt());
+ }
+ }
+ virtual void OnReset()
+ {
+ if (GetDataType() == DBVT_TCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(GetDataSigned() ? LoadSigned() : LoadUnsigned());
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListBox
+
+class CCtrlListBox : public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+public:
+ CCtrlListBox( CDlgBase* dlg, int ctrlId );
+
+ int AddString(TCHAR *text, LPARAM data=0);
+ void DeleteString(int index);
+ int FindString(TCHAR *str, int index = -1, bool exact = false);
+ int GetCount();
+ int GetCurSel();
+ LPARAM GetItemData(int index);
+ TCHAR* GetItemText(int index);
+ TCHAR* GetItemText(int index, TCHAR *buf, int size);
+ bool GetSel(int index);
+ int GetSelCount();
+ int* GetSelItems(int *items, int count);
+ int* GetSelItems();
+ int InsertString(TCHAR *text, int pos, LPARAM data=0);
+ void ResetContent();
+ int SelectString(TCHAR *str);
+ int SetCurSel(int index);
+ void SetItemData(int index, LPARAM data);
+ void SetSel(int index, bool sel=true);
+
+ // Events
+ CCallback<CCtrlListBox> OnDblClick;
+ CCallback<CCtrlListBox> OnSelCancel;
+ CCallback<CCtrlListBox> OnSelChange;
+
+protected:
+ BOOL OnCommand(HWND hwndCtrl, WORD idCtrl, WORD idCode);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCombo
+
+class CCtrlCombo : public CCtrlData
+{
+ typedef CCtrlData CSuper;
+
+public:
+ CCtrlCombo( CDlgBase* dlg, int ctrlId );
+
+ virtual BOOL OnCommand(HWND /*hwndCtrl*/, WORD /*idCtrl*/, WORD idCode)
+ {
+ switch (idCode)
+ {
+ case CBN_CLOSEUP: OnCloseup(this); break;
+ case CBN_DROPDOWN: OnDropdown(this); break;
+
+ case CBN_EDITCHANGE:
+ case CBN_EDITUPDATE:
+ case CBN_SELCHANGE:
+ case CBN_SELENDOK:
+ NotifyChange();
+ break;
+ }
+ return TRUE;
+ }
+
+ virtual void OnInit()
+ {
+ CSuper::OnInit();
+ OnReset();
+ }
+ virtual void OnApply()
+ {
+ if (GetDataType() == DBVT_TCHAR)
+ {
+ int len = GetWindowTextLength(m_hwnd) + 1;
+ TCHAR *buf = (TCHAR *)_alloca(sizeof(TCHAR) * len);
+ GetWindowText(m_hwnd, buf, len);
+ SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED)
+ {
+ SaveInt(GetInt());
+ }
+ }
+ virtual void OnReset()
+ {
+ if (GetDataType() == DBVT_TCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(LoadUnsigned());
+ }
+
+ // Control interface
+ int AddString(const TCHAR *text, LPARAM data = 0 );
+ int AddStringA(const char *text, LPARAM data = 0 );
+ void DeleteString(int index);
+ int FindString(const TCHAR *str, int index = -1, bool exact = false);
+ int FindStringA(const char *str, int index = -1, bool exact = false);
+ int GetCount();
+ int GetCurSel();
+ bool GetDroppedState();
+ LPARAM GetItemData(int index);
+ TCHAR* GetItemText(int index);
+ TCHAR* GetItemText(int index, TCHAR *buf, int size);
+ int InsertString(TCHAR *text, int pos, LPARAM data=0);
+ void ResetContent();
+ int SelectString(TCHAR *str);
+ int SetCurSel(int index);
+ void SetItemData(int index, LPARAM data);
+ void ShowDropdown(bool show = true);
+
+ // Events
+ CCallback<CCtrlCombo> OnCloseup;
+ CCallback<CCtrlCombo> OnDropdown;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListView
+
+class CCtrlListView : public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+public:
+ CCtrlListView( CDlgBase* dlg, int ctrlId );
+
+ // Classic LV interface
+ DWORD ApproximateViewRect(int cx, int cy, int iCount);
+ void Arrange(UINT code);
+ void CancelEditLabel();
+ HIMAGELIST CreateDragImage(int iItem, LPPOINT lpptUpLeft);
+ void DeleteAllItems();
+ void DeleteColumn(int iCol);
+ void DeleteItem(int iItem);
+ HWND EditLabel(int iItem);
+ int EnableGroupView(BOOL fEnable);
+ BOOL EnsureVisible(int i, BOOL fPartialOK);
+ int FindItem(int iStart, const LVFINDINFO *plvfi);
+ COLORREF GetBkColor();
+ void GetBkImage(LPLVBKIMAGE plvbki);
+ UINT GetCallbackMask();
+ BOOL GetCheckState(UINT iIndex);
+ void GetColumn(int iCol, LPLVCOLUMN pcol);
+ void GetColumnOrderArray(int iCount, int *lpiArray);
+ int GetColumnWidth(int iCol);
+ int GetCountPerPage();
+ HWND GetEditControl();
+ //void GetEmptyText(PWSTR pszText, UINT cchText);
+ DWORD GetExtendedListViewStyle();
+ INT GetFocusedGroup();
+ //void GetFooterInfo(LVFOOTERINFO *plvfi);
+ //void GetFooterItem(UINT iItem, LVFOOTERITEM *pfi);
+ //void GetFooterItemRect(UINT iItem, RECT *prc);
+ //void GetFooterRect(RECT *prc);
+ int GetGroupCount();
+ //HIMAGELIST GetGroupHeaderImageList();
+ void GetGroupInfo(int iGroupId, PLVGROUP pgrp);
+ void GetGroupInfoByIndex(int iIndex, PLVGROUP pgrp);
+ void GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics);
+ //BOOL GetGroupRect(int iGroupId, RECT *prc);
+ UINT GetGroupState(UINT dwGroupId, UINT dwMask);
+ HWND GetHeader();
+ HCURSOR GetHotCursor();
+ INT GetHotItem();
+ DWORD GetHoverTime();
+ HIMAGELIST GetImageList(int iImageList);
+ BOOL GetInsertMark(LVINSERTMARK *plvim);
+ COLORREF GetInsertMarkColor();
+ int GetInsertMarkRect(LPRECT prc);
+ BOOL GetISearchString(LPSTR lpsz);
+ void GetItem(LPLVITEM pitem);
+ int GetItemCount();
+ //void GetItemIndexRect(LVITEMINDEX *plvii, LONG iSubItem, LONG code, LPRECT prc);
+ void GetItemPosition(int i, POINT *ppt);
+ void GetItemRect(int i, RECT *prc, int code);
+ DWORD GetItemSpacing(BOOL fSmall);
+ UINT GetItemState(int i, UINT mask);
+ void GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax);
+ int GetNextItem(int iStart, UINT flags);
+ //BOOL GetNextItemIndex(LVITEMINDEX *plvii, LPARAM flags);
+ BOOL GetNumberOfWorkAreas(LPUINT lpuWorkAreas);
+ BOOL GetOrigin(LPPOINT lpptOrg);
+ COLORREF GetOutlineColor();
+ UINT GetSelectedColumn();
+ UINT GetSelectedCount();
+ INT GetSelectionMark();
+ int GetStringWidth(LPCSTR psz);
+ BOOL GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect);
+ COLORREF GetTextBkColor();
+ COLORREF GetTextColor();
+ void GetTileInfo(PLVTILEINFO plvtinfo);
+ void GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo);
+ HWND GetToolTips();
+ int GetTopIndex();
+ BOOL GetUnicodeFormat();
+ DWORD GetView();
+ BOOL GetViewRect(RECT *prc);
+ void GetWorkAreas(INT nWorkAreas, LPRECT lprc);
+ BOOL HasGroup(int dwGroupId);
+ int HitTest(LPLVHITTESTINFO pinfo);
+ int HitTestEx(LPLVHITTESTINFO pinfo);
+ int InsertColumn(int iCol, const LPLVCOLUMN pcol);
+ int InsertGroup(int index, PLVGROUP pgrp);
+ void InsertGroupSorted(PLVINSERTGROUPSORTED structInsert);
+ int InsertItem(const LPLVITEM pitem);
+ BOOL InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim);
+ BOOL IsGroupViewEnabled();
+ UINT IsItemVisible(UINT index);
+ UINT MapIDToIndex(UINT id);
+ UINT MapIndexToID(UINT index);
+ BOOL RedrawItems(int iFirst, int iLast);
+ void RemoveAllGroups();
+ int RemoveGroup(int iGroupId);
+ BOOL Scroll(int dx, int dy);
+ BOOL SetBkColor(COLORREF clrBk);
+ BOOL SetBkImage(LPLVBKIMAGE plvbki);
+ BOOL SetCallbackMask(UINT mask);
+ void SetCheckState(UINT iIndex, BOOL fCheck);
+ BOOL SetColumn(int iCol, LPLVCOLUMN pcol);
+ BOOL SetColumnOrderArray(int iCount, int *lpiArray);
+ BOOL SetColumnWidth(int iCol, int cx);
+ void SetExtendedListViewStyle(DWORD dwExStyle);
+ void SetExtendedListViewStyleEx(DWORD dwExMask, DWORD dwExStyle);
+ //HIMAGELIST SetGroupHeaderImageList(HIMAGELIST himl);
+ int SetGroupInfo(int iGroupId, PLVGROUP pgrp);
+ void SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics);
+ void SetGroupState(UINT dwGroupId, UINT dwMask, UINT dwState);
+ HCURSOR SetHotCursor(HCURSOR hCursor);
+ INT SetHotItem(INT iIndex);
+ void SetHoverTime(DWORD dwHoverTime);
+ DWORD SetIconSpacing(int cx, int cy);
+ HIMAGELIST SetImageList(HIMAGELIST himl, int iImageList);
+ BOOL SetInfoTip(PLVSETINFOTIP plvSetInfoTip);
+ BOOL SetInsertMark(LVINSERTMARK *plvim);
+ COLORREF SetInsertMarkColor(COLORREF color);
+ BOOL SetItem(const LPLVITEM pitem);
+ void SetItemCount(int cItems);
+ void SetItemCountEx(int cItems, DWORD dwFlags);
+ //HRESULT SetItemIndexState(LVITEMINDEX *plvii, UINT data, UINT mask);
+ BOOL SetItemPosition(int i, int x, int y);
+ void SetItemPosition32(int iItem, int x, int y);
+ void SetItemState(int i, UINT state, UINT mask);
+ void SetItemText(int i, int iSubItem, TCHAR *pszText);
+ COLORREF SetOutlineColor(COLORREF color);
+ void SetSelectedColumn(int iCol);
+ INT SetSelectionMark(INT iIndex);
+ BOOL SetTextBkColor(COLORREF clrText);
+ BOOL SetTextColor(COLORREF clrText);
+ BOOL SetTileInfo(PLVTILEINFO plvtinfo);
+ BOOL SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo);
+ HWND SetToolTips(HWND ToolTip);
+ BOOL SetUnicodeFormat(BOOL fUnicode);
+ int SetView(DWORD iView);
+ void SetWorkAreas(INT nWorkAreas, LPRECT lprc);
+ int SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv);
+ BOOL SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
+ BOOL SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
+ INT SubItemHitTest(LPLVHITTESTINFO pInfo);
+ INT SubItemHitTestEx(LPLVHITTESTINFO plvhti);
+ BOOL Update(int iItem);
+
+ // Additional APIs
+ HIMAGELIST CreateImageList(int iImageList);
+ void AddColumn(int iSubItem, TCHAR *name, int cx);
+ void AddGroup(int iGroupId, TCHAR *name);
+ int AddItem(TCHAR *text, int iIcon, LPARAM lParam = 0, int iGroupId = -1);
+ void SetItem(int iItem, int iSubItem, TCHAR *text, int iIcon = -1);
+ LPARAM GetItemData(int iItem);
+
+ // Events
+ struct TEventInfo {
+ CCtrlListView *treeviewctrl;
+ union {
+ NMHDR *nmhdr;
+ NMLISTVIEW *nmlv;
+ NMLVDISPINFO *nmlvdi;
+ NMLVSCROLL *nmlvscr;
+ NMLVGETINFOTIP *nmlvit;
+ NMLVFINDITEM *nmlvfi;
+ NMITEMACTIVATE *nmlvia;
+ NMLVKEYDOWN *nmlvkey;
+ };
+ };
+
+ CCallback<TEventInfo> OnBeginDrag;
+ CCallback<TEventInfo> OnBeginLabelEdit;
+ CCallback<TEventInfo> OnBeginRDrag;
+ CCallback<TEventInfo> OnBeginScroll;
+ CCallback<TEventInfo> OnColumnClick;
+ //CCallback<TEventInfo> OnColumnDropdown;
+ //CCallback<TEventInfo> OnColumnOverflowClick;
+ CCallback<TEventInfo> OnDeleteAllItems;
+ CCallback<TEventInfo> OnDeleteItem;
+ CCallback<TEventInfo> OnDoubleClick;
+ CCallback<TEventInfo> OnEndLabelEdit;
+ CCallback<TEventInfo> OnEndScroll;
+ CCallback<TEventInfo> OnGetDispInfo;
+ //CCallback<TEventInfo> OnGetEmptyMarkup;
+ CCallback<TEventInfo> OnGetInfoTip;
+ CCallback<TEventInfo> OnHotTrack;
+ CCallback<TEventInfo> OnIncrementalSearch;
+ CCallback<TEventInfo> OnInsertItem;
+ CCallback<TEventInfo> OnItemActivate;
+ CCallback<TEventInfo> OnItemChanged;
+ CCallback<TEventInfo> OnItemChanging;
+ CCallback<TEventInfo> OnKeyDown;
+ //CCallback<TEventInfo> OnLinkClick;
+ CCallback<TEventInfo> OnMarqueeBegin;
+ CCallback<TEventInfo> OnSetDispInfo;
+
+protected:
+ BOOL OnNotify(int idCtrl, NMHDR *pnmh);
+};
+
+struct CFilterData;
+class CCtrlFilterListView : public CCtrlListView
+{
+ typedef CCtrlListView CSuper;
+
+public:
+ CCtrlFilterListView(CDlgBase* dlg, int ctrlId, bool trackFilter, bool keepHiglight);
+ ~CCtrlFilterListView();
+
+ TCHAR *GetFilterText();
+ CCallback<CCtrlFilterListView> OnFilterChanged;
+
+protected:
+ CFilterData *fdat;
+ bool m_trackFilter;
+ bool m_keepHiglight;
+
+ void OnInit();
+ LRESULT CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam);
+ void FilterHighlight(TCHAR *filter);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlTreeView
+
+class CCtrlTreeView : public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+public:
+ CCtrlTreeView( CDlgBase* dlg, int ctrlId );
+
+ // Classic TV interface
+ HIMAGELIST CreateDragImage(HTREEITEM hItem);
+ void DeleteAllItems();
+ void DeleteItem(HTREEITEM hItem);
+ HWND EditLabel(HTREEITEM hItem);
+ void EndEditLabelNow(BOOL cancel);
+ void EnsureVisible(HTREEITEM hItem);
+ void Expand(HTREEITEM hItem, DWORD flag);
+ COLORREF GetBkColor();
+ DWORD GetCheckState(HTREEITEM hItem);
+ HTREEITEM GetChild(HTREEITEM hItem);
+ int GetCount();
+ HTREEITEM GetDropHilight();
+ HWND GetEditControl();
+ HTREEITEM GetFirstVisible();
+ HIMAGELIST GetImageList(int iImage);
+ int GetIndent();
+ COLORREF GetInsertMarkColor();
+ void GetItem(TVITEMEX *tvi);
+ int GetItemHeight();
+ void GetItemRect(HTREEITEM hItem, RECT *rcItem, BOOL fItemRect);
+ DWORD GetItemState(HTREEITEM hItem, DWORD stateMask);
+ HTREEITEM GetLastVisible();
+ COLORREF GetLineColor();
+ HTREEITEM GetNextItem(HTREEITEM hItem, DWORD flag);
+ HTREEITEM GetNextSibling(HTREEITEM hItem);
+ HTREEITEM GetNextVisible(HTREEITEM hItem);
+ HTREEITEM GetParent(HTREEITEM hItem);
+ HTREEITEM GetPrevSibling(HTREEITEM hItem);
+ HTREEITEM GetPrevVisible(HTREEITEM hItem);
+ HTREEITEM GetRoot();
+ DWORD GetScrollTime();
+ HTREEITEM GetSelection();
+ COLORREF GetTextColor();
+ HWND GetToolTips();
+ BOOL GetUnicodeFormat();
+ unsigned GetVisibleCount();
+ HTREEITEM HitTest(TVHITTESTINFO *hti);
+ HTREEITEM InsertItem(TVINSERTSTRUCT *tvis);
+ //HTREEITEM MapAccIDToHTREEITEM(UINT id);
+ //UINT MapHTREEITEMtoAccID(HTREEITEM hItem);
+ void Select(HTREEITEM hItem, DWORD flag);
+ void SelectDropTarget(HTREEITEM hItem);
+ void SelectItem(HTREEITEM hItem);
+ void SelectSetFirstVisible(HTREEITEM hItem);
+ COLORREF SetBkColor(COLORREF clBack);
+ void SetCheckState(HTREEITEM hItem, DWORD state);
+ void SetImageList(HIMAGELIST hIml, int iImage);
+ void SetIndent(int iIndent);
+ void SetInsertMark(HTREEITEM hItem, BOOL fAfter);
+ COLORREF SetInsertMarkColor(COLORREF clMark);
+ void SetItem(TVITEMEX *tvi);
+ void SetItemHeight(short cyItem);
+ void SetItemState(HTREEITEM hItem, DWORD state, DWORD stateMask);
+ COLORREF SetLineColor(COLORREF clLine);
+ void SetScrollTime(UINT uMaxScrollTime);
+ COLORREF SetTextColor(COLORREF clText);
+ HWND SetToolTips(HWND hwndToolTips);
+ BOOL SetUnicodeFormat(BOOL fUnicode);
+ void SortChildren(HTREEITEM hItem, BOOL fRecurse);
+ void SortChildrenCB(TVSORTCB *cb, BOOL fRecurse);
+
+ // Additional stuff
+ void TranslateItem(HTREEITEM hItem);
+ void TranslateTree();
+ HTREEITEM FindNamedItem(HTREEITEM hItem, const TCHAR *name);
+ void GetItem(HTREEITEM hItem, TVITEMEX *tvi);
+ void GetItem(HTREEITEM hItem, TVITEMEX *tvi, TCHAR *szText, int iTextLength);
+
+ // Events
+ struct TEventInfo {
+ CCtrlTreeView *treeviewctrl;
+ union {
+ NMHDR *nmhdr;
+ NMTREEVIEW *nmtv;
+ NMTVDISPINFO *nmtvdi;
+ NMTVGETINFOTIP *nmtvit;
+ NMTVKEYDOWN *nmtvkey;
+ };
+ };
+
+ CCallback<TEventInfo> OnBeginDrag;
+ CCallback<TEventInfo> OnBeginLabelEdit;
+ CCallback<TEventInfo> OnBeginRDrag;
+ CCallback<TEventInfo> OnDeleteItem;
+ CCallback<TEventInfo> OnEndLabelEdit;
+ CCallback<TEventInfo> OnGetDispInfo;
+ CCallback<TEventInfo> OnGetInfoTip;
+ CCallback<TEventInfo> OnItemExpanded;
+ CCallback<TEventInfo> OnItemExpanding;
+ CCallback<TEventInfo> OnKeyDown;
+ CCallback<TEventInfo> OnSelChanged;
+ CCallback<TEventInfo> OnSelChanging;
+ CCallback<TEventInfo> OnSetDispInfo;
+ CCallback<TEventInfo> OnSingleExpand;
+
+protected:
+ BOOL OnNotify(int idCtrl, NMHDR *pnmh);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlTreeView
+
+class CCtrlPages: public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+public:
+ CCtrlPages( CDlgBase* dlg, int ctrlId );
+
+ void AddPage( TCHAR *ptszName, HICON hIcon, CCallback<void> onCreate = CCallback<void>(), void *param = NULL );
+ void AttachDialog( int iPage, CDlgBase *pDlg );
+
+ void ActivatePage( int iPage );
+
+
+protected:
+ BOOL OnNotify(int idCtrl, NMHDR *pnmh);
+ void OnInit();
+ void OnDestroy();
+
+ virtual LRESULT CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam);
+
+private:
+ HIMAGELIST m_hIml;
+ CDlgBase *m_pActivePage;
+
+ struct TPageInfo
+ {
+ CCallback<void> m_onCreate;
+ void *m_param;
+ CDlgBase *m_pDlg;
+ };
+
+ void ShowPage(CDlgBase *pDlg);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCustom
+
+template<typename TDlg>
+class CCtrlCustom : public CCtrlBase
+{
+ typedef CCtrlBase CSuper;
+
+private:
+ void (TDlg::*m_pfnOnCommand)(HWND hwndCtrl, WORD idCtrl, WORD idCode);
+ void (TDlg::*m_pfnOnNotify)(int idCtrl, NMHDR *pnmh);
+ void (TDlg::*m_pfnOnMeasureItem)(MEASUREITEMSTRUCT *param);
+ void (TDlg::*m_pfnOnDrawItem)(DRAWITEMSTRUCT *param);
+ void (TDlg::*m_pfnOnDeleteItem)(DELETEITEMSTRUCT *param);
+
+public:
+ CCtrlCustom(TDlg *wnd, int idCtrl,
+ void (TDlg::*pfnOnCommand)(HWND hwndCtrl, WORD idCtrl, WORD idCode),
+ void (TDlg::*pfnOnNotify)(int idCtrl, NMHDR *pnmh),
+ void (TDlg::*pfnOnMeasureItem)(MEASUREITEMSTRUCT *param) = NULL,
+ void (TDlg::*pfnOnDrawItem)(DRAWITEMSTRUCT *param) = NULL,
+ void (TDlg::*pfnOnDeleteItem)(DELETEITEMSTRUCT *param) = NULL): CCtrlBase(wnd, idCtrl)
+ {
+ m_pfnOnCommand = pfnOnCommand;
+ m_pfnOnNotify = pfnOnNotify;
+ m_pfnOnMeasureItem = pfnOnMeasureItem;
+ m_pfnOnDrawItem = pfnOnDrawItem;
+ m_pfnOnDeleteItem = pfnOnDeleteItem;
+ }
+
+ virtual BOOL OnCommand(HWND hwndCtrl, WORD idCtrl, WORD idCode)
+ {
+ if (m_parentWnd && m_pfnOnCommand) {
+ m_parentWnd->m_lresult = 0;
+ (((TDlg *)m_parentWnd)->*m_pfnOnCommand)(hwndCtrl, idCtrl, idCode);
+ return m_parentWnd->m_lresult;
+ }
+ return FALSE;
+ }
+ virtual BOOL OnNotify(int idCtrl, NMHDR *pnmh)
+ {
+ if (m_parentWnd && m_pfnOnNotify) {
+ m_parentWnd->m_lresult = 0;
+ (((TDlg *)m_parentWnd)->*m_pfnOnNotify)(idCtrl, pnmh);
+ return m_parentWnd->m_lresult;
+ }
+ return FALSE;
+ }
+
+ virtual BOOL OnMeasureItem(MEASUREITEMSTRUCT *param)
+ {
+ if (m_parentWnd && m_pfnOnMeasureItem) {
+ m_parentWnd->m_lresult = 0;
+ (((TDlg *)m_parentWnd)->*m_pfnOnMeasureItem)(param);
+ return m_parentWnd->m_lresult;
+ }
+ return FALSE;
+ }
+ virtual BOOL OnDrawItem(DRAWITEMSTRUCT *param)
+ {
+ if (m_parentWnd && m_pfnOnDrawItem) {
+ m_parentWnd->m_lresult = 0;
+ (((TDlg *)m_parentWnd)->*m_pfnOnDrawItem)(param);
+ return m_parentWnd->m_lresult;
+ }
+ return FALSE;
+ }
+ virtual BOOL OnDeleteItem(DELETEITEMSTRUCT *param)
+ {
+ if (m_parentWnd && m_pfnOnDeleteItem) {
+ m_parentWnd->m_lresult = 0;
+ (((TDlg *)m_parentWnd)->*m_pfnOnDeleteItem)(param);
+ return m_parentWnd->m_lresult;
+ }
+ return FALSE;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CProtoDlgBase
+
+#define WM_PROTO_REFRESH (WM_USER + 100)
+#define WM_PROTO_CHECK_ONLINE (WM_USER + 101)
+#define WM_PROTO_ACTIVATE (WM_USER + 102)
+#define WM_PROTO_LAST (WM_USER + 200)
+
+typedef struct tagPROTO_INTERFACE PROTO_INTERFACE;
+
+class CProtoIntDlgBase : public CDlgBase
+{
+ typedef CDlgBase CSuper;
+
+public:
+ __inline CProtoIntDlgBase(PROTO_INTERFACE *proto, int idDialog, HWND parent, bool show_label=true) :
+ CDlgBase( idDialog, parent ),
+ m_proto_interface( proto ),
+ m_show_label( show_label ),
+ m_hwndStatus( NULL )
+ {
+ }
+
+ __inline void CreateLink( CCtrlData& ctrl, char *szSetting, BYTE type, DWORD iValue, bool bSigned = false )
+ {
+ ctrl.CreateDbLink(m_proto_interface->m_szModuleName, szSetting, type, iValue, bSigned );
+ }
+ __inline void CreateLink( CCtrlData& ctrl, const char *szSetting, TCHAR *szValue )
+ {
+ ctrl.CreateDbLink(m_proto_interface->m_szModuleName, szSetting, szValue);
+ }
+
+ template<class T>
+ __inline void CreateLink( CCtrlData& ctrl, CMOption<T> &option )
+ {
+ ctrl.CreateDbLink(new CMOptionLink<T>(option));
+ }
+
+ __inline PROTO_INTERFACE *GetProtoInterface() { return m_proto_interface; }
+
+ void SetStatusText(TCHAR *statusText);
+
+protected:
+ PROTO_INTERFACE *m_proto_interface;
+ bool m_show_label;
+ HWND m_hwndStatus;
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ virtual void OnProtoRefresh(WPARAM, LPARAM) {}
+ virtual void OnProtoActivate(WPARAM, LPARAM) {}
+ virtual void OnProtoCheckOnline(WPARAM, LPARAM) {}
+
+private:
+ void UpdateProtoTitle(TCHAR *szText = NULL);
+ void UpdateStatusBar();
+};
+
+template<typename TProto>
+class CProtoDlgBase : public CProtoIntDlgBase
+{
+ typedef CProtoIntDlgBase CSuper;
+
+public:
+ __inline CProtoDlgBase<TProto>(TProto *proto, int idDialog, HWND parent, bool show_label=true ) :
+ CProtoIntDlgBase( proto, idDialog, parent, show_label ),
+ m_proto( proto )
+ {
+ }
+
+ __inline TProto *GetProto() { return m_proto; }
+
+protected:
+ TProto* m_proto;
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ m_proto->WindowSubscribe(m_hwnd);
+ break;
+ case WM_DESTROY:
+ WindowFreeIcon(m_hwnd);
+ m_proto->WindowUnsubscribe(m_hwnd);
+ break;
+ }
+
+ return CSuper::DlgProc(msg, wParam, lParam);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Safe open/close dialogs
+#define UI_SAFE_OPEN(dlgClass, dlgPtr) \
+ { \
+ if (dlgPtr) \
+ { \
+ SetForegroundWindow((dlgPtr)->GetHwnd()); \
+ } else \
+ { \
+ (dlgPtr) = new dlgClass(this); \
+ (dlgPtr)->Show(); \
+ } \
+ }
+
+#define UI_SAFE_OPEN_EX(dlgClass, dlgPtr, dlgLocal) \
+ if (dlgPtr) \
+ { \
+ ::SetForegroundWindow((dlgPtr)->GetHwnd()); \
+ } else \
+ { \
+ (dlgPtr) = new dlgClass(this); \
+ (dlgPtr)->Show(); \
+ } \
+ dlgClass *dlgLocal = (dlgClass *)(dlgPtr);
+
+#define UI_SAFE_CLOSE(dlg) \
+ { \
+ if ( dlg ) { \
+ (dlg)->Close(); \
+ (dlg) = NULL; \
+ } \
+ }
+
+#define UI_SAFE_CLOSE_HWND(hwnd) \
+ { \
+ if ( hwnd ) { \
+ ::SendMessage( (hwnd), WM_CLOSE, 0, 0 ); \
+ (hwnd) = NULL; \
+ } \
+ }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// NULL-Safe dialog notifications
+#define UI_SAFE_NOTIFY(dlg, msg) \
+ { \
+ if ( dlg ) \
+ ::SendMessage((dlg)->GetHwnd(), msg, 0, 0); \
+ }
+
+#define UI_SAFE_NOTIFY_HWND(hwnd, msg) \
+ { \
+ if ( hwnd ) \
+ ::SendMessage((hwnd), msg, 0, 0); \
+ }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Define message maps
+#define UI_MESSAGE_MAP(dlgClass, baseDlgClass) \
+ typedef baseDlgClass CMessageMapSuperClass; \
+ virtual INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) \
+ { \
+ switch (msg) \
+ { \
+ case 0: \
+ break /* just to handle ";" symbol after macro */
+
+#define UI_MESSAGE(msg, proc) \
+ case msg: \
+ proc(msg, wParam, lParam); \
+ break
+
+#define UI_MESSAGE_EX(msg, func) \
+ case msg: \
+ return func(msg, wParam, lParam)
+
+#define UI_POSTPROCESS_MESSAGE(msg, proc) \
+ case msg: \
+ CMessageMapSuperClass::DlgProc(msg, wParam, lParam); \
+ return FALSE
+
+#define UI_POSTPROCESS_MESSAGE_EX(msg, func) \
+ case msg: \
+ CMessageMapSuperClass::DlgProc(msg, wParam, lParam); \
+ return func(msg, wParam, lParam)
+
+#define UI_MESSAGE_MAP_END() \
+ } \
+ return CMessageMapSuperClass::DlgProc(msg, wParam, lParam); \
+ }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Misc utitlities
+int UIEmulateBtnClick(HWND hwndDlg, UINT idcButton);
+void UIShowControls(HWND hwndDlg, int *idList, int nCmdShow);
+
+#endif // __jabber_ui_utils_h__
diff --git a/protocols/JabberG/src/version.h b/protocols/JabberG/src/version.h new file mode 100644 index 0000000000..cf7a422dc2 --- /dev/null +++ b/protocols/JabberG/src/version.h @@ -0,0 +1,3 @@ +#define __FILEVERSION_STRING 0,11,0,1
+#define __VERSION_STRING "0.11.0.1"
+#define __VERSION_DWORD PLUGIN_MAKE_VERSION(0,11,0,1)
|