diff options
Diffstat (limited to 'protocols/JabberG/src/jabber_util.cpp')
-rw-r--r-- | protocols/JabberG/src/jabber_util.cpp | 1752 |
1 files changed, 1752 insertions, 0 deletions
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 |