diff options
author | watcherhd <watcherhd@e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb> | 2011-04-21 14:14:52 +0000 |
---|---|---|
committer | watcherhd <watcherhd@e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb> | 2011-04-21 14:14:52 +0000 |
commit | cb4a46e7fbe62d788e66ed6121c717a2d22a4d7c (patch) | |
tree | 30df260fdc5a1b5a7049c2f8cac8b7ef17513d6d /miranda-wine/protocols/JabberG/jabber_svc.cpp | |
parent | 19b6f534d2e784a1e120bf52c4aa07004798f473 (diff) |
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
Diffstat (limited to 'miranda-wine/protocols/JabberG/jabber_svc.cpp')
-rw-r--r-- | miranda-wine/protocols/JabberG/jabber_svc.cpp | 1431 |
1 files changed, 1431 insertions, 0 deletions
diff --git a/miranda-wine/protocols/JabberG/jabber_svc.cpp b/miranda-wine/protocols/JabberG/jabber_svc.cpp new file mode 100644 index 0000000..16d25a2 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_svc.cpp @@ -0,0 +1,1431 @@ +/*
+
+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_svc.cpp,v $
+Revision : $Revision: 3711 $
+Last change on : $Date: 2006-09-06 12:33:14 +0400 (Срд, 06 Сен 2006) $
+Last change by : $Author: ghazan $
+
+*/
+
+#include "jabber.h"
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "resource.h"
+#include "jabber_list.h"
+#include "jabber_iq.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberAddToList - adds a contact to the contact list
+
+static HANDLE AddToListByJID( const TCHAR* newJid, DWORD flags )
+{
+ HANDLE hContact;
+ TCHAR* jid, *nick;
+
+ JabberLog( "AddToListByJID jid = " TCHAR_STR_PARAM, newJid );
+
+ if (( hContact=JabberHContactFromJID( newJid )) == NULL ) {
+ // not already there: add
+ jid = mir_tstrdup( newJid );
+ JabberLog( "Add new jid to contact jid = " TCHAR_STR_PARAM, jid );
+ hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_ADD, 0, 0 );
+ JCallService( MS_PROTO_ADDTOCONTACT, ( WPARAM ) hContact, ( LPARAM )jabberProtoName );
+ JSetStringT( hContact, "jid", jid );
+ if (( nick=JabberNickFromJID( newJid )) == NULL )
+ nick = mir_tstrdup( newJid );
+ JSetStringT( hContact, "Nick", nick );
+ mir_free( nick );
+ mir_free( jid );
+
+ // Note that by removing or disable the "NotOnList" will trigger
+ // the plugin to add a particular contact to the roster list.
+ // See DBSettingChanged hook at the bottom part of this source file.
+ // But the add module will delete "NotOnList". So we will not do it here.
+ // Also because we need "MyHandle" and "Group" info, which are set after
+ // PS_ADDTOLIST is called but before the add dialog issue deletion of
+ // "NotOnList".
+ // If temporary add, "NotOnList" won't be deleted, and that's expected.
+ DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 );
+ if ( flags & PALF_TEMPORARY )
+ DBWriteContactSettingByte( hContact, "CList", "Hidden", 1 );
+ }
+ else {
+ // already exist
+ // Set up a dummy "NotOnList" when adding permanently only
+ if ( !( flags&PALF_TEMPORARY ))
+ DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 );
+ }
+
+ return hContact;
+}
+
+int JabberAddToList( WPARAM wParam, LPARAM lParam )
+{
+ JABBER_SEARCH_RESULT* jsr = ( JABBER_SEARCH_RESULT * ) lParam;
+ if ( jsr->hdr.cbSize != sizeof( JABBER_SEARCH_RESULT ))
+ return ( int )NULL;
+
+ return ( int )AddToListByJID( jsr->jid, wParam ); // wParam is flag e.g. PALF_TEMPORARY
+}
+
+int JabberAddToListByEvent( WPARAM wParam, LPARAM lParam )
+{
+ DBEVENTINFO dbei;
+ HANDLE hContact;
+ char* nick, *firstName, *lastName, *jid;
+
+ JabberLog( "AddToListByEvent" );
+ ZeroMemory( &dbei, sizeof( dbei ));
+ dbei.cbSize = sizeof( dbei );
+ if (( dbei.cbBlob=JCallService( MS_DB_EVENT_GETBLOBSIZE, lParam, 0 )) == ( DWORD )( -1 ))
+ return ( int )( HANDLE ) NULL;
+ if (( dbei.pBlob=( PBYTE ) alloca( dbei.cbBlob )) == NULL )
+ return ( int )( HANDLE ) NULL;
+ if ( JCallService( MS_DB_EVENT_GET, lParam, ( LPARAM )&dbei ))
+ return ( int )( HANDLE ) NULL;
+ if ( strcmp( dbei.szModule, jabberProtoName ))
+ return ( int )( HANDLE ) NULL;
+
+/*
+ // EVENTTYPE_CONTACTS is when adding from when we receive contact list ( not used in Jabber )
+ // EVENTTYPE_ADDED is when adding from when we receive "You are added" ( also not used in Jabber )
+ // Jabber will only handle the case of EVENTTYPE_AUTHREQUEST
+ // EVENTTYPE_AUTHREQUEST is when adding from the authorization request dialog
+*/
+
+ if ( dbei.eventType != EVENTTYPE_AUTHREQUEST )
+ return ( int )( HANDLE ) NULL;
+
+ nick = ( char* )( dbei.pBlob + sizeof( DWORD )+ sizeof( HANDLE ));
+ firstName = nick + strlen( nick ) + 1;
+ lastName = firstName + strlen( firstName ) + 1;
+ jid = lastName + strlen( lastName ) + 1;
+
+ #if defined( _UNICODE )
+ TCHAR* newJid = a2u( jid );
+ #else
+ TCHAR* newJid = mir_strdup( jid );
+ #endif
+ hContact = ( HANDLE ) AddToListByJID( newJid, wParam );
+ mir_free( newJid );
+ return ( int ) hContact;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberAuthAllow - processes the successful authorization
+
+int JabberAuthAllow( WPARAM wParam, LPARAM lParam )
+{
+ DBEVENTINFO dbei;
+ char* nick, *firstName, *lastName, *jid;
+
+ if ( !jabberOnline )
+ return 1;
+
+ memset( &dbei, sizeof( dbei ), 0 );
+ dbei.cbSize = sizeof( dbei );
+ if (( dbei.cbBlob=JCallService( MS_DB_EVENT_GETBLOBSIZE, wParam, 0 )) == ( DWORD )( -1 ))
+ return 1;
+ if (( dbei.pBlob=( PBYTE )alloca( dbei.cbBlob )) == NULL )
+ return 1;
+ if ( JCallService( MS_DB_EVENT_GET, wParam, ( LPARAM )&dbei ))
+ return 1;
+ if ( dbei.eventType != EVENTTYPE_AUTHREQUEST )
+ return 1;
+ if ( strcmp( dbei.szModule, jabberProtoName ))
+ return 1;
+
+ nick = ( char* )( dbei.pBlob + sizeof( DWORD )+ sizeof( HANDLE ));
+ firstName = nick + strlen( nick ) + 1;
+ lastName = firstName + strlen( firstName ) + 1;
+ jid = lastName + strlen( lastName ) + 1;
+
+ JabberLog( "Send 'authorization allowed' to " TCHAR_STR_PARAM, jid );
+
+ XmlNode presence( "presence" ); presence.addAttr( "to", jid ); presence.addAttr( "type", "subscribed" );
+ JabberSend( jabberThreadInfo->s, presence );
+
+ #if defined( _UNICODE )
+ TCHAR* newJid = a2u( jid );
+ #else
+ TCHAR* newJid = mir_strdup( jid );
+ #endif
+
+ // Automatically add this user to my roster if option is enabled
+ if ( JGetByte( "AutoAdd", TRUE ) == TRUE ) {
+ HANDLE hContact;
+ JABBER_LIST_ITEM *item;
+
+ if (( item = JabberListGetItemPtr( LIST_ROSTER, newJid )) == NULL || ( item->subscription != SUB_BOTH && item->subscription != SUB_TO )) {
+ JabberLog( "Try adding contact automatically jid = " TCHAR_STR_PARAM, jid );
+ if (( hContact=AddToListByJID( newJid, 0 )) != NULL ) {
+ // Trigger actual add by removing the "NotOnList" added by AddToListByJID()
+ // See AddToListByJID() and JabberDbSettingChanged().
+ DBDeleteContactSetting( hContact, "CList", "NotOnList" );
+ } } }
+
+ mir_free( newJid );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberAuthDeny - handles the unsuccessful authorization
+
+int JabberAuthDeny( WPARAM wParam, LPARAM lParam )
+{
+ DBEVENTINFO dbei;
+ char* nick, *firstName, *lastName, *jid;
+
+ if ( !jabberOnline )
+ return 1;
+
+ JabberLog( "Entering AuthDeny" );
+ memset( &dbei, sizeof( dbei ), 0 );
+ dbei.cbSize = sizeof( dbei );
+ if (( dbei.cbBlob=JCallService( MS_DB_EVENT_GETBLOBSIZE, wParam, 0 )) == ( DWORD )( -1 ))
+ return 1;
+ if (( dbei.pBlob=( PBYTE ) mir_alloc( dbei.cbBlob )) == NULL )
+ return 1;
+ if ( JCallService( MS_DB_EVENT_GET, wParam, ( LPARAM )&dbei )) {
+ mir_free( dbei.pBlob );
+ return 1;
+ }
+ if ( dbei.eventType != EVENTTYPE_AUTHREQUEST ) {
+ mir_free( dbei.pBlob );
+ return 1;
+ }
+ if ( strcmp( dbei.szModule, jabberProtoName )) {
+ mir_free( dbei.pBlob );
+ return 1;
+ }
+
+ nick = ( char* )( dbei.pBlob + sizeof( DWORD )+ sizeof( HANDLE ));
+ firstName = nick + strlen( nick ) + 1;
+ lastName = firstName + strlen( firstName ) + 1;
+ jid = lastName + strlen( lastName ) + 1;
+
+ JabberLog( "Send 'authorization denied' to " TCHAR_STR_PARAM, jid );
+
+ XmlNode presence( "presence" ); presence.addAttr( "to", jid ); presence.addAttr( "type", "unsubscribed" );
+ JabberSend( jabberThreadInfo->s, presence );
+
+ mir_free( dbei.pBlob );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberBasicSearch - searches the contact by JID
+
+struct JABBER_SEARCH_BASIC
+{ int hSearch;
+ char jid[128];
+};
+
+static void __cdecl JabberBasicSearchThread( JABBER_SEARCH_BASIC *jsb )
+{
+ SleepEx( 100, TRUE );
+
+ JABBER_SEARCH_RESULT jsr = { 0 };
+ jsr.hdr.cbSize = sizeof( JABBER_SEARCH_RESULT );
+ jsr.hdr.nick = "";
+ jsr.hdr.firstName = "";
+ jsr.hdr.lastName = "";
+ jsr.hdr.email = jsb->jid;
+ #if defined( _UNICODE )
+ TCHAR* jid = a2u(jsb->jid);
+ _tcsncpy( jsr.jid, jid, SIZEOF( jsr.jid ));
+ mir_free( jid );
+ #else
+ strncpy( jsr.jid, jsb->jid, SIZEOF( jsr.jid ));
+ #endif
+ jsr.jid[SIZEOF( jsr.jid )-1] = '\0';
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, ( HANDLE ) jsb->hSearch, ( LPARAM )&jsr );
+ JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) jsb->hSearch, 0 );
+ mir_free( jsb );
+}
+
+int JabberBasicSearch( WPARAM wParam, LPARAM lParam )
+{
+ char* szJid = ( char* )lParam;
+ JabberLog( "JabberBasicSearch called with lParam = " TCHAR_STR_PARAM, szJid );
+
+ JABBER_SEARCH_BASIC *jsb;
+ if ( !jabberOnline || ( jsb=( JABBER_SEARCH_BASIC * ) mir_alloc( sizeof( JABBER_SEARCH_BASIC )) )==NULL )
+ return 0;
+
+ jsb->hSearch = JabberSerialNext();
+ if ( strchr( szJid, '@' ) == NULL ) {
+ char szServer[ 100 ];
+ if ( JGetStaticString( "LoginServer", NULL, szServer, sizeof szServer ))
+ strcpy( szServer, "jabber.org" );
+
+ mir_snprintf( jsb->jid, SIZEOF(jsb->jid), "%s@%s", szJid, szServer );
+ }
+ else strncpy( jsb->jid, szJid, SIZEOF(jsb->jid));
+
+ JabberForkThread(( JABBER_THREAD_FUNC )JabberBasicSearchThread, 0, jsb );
+ return jsb->hSearch;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberContactDeleted - processes a contact deletion
+
+int JabberContactDeleted( WPARAM wParam, LPARAM lParam )
+{
+ if( !jabberOnline ) // should never happen
+ return 0;
+
+ char* szProto = ( char* )JCallService( MS_PROTO_GETCONTACTBASEPROTO, wParam, 0 );
+ if ( szProto==NULL || strcmp( szProto, jabberProtoName ))
+ return 0;
+
+ DBVARIANT dbv;
+ if ( !JGetStringT(( HANDLE ) wParam, JGetByte( (HANDLE ) wParam, "ChatRoom", 0 )?"ChatRoomID":"jid", &dbv )) {
+ TCHAR* jid, *p, *q = NULL;
+
+ jid = dbv.ptszVal;
+ if (( p = _tcschr( jid, '@' )) != NULL )
+ if (( q = _tcschr( p, '/' )) != NULL )
+ *q = '\0';
+
+ if ( !JabberListExist( LIST_CHATROOM, jid ) || q == NULL )
+ if ( JabberListExist( LIST_ROSTER, jid )) {
+ // Remove from roster, server also handles the presence unsubscription process.
+ XmlNodeIq iq( "set" );
+ XmlNode* query = iq.addQuery( "jabber:iq:roster" );
+ XmlNode* item = query->addChild( "item" ); item->addAttr( "jid", jid ); item->addAttr( "subscription", "remove" );
+ JabberSend( jabberThreadInfo->s, iq );
+ }
+
+ JFreeVariant( &dbv );
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberDbSettingChanged - process database changes
+
+static TCHAR* sttSettingToTchar( DBCONTACTWRITESETTING* cws )
+{
+ switch( cws->value.type ) {
+ case DBVT_ASCIIZ:
+ #if defined( _UNICODE )
+ return a2u( cws->value.pszVal );
+ #else
+ return mir_strdup( cws->value.pszVal );
+ #endif
+
+ case DBVT_UTF8:
+ #if defined( _UNICODE )
+ { TCHAR* result;
+ JabberUtf8Decode( NEWSTR_ALLOCA(cws->value.pszVal), &result );
+ return result;
+ }
+ #else
+ return mir_strdup( JabberUtf8Decode( NEWSTR_ALLOCA(cws->value.pszVal), NULL ));
+ #endif
+ }
+ return NULL;
+}
+
+static void sttRenameGroup( DBCONTACTWRITESETTING* cws, HANDLE hContact )
+{
+ DBVARIANT jid, dbv;
+ if ( JGetStringT( hContact, "jid", &jid ))
+ return;
+
+ JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_ROSTER, jid.ptszVal );
+ JFreeVariant( &jid );
+ if ( item == NULL )
+ return;
+
+ TCHAR* nick;
+ if ( !DBGetContactSettingTString( hContact, "CList", "MyHandle", &dbv )) {
+ nick = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else if ( !JGetStringT( hContact, "Nick", &dbv )) {
+ nick = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else nick = JabberNickFromJID( item->jid );
+ if ( nick == NULL )
+ return;
+
+ if ( cws->value.type == DBVT_DELETED ) {
+ if ( item->group != NULL ) {
+ JabberLog( "Group set to nothing" );
+ JabberAddContactToRoster( item->jid, nick, NULL, item->subscription );
+ }
+ }
+ else {
+ TCHAR* p = sttSettingToTchar( cws );
+ if ( cws->value.pszVal != NULL && lstrcmp( p, item->group )) {
+ JabberLog( "Group set to " TCHAR_STR_PARAM, p );
+ if ( p )
+ JabberAddContactToRoster( item->jid, nick, p, item->subscription );
+ }
+ mir_free( p );
+ }
+ mir_free( nick );
+}
+
+static void sttRenameContact( DBCONTACTWRITESETTING* cws, HANDLE hContact )
+{
+ DBVARIANT jid;
+ if ( JGetStringT( hContact, "jid", &jid ))
+ return;
+
+ JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_ROSTER, jid.ptszVal );
+ JFreeVariant( &jid );
+ if ( item == NULL )
+ return;
+
+ if ( cws->value.type == DBVT_DELETED ) {
+ TCHAR* nick = ( TCHAR* )JCallService( MS_CLIST_GETCONTACTDISPLAYNAME, ( WPARAM )hContact, GCDNF_NOMYHANDLE | GCDNF_TCHAR );
+ JabberAddContactToRoster( item->jid, nick, item->group, item->subscription );
+ mir_free(nick);
+ return;
+ }
+
+ TCHAR* newNick = sttSettingToTchar( cws );
+ if ( newNick ) {
+ if ( lstrcmp( item->nick, newNick )) {
+ JabberLog( "Renaming contact " TCHAR_STR_PARAM ": " TCHAR_STR_PARAM " -> " TCHAR_STR_PARAM, item->jid, item->nick, newNick );
+ JabberAddContactToRoster( item->jid, newNick, item->group, item->subscription );
+ }
+ mir_free( newNick );
+} }
+
+void sttAddContactForever( DBCONTACTWRITESETTING* cws, HANDLE hContact )
+{
+ if ( cws->value.type != DBVT_DELETED && !( cws->value.type==DBVT_BYTE && cws->value.bVal==0 ))
+ return;
+
+ DBVARIANT jid, dbv;
+ if ( JGetStringT( hContact, "jid", &jid ))
+ return;
+
+ TCHAR *nick;
+ JabberLog( "Add " TCHAR_STR_PARAM " permanently to list", jid.pszVal );
+ if ( !DBGetContactSettingTString( hContact, "CList", "MyHandle", &dbv )) {
+ nick = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else if ( !JGetStringT( hContact, "Nick", &dbv )) {
+ nick = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+ else nick = JabberNickFromJID( jid.ptszVal );
+ if ( nick == NULL ) {
+ JFreeVariant( &jid );
+ return;
+ }
+
+ JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_ROSTER, jid.ptszVal );
+ JABBER_SUBSCRIPTION subscription = ( item == NULL ) ? SUB_NONE : item->subscription;
+
+ if ( !DBGetContactSettingTString( hContact, "CList", "Group", &dbv )) {
+ JabberAddContactToRoster( jid.ptszVal, nick, dbv.ptszVal, subscription );
+ JFreeVariant( &dbv );
+ }
+ else JabberAddContactToRoster( jid.ptszVal, nick, NULL, subscription );
+
+ XmlNode presence( "presence" ); presence.addAttr( "to", jid.ptszVal ); presence.addAttr( "type", "subscribe" );
+ JabberSend( jabberThreadInfo->s, presence );
+
+ mir_free( nick );
+ DBDeleteContactSetting( hContact, "CList", "Hidden" );
+ JFreeVariant( &jid );
+}
+
+int JabberDbSettingChanged( WPARAM wParam, LPARAM lParam )
+{
+ HANDLE hContact = ( HANDLE ) wParam;
+ if ( hContact == NULL || !jabberConnected )
+ return 0;
+
+ DBCONTACTWRITESETTING* cws = ( DBCONTACTWRITESETTING* )lParam;
+ if ( strcmp( cws->szModule, "CList" ))
+ return 0;
+
+ char* szProto = ( char* )JCallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
+ if ( szProto == NULL || strcmp( szProto, jabberProtoName ))
+ return 0;
+
+ if ( !strcmp( cws->szSetting, "Group" ))
+ sttRenameGroup( cws, hContact );
+ else if ( !strcmp( cws->szSetting, "MyHandle" ))
+ sttRenameContact( cws, hContact );
+ else if ( !strcmp( cws->szSetting, "NotOnList" ))
+ sttAddContactForever( cws, hContact );
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberFileAllow - starts a file transfer
+
+int JabberFileAllow( WPARAM wParam, LPARAM lParam )
+{
+ if ( !jabberOnline ) return 0;
+
+ CCSDATA *ccs = ( CCSDATA * ) lParam;
+ filetransfer* ft = ( filetransfer* ) ccs->wParam;
+ ft->std.workingDir = mir_strdup(( char* )ccs->lParam );
+ int len = strlen( ft->std.workingDir )-1;
+ if ( ft->std.workingDir[len] == '//' || ft->std.workingDir[len] == '\\' )
+ ft->std.workingDir[len] = 0;
+
+ switch ( ft->type ) {
+ case FT_OOB:
+ JabberForkThread(( JABBER_THREAD_FUNC )JabberFileReceiveThread, 0, ft );
+ break;
+ case FT_BYTESTREAM:
+ JabberFtAcceptSiRequest( ft );
+ break;
+ }
+ return ccs->wParam;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberFileCancel - cancels a file transfer
+
+int JabberFileCancel( WPARAM wParam, LPARAM lParam )
+{
+ CCSDATA *ccs = ( CCSDATA * ) lParam;
+ filetransfer* ft = ( filetransfer* ) ccs->wParam;
+ HANDLE hEvent;
+
+ JabberLog( "Invoking FileCancel()" );
+ if ( ft->type == FT_OOB ) {
+ if ( ft->s ) {
+ JabberLog( "FT canceled" );
+ JabberLog( "Closing ft->s = %d", ft->s );
+ ft->state = FT_ERROR;
+ Netlib_CloseHandle( ft->s );
+ ft->s = NULL;
+ if ( ft->hFileEvent != NULL ) {
+ hEvent = ft->hFileEvent;
+ ft->hFileEvent = NULL;
+ SetEvent( hEvent );
+ }
+ JabberLog( "ft->s is now NULL, ft->state is now FT_ERROR" );
+ }
+ }
+ else JabberFtCancel( ft );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberFileDeny - denies a file transfer
+
+int JabberFileDeny( WPARAM wParam, LPARAM lParam )
+{
+ if ( !jabberOnline ) return 1;
+
+ CCSDATA *ccs = ( CCSDATA * ) lParam;
+ filetransfer* ft = ( filetransfer* )ccs->wParam;
+
+ XmlNodeIq iq( "error", ft->iqId, ft->jid );
+
+ switch ( ft->type ) {
+ case FT_OOB:
+ { XmlNode* e = iq.addChild( "error", _T("File transfer refused"));
+ e->addAttr( "code", 406 );
+ JabberSend( jabberThreadInfo->s, iq );
+ }
+ break;
+ case FT_BYTESTREAM:
+ { XmlNode* e = iq.addChild( "error", _T("File transfer refused"));
+ e->addAttr( "code", 403 ); e->addAttr( "type", "cancel" );
+ XmlNode* f = e->addChild( "forbidden" ); f->addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas" );
+ XmlNode* t = f->addChild( "text", "File transfer refused" ); t->addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas" );
+ JabberSend( jabberThreadInfo->s, iq );
+ }
+ break;
+ }
+ delete ft;
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberFileResume - processes file renaming etc
+
+int JabberFileResume( WPARAM wParam, LPARAM lParam )
+{
+ filetransfer* ft = ( filetransfer* )wParam;
+ if ( !jabberConnected || ft == NULL )
+ return 1;
+
+ PROTOFILERESUME *pfr = (PROTOFILERESUME*)lParam;
+ if ( pfr->action == FILERESUME_RENAME ) {
+ if ( ft->wszFileName != NULL ) {
+ mir_free( ft->wszFileName );
+ ft->wszFileName = NULL;
+ }
+
+ replaceStr( ft->std.currentFile, pfr->szFilename );
+ }
+
+ SetEvent( ft->hWaitEvent );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetAvatarInfo - retrieves the avatar info
+
+static int JabberGetAvatarInfo(WPARAM wParam,LPARAM lParam)
+{
+ if ( !JGetByte( "EnableAvatars", TRUE ))
+ return GAIR_NOAVATAR;
+
+ PROTO_AVATAR_INFORMATION* AI = ( PROTO_AVATAR_INFORMATION* )lParam;
+
+ char szHashValue[ MAX_PATH ];
+ if ( JGetStaticString( "AvatarHash", AI->hContact, szHashValue, sizeof szHashValue )) {
+ JabberLog( "No avatar" );
+ return GAIR_NOAVATAR;
+ }
+
+ JabberGetAvatarFileName( AI->hContact, AI->filename, sizeof AI->filename );
+ AI->format = ( AI->hContact == NULL ) ? PA_FORMAT_PNG : JGetByte( AI->hContact, "AvatarType", 0 );
+
+ if ( ::access( AI->filename, 0 ) == 0 ) {
+ char szSavedHash[ 256 ];
+ if ( !JGetStaticString( "AvatarSaved", AI->hContact, szSavedHash, sizeof szSavedHash )) {
+ if ( !strcmp( szSavedHash, szHashValue )) {
+ JabberLog( "Avatar is Ok: %s == %s", szSavedHash, szHashValue );
+ return GAIR_SUCCESS;
+ } } }
+
+ if (( wParam & GAIF_FORCE ) != 0 && AI->hContact != NULL && jabberOnline ) {
+ DBVARIANT dbv;
+ if ( !JGetStringT( AI->hContact, "jid", &dbv )) {
+ JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal );
+ if ( item != NULL ) {
+ TCHAR szJid[ 512 ];
+ BOOL isXVcard = JGetByte(AI->hContact,"AvatarXVcard",0);
+ if ( (item->resourceCount != NULL) & (!isXVcard)){
+ TCHAR *bestResName = JabberListGetBestClientResourceNamePtr(dbv.ptszVal);
+ mir_sntprintf( szJid, SIZEOF( szJid ), bestResName?_T("%s/%s"):_T("%s"), dbv.ptszVal, bestResName );
+ }else
+ lstrcpyn( szJid, dbv.ptszVal, SIZEOF( szJid ));
+
+ JabberLog( "Rereading %s for " TCHAR_STR_PARAM, isXVcard?"vcard-temp":"jabber:iq:avatar", szJid );
+
+ int iqId = JabberSerialNext();
+ JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultGetAvatar );
+
+ XmlNodeIq iq( "get", iqId, szJid );
+ if (isXVcard) {
+ XmlNode* vs = iq.addChild( "vCard" ); vs->addAttr( "xmlns", "vcard-temp" );
+ } else XmlNode* query = iq.addQuery( isXVcard?"":"jabber:iq:avatar" );
+ JabberSend( jabberThreadInfo->s, iq );
+
+ JFreeVariant( &dbv );
+ return GAIR_WAITFOR;
+ } } }
+
+ JabberLog( "No avatar" );
+ return GAIR_NOAVATAR;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetAwayMsg - returns a contact's away message
+
+static void __cdecl JabberGetAwayMsgThread( HANDLE hContact )
+{
+ DBVARIANT dbv;
+ JABBER_LIST_ITEM *item;
+ JABBER_RESOURCE_STATUS *r;
+ int i, len, msgCount;
+
+ if ( !JGetStringT( hContact, "jid", &dbv )) {
+ if (( item=JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal )) != NULL ) {
+ JFreeVariant( &dbv );
+ if ( item->resourceCount > 0 ) {
+ JabberLog( "resourceCount > 0" );
+ r = item->resource;
+ len = msgCount = 0;
+ for ( i=0; i<item->resourceCount; i++ ) {
+ if ( r[i].statusMessage ) {
+ msgCount++;
+ len += ( _tcslen( r[i].resourceName ) + _tcslen( r[i].statusMessage ) + 8 );
+ } }
+
+ TCHAR* str = ( TCHAR* )alloca( sizeof( TCHAR )*( len+1 ));
+ str[0] = str[len] = '\0';
+ for ( i=0; i < item->resourceCount; i++ ) {
+ if ( r[i].statusMessage ) {
+ if ( str[0] != '\0' ) _tcscat( str, _T("\r\n" ));
+ if ( msgCount > 1 ) {
+ _tcscat( str, _T("( "));
+ _tcscat( str, r[i].resourceName );
+ _tcscat( str, _T(" ): "));
+ }
+ _tcscat( str, r[i].statusMessage );
+ } }
+
+ #if defined( _UNICODE )
+ char* msg = u2a(str);
+ #else
+ char* msg = str;
+ #endif
+ JSendBroadcast( hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, ( HANDLE ) 1, ( LPARAM )msg );
+ #if defined( _UNICODE )
+ mir_free(msg);
+ #endif
+ return;
+ }
+
+ if ( item->statusMessage != NULL ) {
+ #if defined( _UNICODE )
+ char* msg = u2a(item->statusMessage);
+ #else
+ char* msg = item->statusMessage;
+ #endif
+ JSendBroadcast( hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, ( HANDLE ) 1, ( LPARAM )msg );
+ #if defined( _UNICODE )
+ mir_free(msg);
+ #endif
+ return;
+ }
+ }
+ else JFreeVariant( &dbv );
+ }
+
+ JSendBroadcast( hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, ( HANDLE ) 1, ( LPARAM )"" );
+}
+
+int JabberGetAwayMsg( WPARAM wParam, LPARAM lParam )
+{
+ CCSDATA *ccs = ( CCSDATA * ) lParam;
+
+ JabberLog( "GetAwayMsg called, wParam=%d lParam=%d", wParam, lParam );
+ JabberForkThread( JabberGetAwayMsgThread, 0, ( void * ) ccs->hContact );
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetCaps - return protocol capabilities bits
+
+int JabberGetCaps( WPARAM wParam, LPARAM lParam )
+{
+ switch( wParam ) {
+ case PFLAGNUM_1:
+ return PF1_IM|PF1_AUTHREQ|PF1_SERVERCLIST|PF1_MODEMSG|PF1_BASICSEARCH|PF1_SEARCHBYEMAIL|PF1_SEARCHBYNAME|PF1_FILE|PF1_VISLIST|PF1_INVISLIST;
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_HEAVYDND | PF2_FREECHAT;
+ case PFLAGNUM_3:
+ return PF2_ONLINE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_HEAVYDND | PF2_FREECHAT;
+ case PFLAGNUM_4:
+ return PF4_FORCEAUTH | PF4_NOCUSTOMAUTH | PF4_SUPPORTTYPING | PF4_AVATARS;
+ case PFLAG_UNIQUEIDTEXT:
+ return ( int ) JTranslate( "JID" );
+ case PFLAG_UNIQUEIDSETTING:
+ return ( int ) "jid";
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetInfo - retrieves a contact info
+
+int JabberGetInfo( WPARAM wParam, LPARAM lParam )
+{
+ if ( !jabberOnline )
+ return 1;
+
+ CCSDATA *ccs = ( CCSDATA * ) lParam;
+ int result = 1;
+ DBVARIANT dbv;
+ if ( !JGetStringT( ccs->hContact, "jid", &dbv )) {
+ result = JabberSendGetVcard( dbv.ptszVal );
+ JFreeVariant( &dbv );
+ }
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetName - returns the protocol name
+
+int JabberGetName( WPARAM wParam, LPARAM lParam )
+{
+ lstrcpynA(( char* )lParam, jabberModuleName, wParam );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberGetStatus - returns the protocol status
+
+int JabberGetStatus( WPARAM wParam, LPARAM lParam )
+{
+ return jabberStatus;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberLoadIcon - loads an icon for the contact list
+
+int JabberLoadIcon( WPARAM wParam, LPARAM lParam )
+{
+ if (( wParam&0xffff ) == PLI_PROTOCOL )
+ return ( int ) LoadImage( hInst, MAKEINTRESOURCE( IDI_JABBER ), IMAGE_ICON, GetSystemMetrics( wParam&PLIF_SMALL?SM_CXSMICON:SM_CXICON ), GetSystemMetrics( wParam&PLIF_SMALL?SM_CYSMICON:SM_CYICON ), 0 );
+ else
+ return ( int ) ( HICON ) NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberRecvFile - receives a file
+
+int JabberRecvFile( WPARAM wParam, LPARAM lParam )
+{
+ CCSDATA *ccs = ( CCSDATA * ) lParam;
+ PROTORECVEVENT *pre = ( PROTORECVEVENT * ) ccs->lParam;
+ char* szFile = pre->szMessage + sizeof( DWORD );
+ char* szDesc = szFile + strlen( szFile ) + 1;
+ JabberLog( "Description = %s", szDesc );
+
+ DBDeleteContactSetting( ccs->hContact, "CList", "Hidden" );
+
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof( dbei );
+ dbei.szModule = jabberProtoName;
+ dbei.timestamp = pre->timestamp;
+ dbei.flags = pre->flags & ( PREF_CREATEREAD ? DBEF_READ : 0 );
+ dbei.eventType = EVENTTYPE_FILE;
+ dbei.cbBlob = sizeof( DWORD )+ strlen( szFile ) + strlen( szDesc ) + 2;
+ dbei.pBlob = ( PBYTE ) pre->szMessage;
+ JCallService( MS_DB_EVENT_ADD, ( WPARAM ) ccs->hContact, ( LPARAM )&dbei );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberRecvMessage - receives a message
+
+int JabberRecvMessage( WPARAM wParam, LPARAM lParam )
+{
+ CCSDATA *ccs = ( CCSDATA* )lParam;
+ PROTORECVEVENT *pre = ( PROTORECVEVENT* )ccs->lParam;
+
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof( dbei );
+ dbei.szModule = jabberProtoName;
+ dbei.timestamp = pre->timestamp;
+ dbei.flags = pre->flags&PREF_CREATEREAD?DBEF_READ:0;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.cbBlob = strlen( pre->szMessage ) + 1;
+ if ( pre->flags & PREF_UNICODE )
+ dbei.cbBlob *= ( sizeof( wchar_t )+1 );
+
+ dbei.pBlob = ( PBYTE ) pre->szMessage;
+ JCallService( MS_DB_EVENT_ADD, ( WPARAM ) ccs->hContact, ( LPARAM )&dbei );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSearchByEmail - searches the contact by its e-mail
+
+int JabberSearchByEmail( WPARAM wParam, LPARAM lParam )
+{
+ if ( !jabberOnline ) return 0;
+ if (( char* )lParam == NULL ) return 0;
+
+ char szServerName[100];
+ if ( JGetStaticString( "Jud", NULL, szServerName, sizeof szServerName ))
+ strcpy( szServerName, "users.jabber.org" );
+
+ int iqId = JabberSerialNext();
+ JabberIqAdd( iqId, IQ_PROC_GETSEARCH, JabberIqResultSetSearch );
+
+ XmlNodeIq iq( "set", iqId, szServerName );
+ XmlNode* query = iq.addQuery( "jabber:iq:search" );
+ query->addChild( "email", ( char* )lParam );
+ JabberSend( jabberThreadInfo->s, iq );
+ return iqId;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSearchByName - searches the contact by its first or last name, or by a nickname
+
+int JabberSearchByName( WPARAM wParam, LPARAM lParam )
+{
+ if ( !jabberOnline ) return 0;
+
+ PROTOSEARCHBYNAME *psbn = ( PROTOSEARCHBYNAME * ) lParam;
+ BOOL bIsExtFormat = JGetByte( "ExtendedSearch", TRUE );
+
+ char szServerName[100];
+ if ( JGetStaticString( "Jud", NULL, szServerName, sizeof szServerName ))
+ strcpy( szServerName, "users.jabber.org" );
+
+ int iqId = JabberSerialNext();
+ XmlNodeIq iq( "set", iqId, szServerName );
+ XmlNode* query = iq.addChild( "query" ), *field, *x;
+ query->addAttr( "xmlns", "jabber:iq:search" );
+
+ if ( bIsExtFormat ) {
+ JabberIqAdd( iqId, IQ_PROC_GETSEARCH, JabberIqResultExtSearch );
+
+ iq.addAttr( "xml:lang", "en" );
+ x = query->addChild( "x" ); x->addAttr( "xmlns", "jabber:x:data" ); x->addAttr( "type", "submit" );
+ }
+ else JabberIqAdd( iqId, IQ_PROC_GETSEARCH, JabberIqResultSetSearch );
+
+ if ( psbn->pszNick[0] != '\0' ) {
+ if ( bIsExtFormat ) {
+ field = x->addChild( "field" ); field->addAttr( "var", "user" );
+ field->addChild( "value", psbn->pszNick );
+ }
+ else query->addChild( "nick", psbn->pszNick );
+ }
+
+ if ( psbn->pszFirstName[0] != '\0' ) {
+ if ( bIsExtFormat ) {
+ field = x->addChild( "field" ); field->addAttr( "var", "fn" );
+ field->addChild( "value", psbn->pszFirstName );
+ }
+ else query->addChild( "first", psbn->pszFirstName );
+ }
+
+ if ( psbn->pszLastName[0] != '\0' ) {
+ if ( bIsExtFormat ) {
+ field = x->addChild( "field" ); field->addAttr( "var", "given" );
+ field->addChild( "value", psbn->pszLastName );
+ }
+ else query->addChild( "last", psbn->pszLastName );
+ }
+
+ JabberSend( jabberThreadInfo->s, iq );
+ return iqId;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSendFile - sends a file
+
+int JabberSendFile( WPARAM wParam, LPARAM lParam )
+{
+ if ( !jabberOnline ) return 0;
+
+ CCSDATA *ccs = ( CCSDATA * ) lParam;
+ if ( JGetWord( ccs->hContact, "Status", ID_STATUS_OFFLINE ) == ID_STATUS_OFFLINE )
+ return 0;
+
+ DBVARIANT dbv;
+ if ( JGetStringT( ccs->hContact, "jid", &dbv ))
+ return 0;
+
+ char* *files = ( char* * ) ccs->lParam;
+ int i, j;
+ struct _stat statbuf;
+ JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal );
+ if ( item == NULL )
+ return 0;
+
+ // Check if another file transfer session request is pending ( waiting for disco result )
+ if ( item->ft != NULL ) return 0;
+
+ filetransfer* ft = new filetransfer;
+ ft->std.hContact = ccs->hContact;
+ while( files[ ft->std.totalFiles ] != NULL )
+ ft->std.totalFiles++;
+
+ ft->std.files = ( char** ) mir_alloc( sizeof( char* )* ft->std.totalFiles );
+ ft->fileSize = ( long* ) mir_alloc( sizeof( long ) * ft->std.totalFiles );
+ for( i=j=0; i < ft->std.totalFiles; i++ ) {
+ if ( _stat( files[i], &statbuf ))
+ JabberLog( "'%s' is an invalid filename", files[i] );
+ else {
+ ft->std.files[j] = mir_strdup( files[i] );
+ ft->fileSize[j] = statbuf.st_size;
+ j++;
+ ft->std.totalBytes += statbuf.st_size;
+ } }
+
+ ft->std.currentFile = mir_strdup( files[0] );
+ ft->szDescription = mir_strdup(( char* )ccs->wParam );
+ ft->jid = mir_tstrdup( dbv.ptszVal );
+ JFreeVariant( &dbv );
+
+ if (( item->cap & CLIENT_CAP_READY ) == 0 ) {
+ int iqId;
+ TCHAR* rs;
+
+ // Probe client capability
+ if (( rs=JabberListGetBestClientResourceNamePtr( item->jid )) != NULL ) {
+ item->ft = ft;
+ iqId = JabberSerialNext();
+ JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultDiscoClientInfo );
+
+ TCHAR jid[ 200 ];
+ mir_sntprintf( jid, SIZEOF(jid), _T("%s/%s"), item->jid, rs );
+ XmlNodeIq iq( "get", iqId, jid );
+ XmlNode* query = iq.addQuery( "http://jabber.org/protocol/disco#info" );
+ JabberSend( jabberThreadInfo->s, iq );
+ }
+ }
+ else if (( item->cap & CLIENT_CAP_FILE ) && ( item->cap & CLIENT_CAP_BYTESTREAM ))
+ // Use the new standard file transfer
+ JabberFtInitiate( item->jid, ft );
+ else // Use the jabber:iq:oob file transfer
+ JabberForkThread(( JABBER_THREAD_FUNC )JabberFileServerThread, 0, ft );
+
+ return ( int )( HANDLE ) ft;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSendMessage - sends a message
+
+static void __cdecl JabberSendMessageAckThread( HANDLE hContact )
+{
+ SleepEx( 10, TRUE );
+ JabberLog( "Broadcast ACK" );
+ JSendBroadcast( hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, ( HANDLE ) 1, 0 );
+ JabberLog( "Returning from thread" );
+}
+
+static char PGP_PROLOG[] = "-----BEGIN PGP MESSAGE-----\r\n\r\n";
+static char PGP_EPILOG[] = "\r\n-----END PGP MESSAGE-----\r\n";
+
+int JabberSendMessage( WPARAM wParam, LPARAM lParam )
+{
+ CCSDATA *ccs = ( CCSDATA * ) lParam;
+ JABBER_LIST_ITEM *item;
+ int id;
+
+ DBVARIANT dbv;
+ if ( !jabberOnline || JGetStringT( ccs->hContact, "jid", &dbv )) {
+ JSendBroadcast( ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, ( HANDLE ) 1, 0 );
+ return 0;
+ }
+
+ char* pszSrc = ( char* )ccs->lParam, *msg;
+ int isEncrypted;
+
+ char* pdest = strstr( pszSrc, PGP_PROLOG );//pdest-string+1 is index of first occurence
+ if ( pdest != NULL ) {
+ pdest = strstr( pszSrc, PGP_EPILOG );
+ int result = ( pdest ) ? strlen( PGP_PROLOG ) : 0;
+
+ char* tempstring = ( char* )alloca( strlen( pszSrc ));
+ strncpy( tempstring, pszSrc+strlen(PGP_PROLOG), strlen(pszSrc)-strlen(PGP_EPILOG)-result );
+ pszSrc = tempstring;
+ isEncrypted = 1;
+ }
+ else isEncrypted = 0;
+
+ if ( ccs->wParam & PREF_UNICODE )
+ msg = JabberTextEncodeW(( wchar_t* )&pszSrc[ strlen( pszSrc )+1 ] );
+ else
+ msg = JabberTextEncode( pszSrc );
+
+ if ( msg != NULL ) {
+ char msgType[ 16 ];
+ if ( JabberListExist( LIST_CHATROOM, dbv.ptszVal ) && _tcschr( dbv.ptszVal, '/' )==NULL )
+ strcpy( msgType, "groupchat" );
+ else
+ strcpy( msgType, "chat" );
+
+ XmlNode m( "message" ); m.addAttr( "type", msgType );
+ if ( !isEncrypted ) {
+ XmlNode* body = m.addChild( "body" );
+ body->sendText = msg;
+ }
+ else {
+ m.addChild( "body", "[This message is encrypted.]" );
+ XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:encrypted" );
+ x->sendText = msg;
+ }
+
+ XmlNode* active = m.addChild( "active" ); active->addAttr( "xmlns", _T("http://jabber.org/protocol/chatstates"));
+
+ if ( !strcmp( msgType, "groupchat" ) || JGetByte( "MsgAck", FALSE ) == FALSE ) {
+ if ( !strcmp( msgType, "groupchat" ))
+ m.addAttr( "to", dbv.ptszVal );
+ else {
+ id = JabberSerialNext();
+ TCHAR szClientJid[ 256 ];
+ JabberGetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
+
+ m.addAttr( "to", szClientJid ); m.addAttrID( id );
+ XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:event" ); x->addChild( "composing" );
+ }
+
+ JabberSend( jabberThreadInfo->s, m );
+ JabberForkThread( JabberSendMessageAckThread, 0, ( void* )ccs->hContact );
+ }
+ else {
+ id = JabberSerialNext();
+ if (( item=JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal )) != NULL )
+ item->idMsgAckPending = id;
+
+ TCHAR szClientJid[ 256 ];
+ JabberGetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
+ m.addAttr( "to", szClientJid ); m.addAttrID( id );
+
+ XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:event" );
+ x->addChild( "composing" ); x->addChild( "delivered" ); x->addChild( "offline" );
+ JabberSend( jabberThreadInfo->s, m );
+ } }
+
+ JFreeVariant( &dbv );
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSetApparentMode - sets the visibility status
+
+int JabberSetApparentMode( WPARAM wParam, LPARAM lParam )
+{
+ CCSDATA *ccs = ( CCSDATA * ) lParam;
+
+ if ( ccs->wParam!=0 && ccs->wParam!=ID_STATUS_ONLINE && ccs->wParam!=ID_STATUS_OFFLINE ) return 1;
+ int oldMode = JGetWord( ccs->hContact, "ApparentMode", 0 );
+ if (( int ) ccs->wParam == oldMode ) return 1;
+ JSetWord( ccs->hContact, "ApparentMode", ( WORD )ccs->wParam );
+
+ if ( !jabberOnline ) return 0;
+
+ DBVARIANT dbv;
+ if ( !JGetStringT( ccs->hContact, "jid", &dbv )) {
+ TCHAR* jid = dbv.ptszVal;
+ switch ( ccs->wParam ) {
+ case ID_STATUS_ONLINE:
+ if ( jabberStatus == ID_STATUS_INVISIBLE || oldMode == ID_STATUS_OFFLINE ) {
+ XmlNode p( "presence" ); p.addAttr( "to", jid );
+ JabberSend( jabberThreadInfo->s, p );
+ }
+ break;
+ case ID_STATUS_OFFLINE:
+ if ( jabberStatus != ID_STATUS_INVISIBLE || oldMode == ID_STATUS_ONLINE )
+ JabberSendPresenceTo( ID_STATUS_INVISIBLE, jid, NULL );
+ break;
+ case 0:
+ if ( oldMode == ID_STATUS_ONLINE && jabberStatus == ID_STATUS_INVISIBLE )
+ JabberSendPresenceTo( ID_STATUS_INVISIBLE, jid, NULL );
+ else if ( oldMode == ID_STATUS_OFFLINE && jabberStatus != ID_STATUS_INVISIBLE )
+ JabberSendPresenceTo( jabberStatus, jid, NULL );
+ break;
+ }
+ JFreeVariant( &dbv );
+ }
+
+ // TODO: update the zebra list ( jabber:iq:privacy )
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSetAwayMsg - sets the away status message
+
+int JabberSetAwayMsg( WPARAM wParam, LPARAM lParam )
+{
+ JabberLog( "SetAwayMsg called, wParam=%d lParam=%s", wParam, ( char* )lParam );
+
+ EnterCriticalSection( &modeMsgMutex );
+
+ char **szMsg;
+ int desiredStatus = wParam;
+
+ switch ( desiredStatus ) {
+ case ID_STATUS_ONLINE:
+ szMsg = &modeMsgs.szOnline;
+ break;
+ case ID_STATUS_AWAY:
+ case ID_STATUS_ONTHEPHONE:
+ case ID_STATUS_OUTTOLUNCH:
+ szMsg = &modeMsgs.szAway;
+ break;
+ case ID_STATUS_NA:
+ szMsg = &modeMsgs.szNa;
+ break;
+ case ID_STATUS_DND:
+ case ID_STATUS_OCCUPIED:
+ szMsg = &modeMsgs.szDnd;
+ break;
+ case ID_STATUS_FREECHAT:
+ szMsg = &modeMsgs.szFreechat;
+ break;
+ default:
+ LeaveCriticalSection( &modeMsgMutex );
+ return 1;
+ }
+
+ char* newModeMsg = mir_strdup(( char* )lParam );
+
+ if (( *szMsg==NULL && newModeMsg==NULL ) ||
+ ( *szMsg!=NULL && newModeMsg!=NULL && !strcmp( *szMsg, newModeMsg )) ) {
+ // Message is the same, no update needed
+ if ( newModeMsg != NULL ) mir_free( newModeMsg );
+ }
+ else {
+ // Update with the new mode message
+ if ( *szMsg != NULL ) mir_free( *szMsg );
+ *szMsg = newModeMsg;
+ // Send a presence update if needed
+ if ( desiredStatus == jabberStatus ) {
+ JabberSendPresence( jabberStatus );
+ } }
+
+ LeaveCriticalSection( &modeMsgMutex );
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// JabberSetStatus - sets the protocol status
+
+int JabberSetStatus( WPARAM wParam, LPARAM lParam )
+{
+ JabberLog( "PS_SETSTATUS( %d )", wParam );
+ int desiredStatus = wParam;
+ jabberDesiredStatus = desiredStatus;
+
+ if ( desiredStatus == ID_STATUS_OFFLINE ) {
+ if ( jabberThreadInfo ) {
+ HANDLE s = jabberThreadInfo->s;
+ jabberThreadInfo = NULL;
+ if ( jabberConnected ) {
+ JabberSend( s, "</stream:stream>" );
+ jabberConnected = jabberOnline = FALSE;
+ }
+ Netlib_CloseHandle(s); // New Line
+ }
+
+ int oldStatus = jabberStatus;
+ jabberStatus = jabberDesiredStatus = ID_STATUS_OFFLINE;
+ JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus );
+ }
+ else if ( !jabberConnected && !( jabberStatus >= ID_STATUS_CONNECTING && jabberStatus < ID_STATUS_CONNECTING + MAX_CONNECT_RETRIES )) {
+ if ( jabberConnected )
+ return 0;
+
+ ThreadData* thread = ( ThreadData* ) mir_alloc( sizeof( struct ThreadData ));
+
+ ZeroMemory( thread, sizeof( struct ThreadData ));
+ thread->type = JABBER_SESSION_NORMAL;
+ jabberDesiredStatus = desiredStatus;
+
+ int oldStatus = jabberStatus;
+ jabberStatus = ID_STATUS_CONNECTING;
+ JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus );
+ thread->hThread = ( HANDLE ) JabberForkThread(( JABBER_THREAD_FUNC )JabberServerThread, 0, thread );
+ }
+ else JabberSetServerStatus( desiredStatus );
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserIsTyping - sends a UTN notification
+
+int JabberUserIsTyping( WPARAM wParam, LPARAM lParam )
+{
+ if ( !jabberOnline ) return 0;
+
+ HANDLE hContact = ( HANDLE ) wParam;
+ DBVARIANT dbv;
+ if ( JGetStringT( hContact, "jid", &dbv )) return 0;
+
+ JABBER_LIST_ITEM *item;
+ if (( item = JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal )) != NULL ) {
+ TCHAR szClientJid[ 256 ];
+ JabberGetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
+ XmlNode m( "message" ); m.addAttr( "to", szClientJid );
+
+ if ( item->cap & CLIENT_CAP_CHATSTAT ) {
+ m.addAttr( "type", "chat" );
+ m.addAttrID( JabberSerialNext());
+ switch ( lParam ){
+ case PROTOTYPE_SELFTYPING_OFF:
+ m.addChild( "paused" )->addAttr( "xmlns", _T("http://jabber.org/protocol/chatstates"));
+ JabberSend( jabberThreadInfo->s, m );
+ break;
+ case PROTOTYPE_SELFTYPING_ON:
+ m.addChild( "composing" )->addAttr( "xmlns", _T("http://jabber.org/protocol/chatstates"));
+ JabberSend( jabberThreadInfo->s, m );
+ break;
+ }
+ }
+ else if ( item->wantComposingEvent == TRUE ) {
+ XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:event" );
+ if ( item->messageEventIdStr != NULL )
+ x->addChild( "id", item->messageEventIdStr );
+
+ switch ( lParam ){
+ case PROTOTYPE_SELFTYPING_OFF:
+ JabberSend( jabberThreadInfo->s, m );
+ break;
+ case PROTOTYPE_SELFTYPING_ON:
+ x->addChild( "composing" );
+ JabberSend( jabberThreadInfo->s, m );
+ break;
+ } } }
+
+ JFreeVariant( &dbv );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// "/SendXML" - Allows external plugins to send XML to the server
+
+int ServiceSendXML(WPARAM wParam, LPARAM lParam)
+{
+ return JabberSend( jabberThreadInfo->s, (char*)lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Service initialization code
+
+static HANDLE hEventSettingChanged = NULL;
+static HANDLE hEventContactDeleted = NULL;
+static HANDLE hEventRebuildCMenu = NULL;
+
+static HANDLE hMenuAgent = NULL;
+static HANDLE hMenuChangePassword = NULL;
+static HANDLE hMenuGroupchat = NULL;
+
+int JabberMenuHandleAgents( WPARAM wParam, LPARAM lParam );
+int JabberMenuHandleChangePassword( WPARAM wParam, LPARAM lParam );
+int JabberMenuHandleVcard( WPARAM wParam, LPARAM lParam );
+int JabberMenuHandleRequestAuth( WPARAM wParam, LPARAM lParam );
+int JabberMenuHandleGrantAuth( WPARAM wParam, LPARAM lParam );
+int JabberMenuPrebuildContactMenu( WPARAM wParam, LPARAM lParam );
+
+void JabberEnableMenuItems( BOOL bEnable )
+{
+ CLISTMENUITEM clmi = { 0 };
+ clmi.cbSize = sizeof( CLISTMENUITEM );
+ clmi.flags = CMIM_FLAGS;
+ if ( !bEnable )
+ clmi.flags += CMIF_GRAYED;
+
+ JCallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )hMenuAgent, ( LPARAM )&clmi );
+ JCallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )hMenuChangePassword, ( LPARAM )&clmi );
+ JCallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )hMenuGroupchat, ( LPARAM )&clmi );
+}
+
+int JabberSvcInit( void )
+{
+ hEventSettingChanged = HookEvent( ME_DB_CONTACT_SETTINGCHANGED, JabberDbSettingChanged );
+ hEventContactDeleted = HookEvent( ME_DB_CONTACT_DELETED, JabberContactDeleted );
+ hEventRebuildCMenu = HookEvent( ME_CLIST_PREBUILDCONTACTMENU, JabberMenuPrebuildContactMenu );
+
+ JCreateServiceFunction( PS_GETCAPS, JabberGetCaps );
+ JCreateServiceFunction( PS_GETNAME, JabberGetName );
+ JCreateServiceFunction( PS_LOADICON, JabberLoadIcon );
+ JCreateServiceFunction( PS_BASICSEARCH, JabberBasicSearch );
+ JCreateServiceFunction( PS_SEARCHBYEMAIL, JabberSearchByEmail );
+ JCreateServiceFunction( PS_SEARCHBYNAME, JabberSearchByName );
+ JCreateServiceFunction( PS_ADDTOLIST, JabberAddToList );
+ JCreateServiceFunction( PS_ADDTOLISTBYEVENT, JabberAddToListByEvent );
+ JCreateServiceFunction( PS_AUTHALLOW, JabberAuthAllow );
+ JCreateServiceFunction( PS_AUTHDENY, JabberAuthDeny );
+ JCreateServiceFunction( PS_SETSTATUS, JabberSetStatus );
+ JCreateServiceFunction( PS_GETAVATARINFO, JabberGetAvatarInfo );
+ JCreateServiceFunction( PS_GETSTATUS, JabberGetStatus );
+ JCreateServiceFunction( PS_SETAWAYMSG, JabberSetAwayMsg );
+ JCreateServiceFunction( PS_FILERESUME, JabberFileResume );
+
+ JCreateServiceFunction( PSS_GETINFO, JabberGetInfo );
+ JCreateServiceFunction( PSS_SETAPPARENTMODE, JabberSetApparentMode );
+ JCreateServiceFunction( PSS_MESSAGE, JabberSendMessage );
+ JCreateServiceFunction( PSS_GETAWAYMSG, JabberGetAwayMsg );
+ JCreateServiceFunction( PSS_FILEALLOW, JabberFileAllow );
+ JCreateServiceFunction( PSS_FILECANCEL, JabberFileCancel );
+ JCreateServiceFunction( PSS_FILEDENY, JabberFileDeny );
+ JCreateServiceFunction( PSS_FILE, JabberSendFile );
+ JCreateServiceFunction( PSR_MESSAGE, JabberRecvMessage );
+ JCreateServiceFunction( PSR_FILE, JabberRecvFile );
+ JCreateServiceFunction( PSS_USERISTYPING, JabberUserIsTyping );
+
+ // Protocol services and events...
+ heventRawXMLIn = JCreateHookableEvent( JE_RAWXMLIN );
+ heventRawXMLOut = JCreateHookableEvent( JE_RAWXMLOUT );
+ JCreateServiceFunction( JS_SENDXML, ServiceSendXML );
+
+ // Menu items
+ CLISTMENUITEM mi, clmi;
+ memset( &mi, 0, sizeof( CLISTMENUITEM ));
+ mi.cbSize = sizeof( CLISTMENUITEM );
+ memset( &clmi, 0, sizeof( CLISTMENUITEM ));
+ clmi.cbSize = sizeof( CLISTMENUITEM );
+ clmi.flags = CMIM_FLAGS | CMIF_GRAYED;
+
+ // Add Jabber menu to the main menu
+ char text[_MAX_PATH];
+ strcpy( text, jabberProtoName );
+ char* tDest = text + strlen( text );
+
+ if ( !JGetByte( "DisableMainMenu", FALSE )) {
+ // "Agents..."
+ strcpy( tDest, "/Agents" );
+ CreateServiceFunction( text, JabberMenuHandleAgents );
+
+ mi.pszPopupName = jabberModuleName;
+ mi.popupPosition = 500090000;
+ mi.pszName = JTranslate( "Agents..." );
+ mi.position = 2000050000;
+ mi.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_AGENTS ));
+ mi.pszService = text;
+ hMenuAgent = ( HANDLE ) JCallService( MS_CLIST_ADDMAINMENUITEM, 0, ( LPARAM )&mi );
+ JCallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM ) hMenuAgent, ( LPARAM )&clmi );
+
+ // "Change Password..."
+ strcpy( tDest, "/ChangePassword" );
+ CreateServiceFunction( text, JabberMenuHandleChangePassword );
+ mi.pszName = JTranslate( "Change Password..." );
+ mi.position = 2000050001;
+ mi.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_KEYS ));
+ mi.pszService = text;
+ hMenuChangePassword = ( HANDLE ) JCallService( MS_CLIST_ADDMAINMENUITEM, 0, ( LPARAM )&mi );
+ JCallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM ) hMenuChangePassword, ( LPARAM )&clmi );
+
+ // "Multi-User Conference..."
+ strcpy( tDest, "/Groupchat" );
+ CreateServiceFunction( text, JabberMenuHandleGroupchat );
+ mi.pszName = JTranslate( "Multi-User Conference..." );
+ mi.position = 2000050002;
+ mi.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_GROUP ));
+ mi.pszService = text;
+ hMenuGroupchat = ( HANDLE ) JCallService( MS_CLIST_ADDMAINMENUITEM, 0, ( LPARAM )&mi );
+ JCallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM ) hMenuGroupchat, ( LPARAM )&clmi );
+
+ // "Personal vCard..."
+ strcpy( tDest, "/Vcard" );
+ CreateServiceFunction( text, JabberMenuHandleVcard );
+ mi.pszName = JTranslate( "Personal vCard..." );
+ mi.position = 2000050003;
+ mi.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_VCARD ));
+ mi.pszService = text;
+ JCallService( MS_CLIST_ADDMAINMENUITEM, 0, ( LPARAM )&mi );
+ }
+ return 0;
+}
+
+int JabberSvcUninit()
+{
+ if ( hEventSettingChanged ) UnhookEvent( hEventSettingChanged );
+ if ( hEventContactDeleted ) UnhookEvent( hEventContactDeleted );
+ if ( hEventRebuildCMenu ) UnhookEvent( hEventRebuildCMenu );
+ return 0;
+}
|