From cb4a46e7fbe62d788e66ed6121c717a2d22a4d7c Mon Sep 17 00:00:00 2001 From: watcherhd Date: Thu, 21 Apr 2011 14:14:52 +0000 Subject: svn.miranda.im is moving to a new home! git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@7 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb --- miranda-wine/protocols/JabberG/jabber_iqid.cpp | 1429 ++++++++++++++++++++++++ 1 file changed, 1429 insertions(+) create mode 100644 miranda-wine/protocols/JabberG/jabber_iqid.cpp (limited to 'miranda-wine/protocols/JabberG/jabber_iqid.cpp') diff --git a/miranda-wine/protocols/JabberG/jabber_iqid.cpp b/miranda-wine/protocols/JabberG/jabber_iqid.cpp new file mode 100644 index 0000000..67b4808 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_iqid.cpp @@ -0,0 +1,1429 @@ +/* + +Jabber Protocol Plugin for Miranda IM +Copyright ( C ) 2002-04 Santithorn Bunchua +Copyright ( C ) 2005-06 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. + +File name : $Source: /cvsroot/miranda/miranda/protocols/JabberG/jabber_iqid.cpp,v $ +Revision : $Revision: 3703 $ +Last change on : $Date: 2006-09-05 17:54:42 +0400 (Втр, 05 Сен 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "resource.h" +#include "jabber_list.h" +#include "jabber_iq.h" +#include "sha1.h" + +extern char* jabberVcardPhotoFileName; +extern char* jabberVcardPhotoType; + +static void JabberOnLoggedIn( ThreadData* info ) +{ + jabberOnline = TRUE; + jabberLoggedInTime = time(0); + + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultGetRoster ); + + XmlNode iq( "iq" ); iq.addAttr( "type", "get" ); iq.addAttrID( iqId ); + XmlNode* query = iq.addChild( "query" ); query->addAttr( "xmlns", "jabber:iq:roster" ); + JabberSend( info->s, iq ); +} + +void JabberIqResultGetAuth( XmlNode *iqNode, void *userdata ) +{ + // RECVED: result of the request for authentication method + // ACTION: send account authentication information to log in + JabberLog( " iqIdGetAuth" ); + + ThreadData* info = ( ThreadData* ) userdata; + XmlNode *queryNode; + TCHAR* type; + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result"))) { + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultSetAuth ); + + XmlNodeIq iq( "set", iqId ); + XmlNode* query = iq.addQuery( "jabber:iq:auth" ); + query->addChild( "username", info->username ); + if ( JabberXmlGetChild( queryNode, "digest" )!=NULL && streamId ) { + char* str = JabberUtf8Encode( info->password ); + char text[200]; + mir_snprintf( text, SIZEOF(text), "%s%s", streamId, str ); + mir_free( str ); + if (( str=JabberSha1( text )) != NULL ) { + query->addChild( "digest", str ); + mir_free( str ); + } + } + else if ( JabberXmlGetChild( queryNode, "password" ) != NULL ) + query->addChild( "password", info->password ); + else { + JabberLog( "No known authentication mechanism accepted by the server." ); + + JabberSend( info->s, "" ); + return; + } + + if ( JabberXmlGetChild( queryNode, "resource" ) != NULL ) + query->addChild( "resource", info->resource ); + + JabberSend( info->s, iq ); + } + else if ( !lstrcmp( type, _T("error"))) { + JabberSend( info->s, "" ); + + TCHAR text[128]; + mir_sntprintf( text, SIZEOF( text ), _T("%s %s."), TranslateT( "Authentication failed for" ), info->username ); + MessageBox( NULL, text, TranslateT( "Jabber Authentication" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND ); + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD ); + jabberThreadInfo = NULL; // To disallow auto reconnect +} } + +void JabberIqResultSetAuth( XmlNode *iqNode, void *userdata ) +{ + ThreadData* info = ( ThreadData* ) userdata; + TCHAR* type; + int iqId; + + // RECVED: authentication result + // ACTION: if successfully logged in, continue by requesting roster list and set my initial status + JabberLog( " iqIdSetAuth" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result"))) { + DBVARIANT dbv; + if ( JGetStringT( NULL, "Nick", &dbv )) + JSetStringT( NULL, "Nick", info->username ); + else + JFreeVariant( &dbv ); + + jabberOnline = TRUE; + jabberLoggedInTime = time(0); + + iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultGetRoster ); + { XmlNodeIq iq( "get", iqId ); + XmlNode* query = iq.addQuery( "jabber:iq:roster" ); + JabberSend( info->s, iq ); + } + + if ( hwndJabberAgents ) { + // Retrieve agent information + iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_GETAGENTS, JabberIqResultGetAgents ); + + XmlNodeIq iq( "get", iqId ); + XmlNode* query = iq.addQuery( "jabber:iq:agents" ); + JabberSend( info->s, iq ); + } + } + // What to do if password error? etc... + else if ( !lstrcmp( type, _T("error"))) { + TCHAR text[128]; + + JabberSend( info->s, "" ); + mir_sntprintf( text, SIZEOF( text ), _T("%s %s."), TranslateT( "Authentication failed for" ), info->username ); + MessageBox( NULL, text, TranslateT( "Jabber Authentication" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND ); + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD ); + jabberThreadInfo = NULL; // To disallow auto reconnect +} } + +void JabberIqResultBind( XmlNode *iqNode, void *userdata ) +{ + ThreadData* info = ( ThreadData* ) userdata; + XmlNode* n = JabberXmlGetChild( iqNode, "bind" ); + if ( n != NULL ) { + if ( n = JabberXmlGetChild( n, "jid" )) { + if ( n->text ) { + if ( !_tcsncmp( info->fullJID, n->text, SIZEOF( info->fullJID ))) + JabberLog( "Result Bind: "TCHAR_STR_PARAM" %s "TCHAR_STR_PARAM, info->fullJID, "confirmed.", NULL ); + else { + JabberLog( "Result Bind: "TCHAR_STR_PARAM" %s "TCHAR_STR_PARAM, info->fullJID, "changed to", n->text); + _tcsncpy( info->fullJID, n->text, SIZEOF( info->fullJID )); + } } } + + if ( info->bIsSessionAvailable ) { + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultSession ); + + XmlNodeIq iq( "set" ); iq.addAttrID( iqId ); + iq.addChild( "session" )->addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-session" ); + JabberSend( info->s, iq ); + } + else JabberOnLoggedIn( info ); + } + else if ( n = JabberXmlGetChild( n, "error" )) { + //rfc3920 page 39 + TCHAR errorMessage [256]; + int pos=0; + pos = mir_sntprintf( errorMessage, SIZEOF(errorMessage), TranslateT("Resource ")); + XmlNode *tempNode; + if (tempNode = JabberXmlGetChild( n, "resource" )) + pos += mir_sntprintf(errorMessage,256-pos,_T("\"%s\" "),tempNode->text); + pos += mir_sntprintf( errorMessage+pos,256-pos,TranslateT("refused by server\n%s: %s"),TranslateT("Type"),Translate(JabberXmlGetAttrValue( n, "type" ))); + if ( n->numChild ) + pos += mir_sntprintf( errorMessage+pos,256-pos,_T("\n%s: ")_T(TCHAR_STR_PARAM)_T("\n"),TranslateT("Reason"),JTranslate( n->child[0]->name)); + mir_sntprintf( errorMessage,256-pos, _T("%s @")_T(TCHAR_STR_PARAM)_T("."), TranslateT( "Authentication failed for" ), info->username, info->server ); + MessageBox( NULL, errorMessage, TranslateT( "Jabber Protocol" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND ); + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPROTOCOL ); + JabberSend( info->s, "" ); + jabberThreadInfo = NULL; // To disallow auto reconnect +} } + +void JabberIqResultSession( XmlNode *iqNode, void *userdata ) +{ + ThreadData* info = ( ThreadData* )userdata; + + TCHAR* type; + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result"))) + JabberOnLoggedIn( info ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberIqResultGetRoster - populates LIST_ROSTER and creates contact for any new rosters + +void sttGroupchatJoinByHContact( HANDLE hContact ) +{ + DBVARIANT dbv; + if( JGetStringT( hContact, "ChatRoomID", &dbv )) + return; + if( dbv.type != DBVT_ASCIIZ && dbv.type != DBVT_WCHAR ) + return; + + TCHAR* roomjid = mir_tstrdup( dbv.ptszVal ); + JFreeVariant( &dbv ); + if( !roomjid ) return; + + TCHAR* room = roomjid; + TCHAR* server = _tcschr( roomjid, '@' ); + if( !server ) + return; + server[0] = '\0'; server++; + + TCHAR nick[ 256 ]; + if ( JGetStringT( hContact, "MyNick", &dbv )) { + TCHAR* jidnick = JabberNickFromJID( jabberJID ); + if( !jidnick ) { + mir_free( jidnick ); + mir_free( roomjid ); + return; + } + _tcsncpy( nick, jidnick, SIZEOF( nick )); + mir_free( jidnick ); + } + else { + _tcsncpy( nick, dbv.ptszVal, SIZEOF( nick )); + JFreeVariant( &dbv ); + } + + JabberGroupchatJoinRoom( server, room, nick, _T("")); + mir_free( roomjid ); +} + +void CALLBACK sttCreateRoom( ULONG dwParam ) +{ + char* jid = t2a(( TCHAR* )dwParam), *p; + + GCSESSION gcw = {0}; + gcw.cbSize = sizeof(GCSESSION); + gcw.iType = GCW_CHATROOM; + gcw.pszID = jid; + gcw.pszModule = jabberProtoName; + gcw.pszName = strcpy(( char* )alloca( strlen(jid)+1 ), jid ); + if (( p = (char*)strchr( gcw.pszName, '@' )) != NULL ) + *p = 0; + CallService( MS_GC_NEWSESSION, 0, ( LPARAM )&gcw ); + mir_free(jid); +} + +void JabberIqResultGetRoster( XmlNode* iqNode, void* ) +{ + JabberLog( " iqIdGetRoster" ); + TCHAR* type = JabberXmlGetAttrValue( iqNode, "type" ); + if ( lstrcmp( type, _T("result"))) + return; + + XmlNode* queryNode = JabberXmlGetChild( iqNode, "query" ); + if ( queryNode == NULL ) + return; + + if ( lstrcmp( JabberXmlGetAttrValue( queryNode, "xmlns" ), _T("jabber:iq:roster"))) + return; + + TCHAR* name, *nick; + int i; + SortedList chatRooms = {0}; + chatRooms.increment = 10; + + for ( i=0; i < queryNode->numChild; i++ ) { + XmlNode* itemNode = queryNode->child[i]; + if ( strcmp( itemNode->name, "item" )) + continue; + + TCHAR* str = JabberXmlGetAttrValue( itemNode, "subscription" ); + + 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; + + TCHAR* jid = JabberXmlGetAttrValue( itemNode, "jid" ); + if ( jid == NULL ) + continue; + + if (( name = JabberXmlGetAttrValue( itemNode, "name" )) != NULL ) + nick = mir_tstrdup( name ); + else + nick = JabberNickFromJID( jid ); + + if ( nick == NULL ) + continue; + + JABBER_LIST_ITEM* item = JabberListAdd( LIST_ROSTER, jid ); + item->subscription = sub; + + if ( item->nick ) mir_free( item->nick ); + item->nick = nick; + + if ( item->group ) mir_free( item->group ); + XmlNode* groupNode = JabberXmlGetChild( itemNode, "group" ); + if ( groupNode != NULL && groupNode->text != NULL ) + item->group = mir_tstrdup( groupNode->text ); + else + item->group = NULL; + + HANDLE hContact = JabberHContactFromJID( jid ); + if ( hContact == NULL ) { + // Received roster has a new JID. + // Add the jid ( with empty resource ) to Miranda contact list. + hContact = JabberDBCreateContact( jid, nick, FALSE, TRUE ); + } + + 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 ); + + if ( JGetByte( hContact, "ChatRoom", 0 )) { + //DBDeleteContactSetting( hContact, "CList", "Hidden" ); + QueueUserAPC( sttCreateRoom, hMainThread, ( unsigned long )jid ); + DBDeleteContactSetting( hContact, "CList", "Hidden" ); + li.List_Insert( &chatRooms, hContact, chatRooms.realCount ); + } + + 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" ); + } + + // Delete orphaned contacts ( if roster sync is enabled ) + if ( JGetByte( "RosterSync", FALSE ) == TRUE ) { + int listSize = 0, listAllocSize = 0; + HANDLE* list = NULL; + HANDLE hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) { + char* str = ( char* )JCallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 ); + if ( str != NULL && !strcmp( str, jabberProtoName )) { + DBVARIANT dbv; + if ( !JGetStringT( hContact, "jid", &dbv )) { + if ( !JabberListExist( LIST_ROSTER, dbv.ptszVal )) { + JabberLog( "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 = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM ) hContact, 0 ); + } + + for ( i=0; i < listSize; i++ ) { + JabberLog( "Syncing roster: deleting 0x%x", list[i] ); + JCallService( MS_DB_CONTACT_DELETE, ( WPARAM ) list[i], 0 ); + } + if ( list != NULL ) + mir_free( list ); + } + + JabberEnableMenuItems( TRUE ); + + if ( hwndJabberGroupchat ) + SendMessage( hwndJabberGroupchat, WM_JABBER_CHECK_ONLINE, 0, 0 ); + if ( hwndJabberJoinGroupchat ) + SendMessage( hwndJabberJoinGroupchat, WM_JABBER_CHECK_ONLINE, 0, 0 ); + + JabberLog( "Status changed via THREADSTART" ); + modeMsgStatusChangePending = FALSE; + JabberSetServerStatus( jabberDesiredStatus ); + + if ( JGetByte( "AutoJoinConferences", 0 )) { + for ( int i=0; i < chatRooms.realCount; i++ ) + sttGroupchatJoinByHContact(( HANDLE )chatRooms.items[i] ); + } + li.List_Destroy( &chatRooms ); + + if ( hwndJabberAgents ) + SendMessage( hwndJabberAgents, WM_JABBER_TRANSPORT_REFRESH, 0, 0 ); + if ( hwndJabberVcard ) + SendMessage( hwndJabberVcard, WM_JABBER_CHECK_ONLINE, 0, 0 ); +} + +void JabberIqResultGetAgents( XmlNode *iqNode, void *userdata ) +{ + ThreadData* info = ( ThreadData* ) userdata; + XmlNode *queryNode; + TCHAR* type, *jid, *str; + + // RECVED: agent list + // ACTION: refresh agent list dialog + JabberLog( " iqIdGetAgents" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result"))) { + str = JabberXmlGetAttrValue( queryNode, "xmlns" ); + if ( str!=NULL && !lstrcmp( str, _T("jabber:iq:agents"))) { + XmlNode *agentNode, *n; + JABBER_LIST_ITEM *item; + int i; + + JabberListRemoveList( LIST_AGENT ); + for ( i=0; inumChild; i++ ) { + agentNode = queryNode->child[i]; + if ( !lstrcmpA( agentNode->name, "agent" )) { + if (( jid=JabberXmlGetAttrValue( agentNode, "jid" )) != NULL ) { + item = JabberListAdd( LIST_AGENT, jid ); + if ( JabberXmlGetChild( agentNode, "register" ) != NULL ) + item->cap |= AGENT_CAP_REGISTER; + if ( JabberXmlGetChild( agentNode, "search" ) != NULL ) + item->cap |= AGENT_CAP_SEARCH; + if ( JabberXmlGetChild( agentNode, "groupchat" ) != NULL ) + item->cap |= AGENT_CAP_GROUPCHAT; + // set service also??? + // most chatroom servers don't announce so we will + // also treat public as groupchat services + if (( n=JabberXmlGetChild( agentNode, "service" ))!=NULL && n->text!=NULL && !_tcscmp( n->text, _T("public"))) + item->cap |= AGENT_CAP_GROUPCHAT; + if (( n=JabberXmlGetChild( agentNode, "name" ))!=NULL && n->text!=NULL ) + item->name = mir_tstrdup( n->text ); + } } } } + + if ( hwndJabberAgents != NULL ) { + if (( jid = JabberXmlGetAttrValue( iqNode, "from" )) != NULL ) + SendMessage( hwndJabberAgents, WM_JABBER_AGENT_REFRESH, 0, ( LPARAM )jid ); + else + SendMessage( hwndJabberAgents, WM_JABBER_AGENT_REFRESH, 0, ( LPARAM )info->server ); +} } } + +void JabberIqResultGetRegister( XmlNode *iqNode, void *userdata ) +{ + // RECVED: result of the request for ( agent ) registration mechanism + // ACTION: activate ( agent ) registration input dialog + JabberLog( " iqIdGetRegister" ); + + ThreadData* info = ( ThreadData* ) userdata; + XmlNode *queryNode, *n; + TCHAR *type; + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result"))) { + if ( hwndAgentRegInput ) + if (( n = JabberXmlCopyNode( iqNode )) != NULL ) + SendMessage( hwndAgentRegInput, WM_JABBER_REGINPUT_ACTIVATE, 1 /*success*/, ( LPARAM )n ); + } + else if ( !lstrcmp( type, _T("error"))) { + if ( hwndAgentRegInput ) { + XmlNode *errorNode = JabberXmlGetChild( iqNode, "error" ); + TCHAR* str = JabberErrorMsg( errorNode ); + SendMessage( hwndAgentRegInput, WM_JABBER_REGINPUT_ACTIVATE, 0 /*error*/, ( LPARAM )str ); + mir_free( str ); +} } } + +void JabberIqResultSetRegister( XmlNode *iqNode, void *userdata ) +{ + // RECVED: result of registration process + // ACTION: notify of successful agent registration + JabberLog( " iqIdSetRegister" ); + + TCHAR *type; + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result"))) { + if ( hwndRegProgress ) + SendMessage( hwndRegProgress, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Registration successful" )); + } + else if ( !lstrcmp( type, _T("error"))) { + if ( hwndRegProgress ) { + XmlNode *errorNode = JabberXmlGetChild( iqNode, "error" ); + TCHAR* str = JabberErrorMsg( errorNode ); + SendMessage( hwndRegProgress, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )str ); + mir_free( str ); +} } } + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberIqResultGetVcard - processes the server-side v-card + +static void JabberIqResultGetVcardPhoto( const TCHAR* jid, XmlNode* n, HANDLE hContact, BOOL& hasPhoto ) +{ + if ( hasPhoto ) return; + + XmlNode* o = JabberXmlGetChild( n, "BINVAL" ); + if ( o == NULL || o->text == NULL ) return; + + int bufferLen; + char* buffer = JabberBase64Decode( o->text, &bufferLen ); + if ( buffer == NULL ) return; + + XmlNode* m = JabberXmlGetChild( n, "TYPE" ); + if ( m == NULL || m->text == NULL ) { +LBL_NoTypeSpecified: + char* szPicType; + + switch( JabberGetPictureType( buffer )) { + case PA_FORMAT_GIF: szPicType = "image/gif"; break; + case PA_FORMAT_BMP: szPicType = "image/bmp"; break; + case PA_FORMAT_PNG: szPicType = "image/png"; break; + case PA_FORMAT_JPEG: szPicType = "image/jpeg"; break; + default: + goto LBL_Ret; + } + + replaceStr( jabberVcardPhotoType, szPicType ); + } + else { + if ( _tcscmp( m->text, _T("image/jpeg")) && _tcscmp( m->text, _T("image/png")) && _tcscmp( m->text, _T("image/gif")) && _tcscmp( m->text, _T("image/bmp"))) + goto LBL_NoTypeSpecified; + + if ( jabberVcardPhotoType ) mir_free(jabberVcardPhotoType); + jabberVcardPhotoType = t2a( m->text ); + } + + DWORD nWritten; + char szTempPath[MAX_PATH], szTempFileName[MAX_PATH]; + JABBER_LIST_ITEM *item; + DBVARIANT dbv; + + if ( GetTempPathA( sizeof( szTempPath ), szTempPath ) <= 0 ) + lstrcpyA( szTempPath, ".\\" ); + if ( !GetTempFileNameA( szTempPath, jabberProtoName, GetTickCount(), szTempFileName )) { +LBL_Ret: + mir_free( buffer ); + return; + } + + { char* p = strrchr( szTempFileName, '.' ); + if ( p != NULL ) + lstrcpyA( p+1, jabberVcardPhotoType + 6 ); + } + + JabberLog( "Picture file name set to %s", szTempFileName ); + HANDLE hFile = CreateFileA( szTempFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + if ( hFile == INVALID_HANDLE_VALUE ) + goto LBL_Ret; + + JabberLog( "Writing %d bytes", bufferLen ); + if ( !WriteFile( hFile, buffer, bufferLen, &nWritten, NULL )) + goto LBL_Ret; + + JabberLog( "%d bytes written", nWritten ); + if ( hContact == NULL ) { + hasPhoto = TRUE; + if ( jabberVcardPhotoFileName ) { + DeleteFileA( jabberVcardPhotoFileName ); + mir_free( jabberVcardPhotoFileName ); + jabberVcardPhotoFileName = NULL; + } + replaceStr( jabberVcardPhotoFileName, szTempFileName ); + JabberLog( "My picture saved to %s", szTempFileName ); + } + else if ( !JGetStringT( hContact, "jid", &dbv )) { + if (( item = JabberListGetItemPtr( LIST_ROSTER, jid )) != NULL ) { + hasPhoto = TRUE; + if ( item->photoFileName ) + DeleteFileA( item->photoFileName ); + replaceStr( item->photoFileName, szTempFileName ); + JabberLog( "Contact's picture saved to %s", szTempFileName ); + } + JFreeVariant( &dbv ); + } + + CloseHandle( hFile ); + + if ( !hasPhoto ) + DeleteFileA( szTempFileName ); + + goto LBL_Ret; +} + +void JabberIqResultGetVcard( XmlNode *iqNode, void *userdata ) +{ + XmlNode *vCardNode, *m, *n, *o; + TCHAR* type, *jid; + HANDLE hContact; + TCHAR text[128]; + int len; + DBVARIANT dbv; + + JabberLog( " iqIdGetVcard" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( jid=JabberXmlGetAttrValue( iqNode, "from" )) == NULL ) return; + + len = _tcslen( jabberJID ); + if ( !_tcsnicmp( jid, jabberJID, len ) && ( jid[len]=='/' || jid[len]=='\0' )) { + hContact = NULL; + JabberLog( "Vcard for myself" ); + } + else { + if (( hContact = JabberHContactFromJID( jid )) == NULL ) + return; + JabberLog( "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=JabberXmlGetChild( iqNode, "vCard" )) != NULL ) { + for ( int i=0; inumChild; i++ ) { + n = vCardNode->child[i]; + if ( n==NULL || n->name==NULL ) continue; + if ( !strcmp( n->name, "FN" )) { + if ( n->text != NULL ) { + hasFn = TRUE; + JSetStringT( hContact, "FullName", n->text ); + } + } + else if ( !strcmp( n->name, "NICKNAME" )) { + if ( n->text != NULL ) { + hasNick = TRUE; + JSetStringT( hContact, "Nick", n->text ); + } + } + else if ( !strcmp( n->name, "N" )) { + // First/Last name + if ( !hasGiven && !hasFamily && !hasMiddle ) { + if (( m=JabberXmlGetChild( n, "GIVEN" )) != NULL && m->text!=NULL ) { + hasGiven = TRUE; + JSetStringT( hContact, "FirstName", m->text ); + } + if (( m=JabberXmlGetChild( n, "FAMILY" )) != NULL && m->text!=NULL ) { + hasFamily = TRUE; + JSetStringT( hContact, "LastName", m->text ); + } + if (( m=JabberXmlGetChild( n, "MIDDLE" )) != NULL && m->text != NULL ) { + hasMiddle = TRUE; + JSetStringT( hContact, "MiddleName", m->text ); + } } + } + else if ( !strcmp( n->name, "EMAIL" )) { + // E-mail address( es ) + if (( m=JabberXmlGetChild( n, "USERID" )) == NULL ) // Some bad client put e-mail directly in instead of + m = n; + if ( m->text != 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, m->text ); + + if ( hContact == NULL ) { + sprintf( text, "e-mailFlag%d", nEmail ); + int nFlag = 0; + if ( JabberXmlGetChild( n, "HOME" ) != NULL ) nFlag |= JABBER_VCEMAIL_HOME; + if ( JabberXmlGetChild( n, "WORK" ) != NULL ) nFlag |= JABBER_VCEMAIL_WORK; + if ( JabberXmlGetChild( n, "INTERNET" ) != NULL ) nFlag |= JABBER_VCEMAIL_INTERNET; + if ( JabberXmlGetChild( n, "X400" ) != NULL ) nFlag |= JABBER_VCEMAIL_X400; + JSetWord( NULL, text, nFlag ); + } + nEmail++; + } + } + else if ( !strcmp( n->name, "BDAY" )) { + // Birthday + if ( !hasBday && n->text!=NULL ) { + if ( hContact != NULL ) { + if ( _stscanf( n->text, _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 ); + } + } + else { + hasBday = TRUE; + JSetStringT( NULL, "BirthDate", n->text ); + } } + } + else if ( !lstrcmpA( n->name, "GENDER" )) { + // Gender + if ( !hasGender && n->text!=NULL ) { + if ( hContact != NULL ) { + if ( n->text[0] && strchr( "mMfF", n->text[0] )!=NULL ) { + hasGender = TRUE; + JSetByte( hContact, "Gender", ( BYTE ) toupper( n->text[0] )); + } + } + else { + hasGender = TRUE; + JSetStringT( NULL, "GenderString", n->text ); + } } + } + else if ( !strcmp( n->name, "ADR" )) { + if ( !hasHome && JabberXmlGetChild( n, "HOME" )!=NULL ) { + // Home address + hasHome = TRUE; + if (( m=JabberXmlGetChild( n, "STREET" )) != NULL && m->text != NULL ) { + hasHomeStreet = TRUE; + if ( hContact != NULL ) { + if (( o=JabberXmlGetChild( n, "EXTADR" )) != NULL && o->text != NULL ) + mir_sntprintf( text, SIZEOF( text ), _T("%s\r\n%s"), m->text, o->text ); + else if (( o=JabberXmlGetChild( n, "EXTADD" ))!=NULL && o->text!=NULL ) + mir_sntprintf( text, SIZEOF( text ), _T("%s\r\n%s"), m->text, o->text ); + else + _tcsncpy( text, m->text, SIZEOF( text )); + text[sizeof( text )-1] = '\0'; + JSetStringT( hContact, "Street", text ); + } + else { + JSetStringT( hContact, "Street", m->text ); + if (( m=JabberXmlGetChild( n, "EXTADR" )) == NULL ) + m = JabberXmlGetChild( n, "EXTADD" ); + if ( m!=NULL && m->text!=NULL ) { + hasHomeStreet2 = TRUE; + JSetStringT( hContact, "Street2", m->text ); + } } } + + if (( m=JabberXmlGetChild( n, "LOCALITY" ))!=NULL && m->text!=NULL ) { + hasHomeLocality = TRUE; + JSetStringT( hContact, "City", m->text ); + } + if (( m=JabberXmlGetChild( n, "REGION" ))!=NULL && m->text!=NULL ) { + hasHomeRegion = TRUE; + JSetStringT( hContact, "State", m->text ); + } + if (( m=JabberXmlGetChild( n, "PCODE" ))!=NULL && m->text!=NULL ) { + hasHomePcode = TRUE; + JSetStringT( hContact, "ZIP", m->text ); + } + if (( m=JabberXmlGetChild( n, "CTRY" ))==NULL || m->text==NULL ) // Some bad client use instead of + m = JabberXmlGetChild( n, "COUNTRY" ); + if ( m!=NULL && m->text!=NULL ) { + hasHomeCtry = TRUE; + if ( hContact != NULL ) + JSetWord( hContact, "Country", ( WORD )JabberCountryNameToId( m->text )); + else + JSetStringT( hContact, "CountryName", m->text ); + } } + + if ( !hasWork && JabberXmlGetChild( n, "WORK" )!=NULL ) { + // Work address + hasWork = TRUE; + if (( m=JabberXmlGetChild( n, "STREET" ))!=NULL && m->text!=NULL ) { + hasWorkStreet = TRUE; + if ( hContact != NULL ) { + if (( o=JabberXmlGetChild( n, "EXTADR" ))!=NULL && o->text!=NULL ) + mir_sntprintf( text, SIZEOF( text ), _T("%s\r\n%s"), m->text, o->text ); + else if (( o=JabberXmlGetChild( n, "EXTADD" ))!=NULL && o->text!=NULL ) + mir_sntprintf( text, SIZEOF( text ), _T("%s\r\n%s"), m->text, o->text ); + else + _tcsncpy( text, m->text, SIZEOF( text )); + text[sizeof( text )-1] = '\0'; + JSetStringT( hContact, "CompanyStreet", text ); + } + else { + JSetStringT( hContact, "CompanyStreet", m->text ); + if (( m=JabberXmlGetChild( n, "EXTADR" )) == NULL ) + m = JabberXmlGetChild( n, "EXTADD" ); + if ( m!=NULL && m->text!=NULL ) { + hasWorkStreet2 = TRUE; + JSetStringT( hContact, "CompanyStreet2", m->text ); + } } } + + if (( m=JabberXmlGetChild( n, "LOCALITY" ))!=NULL && m->text!=NULL ) { + hasWorkLocality = TRUE; + JSetStringT( hContact, "CompanyCity", m->text ); + } + if (( m=JabberXmlGetChild( n, "REGION" ))!=NULL && m->text!=NULL ) { + hasWorkRegion = TRUE; + JSetStringT( hContact, "CompanyState", m->text ); + } + if (( m=JabberXmlGetChild( n, "PCODE" ))!=NULL && m->text!=NULL ) { + hasWorkPcode = TRUE; + JSetStringT( hContact, "CompanyZIP", m->text ); + } + if (( m=JabberXmlGetChild( n, "CTRY" ))==NULL || m->text==NULL ) // Some bad client use instead of + m = JabberXmlGetChild( n, "COUNTRY" ); + if ( m!=NULL && m->text!=NULL ) { + hasWorkCtry = TRUE; + if ( hContact != NULL ) + JSetWord( hContact, "CompanyCountry", ( WORD )JabberCountryNameToId( m->text )); + else + JSetStringT( hContact, "CompanyCountryName", m->text ); + } } + } + else if ( !strcmp( n->name, "TEL" )) { + // Telephone/Fax/Cellular + if (( m=JabberXmlGetChild( n, "NUMBER" ))!=NULL && m->text!=NULL ) { + if ( hContact != NULL ) { + if ( !hasFax && JabberXmlGetChild( n, "FAX" )!=NULL ) { + hasFax = TRUE; + JSetStringT( hContact, "Fax", m->text ); + } + if ( !hasCell && JabberXmlGetChild( n, "CELL" )!=NULL ) { + hasCell = TRUE; + JSetStringT( hContact, "Cellular", m->text ); + } + if ( !hasPhone && + ( JabberXmlGetChild( n, "HOME" )!=NULL || + JabberXmlGetChild( n, "WORK" )!=NULL || + JabberXmlGetChild( n, "VOICE" )!=NULL || + ( JabberXmlGetChild( n, "FAX" )==NULL && + JabberXmlGetChild( n, "PAGER" )==NULL && + JabberXmlGetChild( n, "MSG" )==NULL && + JabberXmlGetChild( n, "CELL" )==NULL && + JabberXmlGetChild( n, "VIDEO" )==NULL && + JabberXmlGetChild( n, "BBS" )==NULL && + JabberXmlGetChild( n, "MODEM" )==NULL && + JabberXmlGetChild( n, "ISDN" )==NULL && + JabberXmlGetChild( n, "PCS" )==NULL )) ) { + hasPhone = TRUE; + JSetStringT( hContact, "Phone", m->text ); + } + } + else { + char text[ 100 ]; + sprintf( text, "Phone%d", nPhone ); + JSetStringT( NULL, text, m->text ); + + sprintf( text, "PhoneFlag%d", nPhone ); + int nFlag = 0; + if ( JabberXmlGetChild( n, "HOME" ) != NULL ) nFlag |= JABBER_VCTEL_HOME; + if ( JabberXmlGetChild( n, "WORK" ) != NULL ) nFlag |= JABBER_VCTEL_WORK; + if ( JabberXmlGetChild( n, "VOICE" ) != NULL ) nFlag |= JABBER_VCTEL_VOICE; + if ( JabberXmlGetChild( n, "FAX" ) != NULL ) nFlag |= JABBER_VCTEL_FAX; + if ( JabberXmlGetChild( n, "PAGER" ) != NULL ) nFlag |= JABBER_VCTEL_PAGER; + if ( JabberXmlGetChild( n, "MSG" ) != NULL ) nFlag |= JABBER_VCTEL_MSG; + if ( JabberXmlGetChild( n, "CELL" ) != NULL ) nFlag |= JABBER_VCTEL_CELL; + if ( JabberXmlGetChild( n, "VIDEO" ) != NULL ) nFlag |= JABBER_VCTEL_VIDEO; + if ( JabberXmlGetChild( n, "BBS" ) != NULL ) nFlag |= JABBER_VCTEL_BBS; + if ( JabberXmlGetChild( n, "MODEM" ) != NULL ) nFlag |= JABBER_VCTEL_MODEM; + if ( JabberXmlGetChild( n, "ISDN" ) != NULL ) nFlag |= JABBER_VCTEL_ISDN; + if ( JabberXmlGetChild( n, "PCS" ) != NULL ) nFlag |= JABBER_VCTEL_PCS; + JSetWord( NULL, text, nFlag ); + nPhone++; + } } + } + else if ( !strcmp( n->name, "URL" )) { + // Homepage + if ( !hasUrl && n->text!=NULL ) { + hasUrl = TRUE; + JSetStringT( hContact, "Homepage", n->text ); + } + } + else if ( !strcmp( n->name, "ORG" )) { + if ( !hasOrgname && !hasOrgunit ) { + if (( m=JabberXmlGetChild( n, "ORGNAME" ))!=NULL && m->text!=NULL ) { + hasOrgname = TRUE; + JSetStringT( hContact, "Company", m->text ); + } + if (( m=JabberXmlGetChild( n, "ORGUNIT" ))!=NULL && m->text!=NULL ) { // The real vCard can have multiple but we will only display the first one + hasOrgunit = TRUE; + JSetStringT( hContact, "CompanyDepartment", m->text ); + } } + } + else if ( !strcmp( n->name, "ROLE" )) { + if ( !hasRole && n->text!=NULL ) { + hasRole = TRUE; + JSetStringT( hContact, "Role", n->text ); + } + } + else if ( !strcmp( n->name, "TITLE" )) { + if ( !hasTitle && n->text!=NULL ) { + hasTitle = TRUE; + JSetStringT( hContact, "CompanyPosition", n->text ); + } + } + else if ( !strcmp( n->name, "DESC" )) { + if ( !hasDesc && n->text!=NULL ) { + hasDesc = TRUE; + TCHAR* szMemo = JabberUnixToDosT( n->text ); + JSetStringT( hContact, "About", szMemo ); + mir_free( szMemo ); + } + } + else if ( !strcmp( n->name, "PHOTO" )) + JabberIqResultGetVcardPhoto( jid, n, hContact, hasPhoto ); + } } + + 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 ( DBGetContactSetting( hContact, jabberProtoName, text, &dbv )) break; + JFreeVariant( &dbv ); + JDeleteSetting( hContact, text ); + } + nEmail++; + } + } + else { + while ( true ) { + char text[ 100 ]; + sprintf( text, "e-mail%d", nEmail ); + if ( DBGetContactSetting( NULL, jabberProtoName, 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" ); + } + 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 ( DBGetContactSetting( NULL, jabberProtoName, 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 ) { + if ( hContact != NULL ) + JDeleteSetting( hContact, "Country" ); + else + JDeleteSetting( hContact, "CountryName" ); + } + 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 ) { + if ( hContact != NULL ) + JDeleteSetting( hContact, "CompanyCountry" ); + else + JDeleteSetting( hContact, "CompanyCountryName" ); + } + 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 ( !hasPhoto && jabberVcardPhotoFileName!=NULL ) { + DeleteFileA( jabberVcardPhotoFileName ); + jabberVcardPhotoFileName = NULL; + } + + if ( hContact != NULL ) + JSendBroadcast( hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, ( HANDLE ) 1, 0 ); + else if ( hwndJabberVcard ) + SendMessage( hwndJabberVcard, WM_JABBER_REFRESH, 0, 0 ); + } + else if ( !lstrcmp( type, _T("error"))) { + if ( hContact != NULL ) + JSendBroadcast( hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, ( HANDLE ) 1, 0 ); +} } + +void JabberIqResultSetVcard( XmlNode *iqNode, void *userdata ) +{ + JabberLog( " iqIdSetVcard" ); + TCHAR* type = JabberXmlGetAttrValue( iqNode, "type" ); + if ( type == NULL ) + return; + + if ( hwndJabberVcard ) + SendMessage( hwndJabberVcard, WM_JABBER_REFRESH, 0, 0 ); +} + +void JabberIqResultSetSearch( XmlNode *iqNode, void *userdata ) +{ + XmlNode *queryNode, *itemNode, *n; + TCHAR* type, *jid, *str; + int id, i; + JABBER_SEARCH_RESULT jsr; + + JabberLog( " iqIdGetSearch" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( str=JabberXmlGetAttrValue( iqNode, "id" )) == NULL ) return; + id = _ttoi( str+strlen( JABBER_IQID )); + + if ( !lstrcmp( type, _T("result"))) { + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) == NULL ) return; + jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT ); + for ( i=0; inumChild; i++ ) { + itemNode = queryNode->child[i]; + if ( !lstrcmpA( itemNode->name, "item" )) { + if (( jid=JabberXmlGetAttrValue( itemNode, "jid" )) != NULL ) { + _tcsncpy( jsr.jid, jid, SIZEOF( jsr.jid )); + jsr.jid[ SIZEOF( jsr.jid )-1] = '\0'; + JabberLog( "Result jid = " TCHAR_STR_PARAM, jid ); + if (( n=JabberXmlGetChild( itemNode, "nick" ))!=NULL && n->text!=NULL ) + jsr.hdr.nick = t2a( n->text ); + else + jsr.hdr.nick = mir_strdup( "" ); + if (( n=JabberXmlGetChild( itemNode, "first" ))!=NULL && n->text!=NULL ) + jsr.hdr.firstName = t2a( n->text ); + else + jsr.hdr.firstName = mir_strdup( "" ); + if (( n=JabberXmlGetChild( itemNode, "last" ))!=NULL && n->text!=NULL ) + jsr.hdr.lastName = t2a( n->text ); + else + jsr.hdr.lastName = mir_strdup( "" ); + if (( n=JabberXmlGetChild( itemNode, "email" ))!=NULL && n->text!=NULL ) + jsr.hdr.email = t2a( n->text ); + else + jsr.hdr.email = mir_strdup( "" ); + JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, ( HANDLE ) id, ( LPARAM )&jsr ); + mir_free( jsr.hdr.nick ); + mir_free( jsr.hdr.firstName ); + mir_free( jsr.hdr.lastName ); + mir_free( jsr.hdr.email ); + } } } + + 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 JabberIqResultExtSearch( XmlNode *iqNode, void *userdata ) +{ + XmlNode *queryNode; + TCHAR* type, *str; + + JabberLog( " iqIdGetExtSearch" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( str=JabberXmlGetAttrValue( iqNode, "id" )) == NULL ) return; + int id = _ttoi( str+strlen( JABBER_IQID )); + + if ( !lstrcmp( type, _T("result"))) { + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) == NULL ) return; + if (( queryNode=JabberXmlGetChild( queryNode, "x" )) == NULL ) return; + for ( int i=0; inumChild; i++ ) { + XmlNode* itemNode = queryNode->child[i]; + if ( strcmp( itemNode->name, "item" )) + continue; + + JABBER_SEARCH_RESULT jsr = { 0 }; + jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT ); +// jsr.hdr.firstName = ""; + + for ( int j=0; j < itemNode->numChild; j++ ) { + XmlNode* fieldNode = itemNode->child[j]; + if ( strcmp( fieldNode->name, "field" )) + continue; + + TCHAR* fieldName = JabberXmlGetAttrValue( fieldNode, "var" ); + if ( fieldName == NULL ) + continue; + + XmlNode* n = JabberXmlGetChild( fieldNode, "value" ); + if ( n == NULL ) + continue; + + if ( !lstrcmp( fieldName, _T("jid"))) { + _tcsncpy( jsr.jid, n->text, SIZEOF( jsr.jid )); + jsr.jid[sizeof( jsr.jid )-1] = '\0'; + JabberLog( "Result jid = " TCHAR_STR_PARAM, jsr.jid ); + } + else if ( !lstrcmp( fieldName, _T("nickname"))) + jsr.hdr.nick = ( n->text != NULL ) ? t2a( n->text ) : mir_strdup( "" ); + else if ( !lstrcmp( fieldName, _T("fn"))) { + mir_free( jsr.hdr.firstName ); + jsr.hdr.firstName = ( n->text != NULL ) ? t2a(n->text) : mir_strdup( "" ); + } + else if ( !lstrcmp( fieldName, _T("given"))) { + mir_free( jsr.hdr.firstName ); + jsr.hdr.firstName = ( n->text != NULL ) ? t2a(n->text) : mir_strdup( "" ); + } + else if ( !lstrcmp( fieldName, _T("family"))) + jsr.hdr.lastName = ( n->text != NULL ) ? t2a(n->text) : mir_strdup( "" ); + else if ( !lstrcmp( fieldName, _T("email"))) + jsr.hdr.email = ( n->text != NULL ) ? t2a(n->text) : mir_strdup( "" ); + } + + JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, ( HANDLE ) id, ( LPARAM )&jsr ); + mir_free( jsr.hdr.nick ); + mir_free( jsr.hdr.firstName ); + mir_free( jsr.hdr.lastName ); + mir_free( jsr.hdr.email ); + } + + 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 JabberIqResultSetPassword( XmlNode *iqNode, void *userdata ) +{ + JabberLog( " iqIdSetPassword" ); + + TCHAR* type = JabberXmlGetAttrValue( iqNode, "type" ); + if ( type == NULL ) + return; + + if ( !lstrcmp( type, _T("result"))) { + strncpy( jabberThreadInfo->password, jabberThreadInfo->newPassword, SIZEOF( jabberThreadInfo->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 JabberIqResultDiscoAgentItems( XmlNode *iqNode, void *userdata ) +{ + ThreadData* info = ( ThreadData* ) userdata; + XmlNode *queryNode, *itemNode; + TCHAR* type, *jid, *from; + + // RECVED: agent list + // ACTION: refresh agent list dialog + JabberLog( " iqIdDiscoAgentItems" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( from=JabberXmlGetAttrValue( iqNode, "from" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result"))) { + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) != NULL ) { + TCHAR* str = JabberXmlGetAttrValue( queryNode, "xmlns" ); + if ( !lstrcmp( str, _T("http://jabber.org/protocol/disco#items"))) { + JabberListRemoveList( LIST_AGENT ); + for ( int i=0; inumChild; i++ ) { + if (( itemNode=queryNode->child[i] )!=NULL && itemNode->name!=NULL && !lstrcmpA( itemNode->name, "item" )) { + if (( jid=JabberXmlGetAttrValue( itemNode, "jid" )) != NULL ) { + JABBER_LIST_ITEM* item = JabberListAdd( LIST_AGENT, jid ); + replaceStr( item->name, JabberXmlGetAttrValue( itemNode, "name" )); + item->cap = AGENT_CAP_REGISTER | AGENT_CAP_GROUPCHAT; // default to all cap until specific info is later received + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultDiscoAgentInfo ); + + XmlNodeIq iq( "get", iqId, jid ); + XmlNode* query = iq.addQuery( "http://jabber.org/protocol/disco#info" ); + JabberSend( jabberThreadInfo->s, iq ); + } } } } } + + if ( hwndJabberAgents != NULL ) { + if (( jid=JabberXmlGetAttrValue( iqNode, "from" )) != NULL ) + SendMessage( hwndJabberAgents, WM_JABBER_AGENT_REFRESH, 0, ( LPARAM )jid ); + else + SendMessage( hwndJabberAgents, WM_JABBER_AGENT_REFRESH, 0, ( LPARAM )info->server ); + } + } + else if ( !lstrcmp( type, _T("error"))) { + // disco is not supported, try jabber:iq:agents + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_GETAGENTS, JabberIqResultGetAgents ); + + XmlNodeIq iq( "get", iqId, from ); + XmlNode* query = iq.addQuery( "jabber:iq:agents" ); + JabberSend( jabberThreadInfo->s, iq ); +} } + +void JabberIqResultDiscoAgentInfo( XmlNode *iqNode, void *userdata ) +{ + ThreadData* info = ( ThreadData* ) userdata; + XmlNode *queryNode, *itemNode, *identityNode; + TCHAR* type, *from, *var; + JABBER_LIST_ITEM *item; + + // RECVED: info for a specific agent + // ACTION: refresh agent list dialog + JabberLog( " iqIdDiscoAgentInfo" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( from = JabberXmlGetAttrValue( iqNode, "from" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result"))) { + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) != NULL ) { + TCHAR* str = JabberXmlGetAttrValue( queryNode, "xmlns" ); + if ( !lstrcmp( str, _T("http://jabber.org/protocol/disco#info"))) { + if (( item=JabberListGetItemPtr( LIST_AGENT, from )) != NULL ) { + // Use the first to set name + if (( identityNode=JabberXmlGetChild( queryNode, "identity" )) != NULL ) { + if (( str=JabberXmlGetAttrValue( identityNode, "name" )) != NULL ) + replaceStr( item->name, str ); + } + + item->cap = 0; + for ( int i=0; inumChild; i++ ) { + if (( itemNode=queryNode->child[i] )!=NULL && itemNode->name!=NULL ) { + if ( !strcmp( itemNode->name, "feature" )) { + if (( var=JabberXmlGetAttrValue( itemNode, "var" )) != NULL ) { + if ( !lstrcmp( var, _T("jabber:iq:register"))) + item->cap |= AGENT_CAP_REGISTER; + else if ( !lstrcmp( var, _T("http://jabber.org/protocol/muc"))) + item->cap |= AGENT_CAP_GROUPCHAT; + } } } } } } } + + if ( hwndJabberAgents != NULL ) + SendMessage( hwndJabberAgents, WM_JABBER_AGENT_REFRESH, 0, ( LPARAM )NULL ); +} } + +void JabberIqResultDiscoClientInfo( XmlNode *iqNode, void *userdata ) +{ + ThreadData* info = ( ThreadData* ) userdata; + XmlNode *queryNode, *itemNode; + TCHAR* type, *from, *var; + JABBER_LIST_ITEM *item; + + // RECVED: info for a specific client + // ACTION: update client cap + // ACTION: if item->ft is pending, initiate file transfer + JabberLog( " iqIdDiscoClientInfo" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( from=JabberXmlGetAttrValue( iqNode, "from" )) == NULL ) return; + + if ( lstrcmp( type, _T("result")) != 0 ) + return; + if (( item=JabberListGetItemPtr( LIST_ROSTER, from )) == NULL ) + return; + + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) != NULL ) { + TCHAR* str = JabberXmlGetAttrValue( queryNode, "xmlns" ); + if ( !lstrcmp( str, _T("http://jabber.org/protocol/disco#info"))) { + item->cap = CLIENT_CAP_READY; + for ( int i=0; inumChild; i++ ) { + if (( itemNode=queryNode->child[i] )!=NULL && itemNode->name!=NULL ) { + if ( !strcmp( itemNode->name, "feature" )) { + if (( var=JabberXmlGetAttrValue( itemNode, "var" )) != NULL ) { + if ( !lstrcmp( var, _T("http://jabber.org/protocol/si"))) + item->cap |= CLIENT_CAP_SI; + else if ( !lstrcmp( var, _T("http://jabber.org/protocol/si/profile/file-transfer"))) + item->cap |= CLIENT_CAP_SIFILE; + else if ( !lstrcmp( var, _T("http://jabber.org/protocol/bytestreams"))) + item->cap |= CLIENT_CAP_BYTESTREAM; + } } } } } } + + // Check for pending file transfer session request + if ( item->ft != NULL ) { + filetransfer* ft = item->ft; + item->ft = NULL; + if (( item->cap & CLIENT_CAP_FILE ) && ( item->cap & CLIENT_CAP_BYTESTREAM )) + JabberFtInitiate( item->jid, ft ); + else + JabberForkThread(( JABBER_THREAD_FUNC )JabberFileServerThread, 0, ft ); +} } + +void JabberIqResultGetAvatar( XmlNode *iqNode, void *userdata ) +{ + if ( !JGetByte( "EnableAvatars", TRUE )) + return; + + ThreadData* info = ( ThreadData* ) userdata; + TCHAR* type; + + // RECVED: agent list + // ACTION: refresh agent list dialog + JabberLog( " iqIdResultGetAvatar" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if ( _tcscmp( type, _T("result"))) return; + + TCHAR* from = JabberXmlGetAttrValue( iqNode, "from" ); + if ( from == NULL ) + return; + HANDLE hContact = JabberHContactFromJID( from ); + if ( hContact == NULL ) + return; + XmlNode* n = NULL; + TCHAR* mimeType = NULL; + if (JGetByte(hContact,"AvatarXVcard",0)){ + XmlNode *vCard = JabberXmlGetChild( iqNode, "vCard" ); + if (vCard == NULL) return; + vCard = JabberXmlGetChild( vCard, "PHOTO" ); + if (vCard == NULL) return; + XmlNode *typeNode = JabberXmlGetChild( vCard, "TYPE" ); + if (typeNode != NULL) mimeType = typeNode->text; + n = JabberXmlGetChild( vCard, "BINVAL" ); + }else { + XmlNode *queryNode = JabberXmlGetChild( iqNode, "query" ); + if ( queryNode == NULL ) + return; + + TCHAR* xmlns = JabberXmlGetAttrValue( queryNode, "xmlns" ); + if ( lstrcmp( xmlns, _T("jabber:iq:avatar"))) + return; + + mimeType = JabberXmlGetAttrValue( n, "mimetype" ); + + n = JabberXmlGetChild( queryNode, "data" ); + } + if ( n == NULL ) + return; + + int resultLen = 0; + char* body = JabberBase64Decode( n->text, &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: + JabberLog( "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; + + PROTO_AVATAR_INFORMATION AI; + AI.cbSize = sizeof AI; + AI.format = pictureType; + AI.hContact = hContact; + + if ( JGetByte( hContact, "AvatarType", PA_FORMAT_UNKNOWN ) != (unsigned char)pictureType ) { + JabberGetAvatarFileName( hContact, AI.filename, sizeof AI.filename ); + DeleteFileA( AI.filename ); + } + + JSetByte( hContact, "AvatarType", pictureType ); + + char buffer[ 41 ]; + uint8_t digest[20]; + SHA1Context sha; + SHA1Reset( &sha ); + SHA1Input( &sha, ( const unsigned __int8* )body, resultLen ); + SHA1Result( &sha, digest ); + for ( int i=0; i<20; i++ ) + sprintf( buffer+( i<<1 ), "%02x", digest[i] ); + JSetString( hContact, "AvatarSaved", buffer ); + + JabberGetAvatarFileName( hContact, AI.filename, sizeof AI.filename ); + + DBWriteContactSettingString( hContact, "ContactPhoto", "File", AI.filename ); + + FILE* out = fopen( AI.filename, "wb" ); + if ( out != NULL ) { + fwrite( body, resultLen, 1, out ); + fclose( out ); + JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, HANDLE( &AI ), NULL ); + JabberLog("Broadcast new avatar: %s",AI.filename); + } + else JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, HANDLE( &AI ), NULL ); + + mir_free( body ); +} -- cgit v1.2.3