/* 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 #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 ); JCallService( 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 ) JCallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); while ( hContact != NULL ) { char* szProto = ( char* )JCallService( 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 = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM ) hContact, 0 ); } 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 ) JCallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); while ( hContact != NULL ) { char* szProto = ( char* )JCallService( 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 = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM ) hContact, 0 ); } 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; iresourceCount && _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, ""); 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 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"), _T(__VERSION_STRING)); 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 &avs = *(OBJLIST*)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*)JCallService(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 ); } } } JCallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)res); } else hHttpCon = NULL; } delete &avs; if ( hHttpCon ) Netlib_CloseHandle(hHttpCon); }