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/icos/add2roster.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/addcontact.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/block.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/delete.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/grant.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/group.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/jabber.ico | Bin 0 -> 9062 bytes miranda-wine/protocols/JabberG/icos/key.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/open.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/pages.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/rename.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/request.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/save.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/user2room.ico | Bin 0 -> 2550 bytes miranda-wine/protocols/JabberG/icos/write.ico | Bin 0 -> 9062 bytes miranda-wine/protocols/JabberG/jabber.cpp | 350 ++++ miranda-wine/protocols/JabberG/jabber.h | 586 +++++++ miranda-wine/protocols/JabberG/jabber.rc | 839 +++++++++ miranda-wine/protocols/JabberG/jabber_agent.cpp | 565 ++++++ miranda-wine/protocols/JabberG/jabber_bitmap.cpp | 53 + miranda-wine/protocols/JabberG/jabber_byte.cpp | 459 +++++ miranda-wine/protocols/JabberG/jabber_byte.h | 52 + miranda-wine/protocols/JabberG/jabber_chat.cpp | 796 +++++++++ miranda-wine/protocols/JabberG/jabber_file.cpp | 611 +++++++ miranda-wine/protocols/JabberG/jabber_form.cpp | 479 ++++++ miranda-wine/protocols/JabberG/jabber_ft.cpp | 419 +++++ .../protocols/JabberG/jabber_groupchat.cpp | 886 ++++++++++ miranda-wine/protocols/JabberG/jabber_iq.cpp | 173 ++ miranda-wine/protocols/JabberG/jabber_iq.h | 87 + miranda-wine/protocols/JabberG/jabber_iqid.cpp | 1429 ++++++++++++++++ miranda-wine/protocols/JabberG/jabber_iqid_muc.cpp | 531 ++++++ miranda-wine/protocols/JabberG/jabber_libstr.cpp | 76 + miranda-wine/protocols/JabberG/jabber_list.cpp | 388 +++++ miranda-wine/protocols/JabberG/jabber_list.h | 174 ++ miranda-wine/protocols/JabberG/jabber_menu.cpp | 281 +++ miranda-wine/protocols/JabberG/jabber_misc.cpp | 382 +++++ miranda-wine/protocols/JabberG/jabber_opt.cpp | 724 ++++++++ miranda-wine/protocols/JabberG/jabber_password.cpp | 100 ++ miranda-wine/protocols/JabberG/jabber_proxy.cpp | 154 ++ miranda-wine/protocols/JabberG/jabber_proxy.h | 34 + miranda-wine/protocols/JabberG/jabber_ssl.cpp | 181 ++ miranda-wine/protocols/JabberG/jabber_ssl.h | 64 + miranda-wine/protocols/JabberG/jabber_std.cpp | 171 ++ miranda-wine/protocols/JabberG/jabber_svc.cpp | 1431 ++++++++++++++++ miranda-wine/protocols/JabberG/jabber_thread.cpp | 1793 ++++++++++++++++++++ miranda-wine/protocols/JabberG/jabber_userinfo.cpp | 525 ++++++ miranda-wine/protocols/JabberG/jabber_util.cpp | 1133 +++++++++++++ miranda-wine/protocols/JabberG/jabber_vcard.cpp | 1179 +++++++++++++ miranda-wine/protocols/JabberG/jabber_ws.cpp | 92 + miranda-wine/protocols/JabberG/jabber_xml.cpp | 786 +++++++++ miranda-wine/protocols/JabberG/jabber_xml.h | 152 ++ miranda-wine/protocols/JabberG/jabber_xmlns.cpp | 107 ++ miranda-wine/protocols/JabberG/jabber_xmlns.h | 34 + miranda-wine/protocols/JabberG/msvc6.rc | 2 + miranda-wine/protocols/JabberG/resource.h | 232 +++ miranda-wine/protocols/JabberG/sdk/m_smileyadd.h | 78 + miranda-wine/protocols/JabberG/sha1.cpp | 446 +++++ miranda-wine/protocols/JabberG/sha1.h | 133 ++ miranda-wine/protocols/JabberG/version.h | 3 + miranda-wine/protocols/JabberG/version.rc | 52 + 60 files changed, 19222 insertions(+) create mode 100644 miranda-wine/protocols/JabberG/icos/add2roster.ico create mode 100644 miranda-wine/protocols/JabberG/icos/addcontact.ico create mode 100644 miranda-wine/protocols/JabberG/icos/block.ico create mode 100644 miranda-wine/protocols/JabberG/icos/delete.ico create mode 100644 miranda-wine/protocols/JabberG/icos/grant.ico create mode 100644 miranda-wine/protocols/JabberG/icos/group.ico create mode 100644 miranda-wine/protocols/JabberG/icos/jabber.ico create mode 100644 miranda-wine/protocols/JabberG/icos/key.ico create mode 100644 miranda-wine/protocols/JabberG/icos/open.ico create mode 100644 miranda-wine/protocols/JabberG/icos/pages.ico create mode 100644 miranda-wine/protocols/JabberG/icos/rename.ico create mode 100644 miranda-wine/protocols/JabberG/icos/request.ico create mode 100644 miranda-wine/protocols/JabberG/icos/save.ico create mode 100644 miranda-wine/protocols/JabberG/icos/user2room.ico create mode 100644 miranda-wine/protocols/JabberG/icos/write.ico create mode 100644 miranda-wine/protocols/JabberG/jabber.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber.h create mode 100644 miranda-wine/protocols/JabberG/jabber.rc create mode 100644 miranda-wine/protocols/JabberG/jabber_agent.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_bitmap.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_byte.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_byte.h create mode 100644 miranda-wine/protocols/JabberG/jabber_chat.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_file.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_form.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_ft.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_groupchat.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_iq.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_iq.h create mode 100644 miranda-wine/protocols/JabberG/jabber_iqid.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_iqid_muc.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_libstr.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_list.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_list.h create mode 100644 miranda-wine/protocols/JabberG/jabber_menu.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_misc.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_opt.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_password.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_proxy.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_proxy.h create mode 100644 miranda-wine/protocols/JabberG/jabber_ssl.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_ssl.h create mode 100644 miranda-wine/protocols/JabberG/jabber_std.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_svc.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_thread.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_userinfo.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_util.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_vcard.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_ws.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_xml.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_xml.h create mode 100644 miranda-wine/protocols/JabberG/jabber_xmlns.cpp create mode 100644 miranda-wine/protocols/JabberG/jabber_xmlns.h create mode 100644 miranda-wine/protocols/JabberG/msvc6.rc create mode 100644 miranda-wine/protocols/JabberG/resource.h create mode 100644 miranda-wine/protocols/JabberG/sdk/m_smileyadd.h create mode 100644 miranda-wine/protocols/JabberG/sha1.cpp create mode 100644 miranda-wine/protocols/JabberG/sha1.h create mode 100644 miranda-wine/protocols/JabberG/version.h create mode 100644 miranda-wine/protocols/JabberG/version.rc (limited to 'miranda-wine/protocols/JabberG') diff --git a/miranda-wine/protocols/JabberG/icos/add2roster.ico b/miranda-wine/protocols/JabberG/icos/add2roster.ico new file mode 100644 index 0000000..170e255 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/add2roster.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/addcontact.ico b/miranda-wine/protocols/JabberG/icos/addcontact.ico new file mode 100644 index 0000000..2efc4dc Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/addcontact.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/block.ico b/miranda-wine/protocols/JabberG/icos/block.ico new file mode 100644 index 0000000..a71ddd4 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/block.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/delete.ico b/miranda-wine/protocols/JabberG/icos/delete.ico new file mode 100644 index 0000000..c541793 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/delete.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/grant.ico b/miranda-wine/protocols/JabberG/icos/grant.ico new file mode 100644 index 0000000..08b6710 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/grant.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/group.ico b/miranda-wine/protocols/JabberG/icos/group.ico new file mode 100644 index 0000000..3303179 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/group.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/jabber.ico b/miranda-wine/protocols/JabberG/icos/jabber.ico new file mode 100644 index 0000000..27d618c Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/jabber.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/key.ico b/miranda-wine/protocols/JabberG/icos/key.ico new file mode 100644 index 0000000..a82c4c4 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/key.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/open.ico b/miranda-wine/protocols/JabberG/icos/open.ico new file mode 100644 index 0000000..c6d213b Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/open.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/pages.ico b/miranda-wine/protocols/JabberG/icos/pages.ico new file mode 100644 index 0000000..af93fd2 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/pages.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/rename.ico b/miranda-wine/protocols/JabberG/icos/rename.ico new file mode 100644 index 0000000..6dd597e Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/rename.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/request.ico b/miranda-wine/protocols/JabberG/icos/request.ico new file mode 100644 index 0000000..b183e31 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/request.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/save.ico b/miranda-wine/protocols/JabberG/icos/save.ico new file mode 100644 index 0000000..bf2fe98 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/save.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/user2room.ico b/miranda-wine/protocols/JabberG/icos/user2room.ico new file mode 100644 index 0000000..8acddbb Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/user2room.ico differ diff --git a/miranda-wine/protocols/JabberG/icos/write.ico b/miranda-wine/protocols/JabberG/icos/write.ico new file mode 100644 index 0000000..b799ba2 Binary files /dev/null and b/miranda-wine/protocols/JabberG/icos/write.ico differ diff --git a/miranda-wine/protocols/JabberG/jabber.cpp b/miranda-wine/protocols/JabberG/jabber.cpp new file mode 100644 index 0000000..378812a --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber.cpp @@ -0,0 +1,350 @@ +/* + +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.cpp,v $ +Revision : $Revision: 3611 $ +Last change on : $Date: 2006-08-27 23:36:15 +0400 (Вск, 27 Авг 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "jabber_ssl.h" +#include "jabber_iq.h" +#include "resource.h" +#include "version.h" + +HINSTANCE hInst; +PLUGINLINK *pluginLink; + +PLUGININFO pluginInfo = { + sizeof( PLUGININFO ), + #if defined( _UNICODE ) + "Jabber Protocol (Unicode)", + #else + "Jabber Protocol", + #endif + __VERSION_DWORD, + "Jabber protocol plugin for Miranda IM ( "__DATE__" )", + "George Hazan", + "ghazan@miranda-im.org", + "( c ) 2002-05 Santithorn Bunchua, George Hazan", + "http://miranda-im.org/download/details.php?action=viewfile&id=437", + 0, + 0 +}; + +MM_INTERFACE memoryManagerInterface; +LIST_INTERFACE li; + +HANDLE hMainThread = NULL; +DWORD jabberMainThreadId; +char* jabberProtoName; // "JABBER" +char* jabberModuleName; // "Jabber" +CRITICAL_SECTION mutex; +HANDLE hNetlibUser; +// Main jabber server connection thread global variables +struct ThreadData *jabberThreadInfo = NULL; +BOOL jabberConnected = FALSE; +time_t jabberLoggedInTime = 0; +BOOL jabberOnline = FALSE; +BOOL jabberChatDllPresent = FALSE; +int jabberStatus = ID_STATUS_OFFLINE; +int jabberDesiredStatus; +BOOL modeMsgStatusChangePending = FALSE; +BOOL jabberChangeStatusMessageOnly = FALSE; +TCHAR* jabberJID = NULL; +char* streamId = NULL; +DWORD jabberLocalIP; +UINT jabberCodePage; +JABBER_MODEMSGS modeMsgs; +//char* jabberModeMsg; +CRITICAL_SECTION modeMsgMutex; +char* jabberVcardPhotoFileName = NULL; +char* jabberVcardPhotoType = NULL; +BOOL jabberSendKeepAlive; + +// SSL-related global variable +HMODULE hLibSSL = NULL; +PVOID jabberSslCtx; + +const char xmlnsAdmin[] = "http://jabber.org/protocol/muc#admin"; +const char xmlnsOwner[] = "http://jabber.org/protocol/muc#owner"; + +HWND hwndJabberAgents = NULL; +HWND hwndJabberGroupchat = NULL; +HWND hwndJabberJoinGroupchat = NULL; +HWND hwndAgentReg = NULL; +HWND hwndAgentRegInput = NULL; +HWND hwndAgentManualReg = NULL; +HWND hwndRegProgress = NULL; +HWND hwndJabberVcard = NULL; +HWND hwndMucVoiceList = NULL; +HWND hwndMucMemberList = NULL; +HWND hwndMucModeratorList = NULL; +HWND hwndMucBanList = NULL; +HWND hwndMucAdminList = NULL; +HWND hwndMucOwnerList = NULL; +HWND hwndJabberChangePassword = NULL; + +// Service and event handles +HANDLE heventRawXMLIn; +HANDLE heventRawXMLOut; + +int JabberOptInit( WPARAM wParam, LPARAM lParam ); +int JabberUserInfoInit( WPARAM wParam, LPARAM lParam ); +int JabberMsgUserTyping( WPARAM wParam, LPARAM lParam ); +void JabberMenuInit( void ); +int JabberSvcInit( void ); +int JabberSvcUninit( void ); + +extern "C" BOOL WINAPI DllMain( HINSTANCE hModule, DWORD dwReason, LPVOID lpvReserved ) +{ + #ifdef _DEBUG + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); + #endif + hInst = hModule; + return TRUE; +} + +extern "C" __declspec( dllexport ) PLUGININFO *MirandaPluginInfo( DWORD mirandaVersion ) +{ + if ( mirandaVersion < PLUGIN_MAKE_VERSION( 0,5,0,0 )) { + MessageBoxA( NULL, "The Jabber protocol plugin cannot be loaded. It requires Miranda IM 0.5 or later.", "Jabber Protocol Plugin", MB_OK|MB_ICONWARNING|MB_SETFOREGROUND|MB_TOPMOST ); + return NULL; + } + + return &pluginInfo; +} + +/////////////////////////////////////////////////////////////////////////////// +// OnPreShutdown - prepares Miranda to be shut down + +static int OnPreShutdown( WPARAM wParam, LPARAM lParam ) +{ + if ( hwndJabberAgents ) SendMessage( hwndJabberAgents, WM_CLOSE, 0, 0 ); + if ( hwndJabberGroupchat ) SendMessage( hwndJabberGroupchat, WM_CLOSE, 0, 0 ); + if ( hwndJabberJoinGroupchat ) SendMessage( hwndJabberJoinGroupchat, WM_CLOSE, 0, 0 ); + if ( hwndAgentReg ) SendMessage( hwndAgentReg, WM_CLOSE, 0, 0 ); + if ( hwndAgentRegInput ) SendMessage( hwndAgentRegInput, WM_CLOSE, 0, 0 ); + if ( hwndRegProgress ) SendMessage( hwndRegProgress, WM_CLOSE, 0, 0 ); + if ( hwndJabberVcard ) SendMessage( hwndJabberVcard, WM_CLOSE, 0, 0 ); + if ( hwndMucVoiceList ) SendMessage( hwndMucVoiceList, WM_CLOSE, 0, 0 ); + if ( hwndMucMemberList ) SendMessage( hwndMucMemberList, WM_CLOSE, 0, 0 ); + if ( hwndMucModeratorList ) SendMessage( hwndMucModeratorList, WM_CLOSE, 0, 0 ); + if ( hwndMucBanList ) SendMessage( hwndMucBanList, WM_CLOSE, 0, 0 ); + if ( hwndMucAdminList ) SendMessage( hwndMucAdminList, WM_CLOSE, 0, 0 ); + if ( hwndMucOwnerList ) SendMessage( hwndMucOwnerList, WM_CLOSE, 0, 0 ); + if ( hwndJabberChangePassword ) SendMessage( hwndJabberChangePassword, WM_CLOSE, 0, 0 ); + + hwndJabberAgents = NULL; + hwndJabberGroupchat = NULL; + hwndJabberJoinGroupchat = NULL; + hwndAgentReg = NULL; + hwndAgentRegInput = NULL; + hwndAgentManualReg = NULL; + hwndRegProgress = NULL; + hwndJabberVcard = NULL; + hwndMucVoiceList = NULL; + hwndMucMemberList = NULL; + hwndMucModeratorList = NULL; + hwndMucBanList = NULL; + hwndMucAdminList = NULL; + hwndMucOwnerList = NULL; + hwndJabberChangePassword = NULL; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// OnModulesLoaded - execute some code when all plugins are initialized + +int JabberGcEventHook( WPARAM, LPARAM ); +int JabberGcMenuHook( WPARAM, LPARAM ); +int JabberGcInit( WPARAM, LPARAM ); + +static COLORREF crCols[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; +HANDLE hChatEvent = NULL, + hChatMenu = NULL, + hChatMess = NULL, + hInitChat = NULL, + hEvInitChat = NULL, + hEvModulesLoaded = NULL, + hEvOptInit = NULL, + hEvPreShutdown = NULL, + hEvUserInfoInit = NULL; + +static int OnModulesLoaded( WPARAM wParam, LPARAM lParam ) +{ + JabberWsInit(); + JabberSslInit(); + HookEvent( ME_USERINFO_INITIALISE, JabberUserInfoInit ); + + if ( ServiceExists( MS_GC_REGISTER )) { + jabberChatDllPresent = true; + + GCREGISTER gcr = {0}; + gcr.cbSize = sizeof( GCREGISTER ); + gcr.dwFlags = GC_TYPNOTIF|GC_CHANMGR; + gcr.iMaxText = 0; + gcr.nColors = 16; + gcr.pColors = &crCols[0]; + gcr.pszModuleDispName = jabberProtoName; + gcr.pszModule = jabberProtoName; + JCallService( MS_GC_REGISTER, NULL, ( LPARAM )&gcr ); + + hChatEvent = HookEvent( ME_GC_EVENT, JabberGcEventHook ); + hChatMenu = HookEvent( ME_GC_BUILDMENU, JabberGcMenuHook ); + + char szEvent[ 200 ]; + mir_snprintf( szEvent, sizeof szEvent, "%s\\ChatInit", jabberProtoName ); + hInitChat = CreateHookableEvent( szEvent ); + hEvInitChat = HookEvent( szEvent, JabberGcInit ); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// OnLoad - initialize the plugin instance + +extern "C" int __declspec( dllexport ) Load( PLUGINLINK *link ) +{ + pluginLink = link; + + // set the memory manager + memoryManagerInterface.cbSize = sizeof(MM_INTERFACE); + JCallService(MS_SYSTEM_GET_MMI,0,(LPARAM)&memoryManagerInterface); + + // set the lists manager; + li.cbSize = sizeof( li ); + if ( CallService(MS_SYSTEM_GET_LI,0,(LPARAM)&li) == CALLSERVICE_NOTFOUND ) { + MessageBoxA( NULL, "This plugin requires Miranda IM 0.5 or later", "Fatal error", MB_OK ); + return 1; + } + + if ( !ServiceExists( MS_DB_CONTACT_GETSETTING_STR )) { + MessageBoxA( NULL, "This plugin requires db3x plugin version 0.5.1.0 or later", "Jabber", MB_OK ); + return 1; + } + + char text[_MAX_PATH]; + char* p, *q; + + GetModuleFileNameA( hInst, text, sizeof( text )); + p = strrchr( text, '\\' ); + p++; + q = strrchr( p, '.' ); + *q = '\0'; + jabberProtoName = mir_strdup( p ); + _strupr( jabberProtoName ); + + mir_snprintf( text, sizeof( text ), "%s/Status", jabberProtoName ); + JCallService( MS_DB_SETSETTINGRESIDENT, TRUE, ( LPARAM )text ); + + jabberModuleName = mir_strdup( jabberProtoName ); + _strlwr( jabberModuleName ); + jabberModuleName[0] = toupper( jabberModuleName[0] ); + + JabberLog( "Setting protocol/module name to '%s/%s'", jabberProtoName, jabberModuleName ); + + DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hMainThread, THREAD_SET_CONTEXT, FALSE, 0 ); + jabberMainThreadId = GetCurrentThreadId(); + + hEvOptInit = HookEvent( ME_OPT_INITIALISE, JabberOptInit ); + hEvModulesLoaded = HookEvent( ME_SYSTEM_MODULESLOADED, OnModulesLoaded ); + hEvPreShutdown = HookEvent( ME_SYSTEM_PRESHUTDOWN, OnPreShutdown ); + + // Register protocol module + PROTOCOLDESCRIPTOR pd; + ZeroMemory( &pd, sizeof( PROTOCOLDESCRIPTOR )); + pd.cbSize = sizeof( PROTOCOLDESCRIPTOR ); + pd.szName = jabberProtoName; + pd.type = PROTOTYPE_PROTOCOL; + JCallService( MS_PROTO_REGISTERMODULE, 0, ( LPARAM )&pd ); + + // Set all contacts to offline + HANDLE hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) { + char* szProto = ( char* )JCallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 ); + if ( szProto != NULL && !strcmp( szProto, jabberProtoName )) + if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != ID_STATUS_OFFLINE ) + JSetWord( hContact, "Status", ID_STATUS_OFFLINE ); + + hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM ) hContact, 0 ); + } + + memset(( char* )&modeMsgs, 0, sizeof( JABBER_MODEMSGS )); + //jabberModeMsg = NULL; + jabberCodePage = JGetWord( NULL, "CodePage", CP_ACP ); + + InitializeCriticalSection( &mutex ); + InitializeCriticalSection( &modeMsgMutex ); + + srand(( unsigned ) time( NULL )); + JabberSerialInit(); + JabberIqInit(); + JabberListInit(); + JabberSvcInit(); + JabberMenuInit(); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// Unload - destroy the plugin instance + +extern "C" int __declspec( dllexport ) Unload( void ) +{ + if ( hChatEvent ) UnhookEvent( hChatEvent ); + if ( hChatMenu ) UnhookEvent( hChatMenu ); + if ( hChatMess ) UnhookEvent( hChatMess ); + if ( hEvInitChat ) UnhookEvent( hEvInitChat ); + if ( hEvModulesLoaded ) UnhookEvent( hEvModulesLoaded ); + if ( hEvOptInit ) UnhookEvent( hEvOptInit ); + if ( hEvPreShutdown ) UnhookEvent( hEvPreShutdown ); + if ( hEvUserInfoInit ) UnhookEvent( hEvUserInfoInit ); + + if ( hInitChat ) + DestroyHookableEvent( hInitChat ); + + JabberSvcUninit(); + JabberSslUninit(); + JabberListUninit(); + JabberIqUninit(); + JabberSerialUninit(); + JabberWsUninit(); + DeleteCriticalSection( &modeMsgMutex ); + DeleteCriticalSection( &mutex ); + mir_free( modeMsgs.szOnline ); + mir_free( modeMsgs.szAway ); + mir_free( modeMsgs.szNa ); + mir_free( modeMsgs.szDnd ); + mir_free( modeMsgs.szFreechat ); + mir_free( jabberModuleName ); + mir_free( jabberProtoName ); + if ( jabberVcardPhotoFileName ) { + DeleteFileA( jabberVcardPhotoFileName ); + mir_free( jabberVcardPhotoFileName ); + } + if ( jabberVcardPhotoType ) mir_free( jabberVcardPhotoType ); + if ( streamId ) mir_free( streamId ); + + if ( hMainThread ) CloseHandle( hMainThread ); + return 0; +} diff --git a/miranda-wine/protocols/JabberG/jabber.h b/miranda-wine/protocols/JabberG/jabber.h new file mode 100644 index 0000000..006c680 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber.h @@ -0,0 +1,586 @@ +/* + +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. + +*/ + +#ifndef _JABBER_H_ +#define _JABBER_H_ + +#if defined(UNICODE) && !defined(_UNICODE) + #define _UNICODE +#endif + +#include + +#define NEWSTR_ALLOCA(A) (A==NULL)?NULL:strcpy((char*)alloca(strlen(A)+1),A) +#define NEWTSTR_ALLOCA(A) (A==NULL)?NULL:_tcscpy((TCHAR*)alloca(sizeof(TCHAR)*(_tcslen(A)+1)),A) + +#if defined( _UNICODE ) + #define TCHAR_STR_PARAM "%S" +#else + #define TCHAR_STR_PARAM "%s" +#endif + +#ifdef _DEBUG +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif + +/******************************************************************* + * Global header files + *******************************************************************/ +#define _WIN32_WINNT 0x500 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jabber_xml.h" +#include "jabber_byte.h" + +#if !defined(OPENFILENAME_SIZE_VERSION_400) + #define OPENFILENAME_SIZE_VERSION_400 sizeof(OPENFILENAME) +#endif + +/******************************************************************* + * Global constants + *******************************************************************/ +#define JABBER_DEFAULT_PORT 5222 +#define JABBER_IQID "mir_" +#define JABBER_MAX_JID_LEN 256 + +// User-defined message +#define WM_JABBER_REGDLG_UPDATE WM_USER + 100 +#define WM_JABBER_AGENT_REFRESH WM_USER + 101 +#define WM_JABBER_TRANSPORT_REFRESH WM_USER + 102 +#define WM_JABBER_REGINPUT_ACTIVATE WM_USER + 103 +#define WM_JABBER_REFRESH WM_USER + 104 +#define WM_JABBER_CHECK_ONLINE WM_USER + 105 +#define WM_JABBER_CHANGED WM_USER + 106 +#define WM_JABBER_ACTIVATE WM_USER + 107 +#define WM_JABBER_SET_FONT WM_USER + 108 +#define WM_JABBER_FLASHWND WM_USER + 109 +#define WM_JABBER_GC_MEMBER_ADD WM_USER + 110 +#define WM_JABBER_GC_FORCE_QUIT WM_USER + 111 +#define WM_JABBER_SHUTDOWN WM_USER + 112 +#define WM_JABBER_SMILEY WM_USER + 113 +#define WM_JABBER_JOIN WM_USER + 114 +#define WM_JABBER_ADD_TO_ROSTER WM_USER + 115 +// Error code +#define JABBER_ERROR_REDIRECT 302 +#define JABBER_ERROR_BAD_REQUEST 400 +#define JABBER_ERROR_UNAUTHORIZED 401 +#define JABBER_ERROR_PAYMENT_REQUIRED 402 +#define JABBER_ERROR_FORBIDDEN 403 +#define JABBER_ERROR_NOT_FOUND 404 +#define JABBER_ERROR_NOT_ALLOWED 405 +#define JABBER_ERROR_NOT_ACCEPTABLE 406 +#define JABBER_ERROR_REGISTRATION_REQUIRED 407 +#define JABBER_ERROR_REQUEST_TIMEOUT 408 +#define JABBER_ERROR_CONFLICT 409 +#define JABBER_ERROR_INTERNAL_SERVER_ERROR 500 +#define JABBER_ERROR_NOT_IMPLEMENTED 501 +#define JABBER_ERROR_REMOTE_SERVER_ERROR 502 +#define JABBER_ERROR_SERVICE_UNAVAILABLE 503 +#define JABBER_ERROR_REMOTE_SERVER_TIMEOUT 504 +// Vcard flag +#define JABBER_VCEMAIL_HOME 1 +#define JABBER_VCEMAIL_WORK 2 +#define JABBER_VCEMAIL_INTERNET 4 +#define JABBER_VCEMAIL_X400 8 +#define JABBER_VCTEL_HOME 1 +#define JABBER_VCTEL_WORK 2 +#define JABBER_VCTEL_VOICE 4 +#define JABBER_VCTEL_FAX 8 +#define JABBER_VCTEL_PAGER 16 +#define JABBER_VCTEL_MSG 32 +#define JABBER_VCTEL_CELL 64 +#define JABBER_VCTEL_VIDEO 128 +#define JABBER_VCTEL_BBS 256 +#define JABBER_VCTEL_MODEM 512 +#define JABBER_VCTEL_ISDN 1024 +#define JABBER_VCTEL_PCS 2048 +// File transfer setting +#define JABBER_OPTION_FT_DIRECT 0 // Direct connection +#define JABBER_OPTION_FT_PASS 1 // Use PASS server +#define JABBER_OPTION_FT_PROXY 2 // Use proxy with local port forwarding +// Font style saved in DB +#define JABBER_FONT_BOLD 1 +#define JABBER_FONT_ITALIC 2 +// Font for groupchat log dialog +#define JABBER_GCLOG_NUM_FONT 6 // 6 fonts ( 0:send, 1:msg, 2:time, 3:nick, 4:sys, 5:/me ) +// Old SDK don't have this +#ifndef SPI_GETSCREENSAVERRUNNING +#define SPI_GETSCREENSAVERRUNNING 114 +#endif +#define IDC_STATIC ( -1 ) +// Icon list +enum { + JABBER_IDI_GCOWNER = 0, + JABBER_IDI_GCADMIN, + JABBER_IDI_GCMODERATOR, + JABBER_IDI_GCVOICE, + JABBER_ICON_TOTAL +}; + +// Services and Events +#define JE_RAWXMLIN "/RawXMLIn" +#define JE_RAWXMLOUT "/RawXMLOut" + +#define JS_SENDXML "/SendXML" + +/******************************************************************* + * Global data structures and data type definitions + *******************************************************************/ +typedef HANDLE JABBER_SOCKET; + +enum JABBER_SESSION_TYPE +{ + JABBER_SESSION_NORMAL, + JABBER_SESSION_REGISTER +}; + +struct ThreadData { + HANDLE hThread; + JABBER_SESSION_TYPE type; + + TCHAR username[128]; + char password[128]; + char server[128]; + char manualHost[128]; + TCHAR resource[128]; + TCHAR fullJID[256]; + WORD port; + JABBER_SOCKET s; + BOOL useSSL; + + char newPassword[128]; + + HWND reg_hwndDlg; + BOOL reg_done, bIsSessionAvailable; +}; + +struct JABBER_MODEMSGS +{ + char* szOnline; + char* szAway; + char* szNa; + char* szDnd; + char* szFreechat; +}; + +struct JABBER_REG_ACCOUNT +{ + TCHAR username[128]; + TCHAR password[128]; + char server[128]; + char manualHost[128]; + WORD port; + BOOL useSSL; +}; + +typedef enum { FT_SI, FT_OOB, FT_BYTESTREAM } JABBER_FT_TYPE; +typedef enum { FT_CONNECTING, FT_INITIALIZING, FT_RECEIVING, FT_DONE, FT_ERROR, FT_DENIED } JABBER_FILE_STATE; + +struct filetransfer +{ + filetransfer(); + ~filetransfer(); + + void close(); + void complete(); + int create(); + + PROTOFILETRANSFERSTATUS std; + +// HANDLE hContact; + JABBER_FT_TYPE type; + JABBER_SOCKET s; + JABBER_FILE_STATE state; + TCHAR* jid; + int fileId; + TCHAR* iqId; + TCHAR* sid; + int bCompleted; + HANDLE hWaitEvent; + WCHAR* wszFileName; + + // For type == FT_BYTESTREAM + JABBER_BYTE_TRANSFER *jbt; + + // Used by file receiving only + char* httpHostName; + WORD httpPort; + char* httpPath; + + // Used by file sending only + HANDLE hFileEvent; + long *fileSize; + char* szDescription; +}; + +struct JABBER_SEARCH_RESULT +{ + PROTOSEARCHRESULT hdr; + TCHAR jid[256]; +}; + +struct JABBER_GCLOG_FONT +{ + char face[LF_FACESIZE]; // LF_FACESIZE is from LOGFONT struct + BYTE style; + char size; // signed + BYTE charset; + COLORREF color; +}; + +struct JABBER_FIELD_MAP +{ + int id; + char* name; +}; + +enum JABBER_MUC_JIDLIST_TYPE +{ + MUC_VOICELIST, + MUC_MEMBERLIST, + MUC_MODERATORLIST, + MUC_BANLIST, + MUC_ADMINLIST, + MUC_OWNERLIST +}; + +struct JABBER_MUC_JIDLIST_INFO +{ + JABBER_MUC_JIDLIST_TYPE type; + TCHAR* roomJid; // filled-in by the WM_JABBER_REFRESH code + XmlNode *iqNode; + + TCHAR* type2str( void ) const; +}; + +typedef void ( *JABBER_FORM_SUBMIT_FUNC )( XmlNode* values, void *userdata ); +typedef void ( __cdecl *JABBER_THREAD_FUNC )( void * ); + +#include "jabber_list.h" + +/******************************************************************* + * Global variables + *******************************************************************/ +extern HINSTANCE hInst; +extern HANDLE hMainThread; +extern DWORD jabberMainThreadId; +extern char* jabberProtoName; +extern char* jabberModuleName; +extern HANDLE hNetlibUser; +extern HMODULE hLibSSL; +extern PVOID jabberSslCtx; + +extern struct ThreadData *jabberThreadInfo; +extern TCHAR* jabberJID; +extern char* streamId; +extern DWORD jabberLocalIP; +extern BOOL jabberConnected; +extern BOOL jabberOnline; +extern int jabberStatus; +extern int jabberDesiredStatus; +extern time_t jabberLoggedInTime; + +extern CRITICAL_SECTION modeMsgMutex; +extern JABBER_MODEMSGS modeMsgs; +extern BOOL modeMsgStatusChangePending; + +extern BOOL jabberChangeStatusMessageOnly; +extern BOOL jabberSendKeepAlive; +extern BOOL jabberChatDllPresent; + +extern HWND hwndJabberAgents; +extern HWND hwndAgentReg; +extern HWND hwndAgentRegInput; +extern HWND hwndAgentManualReg; +extern HWND hwndRegProgress; +extern HWND hwndJabberVcard; +extern HWND hwndJabberChangePassword; +extern HWND hwndJabberGroupchat; +extern HWND hwndJabberJoinGroupchat; +extern HWND hwndMucVoiceList; +extern HWND hwndMucMemberList; +extern HWND hwndMucModeratorList; +extern HWND hwndMucBanList; +extern HWND hwndMucAdminList; +extern HWND hwndMucOwnerList; + +extern const char xmlnsOwner[], xmlnsAdmin[]; +// Service and event handles +extern HANDLE heventRawXMLIn; +extern HANDLE heventRawXMLOut; + +/******************************************************************* + * Function declarations + *******************************************************************/ + +//---- jabber_bitmap.cpp ---------------------------------------------- + +HBITMAP __stdcall JabberBitmapToAvatar( HBITMAP hBitmap ); +int __stdcall JabberEnterBitmapName( char* szDest ); + +//---- jabber_chat.cpp ---------------------------------------------- + +void JabberGcLogCreate( JABBER_LIST_ITEM* item ); +void JabberGcLogUpdateMemberStatus( JABBER_LIST_ITEM* item, TCHAR* nick, int action, XmlNode* reason ); +void JabberGcQuit( JABBER_LIST_ITEM* jid, int code, XmlNode* reason ); + +//---- jabber_file.c ------------------------------------------------ + +void __cdecl JabberFileReceiveThread( filetransfer* ft ); +void __cdecl JabberFileServerThread( filetransfer* ft ); + +//---- jabber_form.c ------------------------------------------------ + +void JabberFormCreateUI( HWND hwndStatic, XmlNode *xNode, int *formHeight ); +void JabberFormCreateDialog( XmlNode *xNode, TCHAR* defTitle, JABBER_FORM_SUBMIT_FUNC pfnSubmit, void *userdata ); + +XmlNode* JabberFormGetData( HWND hwndStatic, XmlNode *xNode ); + +//---- jabber_ft.c -------------------------------------------------- + +void JabberFtCancel( filetransfer* ft ); +void JabberFtInitiate( TCHAR* jid, filetransfer* ft ); +void JabberFtHandleSiRequest( XmlNode *iqNode ); +void JabberFtAcceptSiRequest( filetransfer* ft ); +BOOL JabberFtHandleBytestreamRequest( XmlNode *iqNode ); + +//---- jabber_groupchat.c ------------------------------------------- + +int JabberMenuHandleGroupchat( WPARAM wParam, LPARAM lParam ); +void JabberGroupchatJoinRoom( const TCHAR* server, const TCHAR* room, const TCHAR* nick, const TCHAR* password ); +void JabberGroupchatProcessPresence( XmlNode *node, void *userdata ); +void JabberGroupchatProcessMessage( XmlNode *node, void *userdata ); +void JabberGroupchatProcessInvite( TCHAR* roomJid, TCHAR* from, TCHAR* reason, TCHAR* password ); + +//---- jabber_libstr.c ---------------------------------------------- + +void __stdcall replaceStr( char*& dest, const char* src ); +void __stdcall replaceStr( WCHAR*& dest, const WCHAR* src ); +char* __stdcall rtrim( char *string ); +#if defined( _UNICODE ) + TCHAR* __stdcall rtrim( TCHAR *string ); +#endif + +//---- jabber_misc.c ------------------------------------------------ + +void JabberAddContactToRoster( const TCHAR* jid, const TCHAR* nick, const TCHAR* grpName, JABBER_SUBSCRIPTION subscription ); +void JabberChatDllError( void ); +int JabberCompareJids( const TCHAR* jid1, const TCHAR* jid2 ); +void JabberContactListCreateGroup( TCHAR* groupName ); +void JabberDBAddAuthRequest( TCHAR* jid, TCHAR* nick ); +HANDLE JabberDBCreateContact( TCHAR* jid, TCHAR* nick, BOOL temporary, BOOL stripResource ); +ULONG JabberForkThread( void ( __cdecl *threadcode )( void* ), unsigned long stacksize, void *arg ); +void JabberGetAvatarFileName( HANDLE hContact, char* pszDest, int cbLen ); +void JabberSetServerStatus( int iNewStatus ); +char* EscapeChatTags(char* pszText); +char* UnEscapeChatTags(char* str_in); + +//---- jabber_svc.c ------------------------------------------------- + +void JabberEnableMenuItems( BOOL bEnable ); + +//---- jabber_std.cpp ---------------------------------------------- + +#if defined( _DEBUG ) + #define JCallService CallService +#else + int __stdcall JCallService( const char* szSvcName, WPARAM wParam, LPARAM lParam ); +#endif + +HANDLE __stdcall JCreateServiceFunction( const char* szService, MIRANDASERVICE serviceProc ); +HANDLE __stdcall JCreateHookableEvent( const char* szService ); +void __stdcall JDeleteSetting( HANDLE hContact, const char* valueName ); +DWORD __stdcall JGetByte( const char* valueName, int parDefltValue ); +DWORD __stdcall JGetByte( HANDLE hContact, const char* valueName, int parDefltValue ); +char* __stdcall JGetContactName( HANDLE hContact ); +DWORD __stdcall JGetDword( HANDLE hContact, const char* valueName, DWORD parDefltValue ); +int __stdcall JGetStaticString( const char* valueName, HANDLE hContact, char* dest, int dest_len ); +int __stdcall JGetStringUtf( HANDLE hContact, char* valueName, DBVARIANT* dbv ); +int __stdcall JGetStringT( HANDLE hContact, char* valueName, DBVARIANT* dbv ); +WORD __stdcall JGetWord( HANDLE hContact, const char* valueName, int parDefltValue ); +void __fastcall JFreeVariant( DBVARIANT* dbv ); +int __stdcall JSendBroadcast( HANDLE hContact, int type, int result, HANDLE hProcess, LPARAM lParam ); +DWORD __stdcall JSetByte( const char* valueName, int parValue ); +DWORD __stdcall JSetByte( HANDLE hContact, const char* valueName, int parValue ); +DWORD __stdcall JSetDword( HANDLE hContact, const char* valueName, DWORD parValue ); +DWORD __stdcall JSetString( HANDLE hContact, const char* valueName, const char* parValue ); +DWORD __stdcall JSetStringT( HANDLE hContact, const char* valueName, const TCHAR* parValue ); +DWORD __stdcall JSetStringUtf( HANDLE hContact, const char* valueName, const char* parValue ); +DWORD __stdcall JSetWord( HANDLE hContact, const char* valueName, int parValue ); +char* __stdcall JTranslate( const char* str ); + +//---- jabber_thread.cpp ------------------------------------------- + +void __cdecl JabberServerThread( struct ThreadData *info ); + +//---- jabber_util.c ---------------------------------------------- + +void __stdcall JabberSerialInit( void ); +void __stdcall JabberSerialUninit( void ); +unsigned int __stdcall JabberSerialNext( void ); +int __stdcall JabberSend( JABBER_SOCKET s, const char* fmt, ... ); +int __stdcall JabberSend( JABBER_SOCKET s, XmlNode& node ); +HANDLE __stdcall JabberHContactFromJID( const TCHAR* jid ); +void __stdcall JabberLog( const char* fmt, ... ); +TCHAR* __stdcall JabberNickFromJID( const TCHAR* jid ); +char* __stdcall JabberUrlDecode( char* str ); +void __stdcall JabberUrlDecodeW( WCHAR* str ); +char* __stdcall JabberUrlEncode( const char* str ); +char* __stdcall JabberUtf8Decode( char*,WCHAR** ); +char* __stdcall JabberUtf8Encode( const char* str ); +char* __stdcall JabberSha1( char* str ); +char* __stdcall JabberUnixToDos( const char* str ); +WCHAR* __stdcall JabberUnixToDosW( const WCHAR* str ); +void __stdcall JabberHttpUrlDecode( char* str ); +char* __stdcall JabberHttpUrlEncode( const char* str ); +int __stdcall JabberCombineStatus( int status1, int status2 ); +TCHAR* __stdcall JabberErrorStr( int errorCode ); +TCHAR* __stdcall JabberErrorMsg( XmlNode *errorNode ); +void __stdcall JabberSendVisibleInvisiblePresence( BOOL invisible ); +char* __stdcall JabberTextEncode( const char* str ); +char* __stdcall JabberTextEncodeW( const wchar_t *str ); +char* __stdcall JabberTextDecode( const char* str ); +void __stdcall JabberUtfToTchar( const char* str, size_t cbLen, LPTSTR& dest ); +char* __stdcall JabberBase64Encode( const char* buffer, int bufferLen ); +char* __stdcall JabberBase64Decode( const TCHAR* buffer, int *resultLen ); +char* __stdcall JabberGetVersionText(); +time_t __stdcall JabberIsoToUnixTime( TCHAR* stamp ); +int __stdcall JabberCountryNameToId( TCHAR* ctry ); +void __stdcall JabberSendPresenceTo( int status, TCHAR* to, XmlNode* extra ); +void __stdcall JabberSendPresence( int ); +void __stdcall JabberStringAppend( char* *str, int *sizeAlloced, const char* fmt, ... ); +TCHAR* __stdcall JabberGetClientJID( const TCHAR* jid, TCHAR*, size_t ); +TCHAR* __stdcall JabberStripJid( const TCHAR* jid, TCHAR* dest, size_t destLen ); +int __stdcall JabberGetPictureType( const char* buf ); + +#if defined( _UNICODE ) + #define JabberUnixToDosT JabberUnixToDosW +#else + #define JabberUnixToDosT JabberUnixToDos +#endif + +//---- jabber_vcard.c ----------------------------------------------- + +int JabberSendGetVcard( const TCHAR* jid ); + +//---- jabber_ws.c ------------------------------------------------- + +BOOL JabberWsInit( void ); +void JabberWsUninit( void ); +JABBER_SOCKET JabberWsConnect( char* host, WORD port ); +int JabberWsSend( JABBER_SOCKET s, char* data, int datalen ); +int JabberWsRecv( JABBER_SOCKET s, char* data, long datalen ); + +/////////////////////////////////////////////////////////////////////////////// +// memory interface + +extern MM_INTERFACE memoryManagerInterface; +#define mir_alloc(n) memoryManagerInterface.mmi_malloc(n) +#define mir_free(ptr) memoryManagerInterface.mmi_free(ptr) +#define mir_realloc(ptr,size) memoryManagerInterface.mmi_realloc(ptr,size) + +__forceinline char * mir_strdup(const char *src) +{ + return (src == NULL) ? NULL : strcpy(( char* )mir_alloc( strlen(src)+1 ), src ); +} + +__forceinline WCHAR* mir_wstrdup(const WCHAR *src) +{ + return (src == NULL) ? NULL : wcscpy(( WCHAR* )mir_alloc(( wcslen(src)+1 )*sizeof( WCHAR )), src ); +} + +#if defined( _UNICODE ) + #define mir_tstrdup mir_wstrdup +#else + #define mir_tstrdup mir_strdup +#endif + +extern LIST_INTERFACE li; + +/////////////////////////////////////////////////////////////////////////////// +// TXT encode helper + +class TextEncoder { + char* m_body; + +public: + __forceinline TextEncoder( const char* pSrc ) : + m_body( JabberTextEncode( pSrc )) + {} + + __forceinline ~TextEncoder() + { mir_free( m_body ); + } + + __forceinline const char* str() const { return m_body; } +}; + +#define TXT(A) TextEncoder(A).str() + +/////////////////////////////////////////////////////////////////////////////// +// UTF encode helper + +class Utf8Encoder { + char* m_body; + +public: + __forceinline Utf8Encoder( const char* pSrc ) : + m_body( JabberUtf8Encode( pSrc )) + {} + + __forceinline ~Utf8Encoder() + { mir_free( m_body ); + } + + __forceinline const char* str() const { return m_body; } +}; + +#define UTF8(A) Utf8Encoder(A).str() + +char* t2a( const TCHAR* src ); +char* u2a( const wchar_t* src ); +wchar_t* a2u( const char* src ); + +#endif diff --git a/miranda-wine/protocols/JabberG/jabber.rc b/miranda-wine/protocols/JabberG/jabber.rc new file mode 100644 index 0000000..cad3677 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber.rc @@ -0,0 +1,839 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPT_JABBERMAIN DIALOGEX 0, 0, 312, 247 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_OPTIONSTAB,"SysTabControl32",WS_TABSTOP,1,1,310,245, + WS_EX_ACCEPTFILES +END + +IDD_OPT_JABBER DIALOGEX 0, 0, 304, 227 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Jabber",IDC_SIMPLE,8,4,290,99 + LTEXT "Username:",IDC_STATIC,19,21,45,8 + EDITTEXT IDC_EDIT_USERNAME,70,19,86,12,ES_AUTOHSCROLL + LTEXT "Password:",IDC_STATIC,19,35,45,8 + EDITTEXT IDC_EDIT_PASSWORD,70,33,86,12,ES_PASSWORD | + ES_AUTOHSCROLL + LTEXT "Resource:",IDC_RESOURCE_T,19,49,45,8 + EDITTEXT IDC_EDIT_RESOURCE,70,47,86,12,ES_AUTOHSCROLL + LTEXT "Priority:",IDC_PRIORITY_LABEL,165,49,31,8 + EDITTEXT IDC_PRIORITY,199,47,42,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_PRIORITY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_NOTHOUSANDS,231,47,11,12 + CONTROL "Save password",IDC_SAVEPASSWORD,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,70,61,172,10 + LTEXT "Login server:",IDC_STATIC,19,75,45,8 + EDITTEXT IDC_EDIT_LOGIN_SERVER,70,73,86,12,ES_AUTOHSCROLL + LTEXT "Port:",IDC_STATIC,19,89,45,8 + EDITTEXT IDC_PORT,70,87,26,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Use SSL",IDC_USE_SSL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,104,88,82,10 + CONTROL "Use TLS",IDC_USE_TLS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,193,88,97,10 + PUSHBUTTON "Register new user",IDC_BUTTON_REGISTER,165,19,78,13 + CONTROL "List of public servers",IDC_LINK_PUBLIC_SERVER, + "Hyperlink",WS_TABSTOP,165,74,79,11 + GROUPBOX "Expert",IDC_STATIC,8,108,290,112 + CONTROL "Manually specify connection host",IDC_MANUAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,19,121,223,10 + LTEXT "Host:",IDC_STATIC,31,134,26,8 + EDITTEXT IDC_HOST,62,132,90,12,ES_AUTOHSCROLL | WS_DISABLED + LTEXT "Port:",IDC_STATIC,161,134,21,8 + EDITTEXT IDC_HOSTPORT,186,132,31,12,ES_AUTOHSCROLL | ES_NUMBER | + WS_DISABLED + CONTROL "Keep connection alive",IDC_KEEPALIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,19,146,221,10 + CONTROL "Automatically delete contacts not in my roster", + IDC_ROSTER_SYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,19, + 158,221,10 + LTEXT "User directory:",IDC_JUD_LABEL,19,175,68,8 + EDITTEXT IDC_JUD,92,172,103,12,ES_AUTOHSCROLL + LTEXT "Messaging language:",IDC_MSGLANG_LABEL,19,189,70,8 + COMBOBOX IDC_MSGLANG,92,187,103,69,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_OPT_JABBER2 DIALOGEX 0, 0, 304, 199 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "File Transfer",IDC_STATIC,8,4,290,69 + CONTROL "Allow file sending through direct peer-to-peer connection", + IDC_DIRECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,18, + 202,10 + CONTROL "Specify proxy server",IDC_PROXY_MANUAL,"Button", + BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,16,54,102,10 + CONTROL "Allow file sending through bytestream proxy server", + IDC_PROXY,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,16,42,202,10 + EDITTEXT IDC_PROXY_ADDR,124,53,81,12,ES_AUTOHSCROLL | WS_DISABLED | + WS_GROUP + CONTROL "Specify external address",IDC_DIRECT_MANUAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,16,30,101,10 + EDITTEXT IDC_DIRECT_ADDR,124,29,81,12,ES_AUTOHSCROLL + GROUPBOX "Miscellaneous",IDC_STATIC,8,75,290,116 + CONTROL "Automatically add contact when accept authorization", + IDC_AUTO_ADD,"Button",BS_AUTOCHECKBOX | BS_TOP | + WS_TABSTOP,16,90,280,12 + CONTROL "Automatically join conferences on login",IDC_AUTOJOIN, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,102,280,10 + CONTROL "Autoaccept multiuser chat invitations", + IDC_AUTO_ACCEPT_MUC,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,16,114,280,10 + CONTROL "Send messages slower, but with full acknowledgement", + IDC_MSG_ACK,"Button",BS_AUTOCHECKBOX | BS_TOP | + WS_TABSTOP,16,126,269,10 + CONTROL "Disable main menu",IDC_DISABLE_MAINMENU,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,16,138,280,10 + CONTROL "Show transport agents on contact list", + IDC_SHOW_TRANSPORT,"Button",BS_AUTOCHECKBOX | BS_TOP | + WS_TABSTOP,16,150,280,10 + CONTROL "Enable avatars",IDC_ENABLE_AVATARS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,16,162,280,10 + CONTROL "Disable SASL authentication (for old servers)", + IDC_DISABLE_SASL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 16,174,279,10 +END + +IDD_INFO_JABBER DIALOGEX 0, 0, 221, 132 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "JID:",IDC_STATIC,7,7,19,8 + EDITTEXT IDC_INFO_JID,31,7,183,12,ES_AUTOHSCROLL | ES_READONLY | + NOT WS_BORDER + LTEXT "Resources:",IDC_STATIC,7,65,81,8 + LISTBOX IDC_INFO_RESOURCE,7,75,69,50,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL + LTEXT "Subscription type:",IDC_STATIC,7,20,60,8,NOT WS_GROUP + EDITTEXT IDC_SUBSCRIPTION,70,20,144,12,ES_READONLY | NOT + WS_BORDER + LTEXT "Software:",IDC_STATIC,81,74,35,8 + LTEXT "Version:",IDC_STATIC,81,103,35,8 + LTEXT "System:",IDC_STATIC,81,117,35,8 + EDITTEXT IDC_SOFTWARE,121,74,93,26,ES_MULTILINE | ES_READONLY | + WS_DISABLED | NOT WS_BORDER + EDITTEXT IDC_VERSION,121,103,93,12,ES_AUTOHSCROLL | ES_READONLY | + WS_DISABLED | NOT WS_BORDER + EDITTEXT IDC_SYSTEM,121,117,93,13,ES_AUTOHSCROLL | ES_READONLY | + WS_DISABLED | NOT WS_BORDER +END + +IDD_OPT_REGISTER DIALOGEX 0, 0, 165, 61 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | + WS_CAPTION +CAPTION "Jabber Account Registration" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "",IDC_REG_STATUS,18,7,139,19 + CONTROL "Progress1",IDC_PROGRESS_REG,"msctls_progress32",NOT + WS_VISIBLE | WS_BORDER | WS_TABSTOP,18,27,130,7 + PUSHBUTTON "OK",IDOK,29,40,50,14 + DEFPUSHBUTTON "Cancel",IDCANCEL,85,40,50,14 + DEFPUSHBUTTON "OK",IDOK2,55,40,50,14,NOT WS_VISIBLE + DEFPUSHBUTTON "Cancel",IDCANCEL2,55,40,50,14,NOT WS_VISIBLE +END + +IDD_AGENTS DIALOGEX 0, 0, 294, 254 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | + WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Jabber Agents" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Register/Search Jabber Agents",IDC_STATIC,7,7,280,115 + LTEXT "Jabber server:",IDC_STATIC,13,20,47,8 + EDITTEXT IDC_AGENT_SERVER,65,19,136,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "Browse",IDC_AGENT_BROWSE,209,19,46,13,WS_DISABLED + CONTROL "List1",IDC_AGENT_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | + WS_BORDER | WS_TABSTOP,13,36,267,60 + PUSHBUTTON "Register...",IDC_AGENT_REGISTER,13,100,50,14, + WS_DISABLED + PUSHBUTTON "Browse/Join chat room...",IDC_JOIN,69,100,93,14, + WS_DISABLED + PUSHBUTTON "Search...",IDC_AGENT_SEARCH,171,100,50,14,NOT + WS_VISIBLE | WS_DISABLED + GROUPBOX "Registered Jabber Transports",IDC_STATIC,7,128,280,99 + CONTROL "List1",IDC_AGENT_TRANSPORT,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | + WS_BORDER | WS_TABSTOP,13,142,267,60 + PUSHBUTTON "Log on",IDC_AGENT_LOGON,13,206,50,14,WS_DISABLED + PUSHBUTTON "Log off",IDC_AGENT_LOGOFF,69,206,50,14,WS_DISABLED + PUSHBUTTON "Unregister",IDC_AGENT_UNREGISTER,125,206,50,14, + WS_DISABLED + PUSHBUTTON "Register with a new service...",IDC_MANUAL_REGISTER,7, + 233,106,14 + PUSHBUTTON "Close",IDCLOSE,237,233,50,14 +END + +IDD_FORM DIALOGEX 0, 0, 258, 224 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | + DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Jabber Form" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Instruction:",IDC_STATIC,7,7,243,8,NOT WS_GROUP + EDITTEXT IDC_INSTRUCTION,7,17,243,38,ES_MULTILINE | ES_READONLY | + WS_VSCROLL + LTEXT "",IDC_FRAME,7,60,243,138,NOT WS_GROUP,WS_EX_CLIENTEDGE + EDITTEXT IDC_FRAME_TEXT,18,101,220,33,ES_CENTER | ES_MULTILINE | + ES_READONLY | NOT WS_BORDER + SCROLLBAR IDC_VSCROLL,238,61,11,136,SBS_VERT | WS_DISABLED + DEFPUSHBUTTON "Submit",IDC_SUBMIT,146,203,50,14,WS_DISABLED + PUSHBUTTON "Cancel",IDCANCEL,200,203,50,14 +END + +IDD_PASSWORD DIALOGEX 0, 0, 286, 63 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | + WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Jabber Password" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_JID,7,7,272,12,ES_READONLY | NOT WS_BORDER | NOT + WS_TABSTOP + EDITTEXT IDC_PASSWORD,7,22,272,12,ES_PASSWORD | ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,23,42,50,14 + PUSHBUTTON "Cancel",IDCANCEL,77,42,50,14 +END + +IDD_VCARD DIALOGEX 0, 0, 238, 210 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | + DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Personal vCard" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Close",IDCLOSE,188,190,45,14 + LTEXT "",IDC_WHITERECT,0,0,238,26 + ICON IDI_WRITE,IDC_LOGO,7,4,20,20 + LTEXT "",IDC_NAME,33,5,200,8,0,WS_EX_TRANSPARENT + CONTROL "Tab1",IDC_TABS,"SysTabControl32",TCS_HOTTRACK | + TCS_MULTILINE | WS_TABSTOP,5,29,228,158 + LTEXT "View and update Jabber personal vCard",IDC_DESCRIPTION, + 44,13,189,8,0,WS_EX_TRANSPARENT + CTEXT "Updating",IDC_UPDATING,65,194,49,8,SS_NOPREFIX | + SS_CENTERIMAGE | NOT WS_VISIBLE + PUSHBUTTON "Update Now",IDC_UPDATE,5,190,55,14,WS_DISABLED + PUSHBUTTON "Save Changes",IDC_SAVE,124,190,61,14,WS_DISABLED +END + +IDD_VCARD_HOME DIALOGEX 0, 0, 222, 132 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Address1:",IDC_STATIC,5,7,50,8 + LTEXT "City:",IDC_STATIC,5,34,50,8 + LTEXT "State:",IDC_STATIC,5,48,50,8 + LTEXT "Address2:",IDC_STATIC,5,20,50,8 + EDITTEXT IDC_ADDRESS1,58,5,148,12,ES_AUTOHSCROLL + EDITTEXT IDC_ADDRESS2,58,19,148,12,ES_AUTOHSCROLL + EDITTEXT IDC_CITY,58,33,75,12,ES_AUTOHSCROLL + EDITTEXT IDC_STATE,58,47,75,12,ES_AUTOHSCROLL + LTEXT "ZIP:",IDC_STATIC,5,63,50,8 + EDITTEXT IDC_ZIP,58,61,58,12,ES_AUTOHSCROLL + LTEXT "Country:",IDC_STATIC,5,76,50,8 + EDITTEXT IDC_COUNTRY,58,75,75,12,ES_AUTOHSCROLL +END + +IDD_VCARD_PERSONAL DIALOGEX 0, 0, 222, 132 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Full name:",IDC_STATIC,5,7,51,8 + EDITTEXT IDC_FULLNAME,58,5,159,12,ES_AUTOHSCROLL + LTEXT "Nick name:",IDC_STATIC,5,20,51,8 + EDITTEXT IDC_NICKNAME,58,19,82,12,ES_AUTOHSCROLL + LTEXT "First name:",IDC_STATIC,5,34,51,8 + EDITTEXT IDC_FIRSTNAME,58,33,82,12,ES_AUTOHSCROLL + LTEXT "Middle:",IDC_STATIC,143,34,25,8 + EDITTEXT IDC_MIDDLE,174,33,43,12,ES_AUTOHSCROLL + LTEXT "Last name:",IDC_STATIC,5,48,51,8 + EDITTEXT IDC_LASTNAME,58,47,82,12,ES_AUTOHSCROLL + LTEXT "Date of birth:",IDC_STATIC,5,63,51,8 + EDITTEXT IDC_BIRTH,58,61,62,12,ES_AUTOHSCROLL + LTEXT "YYYY-MM-DD",IDC_STATIC,124,63,90,8 + LTEXT "Gender:",IDC_STATIC,5,77,51,8 + COMBOBOX IDC_GENDER,58,75,62,52,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + LTEXT "Occupation:",IDC_STATIC,5,90,51,8 + EDITTEXT IDC_OCCUPATION,58,89,159,12,ES_AUTOHSCROLL + LTEXT "Homepage:",IDC_STATIC,6,104,51,8 + EDITTEXT IDC_HOMEPAGE,58,102,159,12,ES_AUTOHSCROLL +END + +IDD_VCARD_WORK DIALOGEX 0, 0, 222, 132 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Company:",IDC_STATIC,6,7,50,8 + EDITTEXT IDC_COMPANY,58,5,148,12,ES_AUTOHSCROLL + LTEXT "Department:",IDC_STATIC,6,20,50,8 + EDITTEXT IDC_DEPARTMENT,58,19,148,12,ES_AUTOHSCROLL + LTEXT "Title:",IDC_STATIC,6,33,50,8 + EDITTEXT IDC_TITLE,58,32,75,12,ES_AUTOHSCROLL + LTEXT "Address1:",IDC_STATIC,5,47,50,8 + EDITTEXT IDC_ADDRESS1,58,45,148,12,ES_AUTOHSCROLL + LTEXT "Address2:",IDC_STATIC,5,60,50,8 + EDITTEXT IDC_ADDRESS2,58,59,148,12,ES_AUTOHSCROLL + LTEXT "City:",IDC_STATIC,5,74,50,8 + EDITTEXT IDC_CITY,58,73,75,12,ES_AUTOHSCROLL + LTEXT "State:",IDC_STATIC,5,88,50,8 + EDITTEXT IDC_STATE,58,87,75,12,ES_AUTOHSCROLL + LTEXT "ZIP:",IDC_STATIC,5,103,50,8 + EDITTEXT IDC_ZIP,58,101,58,12,ES_AUTOHSCROLL + LTEXT "Country:",IDC_STATIC,5,116,50,8 + EDITTEXT IDC_COUNTRY,58,115,75,12,ES_AUTOHSCROLL +END + +IDD_VCARD_CONTACT DIALOGEX 0, 0, 222, 132 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "E-mail:",IDC_STATIC,5,5,212,8 + CONTROL "List1",IDC_EMAILS,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_NOLABELWRAP | LVS_AUTOARRANGE | + LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,5,14, + 212,50 + LTEXT "Phone:",IDC_STATIC,5,68,212,8 + CONTROL "List1",IDC_PHONES,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_NOLABELWRAP | LVS_AUTOARRANGE | + LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,5,77, + 212,50 +END + +IDD_VCARD_ADDEMAIL DIALOGEX 0, 0, 186, 89 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Jabber vCard: Add Email Address" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Email address:",IDC_STATIC,7,8,52,8 + EDITTEXT IDC_EMAIL,61,7,118,12,ES_AUTOHSCROLL + CONTROL "Home",IDC_HOME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,61, + 23,110,10 + CONTROL "Work",IDC_WORK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,61, + 33,111,10 + CONTROL "Internet",IDC_INTERNET,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,61,43,110,10 + CONTROL "X400",IDC_X400,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,61, + 53,110,10 + DEFPUSHBUTTON "OK",IDOK,40,68,50,14 + PUSHBUTTON "Cancel",IDCANCEL,96,68,50,14 +END + +IDD_VCARD_ADDPHONE DIALOGEX 0, 0, 186, 110 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Jabber vCard: Add Phone Number" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Phone number:",IDC_STATIC,7,8,49,8 + EDITTEXT IDC_PHONE,61,7,118,12,ES_AUTOHSCROLL + CONTROL "Home",IDC_HOME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,26, + 23,67,10 + CONTROL "Work",IDC_WORK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,26, + 33,67,10 + CONTROL "Voice",IDC_VOICE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 26,43,67,10 + CONTROL "Fax",IDC_FAX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,26, + 53,67,10 + DEFPUSHBUTTON "OK",IDOK,40,88,50,14 + PUSHBUTTON "Cancel",IDCANCEL,96,88,50,14 + CONTROL "Cellular",IDC_CELL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,101,23,71,10 + CONTROL "Video",IDC_VIDEO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 101,33,71,10 + CONTROL "BBS",IDC_BBS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,101, + 43,71,10 + CONTROL "Modem",IDC_MODEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 101,53,71,10 + CONTROL "Pager",IDC_PAGER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 26,63,67,10 + CONTROL "Text/Messaging",IDC_MSG,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,26,73,67,10 + CONTROL "ISDN",IDC_ISDN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 101,63,71,10 + CONTROL "PCS",IDC_PCS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,101, + 73,71,10 +END + +IDD_VCARD_PHOTO DIALOGEX 0, 0, 222, 132 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + PUSHBUTTON "",IDC_LOAD,200,5,17,14,BS_ICON + PUSHBUTTON "",IDC_SAVE,200,5,17,14,BS_ICON + CTEXT "",IDC_CANVAS,5,5,189,122,SS_CENTERIMAGE + PUSHBUTTON "",IDC_DELETE,200,23,17,14,BS_ICON | WS_DISABLED +END + +IDD_VCARD_NOTE DIALOGEX 0, 0, 222, 132 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Description:",IDC_STATIC,5,5,212,8 + EDITTEXT IDC_DESC,5,15,212,112,ES_MULTILINE +END + +IDD_CHANGEPASSWORD DIALOGEX 0, 0, 181, 79 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | + DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Change Password" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Current Password:",IDC_STATIC,7,8,81,8 + EDITTEXT IDC_OLDPASSWD,95,7,79,12,ES_PASSWORD | ES_AUTOHSCROLL + LTEXT "New Password:",IDC_STATIC,7,22,81,8 + EDITTEXT IDC_NEWPASSWD,95,21,79,12,ES_PASSWORD | ES_AUTOHSCROLL + LTEXT "Confirm New Password:",IDC_STATIC,7,36,81,8 + EDITTEXT IDC_NEWPASSWD2,95,35,79,12,ES_PASSWORD | ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,38,58,50,14 + PUSHBUTTON "Cancel",IDCANCEL,92,58,50,14 +END + +IDD_GROUPCHAT DIALOGEX 0, 0, 306, 208 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | + WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Jabber Multi-User Conference" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Conference server:",-1,5,8,70,8 + COMBOBOX IDC_SERVER,80,7,168,79,CBS_DROPDOWN | CBS_AUTOHSCROLL | + WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Browse",IDC_BROWSE,253,7,46,13 + CONTROL "List1",IDC_ROOM,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | WS_BORDER | + WS_TABSTOP,5,24,294,160 + PUSHBUTTON "Close",IDCLOSE,249,189,50,14 +END + +IDD_GROUPCHAT_JOIN DIALOGEX 0, 0, 208, 96 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | + WS_POPUP | WS_VISIBLE | WS_CAPTION +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Join Jabber Multi-User Conference Room" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Conference server:",IDC_STATIC,7,8,64,8 + EDITTEXT IDC_SERVER,74,7,127,12,ES_AUTOHSCROLL + LTEXT "Room:",IDC_STATIC,7,22,64,8 + EDITTEXT IDC_ROOM,74,21,127,12,ES_AUTOHSCROLL + LTEXT "Nick name:",IDC_STATIC,7,36,64,8 + EDITTEXT IDC_NICK,74,35,127,12,ES_AUTOHSCROLL + LTEXT "Password:",IDC_STATIC,7,50,64,8 + EDITTEXT IDC_PASSWORD,74,49,127,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,51,75,50,14 + PUSHBUTTON "Cancel",IDCANCEL,106,75,50,14 +END + +IDD_GROUPCHAT_INPUT DIALOGEX 0, 0, 275, 46 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | + DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_TOPIC,7,7,261,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,165,25,50,14 + PUSHBUTTON "Cancel",IDCANCEL,218,25,50,14 +END + +IDD_JIDLIST DIALOGEX 0, 0, 207, 112 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | + DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "JID List" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "List4",IDC_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_NOLABELWRAP | LVS_AUTOARRANGE | + LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | + WS_TABSTOP,7,7,193,98 +END + +IDD_AGENT_MANUAL_REGISTER DIALOGEX 0, 0, 186, 45 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | + DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Jabber Agent Registration" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "JID:",IDC_STATIC,7,9,17,8 + EDITTEXT IDC_JID,29,7,150,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "Register",IDOK,75,24,50,14,WS_DISABLED + PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14 +END + +IDD_GROUPCHAT_INVITE DIALOGEX 0, 0, 186, 73 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | + WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Jabber Groupchat Invite a User" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Room JID:",IDC_STATIC,7,6,46,8 + EDITTEXT IDC_ROOM,57,4,122,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "User JID:",IDC_STATIC,7,21,46,8 + COMBOBOX IDC_USER,57,19,122,64,CBS_DROPDOWN | CBS_AUTOHSCROLL | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Reason:",IDC_STATIC,7,37,46,8 + EDITTEXT IDC_REASON,57,35,122,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "&Invite",IDC_INVITE,75,53,50,14 + PUSHBUTTON "Cancel",IDCANCEL,129,53,50,14 +END + +IDD_GROUPCHAT_INVITE_ACCEPT DIALOGEX 0, 0, 232, 112 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | + DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Multi-User Conference Invitation" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "&Accept",IDC_ACCEPT,124,93,50,14 + PUSHBUTTON "Cancel",IDCANCEL,178,93,50,14 + LTEXT "From:",IDC_STATIC,33,24,63,8 + LTEXT "Room JID:",IDC_STATIC,33,38,63,8 + LTEXT "Reason:",IDC_STATIC,33,52,63,8 + LTEXT "Nick:",IDC_STATIC,33,76,62,8 + LTEXT "The following invitation to join a multi-user conference is received.", + IDC_STATIC,3,4,225,8 + EDITTEXT IDC_FROM,99,22,105,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_ROOM,99,36,105,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_REASON,99,50,105,12,ES_AUTOHSCROLL | ES_READONLY + GROUPBOX "",IDC_STATIC,12,13,211,57 + EDITTEXT IDC_NICK,99,74,105,12,ES_AUTOHSCROLL +END + +IDD_OPT_SETAVATAR DIALOGEX 0, 0, 222, 131 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "",IDC_AVATAR,"Static",SS_BITMAP | SS_CENTERIMAGE | + SS_SUNKEN,9,11,96,96 + LTEXT "Note: Only JPGs and GIFs\nImage size max 64x64\nFile size max 6kB", + IDC_STATIC,112,54,106,34 + PUSHBUTTON "Set",IDC_SETAVATAR,9,112,45,13 + PUSHBUTTON "Delete",IDC_DELETEAVATAR,61,112,41,13 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPT_JABBER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 297 + TOPMARGIN, 7 + BOTTOMMARGIN, 220 + END + + IDD_OPT_JABBER2, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 296 + VERTGUIDE, 9 + TOPMARGIN, 3 + BOTTOMMARGIN, 191 + END + + IDD_INFO_JABBER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 214 + TOPMARGIN, 7 + BOTTOMMARGIN, 125 + END + + IDD_OPT_REGISTER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 158 + TOPMARGIN, 7 + BOTTOMMARGIN, 54 + END + + IDD_AGENTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 287 + TOPMARGIN, 7 + BOTTOMMARGIN, 247 + END + + IDD_FORM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 250 + TOPMARGIN, 7 + BOTTOMMARGIN, 217 + END + + IDD_PASSWORD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 279 + TOPMARGIN, 7 + BOTTOMMARGIN, 56 + END + + IDD_VCARD, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 233 + TOPMARGIN, 5 + BOTTOMMARGIN, 204 + END + + IDD_VCARD_HOME, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 217 + TOPMARGIN, 5 + BOTTOMMARGIN, 127 + END + + IDD_VCARD_PERSONAL, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 217 + TOPMARGIN, 5 + BOTTOMMARGIN, 127 + END + + IDD_VCARD_WORK, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 217 + TOPMARGIN, 5 + BOTTOMMARGIN, 127 + END + + IDD_VCARD_CONTACT, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 217 + TOPMARGIN, 5 + BOTTOMMARGIN, 127 + END + + IDD_VCARD_ADDEMAIL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 82 + END + + IDD_VCARD_ADDPHONE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 102 + END + + IDD_VCARD_PHOTO, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 217 + TOPMARGIN, 5 + BOTTOMMARGIN, 127 + END + + IDD_VCARD_NOTE, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 217 + TOPMARGIN, 5 + BOTTOMMARGIN, 127 + END + + IDD_CHANGEPASSWORD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 174 + TOPMARGIN, 7 + BOTTOMMARGIN, 72 + END + + IDD_GROUPCHAT, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 299 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_GROUPCHAT_JOIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 201 + TOPMARGIN, 7 + BOTTOMMARGIN, 89 + END + + IDD_GROUPCHAT_INPUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 268 + TOPMARGIN, 7 + BOTTOMMARGIN, 39 + END + + IDD_JIDLIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 200 + TOPMARGIN, 7 + BOTTOMMARGIN, 105 + END + + IDD_AGENT_MANUAL_REGISTER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 38 + END + + IDD_GROUPCHAT_INVITE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 4 + BOTTOMMARGIN, 67 + END + + IDD_GROUPCHAT_INVITE_ACCEPT, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 107 + END + + IDD_OPT_SETAVATAR, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 218 + VERTGUIDE, 9 + TOPMARGIN, 3 + BOTTOMMARGIN, 126 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_JABBER ICON "icos\\jabber.ico" +IDI_GROUP ICON "icos\\group.ico" +IDI_KEYS ICON "icos\\key.ico" +IDI_ADDCONTACT ICON "icos\\addcontact.ico" +IDI_ADDROSTER ICON "icos\\add2roster.ico" +IDI_AGENTS ICON "icos\\block.ico" +IDI_VCARD ICON "icos\\pages.ico" +IDI_DELETE ICON "icos\\delete.ico" +IDI_EDIT ICON "icos\\rename.ico" +IDI_GRANT ICON "icos\\grant.ico" +IDI_OPEN ICON "icos\\open.ico" +IDI_REQUEST ICON "icos\\request.ico" +IDI_USER2ROOM ICON "icos\\user2room.ico" +IDI_WRITE ICON "icos\\write.ico" +IDI_SAVE ICON "icos\\save.ico" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/miranda-wine/protocols/JabberG/jabber_agent.cpp b/miranda-wine/protocols/JabberG/jabber_agent.cpp new file mode 100644 index 0000000..061cfba --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_agent.cpp @@ -0,0 +1,565 @@ +/* + +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_agent.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include +#include "resource.h" +#include "jabber_iq.h" + +static BOOL CALLBACK JabberAgentsDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ); +BOOL CALLBACK JabberAgentRegInputDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ); +static BOOL CALLBACK JabberAgentRegDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ); +static BOOL CALLBACK JabberAgentManualRegDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ); + +int JabberMenuHandleAgents( WPARAM wParam, LPARAM lParam ) +{ + if ( IsWindow( hwndJabberAgents )) + SetForegroundWindow( hwndJabberAgents ); + else + CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_AGENTS ), NULL, JabberAgentsDlgProc, ( LPARAM )NULL ); + + return 0; +} + +static void JabberRegisterAgent( HWND hwndDlg, TCHAR* jid ) +{ + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_GETREGISTER, JabberIqResultGetRegister ); + XmlNodeIq iq( "get", iqId, jid ); + XmlNode* query = iq.addQuery( "jabber:iq:register" ); + JabberSend( jabberThreadInfo->s, iq ); + hwndAgentRegInput = CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_FORM ), hwndDlg, JabberAgentRegInputDlgProc, 0 ); +} + +static BOOL CALLBACK JabberAgentsDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + HWND lv; + LVCOLUMN lvCol; + LVITEM lvItem; + JABBER_LIST_ITEM *item; + int i; + TCHAR text[128]; + TCHAR* p; + int iqId; + + switch ( msg ) { + case WM_INITDIALOG: + hwndJabberAgents = hwndDlg; + SendMessage( hwndDlg, WM_SETICON, ICON_BIG, ( LPARAM )LoadIcon( hInst, MAKEINTRESOURCE( IDI_AGENTS )) ); + TranslateDialogDefault( hwndDlg ); + // Add columns to the top list + lv = GetDlgItem( hwndDlg, IDC_AGENT_LIST ); + lvCol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvCol.pszText = TranslateT( "JID" ); + lvCol.cx = 120; + lvCol.iSubItem = 0; + ListView_InsertColumn( lv, 0, &lvCol ); + lvCol.pszText = TranslateT( "Description" ); + lvCol.cx = 250; + lvCol.iSubItem = 1; + ListView_InsertColumn( lv, 1, &lvCol ); + // Add columns to the bottom list + lv = GetDlgItem( hwndDlg, IDC_AGENT_TRANSPORT ); + lvCol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvCol.pszText = TranslateT( "JID" ); + lvCol.cx = 120; + lvCol.iSubItem = 0; + ListView_InsertColumn( lv, 0, &lvCol ); + lvCol.pszText = TranslateT( "Status" ); + lvCol.cx = 80; + lvCol.iSubItem = 1; + ListView_InsertColumn( lv, 1, &lvCol ); + if ( jabberOnline ) { + SetDlgItemTextA( hwndDlg, IDC_AGENT_SERVER, jabberThreadInfo->server ); + JabberListRemoveList( LIST_AGENT ); + iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_DISCOAGENTS, JabberIqResultDiscoAgentItems ); + + XmlNodeIq iq( "get", iqId, jabberThreadInfo->server ); + XmlNode* query = iq.addQuery( "http://jabber.org/protocol/disco#items" ); + JabberSend( jabberThreadInfo->s, iq ); + + SendMessage( hwndDlg, WM_JABBER_TRANSPORT_REFRESH, 0, 0 ); + } + return TRUE; + case WM_NOTIFY: + switch (( ( LPNMHDR ) lParam )->code ) { + case LVN_ITEMCHANGED: + { + LPNMLISTVIEW lpnm; + + lpnm = ( LPNMLISTVIEW ) lParam; + if ( lpnm->hdr.idFrom == IDC_AGENT_LIST ) { + lv = GetDlgItem( hwndDlg, IDC_AGENT_LIST ); + if ( lpnm->uChanged & LVIF_STATE ) { + if ( lpnm->uNewState & LVIS_SELECTED ) { + lvItem.iItem = lpnm->iItem; + lvItem.iSubItem = 0; + lvItem.mask = LVIF_TEXT; + lvItem.pszText = text; + lvItem.cchTextMax = SIZEOF( text ); + ListView_GetItem( lv, &lvItem ); + if (( item=JabberListGetItemPtr( LIST_AGENT, lvItem.pszText )) != NULL ) { + if ( item->cap & AGENT_CAP_REGISTER ) + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_REGISTER ), TRUE ); + //if ( item->canSearch ) EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_SEARCH ), TRUE ); + if ( item->cap & AGENT_CAP_GROUPCHAT ) + EnableWindow( GetDlgItem( hwndDlg, IDC_JOIN ), TRUE ); + } + } + else { + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_REGISTER ), FALSE ); + //EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_SEARCH ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_JOIN ), FALSE ); + } + return TRUE; + } + } + else if ( lpnm->hdr.idFrom == IDC_AGENT_TRANSPORT ) { + lv = GetDlgItem( hwndDlg, IDC_AGENT_TRANSPORT ); + if ( lpnm->uChanged & LVIF_STATE ) { + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGON ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGOFF ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_UNREGISTER ), FALSE ); + if ( lpnm->uNewState & LVIS_SELECTED ) { + lvItem.iItem = lpnm->iItem; + lvItem.iSubItem = 0; + lvItem.mask = LVIF_TEXT; + lvItem.pszText = text; + lvItem.cchTextMax = SIZEOF( text ); + ListView_GetItem( lv, &lvItem ); + if (( item=JabberListGetItemPtr( LIST_ROSTER, lvItem.pszText )) != NULL ) { + if ( item->status == ID_STATUS_OFFLINE ) + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGON ), TRUE ); + else + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGOFF ), TRUE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_UNREGISTER ), TRUE ); + } } + return TRUE; + } } } + break; + } + break; + case WM_JABBER_AGENT_REFRESH: + // lParam = server from which agent information is obtained + if ( lParam ) + SetDlgItemText( hwndDlg, IDC_AGENT_SERVER, ( TCHAR* )lParam ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_REGISTER ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_SEARCH ), FALSE ); + i = 0; + lv = GetDlgItem( hwndDlg, IDC_AGENT_LIST ); + ListView_DeleteAllItems( lv ); + lvItem.iItem = 0; + while (( i=JabberListFindNext( LIST_AGENT, i )) >= 0 ) { + if (( item=JabberListGetItemPtrFromIndex( i )) != NULL ) { + lvItem.mask = LVIF_TEXT; + lvItem.iSubItem = 0; + lvItem.pszText = item->jid; + ListView_InsertItem( lv, &lvItem ); + lvItem.iSubItem = 1; + lvItem.pszText = item->name; + ListView_SetItem( lv, &lvItem ); + lvItem.iItem++; + } + i++; + } + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_SERVER ), TRUE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_BROWSE ), TRUE ); + return TRUE; + case WM_JABBER_TRANSPORT_REFRESH: + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGON ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGOFF ), FALSE ); + i = 0; + lv = GetDlgItem( hwndDlg, IDC_AGENT_TRANSPORT ); + ListView_DeleteAllItems( lv ); + lvItem.iItem = 0; + while (( i=JabberListFindNext( LIST_ROSTER, i )) >= 0 ) { + if (( item=JabberListGetItemPtrFromIndex( i )) != NULL ) { + if ( _tcschr( item->jid, '@' )==NULL && item->subscription!=SUB_NONE ) { + _tcscpy( text, item->jid ); + if (( p=_tcschr( text, '/' )) != NULL ) + *p = '\0'; + lvItem.mask = LVIF_TEXT; + lvItem.iSubItem = 0; + lvItem.pszText = text; + ListView_InsertItem( lv, &lvItem ); + lvItem.iSubItem = 1; + if ( item->status != ID_STATUS_OFFLINE ) + lvItem.pszText = TranslateT( "Online" ); + else + lvItem.pszText = TranslateT( "Offline" ); + ListView_SetItem( lv, &lvItem ); + lvItem.iItem++; + } } + i++; + } + return TRUE; + case WM_JABBER_CHECK_ONLINE: + if ( !jabberOnline ) { + if ( hwndAgentRegInput ) + DestroyWindow( hwndAgentRegInput ); + if ( hwndAgentManualReg ) + DestroyWindow( hwndAgentManualReg ); + } + break; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_MANUAL_REGISTER: + hwndAgentManualReg = CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_AGENT_MANUAL_REGISTER ), hwndDlg, JabberAgentManualRegDlgProc, 0 ); + return TRUE; + case IDC_AGENT_REGISTER: + lv = GetDlgItem( hwndDlg, IDC_AGENT_LIST ); + if (( lvItem.iItem=ListView_GetNextItem( lv, -1, LVNI_SELECTED )) >= 0 ) { + lvItem.iSubItem = 0; + lvItem.mask = LVIF_TEXT; + lvItem.pszText = text; + lvItem.cchTextMax = SIZEOF( text ); + ListView_GetItem( lv, &lvItem ); + ListView_SetItemState( lv, lvItem.iItem, 0, LVIS_SELECTED ); // Unselect the item + if (( item=JabberListGetItemPtr( LIST_AGENT, lvItem.pszText )) != NULL ) + JabberRegisterAgent( hwndDlg, item->jid ); + } + return TRUE; + case IDC_JOIN: + lv = GetDlgItem( hwndDlg, IDC_AGENT_LIST ); + if (( lvItem.iItem=ListView_GetNextItem( lv, -1, LVNI_SELECTED )) >= 0 ) { + lvItem.iSubItem = 0; + lvItem.mask = LVIF_TEXT; + lvItem.pszText = text; + lvItem.cchTextMax = SIZEOF( text ); + ListView_GetItem( lv, &lvItem ); + ListView_SetItemState( lv, lvItem.iItem, 0, LVIS_SELECTED ); // Unselect the item + if (( item=JabberListGetItemPtr( LIST_AGENT, lvItem.pszText )) != NULL ) + JabberMenuHandleGroupchat( 0, ( LPARAM )item->jid ); + } + return TRUE; + case IDC_AGENT_SERVER: + GetDlgItemText( hwndDlg, IDC_AGENT_SERVER, text, SIZEOF( text )); + if ( jabberOnline && text[0] ) + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_BROWSE ), TRUE ); + else + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_BROWSE ), FALSE ); + break; + case IDC_AGENT_BROWSE: + GetDlgItemText( hwndDlg, IDC_AGENT_SERVER, text, SIZEOF( text )); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_BROWSE ), FALSE ); + ListView_DeleteAllItems( GetDlgItem( hwndDlg, IDC_AGENT_LIST )); + JabberListRemoveList( LIST_AGENT ); + iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_DISCOAGENTS, JabberIqResultDiscoAgentItems ); + { XmlNodeIq iq( "get", iqId, text ); + XmlNode* query = iq.addQuery( "http://jabber.org/protocol/disco#items" ); + JabberSend( jabberThreadInfo->s, iq ); + } + return TRUE; + + case IDC_AGENT_LOGON: + case IDC_AGENT_LOGOFF: + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_UNREGISTER ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGON ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGOFF ), FALSE ); + lv = GetDlgItem( hwndDlg, IDC_AGENT_TRANSPORT ); + if (( lvItem.iItem=ListView_GetNextItem( lv, -1, LVNI_SELECTED )) >= 0 ) { + lvItem.iSubItem = 0; + lvItem.mask = LVIF_TEXT; + lvItem.pszText = text; + lvItem.cchTextMax = SIZEOF( text ); + ListView_GetItem( lv, &lvItem ); + if (( item=JabberListGetItemPtr( LIST_ROSTER, lvItem.pszText )) != NULL ) { + XmlNode p( "presence" ); p.addAttr( "to", item->jid ); + if ( LOWORD( wParam ) != IDC_AGENT_LOGON ) + p.addAttr( "type", "unavailable" ); + JabberSend( jabberThreadInfo->s, p ); + } } + return TRUE; + case IDC_AGENT_UNREGISTER: + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_UNREGISTER ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGON ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_AGENT_LOGOFF ), FALSE ); + lv = GetDlgItem( hwndDlg, IDC_AGENT_TRANSPORT ); + if (( lvItem.iItem=ListView_GetNextItem( lv, -1, LVNI_SELECTED )) >= 0 ) { + lvItem.iSubItem = 0; + lvItem.mask = LVIF_TEXT; + lvItem.pszText = text; + lvItem.cchTextMax = SIZEOF( text ); + ListView_GetItem( lv, &lvItem ); + if (( item=JabberListGetItemPtr( LIST_ROSTER, lvItem.pszText )) != NULL ) { + { XmlNodeIq iq( "set", NOID, item->jid ); + XmlNode* query = iq.addQuery( "jabber:iq:register" ); + query->addChild( "remove" ); + JabberSend( jabberThreadInfo->s, iq ); + } + { + XmlNodeIq iq( "set" ); + XmlNode* query = iq.addQuery( "jabber:iq:roster" ); + XmlNode* itm = query->addChild( "item" ); itm->addAttr( "jid", item->jid ); itm->addAttr( "subscription", "remove" ); + JabberSend( jabberThreadInfo->s, iq ); + } } } + return TRUE; + + case IDCLOSE: + DestroyWindow( hwndDlg ); + return TRUE; + } + break; + case WM_CLOSE: + DestroyWindow( hwndDlg ); + break; + case WM_DESTROY: + hwndJabberAgents = NULL; + break; + } + return FALSE; +} + +BOOL CALLBACK JabberAgentRegInputDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + static XmlNode *agentRegIqNode; + + int id, ypos, i; + TCHAR *from, *str, *str2; + + switch ( msg ) { + case WM_INITDIALOG: + { + EnableWindow( GetParent( hwndDlg ), FALSE ); + TranslateDialogDefault( hwndDlg ); + agentRegIqNode = NULL; + SetWindowText( hwndDlg, TranslateT( "Jabber Agent Registration" )); + SetDlgItemText( hwndDlg, IDC_SUBMIT, TranslateT( "Register" )); + SetDlgItemText( hwndDlg, IDC_FRAME_TEXT, TranslateT( "Please wait..." )); + + // Enable WS_EX_CONTROLPARENT on IDC_FRAME ( so tab stop goes through all its children ) + LONG frameExStyle = GetWindowLong( GetDlgItem( hwndDlg, IDC_FRAME ), GWL_EXSTYLE ); + frameExStyle |= WS_EX_CONTROLPARENT; + SetWindowLong( GetDlgItem( hwndDlg, IDC_FRAME ), GWL_EXSTYLE, frameExStyle ); + return TRUE; + } + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_SUBMIT: + { + XmlNode *queryNode, *xNode, *n; + + if ( agentRegIqNode == NULL ) return TRUE; + if (( from=JabberXmlGetAttrValue( agentRegIqNode, "from" )) == NULL ) return TRUE; + if (( queryNode=JabberXmlGetChild( agentRegIqNode, "query" )) == NULL ) return TRUE; + HWND hFrame = GetDlgItem( hwndDlg, IDC_FRAME ); + + str = ( TCHAR* )alloca( sizeof(TCHAR) * 128 ); + str2 = ( TCHAR* )alloca( sizeof(TCHAR) * 128 ); + id = 0; + + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_SETREGISTER, JabberIqResultSetRegister ); + + XmlNodeIq iq( "set", iqId, from ); + XmlNode* query = iq.addQuery( "jabber:iq:register" ); + + if (( xNode=JabberXmlGetChild( queryNode, "x" )) != NULL ) { + // use new jabber:x:data form + query->addChild( JabberFormGetData( hFrame, xNode )); + } + else { + // use old registration information form + for ( i=0; inumChild; i++ ) { + n = queryNode->child[i]; + if ( n->name ) { + if ( !strcmp( n->name, "key" )) { + // field that must be passed along with the registration + if ( n->text ) + query->addChild( n->name, n->text ); + else + query->addChild( n->name ); + } + else if ( !strcmp( n->name, "registered" ) || !strcmp( n->name, "instructions" )) { + // do nothing, we will skip these + } + else { + GetDlgItemText( hFrame, id, str2, 128 ); + query->addChild( n->name, str2 ); + id++; + } } } } + + JabberSend( jabberThreadInfo->s, iq ); + DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_OPT_REGISTER ), hwndDlg, JabberAgentRegDlgProc, 0 ); + // Fall through to IDCANCEL + } + case IDCANCEL: + if ( agentRegIqNode ) + delete agentRegIqNode; + DestroyWindow( hwndDlg ); + return TRUE; + } + break; + case WM_JABBER_REGINPUT_ACTIVATE: + if ( wParam == 1 ) { // success + // lParam = node from agent JID as a result of "get jabber:iq:register" + HWND hFrame = GetDlgItem( hwndDlg, IDC_FRAME ); + HFONT hFont = ( HFONT ) SendMessage( hFrame, WM_GETFONT, 0, 0 ); + ShowWindow( GetDlgItem( hwndDlg, IDC_FRAME_TEXT ), SW_HIDE ); + + XmlNode *queryNode, *xNode, *n; + if (( agentRegIqNode=( XmlNode * ) lParam ) == NULL ) return TRUE; + if (( queryNode=JabberXmlGetChild( agentRegIqNode, "query" )) == NULL ) return TRUE; + id = 0; + ypos = 14; + if (( xNode=JabberXmlGetChild( queryNode, "x" )) != NULL ) { + // use new jabber:x:data form + if (( n=JabberXmlGetChild( xNode, "instructions" ))!=NULL && n->text!=NULL ) + SetDlgItemText( hwndDlg, IDC_INSTRUCTION, n->text ); + + JabberFormCreateUI( hFrame, xNode, &i /*dummy*/ ); + } + else { + // use old registration information form + for ( i=0; inumChild; i++ ) { + n = queryNode->child[i]; + if ( n->name ) { + if ( !strcmp( n->name, "instructions" )) { + SetDlgItemText( hwndDlg, IDC_INSTRUCTION, n->text ); + } + else if ( !strcmp( n->name, "key" ) || !strcmp( n->name, "registered" )) { + // do nothing + } + else if ( !strcmp( n->name, "password" )) { + HWND hCtrl = CreateWindowA( "static", n->name, WS_CHILD|WS_VISIBLE|SS_RIGHT, 10, ypos+4, 100, 18, hFrame, ( HMENU ) IDC_STATIC, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + hCtrl = CreateWindowEx( WS_EX_CLIENTEDGE, _T("edit"), n->text, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL|ES_PASSWORD, 120, ypos, 128, 24, hFrame, ( HMENU ) id, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + id++; + ypos += 24; + } + else { // everything else is a normal text field + HWND hCtrl = CreateWindowA( "static", n->name, WS_CHILD|WS_VISIBLE|SS_RIGHT, 10, ypos+4, 100, 18, hFrame, ( HMENU ) IDC_STATIC, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + hCtrl = CreateWindowEx( WS_EX_CLIENTEDGE, _T("edit"), n->text, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL, 120, ypos, 128, 24, hFrame, ( HMENU ) id, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + id++; + ypos += 24; + } } } } + + EnableWindow( GetDlgItem( hwndDlg, IDC_SUBMIT ), TRUE ); + } + else if ( wParam == 0 ) { + // lParam = error message + SetDlgItemTextA( hwndDlg, IDC_FRAME_TEXT, ( LPCSTR ) lParam ); + } + return TRUE; + case WM_DESTROY: + hwndAgentRegInput = NULL; + EnableWindow( GetParent( hwndDlg ), TRUE ); + SetActiveWindow( GetParent( hwndDlg )); + break; + } + + return FALSE; +} + +static BOOL CALLBACK JabberAgentManualRegDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + static BOOL dontEnableParent; // Very ugly hack + + switch ( msg ) { + case WM_INITDIALOG: + EnableWindow( GetParent( hwndDlg ), FALSE ); + dontEnableParent = FALSE; + SendMessage( hwndDlg, WM_SETICON, ICON_BIG, ( LPARAM )LoadIcon( hInst, MAKEINTRESOURCE( IDI_AGENTS )) ); + TranslateDialogDefault( hwndDlg ); + return TRUE; + case WM_COMMAND: + { + TCHAR jid[256]; + + switch ( LOWORD( wParam )) { + case IDC_JID: + GetDlgItemText( hwndDlg, IDC_JID, jid, SIZEOF( jid )); + EnableWindow( GetDlgItem( hwndDlg, IDOK ), ( jid[0]=='\0' )?FALSE:TRUE ); + break; + case IDOK: + GetDlgItemText( hwndDlg, IDC_JID, jid, SIZEOF( jid )); + JabberRegisterAgent( GetParent( hwndDlg ), jid ); + dontEnableParent = TRUE; + // Fall through + case IDCANCEL: + case IDCLOSE: + DestroyWindow( hwndDlg ); + return TRUE; + } } + break; + case WM_DESTROY: + hwndAgentManualReg = NULL; + if ( dontEnableParent == FALSE ) { + EnableWindow( GetParent( hwndDlg ), TRUE ); + SetActiveWindow( GetParent( hwndDlg )); + } + break; + } + + return FALSE; +} + +static BOOL CALLBACK JabberAgentRegDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + hwndRegProgress = hwndDlg; + SetWindowTextA( hwndDlg, "Jabber Agent Registration" ); + TranslateDialogDefault( hwndDlg ); + ShowWindow( GetDlgItem( hwndDlg, IDOK ), SW_HIDE ); + ShowWindow( GetDlgItem( hwndDlg, IDCANCEL ), SW_HIDE ); + ShowWindow( GetDlgItem( hwndDlg, IDC_PROGRESS_REG ), SW_SHOW ); + ShowWindow( GetDlgItem( hwndDlg, IDCANCEL2 ), SW_SHOW ); + return TRUE; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDCANCEL2: + case IDOK2: + hwndRegProgress = NULL; + EndDialog( hwndDlg, 0 ); + return TRUE; + } + break; + case WM_JABBER_REGDLG_UPDATE: // wParam=progress ( 0-100 ), lparam=status string + if (( TCHAR* )lParam == NULL ) + SetDlgItemText( hwndDlg, IDC_REG_STATUS, TranslateT( "No message" )); + else + SetDlgItemText( hwndDlg, IDC_REG_STATUS, ( TCHAR* )lParam ); + if ( wParam >= 0 ) + SendMessage( GetDlgItem( hwndDlg, IDC_PROGRESS_REG ), PBM_SETPOS, wParam, 0 ); + if ( wParam >= 100 ) { + ShowWindow( GetDlgItem( hwndDlg, IDCANCEL2 ), SW_HIDE ); + ShowWindow( GetDlgItem( hwndDlg, IDOK2 ), SW_SHOW ); + SetFocus( GetDlgItem( hwndDlg, IDOK2 )); + } + else + SetFocus( GetDlgItem( hwndDlg, IDCANCEL2 )); + return TRUE; + } + + return FALSE; +} diff --git a/miranda-wine/protocols/JabberG/jabber_bitmap.cpp b/miranda-wine/protocols/JabberG/jabber_bitmap.cpp new file mode 100644 index 0000000..68a6fd1 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_bitmap.cpp @@ -0,0 +1,53 @@ +/* + +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_bitmap.cpp,v $ +Revision : $Revision: 3016 $ +Last change on : $Date: 2006-06-04 23:07:43 +0400 (Вск, 04 Июн 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberEnterBitmapName - enters the picture filename + +int __stdcall JabberEnterBitmapName( char* szDest ) +{ + *szDest = 0; + + char szFilter[ 512 ]; + JCallService( MS_UTILS_GETBITMAPFILTERSTRINGS, sizeof szFilter, ( LPARAM )szFilter ); + + char str[ MAX_PATH ]; str[0] = 0; + OPENFILENAMEA ofn = {0}; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.lpstrFilter = szFilter; + ofn.lpstrFile = szDest; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.nMaxFile = MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrDefExt = "bmp"; + if ( !GetOpenFileNameA( &ofn )) + return 1; + + return ERROR_SUCCESS; +} diff --git a/miranda-wine/protocols/JabberG/jabber_byte.cpp b/miranda-wine/protocols/JabberG/jabber_byte.cpp new file mode 100644 index 0000000..049d3fb --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_byte.cpp @@ -0,0 +1,459 @@ +/* + +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_byte.cpp,v $ +Revision : $Revision: 3398 $ +Last change on : $Date: 2006-07-31 15:37:09 +0400 (Пнд, 31 Июл 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "jabber_iq.h" +#include "jabber_byte.h" + +#define JABBER_NETWORK_BUFFER_SIZE 4096 + +///////////////// Bytestream sending ///////////////////////// + +static void JabberByteInitiateResult( XmlNode *iqNode, void *userdata ); +static void JabberByteSendConnection( HANDLE hNewConnection, DWORD dwRemoteIP ); +static int JabberByteSendParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen ); + +void JabberByteFreeJbt( JABBER_BYTE_TRANSFER *jbt ) +{ + if ( !jbt ) return; + if ( jbt->srcJID ) mir_free( jbt->srcJID ); + if ( jbt->dstJID ) mir_free( jbt->dstJID ); + if ( jbt->streamhostJID ) mir_free( jbt->streamhostJID ); + if ( jbt->iqId ) mir_free( jbt->iqId ); + if ( jbt->sid ) mir_free( jbt->sid ); + if ( jbt->iqNode ) delete jbt->iqNode; + mir_free( jbt ); +} + +void __cdecl JabberByteSendThread( JABBER_BYTE_TRANSFER *jbt ) +{ + BOOL bDirect, bProxy; + char* localAddr; + struct in_addr in; + DBVARIANT dbv; + NETLIBBIND nlb = {0}; + TCHAR szPort[8]; + int iqId; + JABBER_LIST_ITEM *item; + HANDLE hEvent; + + JabberLog( "Thread started: type=bytestream_send" ); + + bDirect = JGetByte( "BsDirect", TRUE ); + bProxy = JGetByte( "BsProxy", FALSE ); + + iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberByteInitiateResult ); + XmlNodeIq iq( "set", iqId, jbt->dstJID ); + XmlNode* query = iq.addQuery( "http://jabber.org/protocol/bytestreams" ); + query->addAttr( "sid", jbt->sid ); + + if ( bDirect ) { + localAddr = NULL; + if ( JGetByte( "BsDirectManual", FALSE ) == TRUE ) { + if ( !DBGetContactSetting( NULL, jabberProtoName, "BsDirectAddr", &dbv )) { + localAddr = mir_strdup( dbv.pszVal ); + JFreeVariant( &dbv ); + } + } + if ( localAddr == NULL ) { + in.S_un.S_addr = jabberLocalIP; + localAddr = mir_strdup( inet_ntoa( in )); + } + nlb.cbSize = sizeof( NETLIBBIND ); + nlb.pfnNewConnection = JabberByteSendConnection; + nlb.wPort = 0; // Use user-specified incoming port ranges, if available + jbt->hConn = ( HANDLE ) JCallService( MS_NETLIB_BINDPORT, ( WPARAM ) hNetlibUser, ( LPARAM )&nlb ); + if ( jbt->hConn == NULL ) { + JabberLog( "Cannot allocate port for bytestream_send thread, thread ended." ); + JabberByteFreeJbt( jbt ); + return; + } + mir_sntprintf( szPort, SIZEOF( szPort ), _T("%d"), nlb.wPort ); + item = JabberListAdd( LIST_BYTE, szPort ); + item->jbt = jbt; + hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + jbt->hEvent = hEvent; + XmlNode* h = query->addChild( "streamhost" ); + h->addAttr( "jid", jabberThreadInfo->fullJID ); h->addAttr( "host", localAddr ); h->addAttr( "port", nlb.wPort ); + mir_free( localAddr ); + } + JabberSend( jabberThreadInfo->s, iq ); + + if ( bDirect ) { + WaitForSingleObject( hEvent, INFINITE ); + CloseHandle( hEvent ); + jbt->hEvent = NULL; + jbt->pfnFinal(( jbt->state==JBT_DONE )?TRUE:FALSE, jbt->userdata ); + if ( jbt->hConn != NULL ) + Netlib_CloseHandle( jbt->hConn ); + JabberByteFreeJbt( jbt ); + JabberListRemove( LIST_BYTE, szPort ); + } + + JabberLog( "Thread ended: type=bytestream_send" ); +} + +static void JabberByteInitiateResult( XmlNode *iqNode, void *userdata ) +{ + TCHAR* type; + + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if ( !lstrcmp( type, _T("result"))) { + } + else if ( !lstrcmp( type, _T("error"))) { + } +} + +static void JabberByteSendConnection( HANDLE hConn, DWORD dwRemoteIP ) +{ + SOCKET s; + SOCKADDR_IN saddr; + int len; + WORD localPort; + TCHAR szPort[8]; + JABBER_BYTE_TRANSFER *jbt; + int recvResult, bytesParsed; + HANDLE hListen; + JABBER_LIST_ITEM *item; + char* buffer; + int datalen; + + localPort = 0; + if (( s=JCallService( MS_NETLIB_GETSOCKET, ( WPARAM ) hConn, 0 )) != INVALID_SOCKET ) { + len = sizeof( saddr ); + if ( getsockname( s, ( SOCKADDR * ) &saddr, &len ) != SOCKET_ERROR ) + localPort = ntohs( saddr.sin_port ); + } + if ( localPort == 0 ) { + JabberLog( "bytestream_send_connection unable to determine the local port, connection closed." ); + Netlib_CloseHandle( hConn ); + return; + } + + mir_sntprintf( szPort, SIZEOF( szPort ), _T("%d"), localPort ); + JabberLog( "bytestream_send_connection incoming connection accepted: local_port=" TCHAR_STR_PARAM, szPort ); + + if (( item=JabberListGetItemPtr( LIST_BYTE, szPort )) == NULL ) { + JabberLog( "No bytestream session is currently active, connection closed." ); + Netlib_CloseHandle( hConn ); + return; + } + + jbt = item->jbt; + + if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE )) == NULL ) { + JabberLog( "bytestream_send cannot allocate network buffer, connection closed." ); + jbt->state = JBT_ERROR; + Netlib_CloseHandle( hConn ); + if ( jbt->hEvent != NULL ) SetEvent( jbt->hEvent ); + return; + } + + hListen = jbt->hConn; + jbt->hConn = hConn; + jbt->state = JBT_INIT; + datalen = 0; + while ( jbt->state!=JBT_DONE && jbt->state!=JBT_ERROR ) { + recvResult = Netlib_Recv( hConn, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 ); + if ( recvResult <= 0 ) break; + datalen += recvResult; + bytesParsed = JabberByteSendParse( hConn, jbt, buffer, datalen ); + if ( bytesParsed < datalen ) + memmove( buffer, buffer+bytesParsed, datalen-bytesParsed ); + datalen -= bytesParsed; + } + if ( jbt->hConn ) Netlib_CloseHandle( jbt->hConn ); + JabberLog( "bytestream_send_connection closing connection" ); + jbt->hConn = hListen; + mir_free( buffer ); + + if ( jbt->hEvent != NULL ) SetEvent( jbt->hEvent ); +} + +static int JabberByteSendParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen ) +{ + int nMethods; + BYTE data[10]; + int i; + char* str; + + switch ( jbt->state ) { + case JBT_INIT: + // received: + // 00-00 ver ( 0x05 ) + // 01-01 nmethods + // 02-xx list of methods ( nmethods bytes ) + // send: + // 00-00 ver ( 0x05 ) + // 01-01 select method ( 0=no auth required ) + if ( datalen>=2 && buffer[0]==5 && buffer[1]+2==datalen ) { + nMethods = buffer[1]; + for ( i=0; istate = JBT_CONNECT; + } + else { + data[1] = 0xff; + jbt->state = JBT_ERROR; + } + data[0] = 5; + Netlib_Send( hConn, ( char* )data, 2, 0 ); + } + else jbt->state = JBT_ERROR; + break; + case JBT_CONNECT: + // received: + // 00-00 ver ( 0x05 ) + // 01-01 cmd ( 1=connect ) + // 02-02 reserved ( 0 ) + // 03-03 address type ( 3 ) + // 04-44 dst.addr ( 41 bytes: 1-byte length, 40-byte SHA1 hash of [sid,srcJID,dstJID] ) + // 45-46 dst.port ( 0 ) + // send: + // 00-00 ver ( 0x05 ) + // 01-01 reply ( 0=success,2=not allowed ) + // 02-02 reserved ( 0 ) + // 03-03 address type ( 1=IPv4 address ) + // 04-07 bnd.addr server bound address + // 08-09 bnd.port server bound port + if ( datalen==47 && *(( DWORD* )buffer )==0x03000105 && buffer[4]==40 && *(( WORD* )( buffer+45 ))==0 ) { + TCHAR text[256]; + mir_sntprintf( text, SIZEOF( text ), _T("%s%s%s"), jbt->sid, jbt->srcJID, jbt->dstJID ); + char* szAuthString = t2a( text ); + JabberLog( "Auth: '%s'", szAuthString ); + if (( str = JabberSha1( szAuthString )) != NULL ) { + for ( i=0; i<40 && buffer[i+5]==str[i]; i++ ); + mir_free( str ); + + ZeroMemory( data, 10 ); + data[1] = ( i>=20 )?0:2; + data[0] = 5; + data[3] = 1; + Netlib_Send( hConn, ( char* )data, 10, 0 ); + if ( i>=20 && jbt->pfnSend( hConn, jbt->userdata )==TRUE ) + jbt->state = JBT_DONE; + else + jbt->state = JBT_ERROR; + } + mir_free( szAuthString ); + } + else + jbt->state = JBT_ERROR; + break; + } + + return datalen; +} + +///////////////// Bytestream receiving ///////////////////////// + +static int JabberByteReceiveParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen ); + +void __cdecl JabberByteReceiveThread( JABBER_BYTE_TRANSFER *jbt ) +{ + XmlNode *iqNode, *queryNode, *n; + TCHAR *from, *to, *sid, *szId, *szHost, *szPort, *str; + int i; + WORD port; + HANDLE hConn; + char data[3]; + char* buffer; + int datalen, bytesParsed, recvResult; + BOOL validStreamhost; + + if ( jbt == NULL ) return; + if (( iqNode=jbt->iqNode )!=NULL && + ( from=JabberXmlGetAttrValue( iqNode, "from" ))!=NULL && + ( to=JabberXmlGetAttrValue( iqNode, "to" ))!=NULL && + ( queryNode=JabberXmlGetChild( iqNode, "query" ))!=NULL && + ( sid=JabberXmlGetAttrValue( queryNode, "sid" ))!=NULL && + ( n=JabberXmlGetChild( queryNode, "streamhost" ))!=NULL ) { + + szId = JabberXmlGetAttrValue( iqNode, "id" ); + jbt->iqId = ( szId ) ? mir_tstrdup( szId ):NULL; + jbt->srcJID = mir_tstrdup( from ); + jbt->dstJID = mir_tstrdup( to ); + jbt->sid = mir_tstrdup( sid ); + + if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE )) == NULL ) { + JabberLog( "bytestream_send cannot allocate network buffer, connection closed." ); + + XmlNodeIq iq( "error", jbt->iqId, jbt->srcJID ); + XmlNode* e = iq.addChild( "error" ); e->addAttr( "code", 406 ); e->addAttr( "type", "auth" ); + XmlNode* na = e->addChild( "not-acceptable" ); na->addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas" ); + JabberSend( jabberThreadInfo->s, iq ); + + JabberByteFreeJbt( jbt ); + return; + } + + jbt->state = JBT_INIT; + validStreamhost = FALSE; + for ( i=1; ( n=JabberXmlGetNthChild( queryNode, "streamhost", i ))!=NULL; i++ ) { + if (( szHost=JabberXmlGetAttrValue( n, "host" ))!=NULL && + ( szPort=JabberXmlGetAttrValue( n, "port" ))!=NULL && + ( str=JabberXmlGetAttrValue( n, "jid" ))!=NULL ) { + + port = ( WORD )_ttoi( szPort ); + if ( jbt->streamhostJID ) mir_free( jbt->streamhostJID ); + jbt->streamhostJID = mir_tstrdup( str ); + + JabberLog( "bytestream_recv connecting to " TCHAR_STR_PARAM ":%d", szHost, port ); + NETLIBOPENCONNECTION nloc = { 0 }; + nloc.cbSize = sizeof( nloc ); + #if defined( _UNICODE ) + nloc.szHost = u2a(szHost); + #else + nloc.szHost = szHost; + #endif + nloc.wPort = port; + hConn = ( HANDLE ) JCallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) hNetlibUser, ( LPARAM )&nloc ); + #if defined( _UNICODE ) + mir_free((void*)nloc.szHost); + #endif + if ( hConn == NULL ) { + JabberLog( "bytestream_recv_connection connection failed ( %d ), try next streamhost", WSAGetLastError()); + continue; + } + + jbt->hConn = hConn; + + data[0] = 5; + data[1] = 1; + data[2] = 0; + Netlib_Send( hConn, data, 3, 0 ); + + jbt->state = JBT_INIT; + datalen = 0; + while ( jbt->state!=JBT_DONE && jbt->state!=JBT_ERROR && jbt->state!=JBT_SOCKSERR ) { + recvResult = Netlib_Recv( hConn, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 ); + if ( recvResult <= 0 ) break; + datalen += recvResult; + bytesParsed = JabberByteReceiveParse( hConn, jbt, buffer, datalen ); + if ( bytesParsed < datalen ) + memmove( buffer, buffer+bytesParsed, datalen-bytesParsed ); + datalen -= bytesParsed; + if ( jbt->state == JBT_RECVING ) validStreamhost = TRUE; + } + Netlib_CloseHandle( hConn ); + JabberLog( "bytestream_recv_connection closing connection" ); + } + if ( jbt->state==JBT_ERROR || validStreamhost==TRUE ) + break; + JabberLog( "bytestream_recv_connection stream cannot be established, try next streamhost" ); + } + mir_free( buffer ); + jbt->pfnFinal(( jbt->state==JBT_DONE )?TRUE:FALSE, jbt->userdata ); + if ( !validStreamhost ) { + JabberLog( "bytestream_recv_connection session not completed" ); + + XmlNodeIq iq( "error", jbt->iqId, jbt->srcJID ); + XmlNode* e = iq.addChild( "error" ); e->addAttr( "code", 404 ); e->addAttr( "type", _T("cancel")); + XmlNode* na = e->addChild( "item-not-found" ); na->addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas" ); + JabberSend( jabberThreadInfo->s, iq ); + } } + + JabberByteFreeJbt( jbt ); + JabberLog( "Thread ended: type=bytestream_recv" ); +} + +static int JabberByteReceiveParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen ) +{ + int bytesReceived, num = datalen; + + switch ( jbt->state ) { + case JBT_INIT: + // received: + // 00-00 ver ( 0x05 ) + // 01-01 selected method ( 0=no auth, 0xff=error ) + // send: + // 00-00 ver ( 0x05 ) + // 01-01 cmd ( 1=connect ) + // 02-02 reserved ( 0 ) + // 03-03 address type ( 3 ) + // 04-44 dst.addr ( 41 bytes: 1-byte length, 40-byte SHA1 hash of [sid,srcJID,dstJID] ) + // 45-46 dst.port ( 0 ) + if ( datalen==2 && buffer[0]==5 && buffer[1]==0 ) { + BYTE data[47]; + ZeroMemory( data, sizeof( data )); + *(( DWORD* )data ) = 0x03000105; + data[4] = 40; + + TCHAR text[256]; + mir_sntprintf( text, SIZEOF( text ), _T("%s%s%s"), jbt->sid, jbt->srcJID, jbt->dstJID ); + char* szAuthString = t2a( text ); + JabberLog( "Auth: '%s'", szAuthString ); + char* szHash = JabberSha1( szAuthString ); + strncpy(( char* )( data+5 ), szHash, 40 ); + mir_free( szHash ); + Netlib_Send( hConn, ( char* )data, 47, 0 ); + jbt->state = JBT_CONNECT; + mir_free( szAuthString ); + } + else jbt->state = JBT_SOCKSERR; + break; + + case JBT_CONNECT: + // received: + // 00-00 ver ( 0x05 ) + // 01-01 reply ( 0=success,2=not allowed ) + // 02-02 reserved ( 0 ) + // 03-03 address type ( 1=IPv4 address,3=host address ) + // 04-mm bnd.addr server bound address ( 4-byte IP if IPv4, 1-byte length + n-byte host address string if host address ) + // nn-nn+1 bnd.port server bound port + if ( datalen>=5 && buffer[0]==5 && buffer[1]==0 && ( buffer[3]==1 || buffer[3]==3 )) { + if ( buffer[3]==1 && datalen>=10 ) + num = 10; + else if ( buffer[3]==3 && datalen>=buffer[4]+7 ) + num = buffer[4] + 7; + else { + jbt->state = JBT_SOCKSERR; + break; + } + jbt->state = JBT_RECVING; + + XmlNodeIq iq( "result", jbt->iqId, jbt->srcJID ); + XmlNode* query = iq.addQuery( "http://jabber.org/protocol/bytestreams" ); + XmlNode* stream = query->addChild( "streamhost-used" ); stream->addAttr( "jid", jbt->streamhostJID ); + JabberSend( jabberThreadInfo->s, iq ); + } + else jbt->state = JBT_SOCKSERR; + break; + + case JBT_RECVING: + bytesReceived = jbt->pfnRecv( hConn, jbt->userdata, buffer, datalen ); + if ( bytesReceived < 0 ) + jbt->state = JBT_ERROR; + else if ( bytesReceived == 0 ) + jbt->state = JBT_DONE; + break; + } + + return num; +} diff --git a/miranda-wine/protocols/JabberG/jabber_byte.h b/miranda-wine/protocols/JabberG/jabber_byte.h new file mode 100644 index 0000000..cd16478 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_byte.h @@ -0,0 +1,52 @@ +/* + +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_byte.h,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#ifndef _JABBER_BYTE_H_ +#define _JABBER_BYTE_H_ + +typedef enum { JBT_INIT, JBT_AUTH, JBT_CONNECT, JBT_SOCKSERR, JBT_SENDING, JBT_RECVING, JBT_DONE, JBT_ERROR } JABBER_BYTE_STATE; + +typedef struct { + TCHAR* sid; + TCHAR* srcJID; + TCHAR* dstJID; + TCHAR* streamhostJID; + TCHAR* iqId; + JABBER_BYTE_STATE state; + HANDLE hConn; + HANDLE hEvent; + XmlNode *iqNode; + BOOL ( *pfnSend )( HANDLE hConn, void *userdata ); + int ( *pfnRecv )( HANDLE hConn, void *userdata, char* buffer, int datalen ); + void ( *pfnFinal )( BOOL success, void *userdata ); + void *userdata; +} JABBER_BYTE_TRANSFER; + +void __cdecl JabberByteSendThread( JABBER_BYTE_TRANSFER *jbt ); +void __cdecl JabberByteReceiveThread( JABBER_BYTE_TRANSFER *jbt ); + +#endif diff --git a/miranda-wine/protocols/JabberG/jabber_chat.cpp b/miranda-wine/protocols/JabberG/jabber_chat.cpp new file mode 100644 index 0000000..946e38b --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_chat.cpp @@ -0,0 +1,796 @@ +/* + +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_chat.cpp,v $ +Revision : $Revision: 3666 $ +Last change on : $Date: 2006-08-31 16:51:33 +0400 (Чтв, 31 Авг 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "jabber_iq.h" +#include "resource.h" + +extern HANDLE hInitChat; + +///////////////////////////////////////////////////////////////////////////////////////// +// One string entry dialog + +struct JabberEnterStringParams +{ TCHAR* result; + size_t resultLen; +}; + +BOOL CALLBACK JabberEnterStringDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + { + TranslateDialogDefault( hwndDlg ); + JabberEnterStringParams* params = ( JabberEnterStringParams* )lParam; + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG )params ); + SetWindowText( hwndDlg, params->result ); + return TRUE; + } + + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDOK: + { JabberEnterStringParams* params = ( JabberEnterStringParams* )GetWindowLong( hwndDlg, GWL_USERDATA ); + GetDlgItemText( hwndDlg, IDC_TOPIC, params->result, params->resultLen ); + params->result[ params->resultLen-1 ] = 0; + EndDialog( hwndDlg, 1 ); + break; + } + case IDCANCEL: + EndDialog( hwndDlg, 0 ); + break; + } } + + return FALSE; +} + +BOOL JabberEnterString( TCHAR* result, size_t resultLen ) +{ + JabberEnterStringParams params = { result, resultLen }; + return DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_GROUPCHAT_INPUT ), NULL, JabberEnterStringDlgProc, LPARAM( ¶ms )); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberGcInit - initializes the new chat + +static char* sttRoles[] = { "Other", "Visitors", "Participants", "Moderators" }; + +int JabberGcInit( WPARAM wParam, LPARAM lParam ) +{ + JABBER_LIST_ITEM* item = ( JABBER_LIST_ITEM* )wParam; + GCSESSION gcw = {0}; + GCEVENT gce = {0}; + + #if defined( _UNICODE ) + TCHAR* wszNick = JabberNickFromJID( item->jid ); + char* szNick = u2a( wszNick ); + mir_free( wszNick ); + char* jid = u2a( item->jid ); + #else + char* szNick = JabberNickFromJID( item->jid ); + char* jid = item->jid; + #endif + gcw.cbSize = sizeof(GCSESSION); + gcw.iType = GCW_CHATROOM; + gcw.pszModule = jabberProtoName; + gcw.pszName = szNick; + gcw.pszID = jid; + gcw.pszStatusbarText = NULL; + gcw.bDisableNickList = FALSE; + JCallService(MS_GC_NEWSESSION, NULL, (LPARAM)&gcw); + + HANDLE hContact = JabberHContactFromJID( item->jid ); + if ( hContact != NULL ) { + DBVARIANT dbv; + if ( !DBGetContactSetting( hContact, jabberProtoName, "MyNick", &dbv )) { + if ( !strcmp( dbv.pszVal, szNick )) + JDeleteSetting( hContact, "MyNick" ); + else + JSetStringT( hContact, "MyNick", item->nick ); + JFreeVariant( &dbv ); + } + else JSetStringT( hContact, "MyNick", item->nick ); + } + mir_free( szNick ); + + item->bChatActive = TRUE; + + GCDEST gcd = { jabberProtoName, jid, GC_EVENT_ADDGROUP }; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + for ( int i=sizeof(sttRoles)/sizeof(char*)-1; i >= 0; i-- ) { + gce.pszStatus = Translate( sttRoles[i] ); + JCallService(MS_GC_EVENT, NULL, ( LPARAM )&gce ); + } + + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + gcd.iType = GC_EVENT_CONTROL; + JCallService(MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gce); + JCallService(MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gce); + JCallService(MS_GC_EVENT, WINDOW_VISIBLE, (LPARAM)&gce); + #if defined( _UNICODE ) + mir_free( jid ); + #endif + return 0; +} + +void JabberGcLogCreate( JABBER_LIST_ITEM* item ) +{ + if ( item->bChatActive ) + return; + + NotifyEventHooks( hInitChat, (WPARAM)item, 0 ); +} + +void JabberGcLogUpdateMemberStatus( JABBER_LIST_ITEM* item, TCHAR* nick, int action, XmlNode* reason ) +{ + int statusToSet = 0; + char* szReason = NULL; + if ( reason != NULL && reason->text != NULL ) { + #if defined( _UNICODE ) + szReason = u2a( reason->text ); + #else + szReason = reason->text; + #endif + } + + TCHAR* myNick = (item->nick == NULL) ? NULL : mir_tstrdup( item->nick ); + if ( myNick == NULL ) + myNick = JabberNickFromJID( jabberJID ); + + #if defined( _UNICODE ) + char* dispNick = u2a( nick ); + char* szNick = u2a( myNick ); + char* jid = u2a( item->jid ); + #else + char* dispNick = nick; + char* szNick = myNick; + char* jid = item->jid; + #endif + + GCDEST gcd = { jabberProtoName, jid, 0 }; + GCEVENT gce = {0}; + gce.cbSize = sizeof(GCEVENT); + gce.pszNick = dispNick; + gce.pszUID = dispNick; + gce.pDest = &gcd; + gce.pszText = szReason; + if ( item->bChatActive == 2 ) { + gce.bAddToLog = TRUE; + gce.time = time(0); + } + + switch( gcd.iType = action ) { + case GC_EVENT_PART: break; + case GC_EVENT_KICK: gce.pszStatus = Translate( "Moderator" ); break; + default: + for ( int i=0; i < item->resourceCount; i++ ) { + JABBER_RESOURCE_STATUS& JS = item->resource[i]; + if ( !lstrcmp( nick, JS.resourceName )) { + if ( action != GC_EVENT_JOIN ) { + switch( action ) { + case 0: + gcd.iType = GC_EVENT_ADDSTATUS; + case GC_EVENT_REMOVESTATUS: + gce.bAddToLog = false; + } + gce.pszText = Translate( "Moderator" ); + } + gce.pszStatus = JTranslate( sttRoles[ JS.role ] ); + gce.bIsMe = ( lstrcmpA( szNick, dispNick ) == 0 ); + statusToSet = JS.status; + break; + } } } + + JCallService( MS_GC_EVENT, NULL, ( LPARAM )&gce ); + + if ( statusToSet != 0 ) { + gce.pszText = dispNick; + if ( statusToSet == ID_STATUS_AWAY || statusToSet == ID_STATUS_NA || statusToSet == ID_STATUS_DND ) + gce.dwItemData = 3; + else + gce.dwItemData = 1; + gcd.iType = GC_EVENT_SETSTATUSEX; + JCallService( MS_GC_EVENT, NULL, ( LPARAM )&gce ); + } + + mir_free( myNick ); + #if defined( _UNICODE ) + mir_free( dispNick ); + mir_free( szReason ); + mir_free( jid ); + mir_free( szNick ); + #endif +} + +void JabberGcQuit( JABBER_LIST_ITEM* item, int code, XmlNode* reason ) +{ + char* szReason = NULL; + if ( reason != NULL && reason->text != NULL ) { + #if defined( _UNICODE ) + szReason = u2a( reason->text ); + #else + szReason = reason->text; + #endif + } + + #if defined( _UNICODE ) + char* jid = u2a( item->jid ); + #else + char* jid = item->jid; + #endif + + GCDEST gcd = { jabberProtoName, jid, GC_EVENT_CONTROL }; + GCEVENT gce = {0}; + gce.cbSize = sizeof(GCEVENT); + gce.pszUID = jid; + gce.pDest = &gcd; + gce.pszText = szReason; + + if ( code != 307 ) { + JCallService( MS_GC_EVENT, SESSION_TERMINATE, ( LPARAM )&gce ); + JCallService( MS_GC_EVENT, WINDOW_CLEARLOG, ( LPARAM )&gce ); + } + else { + TCHAR* myNick = JabberNickFromJID( jabberJID ); + JabberGcLogUpdateMemberStatus( item, myNick, GC_EVENT_KICK, reason ); + mir_free( myNick ); + JCallService( MS_GC_EVENT, SESSION_OFFLINE, ( LPARAM )&gce ); + } + + DBDeleteContactSetting( JabberHContactFromJID( item->jid ), "CList", "Hidden" ); + item->bChatActive = FALSE; + + if ( jabberOnline ) { + XmlNode p( "presence" ); p.addAttr( "to", item->jid ); p.addAttr( "type", "unavailable" ); + JabberSend( jabberThreadInfo->s, p ); + JabberListRemove( LIST_CHATROOM, item->jid ); + } + + #if defined( _UNICODE ) + mir_free( szReason ); + mir_free( jid ); + #endif +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Context menu hooks + +#define IDM_LEAVE 10 +#define IDM_TOPIC 12 + +int JabberGcMenuHook( WPARAM wParam, LPARAM lParam ) +{ + GCMENUITEMS* gcmi= ( GCMENUITEMS* )lParam; + if ( gcmi == NULL ) + return 0; + + if ( lstrcmpiA( gcmi->pszModule, jabberProtoName )) + return 0; + + #if defined( _UNICODE ) + TCHAR* pszID = a2u( gcmi->pszID ); + #else + char* pszID = gcmi->pszID; + #endif + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_CHATROOM, pszID ); + #if defined( _UNICODE ) + mir_free( pszID ); + #endif + if ( item == NULL ) + return 0; + + #if defined( _UNICODE ) + TCHAR* pszUID = a2u( gcmi->pszUID ); + #else + char* pszUID = gcmi->pszUID; + #endif + + JABBER_RESOURCE_STATUS *me = NULL, *him = NULL; + for ( int i=0; i < item->resourceCount; i++ ) { + JABBER_RESOURCE_STATUS& p = item->resource[i]; + if ( !lstrcmp( p.resourceName, item->nick )) me = &p; + if ( !lstrcmp( p.resourceName, pszUID )) him = &p; + } + + #if defined( _UNICODE ) + mir_free( pszUID ); + #endif + + if ( gcmi->Type == MENU_ON_LOG ) { + static struct gc_item sttLogListItems[] = { + { JTranslate( "&Leave chat session" ), IDM_LEAVE, MENU_ITEM, FALSE }, + { NULL, 0, MENU_SEPARATOR, FALSE }, + { JTranslate( "&Voice List..." ), IDM_VOICE, MENU_ITEM, TRUE }, + { JTranslate( "&Ban List..." ), IDM_BAN, MENU_ITEM, TRUE }, + { NULL, 0, MENU_SEPARATOR, FALSE }, + { JTranslate( "&Member List..." ), IDM_MEMBER, MENU_ITEM, TRUE }, + { JTranslate( "Mo&derator List..." ), IDM_MODERATOR, MENU_ITEM, TRUE }, + { JTranslate( "&Admin List..." ), IDM_ADMIN, MENU_ITEM, TRUE }, + { JTranslate( "&Owner List..." ), IDM_OWNER, MENU_ITEM, TRUE }, + { NULL, 0, MENU_SEPARATOR, FALSE }, + { JTranslate( "Change &Nickname..." ), IDM_NICK, MENU_ITEM, FALSE }, + { JTranslate( "Set &Topic..." ), IDM_TOPIC, MENU_ITEM, FALSE }, + { JTranslate( "&Invite a User..." ), IDM_INVITE, MENU_ITEM, FALSE }, + { JTranslate( "Room Con&figuration..." ), IDM_CONFIG, MENU_ITEM, TRUE }, + { NULL, 0, MENU_SEPARATOR, FALSE }, + { JTranslate( "Destroy Room..." ), IDM_DESTROY, MENU_ITEM, TRUE }}; + + gcmi->nItems = sizeof( sttLogListItems ) / sizeof( sttLogListItems[0] ); + gcmi->Item = sttLogListItems; + + if ( me != NULL ) { + if ( me->role == ROLE_MODERATOR ) + sttLogListItems[2].bDisabled = FALSE; + + if ( me->affiliation == AFFILIATION_ADMIN ) + sttLogListItems[3].bDisabled = sttLogListItems[5].bDisabled = sttLogListItems[6].bDisabled = FALSE; + else if ( me->affiliation == AFFILIATION_OWNER ) + sttLogListItems[3].bDisabled = sttLogListItems[5].bDisabled = + sttLogListItems[6].bDisabled = sttLogListItems[7].bDisabled = + sttLogListItems[8].bDisabled = sttLogListItems[13].bDisabled = + sttLogListItems[15].bDisabled = FALSE; + } + } + else if ( gcmi->Type == MENU_ON_NICKLIST ) { + static struct gc_item sttListItems[] = { + { JTranslate( "&Leave chat session" ), IDM_LEAVE, MENU_ITEM, FALSE }, + { NULL, 0, MENU_SEPARATOR, FALSE }, + { JTranslate( "Kick" ), IDM_KICK, MENU_ITEM, TRUE }, + { JTranslate( "Ban" ), IDM_BAN, MENU_ITEM, TRUE }, + { NULL, 0, MENU_SEPARATOR, FALSE }, + { JTranslate( "Toggle &Voice" ), IDM_VOICE, MENU_ITEM, TRUE }, + { JTranslate( "Toggle Moderator" ), IDM_MODERATOR, MENU_ITEM, TRUE }, + { JTranslate( "Toggle Admin" ), IDM_ADMIN, MENU_ITEM, TRUE }, + { JTranslate( "Toggle Owner" ), IDM_OWNER, MENU_ITEM, TRUE }}; + + gcmi->nItems = sizeof( sttListItems )/sizeof( sttListItems[0] ); + gcmi->Item = sttListItems; + + if ( me != NULL && him != NULL ) { + if ( me->role == ROLE_MODERATOR ) + if ( him->affiliation != AFFILIATION_ADMIN && him->affiliation != AFFILIATION_OWNER ) + sttListItems[2].bDisabled = sttListItems[3].bDisabled = FALSE; + + if ( me->affiliation == AFFILIATION_ADMIN ) { + if ( him->affiliation != AFFILIATION_ADMIN && him->affiliation != AFFILIATION_OWNER ) + sttListItems[5].bDisabled = sttListItems[6].bDisabled = FALSE; + } + else if ( me->affiliation == AFFILIATION_OWNER ) + sttListItems[5].bDisabled = sttListItems[6].bDisabled = + sttListItems[7].bDisabled = sttListItems[8].bDisabled = FALSE; + } } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Conference invitation dialog + +static BOOL CALLBACK JabberGcLogInviteDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + { + TranslateDialogDefault( hwndDlg ); + SendMessage( hwndDlg, WM_SETICON, ICON_BIG, ( LPARAM )LoadIcon( hInst, MAKEINTRESOURCE( IDI_GROUP )) ); + SetDlgItemTextA( hwndDlg, IDC_ROOM, ( char* )lParam ); + HWND hwndComboBox = GetDlgItem( hwndDlg, IDC_USER ); + int index = 0; + while (( index=JabberListFindNext( LIST_ROSTER, index )) >= 0 ) { + JABBER_LIST_ITEM* item = JabberListGetItemPtrFromIndex( index ); + if ( item->status != ID_STATUS_OFFLINE ) { + // Add every non-offline users to the combobox + int n = SendMessage( hwndComboBox, CB_ADDSTRING, 0, ( LPARAM )item->jid ); + SendMessage( hwndComboBox, CB_SETITEMDATA, n, ( LPARAM )item->jid ); + } + index++; + } + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG ) mir_strdup(( char* )lParam )); + } + return TRUE; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_INVITE: + { + char* room = ( char* )GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( room != NULL ) { + TCHAR text[256], user[256], *pUser; + HWND hwndComboBox = GetDlgItem( hwndDlg, IDC_USER ); + int n = SendMessage( hwndComboBox, CB_GETCURSEL, 0, 0 ); + if ( n < 0 ) { + GetWindowText( hwndComboBox, user, SIZEOF( user )); + pUser = user; + } + else pUser = ( TCHAR* )SendMessage( hwndComboBox, CB_GETITEMDATA, n, 0 ); + + if ( pUser != NULL ) { + GetDlgItemText( hwndDlg, IDC_REASON, text, SIZEOF( text )); + int iqId = JabberSerialNext(); + + XmlNode m( "message" ); m.addAttr( "from", jabberJID ); m.addAttr( "to", room ); m.addAttrID( iqId ); + XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", _T("http://jabber.org/protocol/muc#user")); + XmlNode* i = x->addChild( "invite" ); i->addAttr( "to", pUser ); + if ( text[0] != 0 ) + i->addChild( "reason", text ); + JabberSend( jabberThreadInfo->s, m ); + } } } + // Fall through + case IDCANCEL: + case IDCLOSE: + DestroyWindow( hwndDlg ); + return TRUE; + } + break; + case WM_CLOSE: + DestroyWindow( hwndDlg ); + break; + case WM_DESTROY: + { + char* str; + + if (( str=( char* )GetWindowLong( hwndDlg, GWL_USERDATA )) != NULL ) + mir_free( str ); + } + break; + } + + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Context menu processing + +static void JabberAdminSet( const TCHAR* to, const char* ns, const char* szItem, const TCHAR* itemVal, const char* var, const TCHAR* varVal ) +{ + XmlNodeIq iq( "set", NOID, to ); + XmlNode* query = iq.addQuery( ns ); + XmlNode* item = query->addChild( "item" ); item->addAttr( szItem, itemVal ); item->addAttr( var, varVal ); + JabberSend( jabberThreadInfo->s, iq ); +} + +static void JabberAdminGet( const TCHAR* to, const char* ns, const char* var, const TCHAR* varVal, JABBER_IQ_PFUNC foo ) +{ + int id = JabberSerialNext(); + JabberIqAdd( id, IQ_PROC_NONE, foo ); + + XmlNodeIq iq( "get", id, to ); + XmlNode* query = iq.addQuery( ns ); + XmlNode* item = query->addChild( "item" ); item->addAttr( var, varVal ); + JabberSend( jabberThreadInfo->s, iq ); +} + +static void sttNickListHook( JABBER_LIST_ITEM* item, GCHOOK* gch ) +{ + #if defined( _UNICODE ) + TCHAR* pszUID = a2u( gch->pszUID ); + #else + char* pszUID = gch->pszUID; + #endif + + JABBER_RESOURCE_STATUS *me = NULL, *him = NULL; + for ( int i=0; i < item->resourceCount; i++ ) { + JABBER_RESOURCE_STATUS& p = item->resource[i]; + if ( !lstrcmp( p.resourceName, item->nick )) me = &p; + if ( !lstrcmp( p.resourceName, pszUID )) him = &p; + } + #if defined( _UNICODE ) + mir_free( pszUID ); + #endif + + if ( him == NULL || me == NULL ) + return; + + TCHAR szBuffer[ 1024 ]; + + switch( gch->dwData ) { + case IDM_LEAVE: + JabberGcQuit( item, 0, 0 ); + break; + + case IDM_KICK: + { + mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s %s"), TranslateT( "Reason to kick" ), him->resourceName ); + if ( JabberEnterString( szBuffer, SIZEOF(szBuffer))) { + XmlNodeIq iq( "set", NOID, item->jid ); + XmlNode* query = iq.addQuery( xmlnsAdmin ); + XmlNode* item = query->addChild( "item" ); item->addAttr( "nick", him->resourceName ); item->addAttr( "role", "none" ); + item->addChild( "reason", szBuffer ); + JabberSend( jabberThreadInfo->s, iq ); + } + break; + } + + case IDM_BAN: + mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s %s"), TranslateT( "Reason to ban" ), him->resourceName ); + if ( JabberEnterString( szBuffer, SIZEOF(szBuffer))) { + XmlNodeIq iq( "set", NOID, item->jid ); + XmlNode* query = iq.addQuery( xmlnsAdmin ); + XmlNode* item = query->addChild( "item" ); item->addAttr( "nick", him->resourceName ); item->addAttr( "affiliation", "outcast" ); + item->addChild( "reason", szBuffer ); + JabberSend( jabberThreadInfo->s, iq ); + } + break; + + case IDM_VOICE: + JabberAdminSet( item->jid, xmlnsAdmin, "nick", him->resourceName, + "role", ( him->role == ROLE_PARTICIPANT ) ? _T("visitor") : _T("participant")); + break; + + case IDM_MODERATOR: + JabberAdminSet( item->jid, xmlnsAdmin, "nick", him->resourceName, + "role", ( him->role == ROLE_MODERATOR ) ? _T("participant") : _T("moderator")); + break; + + case IDM_ADMIN: + JabberAdminSet( item->jid, xmlnsAdmin, "nick", him->resourceName, + "affiliation", ( him->affiliation==AFFILIATION_ADMIN )? _T("member") : _T("admin")); + break; + + case IDM_OWNER: + JabberAdminSet( item->jid, xmlnsAdmin, "nick", him->resourceName, + "affiliation", ( him->affiliation==AFFILIATION_OWNER ) ? _T("admin") : _T("owner")); + break; +} } + +static void sttLogListHook( JABBER_LIST_ITEM* item, GCHOOK* gch ) +{ + TCHAR szBuffer[ 1024 ]; + #if defined( _UNICODE ) + TCHAR* pszID = a2u(gch->pDest->pszID); + #else + TCHAR* pszID = gch->pDest->pszID; + #endif + + switch( gch->dwData ) { + case IDM_VOICE: + JabberAdminGet( pszID, xmlnsAdmin, "role", _T("participant"), JabberIqResultMucGetVoiceList ); + break; + + case IDM_MEMBER: + JabberAdminGet( pszID, xmlnsAdmin, "affiliation", _T("member"), JabberIqResultMucGetMemberList ); + break; + + case IDM_MODERATOR: + JabberAdminGet( pszID, xmlnsAdmin, "role", _T("moderator"), JabberIqResultMucGetModeratorList ); + break; + + case IDM_BAN: + JabberAdminGet( pszID, xmlnsAdmin, "affiliation", _T("outcast"), JabberIqResultMucGetBanList ); + break; + + case IDM_ADMIN: + JabberAdminGet( pszID, xmlnsOwner, "affiliation", _T("admin"), JabberIqResultMucGetAdminList ); + break; + + case IDM_OWNER: + JabberAdminGet( pszID, xmlnsOwner, "affiliation", _T("owner"), JabberIqResultMucGetOwnerList ); + break; + + case IDM_TOPIC: + mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s %s"), TranslateT( "Set topic for" ), pszID ); + if ( JabberEnterString( szBuffer, SIZEOF(szBuffer))) { + XmlNode msg( "message" ); msg.addAttr( "to", pszID ); msg.addAttr( "type", "groupchat" ); + msg.addChild( "subject", szBuffer ); + JabberSend( jabberThreadInfo->s, msg ); + } + break; + + case IDM_NICK: + mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s %s"), TranslateT( "Change nickname in" ), pszID ); + if ( JabberEnterString( szBuffer, SIZEOF(szBuffer))) { + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_CHATROOM, pszID ); + if ( item != NULL ) { + TCHAR text[ 1024 ]; + mir_sntprintf( text, SIZEOF( text ), _T("%s/%s"), pszID, szBuffer ); + JabberSendPresenceTo( jabberStatus, text, NULL ); + } } + break; + + case IDM_INVITE: + CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_GROUPCHAT_INVITE ), NULL, JabberGcLogInviteDlgProc, ( LPARAM )gch->pDest->pszID ); + break; + + case IDM_CONFIG: + { + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultGetMuc ); + + XmlNodeIq iq( "get", iqId, pszID ); + XmlNode* query = iq.addQuery( xmlnsOwner ); + JabberSend( jabberThreadInfo->s, iq ); + break; + } + case IDM_DESTROY: + mir_sntprintf( szBuffer, SIZEOF(szBuffer), _T("%s %s"), TranslateT( "Reason to destroy" ), pszID ); + if ( !JabberEnterString( szBuffer, SIZEOF(szBuffer))) + break; + + { XmlNodeIq iq( "set", NOID, pszID ); + XmlNode* query = iq.addQuery( xmlnsOwner ); + query->addChild( "destroy" )->addChild( "reason", szBuffer ); + JabberSend( jabberThreadInfo->s, iq ); + } + + case IDM_LEAVE: + JabberGcQuit( item, 0, 0 ); + break; + } + + #if defined( _UNICODE ) + mir_free( pszID ); + #endif +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Sends a private message to a chat user + +static void sttSendPrivateMessage( JABBER_LIST_ITEM* item, const TCHAR* nick ) +{ + TCHAR szFullJid[ 256 ]; + mir_sntprintf( szFullJid, SIZEOF(szFullJid), _T("%s/%s"), item->jid, nick ); + HANDLE hContact = JabberDBCreateContact( szFullJid, NULL, TRUE, FALSE ); + if ( hContact != NULL ) { + for ( int i=0; i < item->resourceCount; i++ ) { + if ( _tcsicmp( item->resource[i].resourceName, nick ) == 0 ) { + JSetWord( hContact, "Status", item->resource[i].status ); + break; + } } + + DBWriteContactSettingByte( hContact, "CList", "Hidden", 1 ); + JSetStringT( hContact, "Nick", nick ); + DBWriteContactSettingDword( hContact, "Ignore", "Mask1", 0 ); + JCallService( MS_MSG_SENDMESSAGE, ( WPARAM )hContact, 0 ); +} } + +///////////////////////////////////////////////////////////////////////////////////////// +// General chat event processing hook + +int JabberGcEventHook(WPARAM wParam,LPARAM lParam) +{ + GCHOOK* gch = ( GCHOOK* )lParam; + if ( gch == NULL ) + return 0; + + if ( lstrcmpiA( gch->pDest->pszModule, jabberProtoName )) + return 0; + + #if defined( _UNICODE ) + TCHAR* pszID = a2u(gch->pDest->pszID); + #else + TCHAR* pszID = gch->pDest->pszID; + #endif + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_CHATROOM, pszID ); + #if defined( _UNICODE ) + mir_free( pszID ); + #endif + if ( item == NULL ) + return 0; + + switch ( gch->pDest->iType ) { + case GC_USER_MESSAGE: + if ( gch->pszText && lstrlenA( gch->pszText) > 0 ) { + rtrim( gch->pszText ); + + if ( jabberOnline ) { + XmlNode m( "message" ); m.addAttr( "to", item->jid ); m.addAttr( "type", "groupchat" ); + XmlNode* b = m.addChild( "body", gch->pszText ); + if ( b->sendText != NULL ) + UnEscapeChatTags( b->sendText ); + JabberSend( jabberThreadInfo->s, m ); + } } + break; + + case GC_USER_PRIVMESS: + #if defined( _UNICODE ) + { TCHAR* id = a2u( gch->pszUID ); + sttSendPrivateMessage( item, id ); + mir_free( id ); + } + #else + sttSendPrivateMessage( item, gch->pszUID ); + #endif + break; + + case GC_USER_LOGMENU: + sttLogListHook( item, gch ); + break; + + case GC_USER_NICKLISTMENU: + sttNickListHook( item, gch ); + break; + + case GC_USER_CHANMGR: + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultGetMuc ); + + XmlNodeIq iq( "get", iqId, item->jid ); + XmlNode* query = iq.addQuery( xmlnsOwner ); + JabberSend( jabberThreadInfo->s, iq ); + break; + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +void JabberAddMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* str ) +{ + const char* field = _tcschr(str,'@') ? "jid" : "nick"; + TCHAR* roomJid = jidListInfo->roomJid; + + switch (jidListInfo->type) { + case MUC_VOICELIST: + JabberAdminSet( roomJid, xmlnsAdmin, field, str, "role", _T("participant")); + JabberAdminGet( roomJid, xmlnsAdmin, "role", _T("participant"), JabberIqResultMucGetVoiceList); + break; + case MUC_MEMBERLIST: + JabberAdminSet( roomJid, xmlnsAdmin, field, str, "affiliation", _T("member")); + JabberAdminGet( roomJid, xmlnsAdmin, "affiliation", _T("member"), JabberIqResultMucGetMemberList); + break; + case MUC_MODERATORLIST: + JabberAdminSet( roomJid, xmlnsAdmin, field, str, "role", _T("moderator")); + JabberAdminGet( roomJid, xmlnsAdmin, "role", _T("moderator"), JabberIqResultMucGetModeratorList); + break; + case MUC_BANLIST: + JabberAdminSet( roomJid, xmlnsAdmin, field, str, "affiliation", _T("outcast")); + JabberAdminGet( roomJid, xmlnsAdmin, "affiliation", _T("outcast"), JabberIqResultMucGetBanList); + break; + case MUC_ADMINLIST: + JabberAdminSet( roomJid, xmlnsOwner, field, str, "affiliation", _T("admin")); + JabberAdminGet( roomJid, xmlnsOwner, "affiliation", _T("admin"), JabberIqResultMucGetAdminList); + break; + case MUC_OWNERLIST: + JabberAdminSet( roomJid, xmlnsOwner, field, str, "affiliation", _T("owner")); + JabberAdminGet( roomJid, xmlnsOwner, "affiliation", _T("owner"), JabberIqResultMucGetOwnerList); + break; +} } + +void JabberDeleteMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* jid ) +{ + TCHAR* roomJid = jidListInfo->roomJid; + + switch ( jidListInfo->type ) { + case MUC_VOICELIST: // change role to visitor ( from participant ) + JabberAdminSet( roomJid, xmlnsAdmin, "jid", jid, "role", _T("visitor")); + break; + case MUC_BANLIST: // change affiliation to none ( from outcast ) + case MUC_MEMBERLIST: // change affiliation to none ( from member ) + JabberAdminSet( roomJid, xmlnsAdmin, "jid", jid, "affiliation", _T("none")); + break; + case MUC_MODERATORLIST: // change role to participant ( from moderator ) + JabberAdminSet( roomJid, xmlnsAdmin, "jid", jid, "role", _T("participant")); + break; + case MUC_ADMINLIST: // change affiliation to member ( from admin ) + JabberAdminSet( roomJid, xmlnsOwner, "jid", jid, "affiliation", _T("member")); + break; + case MUC_OWNERLIST: // change affiliation to admin ( from owner ) + JabberAdminSet( roomJid, xmlnsOwner, "jid", jid, "affiliation", _T("admin")); + break; +} } diff --git a/miranda-wine/protocols/JabberG/jabber_file.cpp b/miranda-wine/protocols/JabberG/jabber_file.cpp new file mode 100644 index 0000000..56eafd7 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_file.cpp @@ -0,0 +1,611 @@ +/* + +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_file.cpp,v $ +Revision : $Revision: 2961 $ +Last change on : $Date: 2006-05-26 23:39:41 +0400 (Птн, 26 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include +#include +#include +#include + +static char* fileBaseName; +static char* filePathName; + +static int JabberFileReceiveParse( filetransfer* ft, char* buffer, int datalen ); +static void JabberFileServerConnection( HANDLE hNewConnection, DWORD dwRemoteIP ); +static int JabberFileSendParse( JABBER_SOCKET s, filetransfer* ft, char* buffer, int datalen ); + +#define JABBER_NETWORK_BUFFER_SIZE 2048 + +void __cdecl JabberFileReceiveThread( filetransfer* ft ) +{ + char* buffer; + int datalen; + JABBER_SOCKET s; + + JabberLog( "Thread started: type=file_receive server='%s' port='%d'", ft->httpHostName, ft->httpPort ); + + ft->type = FT_OOB; + + if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE )) == NULL ) { + JabberLog( "Cannot allocate network buffer, thread ended" ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + delete ft; + return; + } + + NETLIBOPENCONNECTION nloc = { 0 }; + nloc.cbSize = sizeof( nloc ); + nloc.cbSize = sizeof( NETLIBOPENCONNECTION ); + nloc.szHost = ft->httpHostName; + nloc.wPort = ft->httpPort; + s = ( HANDLE ) JCallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) hNetlibUser, ( LPARAM )&nloc ); + if ( s == NULL ) { + JabberLog( "Connection failed ( %d ), thread ended", WSAGetLastError()); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + mir_free( buffer ); + delete ft; + return; + } + + ft->s = s; + + JabberSend( s, "GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n", ft->httpPath, ft->httpHostName ); + ft->state = FT_CONNECTING; + + JabberLog( "Entering file_receive recv loop" ); + datalen = 0; + + while ( ft->state != FT_DONE && ft->state != FT_ERROR ) { + int recvResult, bytesParsed; + + JabberLog( "Waiting for data..." ); + recvResult = Netlib_Recv( s, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 ); + if ( recvResult <= 0 ) + break; + datalen += recvResult; + + bytesParsed = JabberFileReceiveParse( ft, buffer, datalen ); + if ( bytesParsed < datalen ) + memmove( buffer, buffer+bytesParsed, datalen-bytesParsed ); + datalen -= bytesParsed; + } + + if ( ft->s ) + Netlib_CloseHandle( s ); + ft->s = NULL; + + if ( ft->state==FT_RECEIVING || ft->state==FT_DONE ) + _close( ft->fileId ); + + if ( ft->state==FT_DONE || ( ft->state==FT_RECEIVING && ft->std.currentFileSize < 0 )) + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0 ); + else + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + + JabberLog( "Thread ended: type=file_receive server='%s'", ft->httpHostName ); + + mir_free( buffer ); + delete ft; +} + +static int JabberFileReceiveParse( filetransfer* ft, char* buffer, int datalen ) +{ + char* p, *q, *s, *eob; + char* str; + int num, code; + + eob = buffer + datalen; + p = buffer; + num = 0; + for ( ;; ) { + if ( ft->state==FT_CONNECTING || ft->state==FT_INITIALIZING ) { + for ( q=p; q+1state == FT_CONNECTING ) { + // looking for "HTTP/1.1 200 OK" + if ( sscanf( str, "HTTP/%*d.%*d %d %*s", &code )==1 && code==200 ) { + ft->state = FT_INITIALIZING; + ft->std.currentFileSize = -1; + JabberLog( "Change to FT_INITIALIZING" ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, ft, 0 ); + } + } + else { // FT_INITIALIZING + if ( str[0] == '\0' ) { + if (( s=strrchr( ft->httpPath, '/' )) != NULL ) + s++; + else + s = ft->httpPath; + ft->std.currentFile = mir_strdup( s ); + JabberHttpUrlDecode( ft->std.currentFile ); + if ( ft->create() == -1 ) { + ft->state = FT_ERROR; + break; + } + ft->state = FT_RECEIVING; + ft->std.currentFileSize = 0; + JabberLog( "Change to FT_RECEIVING" ); + } + else if (( s=strchr( str, ':' )) != NULL ) { + *s = '\0'; + if ( !strcmp( str, "Content-Length" )) + ft->std.totalBytes = strtol( s+1, NULL, 10 ); + } } + + mir_free( str ); + q += 2; + num += ( q-p ); + p = q; + } + else { + ft->state = FT_ERROR; + break; + } + } + else { + break; + } + } + else if ( ft->state == FT_RECEIVING ) { + int bufferSize, remainingBytes, writeSize; + + if ( ft->std.currentFileSize < 0 || ft->std.currentFileProgress < ft->std.currentFileSize ) { + bufferSize = eob - p; + remainingBytes = ft->std.currentFileSize - ft->std.currentFileProgress; + if ( remainingBytes < bufferSize ) + writeSize = remainingBytes; + else + writeSize = bufferSize; + if ( _write( ft->fileId, p, writeSize ) != writeSize ) { + JabberLog( "_write() error" ); + ft->state = FT_ERROR; + } + else { + ft->std.currentFileProgress += writeSize; + ft->std.totalProgress += writeSize; + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std ); + if ( ft->std.currentFileProgress == ft->std.currentFileSize ) + ft->state = FT_DONE; + } + } + num = datalen; + break; + } + else { + break; + } + } + + return num; +} + +void __cdecl JabberFileServerThread( filetransfer* ft ) +{ + NETLIBBIND nlb = {0}; + JABBER_SOCKET s; + int i; + JABBER_LIST_ITEM *item; + struct in_addr in; + HANDLE hEvent; + TCHAR *p, *resource; + char *myAddr; + char *pFileName; + DBVARIANT dbv; + TCHAR szPort[20]; + int id; + + JabberLog( "Thread started: type=file_send" ); + + ft->type = FT_OOB; + + nlb.cbSize = sizeof( NETLIBBIND ); + nlb.pfnNewConnection = JabberFileServerConnection; + nlb.wPort = 0; // Use user-specified incoming port ranges, if available + s = ( HANDLE ) JCallService( MS_NETLIB_BINDPORT, ( WPARAM ) hNetlibUser, ( LPARAM )&nlb ); + if ( s == NULL ) { + JabberLog( "Cannot allocate port to bind for file server thread, thread ended." ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + delete ft; + return; + } + + ft->s = s; + JabberLog( "ft->s = %d", s ); + + hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + ft->hFileEvent = hEvent; + + mir_sntprintf( szPort, SIZEOF( szPort ), _T("%d"), nlb.wPort ); + item = JabberListAdd( LIST_FILE, szPort ); + item->ft = ft; + + if (( p=JabberListGetBestClientResourceNamePtr( ft->jid )) == NULL ) + resource = NULL; + else + resource = mir_tstrdup( p ); + + if ( resource != NULL ) { + ft->state = FT_CONNECTING; + for ( i=0; i < ft->std.totalFiles && ft->state!=FT_ERROR && ft->state!=FT_DENIED; i++ ) { + ft->std.currentFileNumber = i; + ft->state = FT_CONNECTING; + if ( ft->httpPath ) mir_free( ft->httpPath ); + ft->httpPath = NULL; + + char* p; + if (( p=strrchr( ft->std.files[i], '\\' )) != NULL ) + p++; + else + p = ft->std.files[i]; + in.S_un.S_addr = jabberLocalIP; + if (( pFileName=JabberHttpUrlEncode( p )) != NULL ) { + id = JabberSerialNext(); + if ( ft->iqId ) mir_free( ft->iqId ); + ft->iqId = ( TCHAR* )mir_alloc( sizeof(TCHAR)*( strlen( JABBER_IQID )+20 )); + wsprintf( ft->iqId, _T(JABBER_IQID)_T("%d"), id ); + + if ( JGetByte( "BsDirect", TRUE ) && JGetByte( "BsDirectManual", FALSE )) { + if ( !DBGetContactSetting( NULL, jabberProtoName, "BsDirectAddr", &dbv )) { + myAddr = NEWSTR_ALLOCA( dbv.pszVal ); + JFreeVariant( &dbv ); + } + else myAddr = inet_ntoa( in ); + } + else myAddr = inet_ntoa( in ); + + char szAddr[ 256 ]; + mir_snprintf( szAddr, sizeof(szAddr), "http://%s:%d/%s", myAddr, nlb.wPort, pFileName ); + + XmlNodeIq iq( "set", id, ft->jid ); + XmlNode* query = iq.addQuery( "jabber:iq:oob" ); + query->addChild( "url", szAddr ); + query->addChild( "desc", ft->szDescription ); + JabberSend( jabberThreadInfo->s, iq ); + + JabberLog( "Waiting for the file to be sent..." ); + WaitForSingleObject( hEvent, INFINITE ); + mir_free( pFileName ); + } + JabberLog( "File sent, advancing to the next file..." ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0 ); + } + CloseHandle( hEvent ); + ft->hFileEvent = NULL; + JabberLog( "Finish all files" ); + + Netlib_CloseHandle( s ); + + mir_free( resource ); + } + + ft->s = NULL; + JabberLog( "ft->s is NULL" ); + + JabberListRemove( LIST_FILE, szPort ); + + switch ( ft->state ) { + case FT_DONE: + JabberLog( "Finish successfully" ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0 ); + break; + case FT_DENIED: + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DENIED, ft, 0 ); + break; + default: // FT_ERROR: + JabberLog( "Finish with errors" ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + break; + } + + JabberLog( "Thread ended: type=file_send" ); + + delete ft; +} + +static void JabberFileServerConnection( JABBER_SOCKET hConnection, DWORD dwRemoteIP ) +{ + SOCKET s; + JABBER_SOCKET slisten; + SOCKADDR_IN saddr; + int len; + WORD localPort; + JABBER_LIST_ITEM *item; + char* buffer; + int datalen; + filetransfer* ft; + TCHAR szPort[20]; + + localPort = 0; + if (( s=JCallService( MS_NETLIB_GETSOCKET, ( WPARAM ) hConnection, 0 )) != INVALID_SOCKET ) { + len = sizeof( saddr ); + if ( getsockname( s, ( SOCKADDR * ) &saddr, &len ) != SOCKET_ERROR ) { + localPort = ntohs( saddr.sin_port ); + } + } + if ( localPort == 0 ) { + JabberLog( "Unable to determine the local port, file server connection closed." ); + Netlib_CloseHandle( hConnection ); + return; + } + + mir_sntprintf( szPort, sizeof( szPort ), _T("%d"), localPort ); + JabberLog( "File server incoming connection accepted: local_port=" TCHAR_STR_PARAM, szPort ); + + if (( item=JabberListGetItemPtr( LIST_FILE, szPort )) == NULL ) { + JabberLog( "No file is currently served, file server connection closed." ); + Netlib_CloseHandle( hConnection ); + return; + } + + ft = item->ft; + slisten = ft->s; + ft->s = hConnection; + JabberLog( "Set ft->s to %d ( saving %d )", hConnection, slisten ); + + if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE+1 )) == NULL ) { + JabberLog( "Cannot allocate network buffer, file server connection closed." ); + Netlib_CloseHandle( hConnection ); + ft->state = FT_ERROR; + if ( ft->hFileEvent != NULL ) + SetEvent( ft->hFileEvent ); + return; + } + JabberLog( "Entering recv loop for this file connection... ( ft->s is hConnection )" ); + datalen = 0; + while ( ft->state!=FT_DONE && ft->state!=FT_ERROR ) { + int recvResult, bytesParsed; + + //recvResult = JabberWsRecv( hConnection, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen ); + recvResult = Netlib_Recv( hConnection, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 ); + if ( recvResult <= 0 ) + break; + datalen += recvResult; + + buffer[datalen] = '\0'; + JabberLog( "RECV:%s", buffer ); + + bytesParsed = JabberFileSendParse( hConnection, ft, buffer, datalen ); + if ( bytesParsed < datalen ) + memmove( buffer, buffer+bytesParsed, datalen-bytesParsed ); + datalen -= bytesParsed; + } + + JabberLog( "Closing connection for this file transfer... ( ft->s is now hBind )" ); + Netlib_CloseHandle( hConnection ); + ft->s = slisten; + JabberLog( "ft->s is restored to %d", ft->s ); + if ( ft->hFileEvent != NULL ) + SetEvent( ft->hFileEvent ); + mir_free( buffer ); +} + +static int JabberFileSendParse( JABBER_SOCKET s, filetransfer* ft, char* buffer, int datalen ) +{ + char* p, *q, *t, *eob; + char* str; + int num; + int currentFile; + int fileId; + int numRead; + + eob = buffer + datalen; + p = buffer; + num = 0; + while ( ft->state==FT_CONNECTING || ft->state==FT_INITIALIZING ) { + for ( q=p; q+1= eob ) + break; + if (( str=( char* )mir_alloc( q-p+1 )) == NULL ) { + ft->state = FT_ERROR; + break; + } + strncpy( str, p, q-p ); + str[q-p] = '\0'; + JabberLog( "FT Got: %s", str ); + if ( ft->state == FT_CONNECTING ) { + // looking for "GET filename.ext HTTP/1.1" + if ( !strncmp( str, "GET ", 4 )) { + for ( t=str+4; *t!='\0' && *t!=' '; t++ ); + *t = '\0'; + for ( t=str+4; *t!='\0' && *t=='/'; t++ ); + ft->httpPath = mir_strdup( t ); + JabberHttpUrlDecode( ft->httpPath ); + ft->state = FT_INITIALIZING; + JabberLog( "Change to FT_INITIALIZING" ); + } + } + else { // FT_INITIALIZING + if ( str[0] == '\0' ) { + struct _stat statbuf; + + mir_free( str ); + num += 2; + + currentFile = ft->std.currentFileNumber; + if (( t=strrchr( ft->std.files[ currentFile ], '\\' )) != NULL ) + t++; + else + t = ft->std.files[currentFile]; + if ( ft->httpPath==NULL || strcmp( ft->httpPath, t )) { + if ( ft->httpPath == NULL ) + JabberLog( "Requested file name does not matched ( httpPath==NULL )" ); + else + JabberLog( "Requested file name does not matched ( '%s' vs. '%s' )", ft->httpPath, t ); + ft->state = FT_ERROR; + break; + } + JabberLog( "Sending [%s]", ft->std.files[ currentFile ] ); + _stat( ft->std.files[ currentFile ], &statbuf ); // file size in statbuf.st_size + if (( fileId=_open( ft->std.files[currentFile], _O_BINARY|_O_RDONLY )) < 0 ) { + JabberLog( "File cannot be opened" ); + ft->state = FT_ERROR; + mir_free( ft->httpPath ); + ft->httpPath = NULL; + break; + } + JabberSend( s, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n", statbuf.st_size ); + + ft->std.sending = TRUE; + ft->std.currentFileProgress = 0; + JabberLog( "Sending file data..." ); + + char fileBuffer[ 2048 ]; + while (( numRead = _read( fileId, fileBuffer, 2048 )) > 0 ) { + if ( Netlib_Send( s, fileBuffer, numRead, 0 ) != numRead ) { + ft->state = FT_ERROR; + break; + } + ft->std.currentFileProgress += numRead; + ft->std.totalProgress += numRead; + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std ); + } + _close( fileId ); + if ( ft->state != FT_ERROR ) + ft->state = FT_DONE; + JabberLog( "Finishing this file..." ); + mir_free( ft->httpPath ); + ft->httpPath = NULL; + break; + } } + + mir_free( str ); + q += 2; + num += ( q-p ); + p = q; + } + + return num; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// filetransfer class members + +filetransfer::filetransfer() +{ + memset( this, 0, sizeof( filetransfer )); + fileId = -1; + std.cbSize = sizeof( std ); +} + +filetransfer::~filetransfer() +{ + JabberLog( "Destroying file transfer session %08p", this ); + + if ( !bCompleted ) + JSendBroadcast( std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, this, 0 ); + + close(); + + if ( hWaitEvent != INVALID_HANDLE_VALUE ) + CloseHandle( hWaitEvent ); + + if ( jid ) mir_free( jid ); + if ( sid ) mir_free( sid ); + if ( iqId ) mir_free( iqId ); + if ( fileSize ) mir_free( fileSize ); + if ( httpHostName ) mir_free( httpHostName ); + if ( httpPath ) mir_free( httpPath ); + if ( szDescription ) mir_free( szDescription ); + + if ( std.workingDir ) mir_free( std.workingDir ); + if ( std.currentFile ) mir_free( std.currentFile ); + + if ( std.files ) { + for ( int i=0; i < std.totalFiles; i++ ) + if ( std.files[i] ) mir_free( std.files[i] ); + + mir_free( std.files ); +} } + +void filetransfer::close() +{ + if ( fileId != -1 ) { + _close( fileId ); + fileId = -1; +} } + +void filetransfer::complete() +{ + close(); + + bCompleted = true; + JSendBroadcast( std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, this, 0); +} + +int filetransfer::create() +{ + if ( fileId != -1 ) + return fileId; + + char filefull[ MAX_PATH ]; + mir_snprintf( filefull, sizeof filefull, "%s\\%s", std.workingDir, std.currentFile ); + replaceStr( std.currentFile, filefull ); + + if ( hWaitEvent != INVALID_HANDLE_VALUE ) + CloseHandle( hWaitEvent ); + hWaitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + + if ( JSendBroadcast( std.hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, this, ( LPARAM )&std )) + WaitForSingleObject( hWaitEvent, INFINITE ); + + if ( IsWinVerNT() && wszFileName != NULL ) { + WCHAR wszTemp[ MAX_PATH ]; + _snwprintf( wszTemp, sizeof wszTemp, L"%S\\%s", std.workingDir, wszFileName ); + wszTemp[ MAX_PATH-1 ] = 0; + fileId = _wopen( wszTemp, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE); + if ( fileId != -1 ) { + WIN32_FIND_DATAW data; + HANDLE hFind = FindFirstFileW( wszFileName, &data ); + if ( hFind != INVALID_HANDLE_VALUE ) { + mir_free( std.currentFile ); + + char tShortName[ 20 ]; + WideCharToMultiByte( CP_ACP, 0, + ( data.cAlternateFileName[0] != 0 ) ? data.cAlternateFileName : data.cFileName, + -1, tShortName, sizeof tShortName, 0, 0 ); + mir_snprintf( filefull, sizeof( filefull ), "%s\\%s", std.workingDir, tShortName ); + std.currentFile = mir_strdup( filefull ); + JabberLog( "Saving to [%s]", std.currentFile ); + FindClose( hFind ); + } } } + + if ( fileId == -1 ) { + JabberLog( "Saving to [%s]", std.currentFile ); + fileId = _open( std.currentFile, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE ); + } + + if ( fileId == -1 ) + JabberLog( "Cannot create file '%s' during a file transfer", filefull ); + else if ( std.currentFileSize != 0 ) + _chsize( fileId, std.currentFileSize ); + + return fileId; +} diff --git a/miranda-wine/protocols/JabberG/jabber_form.cpp b/miranda-wine/protocols/JabberG/jabber_form.cpp new file mode 100644 index 0000000..a676e7c --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_form.cpp @@ -0,0 +1,479 @@ +/* + +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_form.cpp,v $ +Revision : $Revision: 2961 $ +Last change on : $Date: 2006-05-26 23:39:41 +0400 (Птн, 26 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "resource.h" + +static BOOL CALLBACK JabberFormMultiLineWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + //case WM_GETDLGCODE: + // return DLGC_WANTARROWS|DLGC_WANTCHARS|DLGC_HASSETSEL|DLGC_WANTALLKEYS; + case WM_KEYDOWN: + if ( wParam == VK_TAB ) { + SetFocus( GetNextDlgTabItem( GetParent( GetParent( hwnd )), hwnd, GetKeyState( VK_SHIFT )<0?TRUE:FALSE )); + return TRUE; + }; + break; + } + return CallWindowProc(( WNDPROC ) GetWindowLong( hwnd, GWL_USERDATA ), hwnd, msg, wParam, lParam ); +} + +void JabberFormCreateUI( HWND hwndStatic, XmlNode *xNode, int *formHeight ) +{ + HWND hFrame, hCtrl; + HFONT hFont; + XmlNode *n, *v, *o, *vs; + int id, i, j, k, ypos, index, size; + TCHAR* label, *type, *labelStr, *valueStr, *varStr, *str, *p, *valueText; + int labelOffset, labelWidth, labelHeight; + int ctrlOffset, ctrlWidth; + RECT frameRect, strRect; + + if ( xNode==NULL || xNode->name==NULL || strcmp( xNode->name, "x" ) || hwndStatic==NULL ) return; + hFrame = hwndStatic; + hFont = ( HFONT ) SendMessage( hFrame, WM_GETFONT, 0, 0 ); + + GetClientRect( hwndStatic, &frameRect ); + labelOffset = 10; + labelWidth = ( frameRect.right - frameRect.left )/2 - 20 - 20; + ctrlOffset = labelWidth + 20; + ctrlWidth = frameRect.right - frameRect.left - labelWidth - 20 - 20; + + id = 0; + ypos = 14; + for ( i=0; inumChild; i++ ) { + n = xNode->child[i]; + if ( n->name ) { + if ( !strcmp( n->name, "field" )) { + if (( varStr=JabberXmlGetAttrValue( n, "var" )) != NULL && + ( type=JabberXmlGetAttrValue( n, "type" )) != NULL ) { + + if (( label=JabberXmlGetAttrValue( n, "label" )) != NULL ) + labelStr = mir_tstrdup( label ); + else + labelStr = mir_tstrdup( varStr ); + strRect.top = strRect.left = 0; strRect.right = labelWidth; strRect.bottom = 1; + labelHeight = DrawText( GetDC( hFrame ), labelStr, -1, &strRect, DT_CALCRECT|DT_RIGHT|DT_WORDBREAK ); + //labelHeight = labelHeight * 6 / 7; + if ( labelHeight < 18 ) labelHeight = 18; + + if (( v=JabberXmlGetChild( n, "value" )) != NULL ) { + valueStr = mir_tstrdup( v->text ); + valueText = v->text; + } + else valueStr = valueText = NULL; + + if ( !_tcscmp( type, _T("text-private"))) { + if ( labelStr ) { + hCtrl = CreateWindow( _T("static"), labelStr, WS_CHILD|WS_VISIBLE|SS_RIGHT, labelOffset, ypos+4, labelWidth, labelHeight, hFrame, ( HMENU ) IDC_STATIC, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + } + hCtrl = CreateWindowEx( WS_EX_CLIENTEDGE, _T("edit"), valueStr, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL|ES_PASSWORD, ctrlOffset, ypos, ctrlWidth, 24, hFrame, ( HMENU ) id, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + id++; + ypos += (( labelHeight>24 )?labelHeight:24 ); + } + else if ( !_tcscmp( type, _T("text-multi")) || !_tcscmp( type, _T("jid-multi"))) { + WNDPROC oldWndProc; + + if ( labelStr ) { + hCtrl = CreateWindow( _T("static"), labelStr, WS_CHILD|WS_VISIBLE|SS_RIGHT, labelOffset, ypos+4, labelWidth, labelHeight, hFrame, ( HMENU ) IDC_STATIC, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + } + size = 1; + for ( j=0; jnumChild; j++ ) { + v = n->child[j]; + if ( v->name && !strcmp( v->name, "value" ) && v->text ) + size += _tcslen( v->text ) + 2; + } + str = ( TCHAR* )mir_alloc( sizeof(TCHAR)*size ); + str[0] = '\0'; + for ( j=0; jnumChild; j++ ) { + v = n->child[j]; + if ( v->name && !strcmp( v->name, "value" ) && v->text ) { + if ( str[0] ) _tcscat( str, _T("\r\n")); + _tcscat( str, v->text ); + } } + + hCtrl = CreateWindowEx( WS_EX_CLIENTEDGE, _T("edit"), str, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP|WS_VSCROLL|ES_LEFT|ES_MULTILINE|ES_AUTOVSCROLL|ES_WANTRETURN, ctrlOffset, ypos, ctrlWidth, 24*3, hFrame, ( HMENU ) id, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + oldWndProc = ( WNDPROC ) SetWindowLong( hCtrl, GWL_WNDPROC, ( LPARAM )JabberFormMultiLineWndProc ); + SetWindowLong( hCtrl, GWL_USERDATA, ( LONG ) oldWndProc ); + id++; + ypos += (( labelHeight>24*3 )?labelHeight:24*3 ); + mir_free( str ); + } + else if ( !_tcscmp( type, _T("boolean"))) { + strRect.top = strRect.left = 0; + strRect.right = ctrlWidth-20; strRect.bottom = 1; + labelHeight = DrawText( GetDC( hFrame ), labelStr, -1, &strRect, DT_CALCRECT|DT_WORDBREAK ); + if ( labelHeight < 24 ) labelHeight = 24; + hCtrl = CreateWindowEx( 0, _T("button"), labelStr, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX|BS_MULTILINE, ctrlOffset, ypos, ctrlWidth, ( labelHeight>24 )?labelHeight:24, hFrame, ( HMENU ) id, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + if ( valueStr && !_tcscmp( valueStr, _T("1"))) + SendMessage( hCtrl, BM_SETCHECK, 1, 0 ); + id++; + ypos += (( labelHeight>24 )?labelHeight:24 ); + } + else if ( !_tcscmp( type, _T("list-single"))) { + if ( labelStr ) { + hCtrl = CreateWindow( _T("static"), labelStr, WS_CHILD|WS_VISIBLE|SS_RIGHT, labelOffset, ypos+4, labelWidth, labelHeight, hFrame, ( HMENU ) IDC_STATIC, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + } + hCtrl = CreateWindowExA( WS_EX_CLIENTEDGE, "combobox", NULL, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP|CBS_DROPDOWNLIST, ctrlOffset, ypos, ctrlWidth, 24*4, hFrame, ( HMENU ) id, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + for ( j=0; jnumChild; j++ ) { + o = n->child[j]; + if ( o->name && !strcmp( o->name, "option" )) { + if (( v=JabberXmlGetChild( o, "value" ))!=NULL && v->text ) { + if (( str=JabberXmlGetAttrValue( o, "label" )) == NULL ) + str = v->text; + if (( p = mir_tstrdup( str )) != NULL ) { + index = SendMessage( hCtrl, CB_ADDSTRING, 0, ( LPARAM )p ); + mir_free( p ); + if ( valueText!=NULL && !_tcscmp( valueText, v->text )) { + SendMessage( hCtrl, CB_SETCURSEL, index, 0 ); + } } } } } + id++; + ypos += (( labelHeight>24 )?labelHeight:24 ); + } + else if ( !_tcscmp( type, _T("list-multi"))) { + if ( labelStr ) { + hCtrl = CreateWindow( _T("static"), labelStr, WS_CHILD|WS_VISIBLE|SS_RIGHT, labelOffset, ypos+4, labelWidth, labelHeight, hFrame, ( HMENU ) IDC_STATIC, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + } + hCtrl = CreateWindowExA( WS_EX_CLIENTEDGE, "listbox", NULL, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP|LBS_MULTIPLESEL, ctrlOffset, ypos, ctrlWidth, 24*3, hFrame, ( HMENU ) id, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + for ( j=0; jnumChild; j++ ) { + o = n->child[j]; + if ( o->name && !strcmp( o->name, "option" )) { + if (( v = JabberXmlGetChild( o, "value" ))!=NULL && v->text ) { + if (( str=JabberXmlGetAttrValue( o, "label" )) == NULL ) + str = v->text; + if (( p = mir_tstrdup( str )) != NULL ) { + index = SendMessage( hCtrl, LB_ADDSTRING, 0, ( LPARAM )p ); + mir_free( p ); + for ( k=0; knumChild; k++ ) { + vs = n->child[k]; + if ( vs->name && !strcmp( vs->name, "value" ) && vs->text && !_tcscmp( vs->text, v->text )) + SendMessage( hCtrl, LB_SETSEL, TRUE, index ); + } } } } } + id++; + ypos += (( labelHeight>24*3 )?labelHeight:24*3 ); + } + else if ( !_tcscmp( type, _T("fixed"))) { + if ( valueStr ) { + strRect.top = strRect.left = 0; + strRect.right = ctrlWidth; strRect.bottom = 1; + labelHeight = DrawText( GetDC( hFrame ), labelStr, -1, &strRect, DT_CALCRECT|DT_WORDBREAK ); + labelHeight = labelHeight * 6 / 7; + if ( labelHeight < 24 ) labelHeight = 24; + hCtrl = CreateWindow( _T("static"), valueStr, WS_CHILD|WS_VISIBLE|SS_RIGHT, labelOffset, ypos+4, labelWidth+ctrlWidth, labelHeight, hFrame, ( HMENU ) IDC_STATIC, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + ypos += labelHeight; + } + } + else if ( !_tcscmp( type, _T("hidden"))) { + // skip + } + else { // everything else is considered "text-single" + if ( labelStr ) { + hCtrl = CreateWindow( _T("static"), labelStr, WS_CHILD|WS_VISIBLE|SS_RIGHT, labelOffset, ypos+4, labelWidth, labelHeight, hFrame, ( HMENU ) IDC_STATIC, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + } + hCtrl = CreateWindowEx( WS_EX_CLIENTEDGE, _T("edit"), valueStr, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL, ctrlOffset, ypos, ctrlWidth, 24, hFrame, ( HMENU ) id, hInst, NULL ); + SendMessage( hCtrl, WM_SETFONT, ( WPARAM ) hFont, 0 ); + id++; + ypos += (( labelHeight>24 )?labelHeight:24 ); + } + mir_free( labelStr ); + if ( valueStr ) mir_free( valueStr ); + } } } } + + *formHeight = ypos; +} + +XmlNode* JabberFormGetData( HWND hwndStatic, XmlNode* xNode ) +{ + HWND hFrame, hCtrl; + XmlNode *n, *v, *o, *x; + int id, j, k, len; + TCHAR *varName, *type, *fieldStr, *str, *str2, *p, *q, *labelText; + + if ( xNode==NULL || xNode->name==NULL || strcmp( xNode->name, "x" ) || hwndStatic==NULL ) + return NULL; + + hFrame = hwndStatic; + id = 0; + x = new XmlNode( "x" ); x->addAttr( "xmlns", "jabber:x:data" ); x->addAttr( "type", "submit" ); + for ( int i=0; inumChild; i++ ) { + n = xNode->child[i]; + fieldStr = NULL; + if ( lstrcmpA( n->name, "field" )) + continue; + + if (( varName=JabberXmlGetAttrValue( n, "var" )) == NULL || ( type=JabberXmlGetAttrValue( n, "type" )) == NULL ) + continue; + + hCtrl = GetDlgItem( hFrame, id ); + XmlNode* field = x->addChild( "field" ); field->addAttr( "var", varName ); + + if ( !_tcscmp( type, _T("text-multi")) || !_tcscmp( type, _T("jid-multi"))) { + len = GetWindowTextLength( GetDlgItem( hFrame, id )); + str = ( TCHAR* )mir_alloc( sizeof(TCHAR)*( len+1 )); + GetDlgItemText( hFrame, id, str, len+1 ); + p = str; + while ( p != NULL ) { + if (( q = _tcsstr( p, _T("\r\n"))) != NULL ) + *q = '\0'; + field->addChild( "value", p ); + p = q ? q+2 : NULL; + } + mir_free( str ); + id++; + } + else if ( !_tcscmp( type, _T("boolean"))) { + TCHAR buf[ 10 ]; + _itot( IsDlgButtonChecked( hFrame, id ) == BST_CHECKED ? 1 : 0, buf, 10 ); + field->addChild( "value", buf ); + id++; + } + else if ( !_tcscmp( type, _T("list-single"))) { + len = GetWindowTextLength( GetDlgItem( hFrame, id )); + str = ( TCHAR* )mir_alloc( sizeof( TCHAR )*( len+1 )); + GetDlgItemText( hFrame, id, str, len+1 ); + for ( j=0; j < n->numChild; j++ ) { + o = n->child[j]; + if ( o && o->name && !strcmp( o->name, "option" )) { + if (( v=JabberXmlGetChild( o, "value" ))!=NULL && v->text ) { + if (( str2=JabberXmlGetAttrValue( o, "label" )) == NULL ) + str2 = v->text; + if ( !lstrcmp( str2, str )) + break; + } } } + + if ( j < n->numChild ) + field->addChild( "value", v->text ); + + mir_free( str ); + id++; + } + else if ( !_tcscmp( type, _T("list-multi"))) { + int count = SendMessage( hCtrl, LB_GETCOUNT, 0, 0 ); + for ( j=0; j 0 ) { + // an entry is selected + len = SendMessage( hCtrl, LB_GETTEXTLEN, 0, 0 ); + if (( str = ( TCHAR* )mir_alloc(( len+1 )*sizeof( TCHAR ))) != NULL ) { + SendMessage( hCtrl, LB_GETTEXT, j, ( LPARAM )str ); + for ( k=0; k < n->numChild; k++ ) { + o = n->child[k]; + if ( o && o->name && !strcmp( o->name, "option" )) { + if (( v=JabberXmlGetChild( o, "value" ))!=NULL && v->text ) { + if (( labelText=JabberXmlGetAttrValue( o, "label" )) == NULL ) + labelText = v->text; + + if ( !lstrcmp( labelText, str )) + field->addChild( "value", v->text ); + } } } + mir_free( str ); + } } } + id++; + } + else if ( !_tcscmp( type, _T("fixed")) || !_tcscmp( type, _T("hidden"))) { + v = JabberXmlGetChild( n, "value" ); + if ( v != NULL && v->text != NULL ) + field->addChild( "value", v->text ); + } + else { // everything else is considered "text-single" or "text-private" + len = GetWindowTextLength( GetDlgItem( hFrame, id )); + str = ( TCHAR* )mir_alloc( sizeof(TCHAR)*( len+1 )); + GetDlgItemText( hFrame, id, str, len+1 ); + field->addChild( "value", str ); + mir_free( str ); + id++; + } } + + return x; +} + +typedef struct { + XmlNode *xNode; + TCHAR defTitle[128]; // Default title if no in xNode + RECT frameRect; // Clipping region of the frame to scroll + int frameHeight; // Height of the frame ( can be eliminated, redundant to frameRect ) + int formHeight; // Actual height of the form + int curPos; // Current scroll position + JABBER_FORM_SUBMIT_FUNC pfnSubmit; + void *userdata; +} JABBER_FORM_INFO; + +static BOOL CALLBACK JabberFormDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + JABBER_FORM_INFO *jfi; + + switch ( msg ) { + case WM_INITDIALOG: + { + XmlNode *n; + LONG frameExStyle; + + // lParam is ( JABBER_FORM_INFO * ) + TranslateDialogDefault( hwndDlg ); + ShowWindow( GetDlgItem( hwndDlg, IDC_FRAME_TEXT ), SW_HIDE ); + jfi = ( JABBER_FORM_INFO * ) lParam; + if ( jfi != NULL ) { + // Set dialog title + if ( jfi->xNode!=NULL && ( n=JabberXmlGetChild( jfi->xNode, "title" ))!=NULL && n->text!=NULL ) + SetWindowText( hwndDlg, n->text ); + else if ( jfi->defTitle != NULL ) + SetWindowText( hwndDlg, TranslateTS( jfi->defTitle )); + // Set instruction field + if ( jfi->xNode!=NULL && ( n=JabberXmlGetChild( jfi->xNode, "instructions" ))!=NULL && n->text!=NULL ) + SetDlgItemText( hwndDlg, IDC_INSTRUCTION, n->text ); + + // Create form + if ( jfi->xNode != NULL ) { + RECT rect; + + GetClientRect( GetDlgItem( hwndDlg, IDC_FRAME ), &( jfi->frameRect )); + GetClientRect( GetDlgItem( hwndDlg, IDC_VSCROLL ), &rect ); + jfi->frameRect.right -= ( rect.right - rect.left ); + GetClientRect( GetDlgItem( hwndDlg, IDC_FRAME ), &rect ); + jfi->frameHeight = rect.bottom - rect.top; + JabberFormCreateUI( GetDlgItem( hwndDlg, IDC_FRAME ), jfi->xNode, &( jfi->formHeight )); + } + } + + if ( jfi->formHeight > jfi->frameHeight ) { + HWND hwndScroll; + + hwndScroll = GetDlgItem( hwndDlg, IDC_VSCROLL ); + EnableWindow( hwndScroll, TRUE ); + SetScrollRange( hwndScroll, SB_CTL, 0, jfi->formHeight - jfi->frameHeight, FALSE ); + jfi->curPos = 0; + } + + // Enable WS_EX_CONTROLPARENT on IDC_FRAME ( so tab stop goes through all its children ) + frameExStyle = GetWindowLong( GetDlgItem( hwndDlg, IDC_FRAME ), GWL_EXSTYLE ); + frameExStyle |= WS_EX_CONTROLPARENT; + SetWindowLong( GetDlgItem( hwndDlg, IDC_FRAME ), GWL_EXSTYLE, frameExStyle ); + + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG ) jfi ); + if ( jfi->pfnSubmit != NULL ) + EnableWindow( GetDlgItem( hwndDlg, IDC_SUBMIT ), TRUE ); + } + return TRUE; + case WM_VSCROLL: + { + int pos; + + jfi = ( JABBER_FORM_INFO * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( jfi != NULL ) { + pos = jfi->curPos; + switch ( LOWORD( wParam )) { + case SB_LINEDOWN: + pos += 10; + break; + case SB_LINEUP: + pos -= 10; + break; + case SB_PAGEDOWN: + pos += ( jfi->frameHeight - 10 ); + break; + case SB_PAGEUP: + pos -= ( jfi->frameHeight - 10 ); + break; + case SB_THUMBTRACK: + pos = HIWORD( wParam ); + break; + } + if ( pos > ( jfi->formHeight - jfi->frameHeight )) + pos = jfi->formHeight - jfi->frameHeight; + if ( pos < 0 ) + pos = 0; + if ( jfi->curPos != pos ) { + ScrollWindow( GetDlgItem( hwndDlg, IDC_FRAME ), 0, jfi->curPos - pos, NULL, &( jfi->frameRect )); + SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, pos, TRUE ); + jfi->curPos = pos; + } + } + } + break; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_SUBMIT: + jfi = ( JABBER_FORM_INFO * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( jfi != NULL ) { + XmlNode* n = JabberFormGetData( GetDlgItem( hwndDlg, IDC_FRAME ), jfi->xNode ); + ( jfi->pfnSubmit )( n, jfi->userdata ); + } + // fall through + case IDCANCEL: + DestroyWindow( hwndDlg ); + return TRUE; + } + break; + case WM_CLOSE: + DestroyWindow( hwndDlg ); + break; + case WM_DESTROY: + jfi = ( JABBER_FORM_INFO * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( jfi != NULL ) { + if ( jfi->xNode != NULL ) + delete jfi->xNode; + mir_free( jfi ); + } + break; + } + + return FALSE; +} + +static VOID CALLBACK JabberFormCreateDialogApcProc( DWORD param ) +{ + CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_FORM ), NULL, JabberFormDlgProc, ( LPARAM )param ); +} + +void JabberFormCreateDialog( XmlNode *xNode, TCHAR* defTitle, JABBER_FORM_SUBMIT_FUNC pfnSubmit, void *userdata ) +{ + JABBER_FORM_INFO *jfi; + + jfi = ( JABBER_FORM_INFO * ) mir_alloc( sizeof( JABBER_FORM_INFO )); + memset( jfi, 0, sizeof( JABBER_FORM_INFO )); + jfi->xNode = JabberXmlCopyNode( xNode ); + if ( defTitle ) + _tcsncpy( jfi->defTitle, defTitle, SIZEOF( jfi->defTitle )); + jfi->pfnSubmit = pfnSubmit; + jfi->userdata = userdata; + + if ( GetCurrentThreadId() != jabberMainThreadId ) + QueueUserAPC( JabberFormCreateDialogApcProc, hMainThread, ( DWORD )jfi ); + else + JabberFormCreateDialogApcProc(( DWORD )jfi ); +} diff --git a/miranda-wine/protocols/JabberG/jabber_ft.cpp b/miranda-wine/protocols/JabberG/jabber_ft.cpp new file mode 100644 index 0000000..1c1897f --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_ft.cpp @@ -0,0 +1,419 @@ +/* + +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_ft.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include <io.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "jabber_iq.h" +#include "jabber_byte.h" + +void JabberFtCancel( filetransfer* ft ) +{ + JABBER_LIST_ITEM *item; + JABBER_BYTE_TRANSFER *jbt; + int i; + + JabberLog( "Invoking JabberFtCancel()" ); + + // For file sending session that is still in si negotiation phase + for ( i=0; ( i=JabberListFindNext( LIST_FTSEND, i ))>=0; i++ ) { + item = JabberListGetItemPtrFromIndex( i ); + if ( item->ft == ft ) { + JabberLog( "Canceling file sending session while in si negotiation" ); + JabberListRemoveByIndex( i ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + delete ft; + return; + } + } + // For file receiving session that is still in si negotiation phase + for ( i=0; ( i=JabberListFindNext( LIST_FTRECV, i ))>=0; i++ ) { + item = JabberListGetItemPtrFromIndex( i ); + if ( item->ft == ft ) { + JabberLog( "Canceling file receiving session while in si negotiation" ); + JabberListRemoveByIndex( i ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + delete ft; + return; + } + } + // For file transfer through bytestream + if (( jbt=ft->jbt ) != NULL ) { + JabberLog( "Canceling bytestream session" ); + jbt->state = JBT_ERROR; + if ( jbt->hConn ) { + JabberLog( "Force closing bytestream session" ); + Netlib_CloseHandle( jbt->hConn ); + jbt->hConn = NULL; + } + if ( jbt->hEvent ) SetEvent( jbt->hEvent ); + } +} + +///////////////// File sending using stream initiation ///////////////////////// + +static void JabberFtSiResult( XmlNode *iqNode, void *userdata ); +static BOOL JabberFtSend( HANDLE hConn, void *userdata ); +static void JabberFtSendFinal( BOOL success, void *userdata ); + +void JabberFtInitiate( TCHAR* jid, filetransfer* ft ) +{ + int iqId; + TCHAR *rs; + char *filename, *p; + TCHAR idStr[32]; + JABBER_LIST_ITEM *item; + int i; + TCHAR sid[9]; + + if ( jid==NULL || ft==NULL || !jabberOnline || ( rs=JabberListGetBestClientResourceNamePtr( jid ))==NULL ) { + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + delete ft; + return; + } + iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberFtSiResult ); + mir_sntprintf( idStr, SIZEOF( idStr ), _T(JABBER_IQID)_T("%d"), iqId ); + if (( item=JabberListAdd( LIST_FTSEND, idStr )) == NULL ) { + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + delete ft; + return; + } + item->ft = ft; + ft->type = FT_SI; + for ( i=0; i<8; i++ ) + sid[i] = ( rand()%10 ) + '0'; + sid[8] = '\0'; + if ( ft->sid != NULL ) mir_free( ft->sid ); + ft->sid = mir_tstrdup( sid ); + filename = ft->std.files[ ft->std.currentFileNumber ]; + if (( p=strrchr( filename, '\\' )) != NULL ) + filename = p+1; + + TCHAR tszJid[200]; + mir_sntprintf( tszJid, SIZEOF(tszJid), _T("%s/%s"), jid, rs ); + + XmlNodeIq iq( "set", iqId, tszJid ); + XmlNode* si = iq.addChild( "si" ); si->addAttr( "xmlns", "http://jabber.org/protocol/si" ); + si->addAttr( "id", sid ); si->addAttr( "mime-type", "binary/octet-stream" ); + si->addAttr( "profile", "http://jabber.org/protocol/si/profile/file-transfer" ); + XmlNode* file = si->addChild( "file" ); file->addAttr( "name", filename ); file->addAttr( "size", ft->fileSize[ ft->std.currentFileNumber ] ); + file->addAttr( "xmlns", "http://jabber.org/protocol/si/profile/file-transfer" ); + file->addChild( "desc", ft->szDescription ); + XmlNode* feature = si->addChild( "feature" ); feature->addAttr( "xmlns", "http://jabber.org/protocol/feature-neg" ); + XmlNode* x = feature->addChild( "x" ); x->addAttr( "xmlns", "jabber:x:data" ); x->addAttr( "type", "form" ); + XmlNode* field = x->addChild( "field" ); field->addAttr( "var", "stream-method" ); field->addAttr( "type", "list-single" ); + XmlNode* option = field->addChild( "option" ); option->addChild( "value", "http://jabber.org/protocol/bytestreams" ); + JabberSend( jabberThreadInfo->s, iq ); +} + +static void JabberFtSiResult( XmlNode *iqNode, void *userdata ) +{ + TCHAR* type, *from, *to, *idStr, *str; + XmlNode *siNode, *fileNode, *rangeNode, *featureNode, *xNode, *fieldNode, *valueNode; + JABBER_LIST_ITEM *item; + int offset, length; + JABBER_BYTE_TRANSFER *jbt; + + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( from=JabberXmlGetAttrValue( iqNode, "from" )) == NULL ) return; + if (( to=JabberXmlGetAttrValue( iqNode, "to" )) == NULL ) return; + idStr = JabberXmlGetAttrValue( iqNode, "id" ); + if (( item=JabberListGetItemPtr( LIST_FTSEND, idStr )) == NULL ) return; + + if ( !_tcscmp( type, _T("result"))) { + if (( siNode=JabberXmlGetChild( iqNode, "si" )) != NULL ) { + if (( fileNode=JabberXmlGetChild( siNode, "file" )) != NULL ) { + if (( rangeNode=JabberXmlGetChild( fileNode, "range" )) != NULL ) { +// ************** Need to store offset/length in ft structure ********************** +// but at this tiem, we should not get <range/> tag since we don't sent <range/> on our request + if (( str=JabberXmlGetAttrValue( rangeNode, "offset" )) != NULL ) + offset = _ttoi( str ); + if (( str=JabberXmlGetAttrValue( rangeNode, "length" )) != NULL ) + length = _ttoi( str ); + } + } + if (( featureNode=JabberXmlGetChild( siNode, "feature" )) != NULL ) { + if (( xNode=JabberXmlGetChildWithGivenAttrValue( featureNode, "x", "xmlns", _T("jabber:x:data"))) != NULL ) { + if (( fieldNode=JabberXmlGetChildWithGivenAttrValue( xNode, "field", "var", _T("stream-method"))) != NULL ) { + if (( valueNode=JabberXmlGetChild( fieldNode, "value" ))!=NULL && valueNode->text!=NULL ) { + if ( !_tcscmp( valueNode->text, _T("http://jabber.org/protocol/bytestreams"))) { + // Start Bytestream session + jbt = ( JABBER_BYTE_TRANSFER * ) mir_alloc( sizeof( JABBER_BYTE_TRANSFER )); + ZeroMemory( jbt, sizeof( JABBER_BYTE_TRANSFER )); + jbt->srcJID = mir_tstrdup( to ); + jbt->dstJID = mir_tstrdup( from ); + jbt->sid = mir_tstrdup( item->ft->sid ); + jbt->pfnSend = JabberFtSend; + jbt->pfnFinal = JabberFtSendFinal; + jbt->userdata = item->ft; + item->ft->type = FT_BYTESTREAM; + item->ft->jbt = jbt; + JabberForkThread(( JABBER_THREAD_FUNC )JabberByteSendThread, 0, jbt ); + } } } } } } + } + else if ( !_tcscmp( type, _T("error"))) { + JabberLog( "File transfer stream initiation request denied" ); + JSendBroadcast( item->ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DENIED, item->ft, 0 ); + delete item->ft; + item->ft = NULL; + } + + JabberListRemove( LIST_FTSEND, idStr ); +} + +static BOOL JabberFtSend( HANDLE hConn, void *userdata ) +{ + filetransfer* ft = ( filetransfer* ) userdata; + + struct _stat statbuf; + int fd; + char* buffer; + int numRead; + + JabberLog( "Sending [%s]", ft->std.files[ ft->std.currentFileNumber ] ); + _stat( ft->std.files[ ft->std.currentFileNumber ], &statbuf ); // file size in statbuf.st_size + if (( fd=_open( ft->std.files[ ft->std.currentFileNumber ], _O_BINARY|_O_RDONLY )) < 0 ) { + JabberLog( "File cannot be opened" ); + return FALSE; + } + + ft->std.sending = TRUE; + ft->std.currentFileSize = statbuf.st_size; + ft->std.currentFileProgress = 0; + + if (( buffer=( char* )mir_alloc( 2048 )) != NULL ) { + while (( numRead=_read( fd, buffer, 2048 )) > 0 ) { + if ( Netlib_Send( hConn, buffer, numRead, 0 ) != numRead ) { + mir_free( buffer ); + _close( fd ); + return FALSE; + } + ft->std.currentFileProgress += numRead; + ft->std.totalProgress += numRead; + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std ); + } + mir_free( buffer ); + } + _close( fd ); + return TRUE; +} + +static void JabberFtSendFinal( BOOL success, void *userdata ) +{ + filetransfer* ft = ( filetransfer* )userdata; + + if ( !success ) { + JabberLog( "File transfer complete with error" ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 ); + } + else { + if ( ft->std.currentFileNumber < ft->std.totalFiles-1 ) { + ft->std.currentFileNumber++; + replaceStr( ft->std.currentFile, ft->std.files[ ft->std.currentFileNumber ] ); + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0 ); + JabberFtInitiate( ft->jid, ft ); + return; + } + + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0 ); + } + + delete ft; +} + +///////////////// File receiving through stream initiation ///////////////////////// + +static int JabberFtReceive( HANDLE hConn, void *userdata, char* buffer, int datalen ); +static void JabberFtReceiveFinal( BOOL success, void *userdata ); + +void JabberFtHandleSiRequest( XmlNode *iqNode ) +{ + TCHAR* from, *sid, *str, *szId, *filename; + XmlNode *siNode, *fileNode, *featureNode, *xNode, *fieldNode, *optionNode, *n; + int filesize, i; + JABBER_FT_TYPE ftType; + + if ( iqNode==NULL || + ( from=JabberXmlGetAttrValue( iqNode, "from" ))==NULL || + ( str=JabberXmlGetAttrValue( iqNode, "type" ))==NULL || _tcscmp( str, _T("set")) || + ( siNode=JabberXmlGetChildWithGivenAttrValue( iqNode, "si", "xmlns", _T("http://jabber.org/protocol/si"))) == NULL ) + return; + + szId = JabberXmlGetAttrValue( iqNode, "id" ); + if (( sid=JabberXmlGetAttrValue( siNode, "id" ))!=NULL && + ( fileNode=JabberXmlGetChildWithGivenAttrValue( siNode, "file", "xmlns", _T("http://jabber.org/protocol/si/profile/file-transfer")))!=NULL && + ( filename=JabberXmlGetAttrValue( fileNode, "name" ))!=NULL && + ( str=JabberXmlGetAttrValue( fileNode, "size" ))!=NULL ) { + + filesize = _ttoi( str ); + if (( featureNode=JabberXmlGetChildWithGivenAttrValue( siNode, "feature", "xmlns", _T("http://jabber.org/protocol/feature-neg"))) != NULL && + ( xNode=JabberXmlGetChildWithGivenAttrValue( featureNode, "x", "xmlns", _T("jabber:x:data")))!=NULL && + ( fieldNode=JabberXmlGetChildWithGivenAttrValue( xNode, "field", "var", _T("stream-method")))!=NULL ) { + + for ( i=0; i<fieldNode->numChild; i++ ) { + optionNode = fieldNode->child[i]; + if ( optionNode->name && !strcmp( optionNode->name, "option" )) { + if (( n=JabberXmlGetChild( optionNode, "value" ))!=NULL && n->text ) { + if ( !_tcscmp( n->text, _T("http://jabber.org/protocol/bytestreams"))) { + ftType = FT_BYTESTREAM; + break; + } } } } + + if ( i < fieldNode->numChild ) { + // Found known stream mechanism + CCSDATA ccs; + PROTORECVEVENT pre; + + char *localFilename = t2a( filename ); + char *desc = (( n=JabberXmlGetChild( fileNode, "desc" ))!=NULL && n->text!=NULL ) ? t2a( n->text ) : mir_strdup( "" ); + + filetransfer* ft = new filetransfer; + ft->jid = mir_tstrdup( from ); + ft->std.hContact = JabberHContactFromJID( from ); + ft->sid = mir_tstrdup( sid ); + ft->iqId = ( szId ) ? mir_tstrdup( szId ):NULL; + ft->type = ftType; + ft->std.totalFiles = 1; + ft->std.currentFile = localFilename; + ft->std.totalBytes = ft->std.currentFileSize = filesize; + char* szBlob = ( char* )alloca( sizeof( DWORD )+ strlen( localFilename ) + strlen( desc ) + 2 ); + *(( PDWORD ) szBlob ) = ( DWORD )ft; + strcpy( szBlob + sizeof( DWORD ), localFilename ); + strcpy( szBlob + sizeof( DWORD )+ strlen( localFilename ) + 1, desc ); + pre.flags = 0; + pre.timestamp = time( NULL ); + pre.szMessage = szBlob; + pre.lParam = 0; + ccs.szProtoService = PSR_FILE; + ccs.hContact = ft->std.hContact; + ccs.wParam = 0; + ccs.lParam = ( LPARAM )⪯ + JCallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs ); + mir_free( desc ); + return; + } + else { + // Unknown stream mechanism + XmlNodeIq iq( "error", szId, from ); + XmlNode* e = iq.addChild( "error" ); e->addAttr( "code", 400 ); e->addAttr( "type", "cancel" ); + XmlNode* br = e->addChild( "bad-request" ); br->addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas" ); + XmlNode* nvs = e->addChild( "no-valid-streams" ); nvs->addAttr( "xmlns", "http://jabber.org/protocol/si" ); + JabberSend( jabberThreadInfo->s, iq ); + return; + } } } + + // Bad stream initiation, reply with bad-profile + XmlNodeIq iq( "error", szId, from ); + XmlNode* e = iq.addChild( "error" ); e->addAttr( "code", 400 ); e->addAttr( "type", "cancel" ); + XmlNode* br = e->addChild( "bad-request" ); br->addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas" ); + XmlNode* nvs = e->addChild( "bad-profile" ); nvs->addAttr( "xmlns", "http://jabber.org/protocol/si" ); + JabberSend( jabberThreadInfo->s, iq ); +} + +void JabberFtAcceptSiRequest( filetransfer* ft ) +{ + if ( !jabberOnline || ft==NULL || ft->jid==NULL || ft->sid==NULL ) return; + + JABBER_LIST_ITEM *item; + if (( item=JabberListAdd( LIST_FTRECV, ft->sid )) != NULL ) { + item->ft = ft; + + XmlNodeIq iq( "result", ft->iqId, ft->jid ); + XmlNode* si = iq.addChild( "si" ); si->addAttr( "xmlns", "http://jabber.org/protocol/si" ); + XmlNode* f = si->addChild( "feature" ); f->addAttr( "xmlns", "http://jabber.org/protocol/feature-neg" ); + XmlNode* x = f->addChild( "x" ); x->addAttr( "xmlns", "jabber:x:data" ); x->addAttr( "type", "submit" ); + XmlNode* fl = x->addChild( "field" ); fl->addAttr( "var", "stream-method" ); + fl->addChild( "value", "http://jabber.org/protocol/bytestreams" ); + JabberSend( jabberThreadInfo->s, iq ); +} } + +BOOL JabberFtHandleBytestreamRequest( XmlNode *iqNode ) +{ + XmlNode *queryNode; + TCHAR* sid; + JABBER_LIST_ITEM *item; + JABBER_BYTE_TRANSFER *jbt; + + if ( iqNode == NULL ) return FALSE; + if (( queryNode=JabberXmlGetChildWithGivenAttrValue( iqNode, "query", "xmlns", _T("http://jabber.org/protocol/bytestreams")))!=NULL && + ( sid=JabberXmlGetAttrValue( queryNode, "sid" ))!=NULL && + ( item=JabberListGetItemPtr( LIST_FTRECV, sid ))!=NULL ) { + // Start Bytestream session + jbt = ( JABBER_BYTE_TRANSFER * ) mir_alloc( sizeof( JABBER_BYTE_TRANSFER )); + ZeroMemory( jbt, sizeof( JABBER_BYTE_TRANSFER )); + jbt->iqNode = JabberXmlCopyNode( iqNode ); + jbt->pfnRecv = JabberFtReceive; + jbt->pfnFinal = JabberFtReceiveFinal; + jbt->userdata = item->ft; + item->ft->jbt = jbt; + JabberForkThread(( JABBER_THREAD_FUNC )JabberByteReceiveThread, 0, jbt ); + JabberListRemove( LIST_FTRECV, sid ); + return TRUE; + } + + JabberLog( "File transfer invalid bytestream initiation request received" ); + return FALSE; +} + +static int JabberFtReceive( HANDLE hConn, void *userdata, char* buffer, int datalen ) +{ + filetransfer* ft = ( filetransfer* )userdata; + if ( ft->create() == -1 ) + return -1; + + int remainingBytes = ft->std.currentFileSize - ft->std.currentFileProgress; + if ( remainingBytes > 0 ) { + int writeSize = ( remainingBytes<datalen ) ? remainingBytes : datalen; + if ( _write( ft->fileId, buffer, writeSize ) != writeSize ) { + JabberLog( "_write() error" ); + return -1; + } + + ft->std.currentFileProgress += writeSize; + ft->std.totalProgress += writeSize; + JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std ); + return ( ft->std.currentFileSize == ft->std.currentFileProgress ) ? 0 : writeSize; + } + + return 0; +} + +static void JabberFtReceiveFinal( BOOL success, void *userdata ) +{ + filetransfer* ft = ( filetransfer* )userdata; + + if ( success ) { + JabberLog( "File transfer complete successfully" ); + ft->complete(); + } + else JabberLog( "File transfer complete with error" ); + + delete ft; +} diff --git a/miranda-wine/protocols/JabberG/jabber_groupchat.cpp b/miranda-wine/protocols/JabberG/jabber_groupchat.cpp new file mode 100644 index 0000000..f12e77a --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_groupchat.cpp @@ -0,0 +1,886 @@ +/* + +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_groupchat.cpp,v $ +Revision : $Revision: 3670 $ +Last change on : $Date: 2006-08-31 19:47:56 +0400 (Чтв, 31 Авг 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include <commctrl.h> +#include "resource.h" +#include "jabber_iq.h" + +#define GC_SERVER_LIST_SIZE 5 + +static BOOL CALLBACK JabberGroupchatDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ); +static BOOL CALLBACK JabberGroupchatJoinDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ); + +int JabberMenuHandleGroupchat( WPARAM wParam, LPARAM lParam ) +{ + int iqId; + + // lParam is the initial conference server to browse ( if any ) + if ( IsWindow( hwndJabberGroupchat )) { + SetForegroundWindow( hwndJabberGroupchat ); + if ( lParam != 0 ) { + SendMessage( hwndJabberGroupchat, WM_JABBER_ACTIVATE, 0, lParam ); // Just to update the IDC_SERVER and clear the list + iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_DISCOROOMSERVER, JabberIqResultDiscoRoomItems ); + + XmlNodeIq iq( "get", iqId, ( TCHAR* )lParam ); + XmlNode* query = iq.addQuery( "http://jabber.org/protocol/disco#items" ); + JabberSend( jabberThreadInfo->s, iq ); + // <iq/> result will send WM_JABBER_REFRESH to update the list with real data + } + } + else hwndJabberGroupchat = CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_GROUPCHAT ), NULL, JabberGroupchatDlgProc, lParam ); + + return 0; +} + +static BOOL sortAscending; +static int sortColumn; + +static int CALLBACK GroupchatCompare( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort ) +{ + JABBER_LIST_ITEM *item1, *item2; + int res = 0; + item1 = JabberListGetItemPtr( LIST_ROOM, ( TCHAR* )lParam1 ); + item2 = JabberListGetItemPtr( LIST_ROOM, ( TCHAR* )lParam2 ); + if ( item1!=NULL && item2!=NULL ) { + switch ( lParamSort ) { + case 0: // sort by JID column + res = lstrcmp( item1->jid, item2->jid ); + break; + case 1: // sort by Name column + res = lstrcmp( item1->name, item2->name ); + break; + } } + + if ( !sortAscending ) + res *= -1; + + return res; +} + +static BOOL CALLBACK JabberGroupchatDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + HWND lv; + LVCOLUMN lvCol; + LVITEM lvItem; + JABBER_LIST_ITEM *item; + + switch ( msg ) { + case WM_INITDIALOG: + // lParam is the initial conference server ( if any ) + SendMessage( hwndDlg, WM_SETICON, ICON_BIG, ( LPARAM )LoadIcon( hInst, MAKEINTRESOURCE( IDI_GROUP )) ); + TranslateDialogDefault( hwndDlg ); + sortColumn = -1; + // Add columns + lv = GetDlgItem( hwndDlg, IDC_ROOM ); + lvCol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvCol.pszText = TranslateT( "JID" ); + lvCol.cx = 210; + lvCol.iSubItem = 0; + ListView_InsertColumn( lv, 0, &lvCol ); + lvCol.pszText = TranslateT( "Name" ); + lvCol.cx = 150; + lvCol.iSubItem = 1; + ListView_InsertColumn( lv, 1, &lvCol ); + lvCol.pszText = TranslateT( "Type" ); + lvCol.cx = 60; + lvCol.iSubItem = 2; + ListView_InsertColumn( lv, 2, &lvCol ); + if ( jabberOnline ) { + if (( TCHAR* )lParam != NULL ) { + SetDlgItemText( hwndDlg, IDC_SERVER, ( TCHAR* )lParam ); + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_DISCOROOMSERVER, JabberIqResultDiscoRoomItems ); + + XmlNodeIq iq( "get", iqId, ( TCHAR* )lParam ); + XmlNode* query = iq.addQuery( "http://jabber.org/protocol/disco#items" ); + JabberSend( jabberThreadInfo->s, iq ); + } + else { + for ( int i=0; i < GC_SERVER_LIST_SIZE; i++ ) { + char text[100]; + mir_snprintf( text, sizeof( text ), "GcServerLast%d", i ); + DBVARIANT dbv; + if ( !JGetStringT( NULL, text, &dbv )) { + SendDlgItemMessage( hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, ( LPARAM )dbv.ptszVal ); + JFreeVariant( &dbv ); + } } } + } + else EnableWindow( GetDlgItem( hwndDlg, IDC_JOIN ), FALSE ); + return TRUE; + + case WM_JABBER_ACTIVATE: + // lParam = server from which agent information is obtained + if ( lParam ) + SetDlgItemText( hwndDlg, IDC_SERVER, ( TCHAR* )lParam ); + ListView_DeleteAllItems( GetDlgItem( hwndDlg, IDC_ROOM )); + EnableWindow( GetDlgItem( hwndDlg, IDC_BROWSE ), FALSE ); + return TRUE; + + case WM_JABBER_REFRESH: + // lParam = server from which agent information is obtained + { + int i; + TCHAR szBuffer[256]; + char text[128]; + + if ( lParam ){ + _tcsncpy( szBuffer, ( TCHAR* )lParam, SIZEOF( szBuffer )); + for ( i=0; i<GC_SERVER_LIST_SIZE; i++ ) { + mir_snprintf( text, SIZEOF( text ), "GcServerLast%d", i ); + DBVARIANT dbv; + if ( !JGetStringT( NULL, text, &dbv )) { + JSetStringT( NULL, text, szBuffer ); + if ( !_tcsicmp( dbv.ptszVal, ( TCHAR* )lParam )) { + JFreeVariant( &dbv ); + break; + } + _tcsncpy( szBuffer, dbv.ptszVal, SIZEOF( szBuffer )); + JFreeVariant( &dbv ); + } + else { + JSetStringT( NULL, text, szBuffer ); + break; + } } + + SendDlgItemMessage( hwndDlg, IDC_SERVER, CB_RESETCONTENT, 0, 0 ); + for ( i=0; i<GC_SERVER_LIST_SIZE; i++ ) { + mir_snprintf( text, SIZEOF( text ), "GcServerLast%d", i ); + DBVARIANT dbv; + if ( !JGetStringT( NULL, text, &dbv )) { + SendDlgItemMessage( hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, ( LPARAM )dbv.ptszVal ); + JFreeVariant( &dbv ); + } + } + SetDlgItemText( hwndDlg, IDC_SERVER, ( TCHAR* )lParam ); + } + i = 0; + lv = GetDlgItem( hwndDlg, IDC_ROOM ); + ListView_DeleteAllItems( lv ); + LVITEM lvItem; + lvItem.iItem = 0; + while (( i=JabberListFindNext( LIST_ROOM, i )) >= 0 ) { + if (( item=JabberListGetItemPtrFromIndex( i )) != NULL ) { + lvItem.mask = LVIF_PARAM | LVIF_TEXT; + lvItem.iSubItem = 0; + _tcsncpy( szBuffer, item->jid, SIZEOF(szBuffer)); + szBuffer[ SIZEOF(szBuffer)-1 ] = 0; + lvItem.lParam = ( LPARAM )item->jid; + lvItem.pszText = szBuffer; + ListView_InsertItem( lv, &lvItem ); + + lvItem.mask = LVIF_TEXT; + lvItem.iSubItem = 1; + lvItem.pszText = item->name; + ListView_SetItem( lv, &lvItem ); + + lvItem.iSubItem = 2; + lvItem.pszText = item->type; + ListView_SetItem( lv, &lvItem ); + lvItem.iItem++; + } + i++; + } + EnableWindow( GetDlgItem( hwndDlg, IDC_BROWSE ), TRUE ); + } + return TRUE; + case WM_JABBER_CHECK_ONLINE: + { + TCHAR text[128]; + if ( jabberOnline ) { + EnableWindow( GetDlgItem( hwndDlg, IDC_JOIN ), TRUE ); + GetDlgItemText( hwndDlg, IDC_SERVER, text, SIZEOF( text )); + EnableWindow( GetDlgItem( hwndDlg, IDC_BROWSE ), ( text[0]!='\0' )); + } + else { + EnableWindow( GetDlgItem( hwndDlg, IDC_JOIN ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_BROWSE ), FALSE ); + SetDlgItemTextA( hwndDlg, IDC_SERVER, "" ); + lv = GetDlgItem( hwndDlg, IDC_ROOM ); + ListView_DeleteAllItems( lv ); + } + break; + } + case WM_NOTIFY: + switch ( wParam ) { + case IDC_ROOM: + switch (( ( LPNMHDR )lParam )->code ) { + case LVN_COLUMNCLICK: + { + LPNMLISTVIEW pnmlv = ( LPNMLISTVIEW ) lParam; + + if ( pnmlv->iSubItem>=0 && pnmlv->iSubItem<=1 ) { + if ( pnmlv->iSubItem == sortColumn ) + sortAscending = !sortAscending; + else { + sortAscending = TRUE; + sortColumn = pnmlv->iSubItem; + } + ListView_SortItems( GetDlgItem( hwndDlg, IDC_ROOM ), GroupchatCompare, sortColumn ); + } + } + break; + } + break; + } + break; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case WM_JABBER_JOIN: + if ( jabberChatDllPresent ) { + lv = GetDlgItem( hwndDlg, IDC_ROOM ); + if (( lvItem.iItem=ListView_GetNextItem( lv, -1, LVNI_SELECTED )) >= 0 ) { + lvItem.iSubItem = 0; + lvItem.mask = LVIF_PARAM; + ListView_GetItem( lv, &lvItem ); + ListView_SetItemState( lv, lvItem.iItem, 0, LVIS_SELECTED ); // Unselect the item + DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_GROUPCHAT_JOIN ), hwndDlg, JabberGroupchatJoinDlgProc, ( LPARAM )lvItem.lParam ); + } + else { + TCHAR text[128]; + GetDlgItemText( hwndDlg, IDC_SERVER, text, SIZEOF( text )); + DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_GROUPCHAT_JOIN ), hwndDlg, JabberGroupchatJoinDlgProc, ( LPARAM )text ); + } } + else JabberChatDllError(); + return TRUE; + + case WM_JABBER_ADD_TO_ROSTER: + lv = GetDlgItem( hwndDlg, IDC_ROOM ); + if (( lvItem.iItem=ListView_GetNextItem( lv, -1, LVNI_SELECTED )) >= 0 ) { + lvItem.iSubItem = 0; + lvItem.mask = LVIF_PARAM; + ListView_GetItem( lv, &lvItem ); + TCHAR* jid = ( TCHAR* )lvItem.lParam; + { GCSESSION gcw = {0}; + gcw.cbSize = sizeof(GCSESSION); + gcw.iType = GCW_CHATROOM; + gcw.pszID = t2a(jid); + gcw.pszModule = jabberProtoName; + gcw.pszName = NEWSTR_ALLOCA(gcw.pszID); + char* p = ( char* )strchr( gcw.pszName, '@' ); + if ( p != NULL ) + *p = 0; + CallService( MS_GC_NEWSESSION, 0, ( LPARAM )&gcw ); + mir_free((void*)gcw.pszID); + } + { XmlNodeIq iq( "set" ); + XmlNode* query = iq.addQuery( "jabber:iq:roster" ); + XmlNode* item = query->addChild( "item" ); item->addAttr( "jid", jid ); + JabberSend( jabberThreadInfo->s, iq ); + } + { XmlNode p( "presence" ); p.addAttr( "to", jid ); p.addAttr( "type", "subscribe" ); + JabberSend( jabberThreadInfo->s, p ); + } } + break; + + case IDC_SERVER: + { TCHAR text[ 128 ]; + GetDlgItemText( hwndDlg, IDC_SERVER, text, SIZEOF( text )); + if ( jabberOnline && ( text[0] || HIWORD( wParam )==CBN_SELCHANGE )) + EnableWindow( GetDlgItem( hwndDlg, IDC_BROWSE ), TRUE ); + break; + } + case IDC_BROWSE: + { TCHAR text[ 128 ]; + GetDlgItemText( hwndDlg, IDC_SERVER, text, SIZEOF( text )); + if ( jabberOnline && text[0] ) { + EnableWindow( GetDlgItem( hwndDlg, IDC_BROWSE ), FALSE ); + ListView_DeleteAllItems( GetDlgItem( hwndDlg, IDC_ROOM )); + GetDlgItemText( hwndDlg, IDC_SERVER, text, SIZEOF( text )); + + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_DISCOROOMSERVER, JabberIqResultDiscoRoomItems ); + + XmlNodeIq iq( "get", iqId, text ); + XmlNode* query = iq.addQuery( "http://jabber.org/protocol/disco#items" ); + JabberSend( jabberThreadInfo->s, iq ); + } + return TRUE; + } + case IDCLOSE: + DestroyWindow( hwndDlg ); + return TRUE; + } + break; + case WM_CONTEXTMENU: + if (( HWND )wParam == GetDlgItem( hwndDlg, IDC_ROOM )) { + HMENU hMenu = CreatePopupMenu(); + AppendMenu( hMenu, MF_STRING, WM_JABBER_JOIN, TranslateT( "Join" )); + AppendMenu( hMenu, MF_STRING, WM_JABBER_ADD_TO_ROSTER, TranslateT( "Add to roster" )); + TrackPopupMenu( hMenu, TPM_LEFTALIGN | TPM_NONOTIFY, LOWORD(lParam), HIWORD(lParam), 0, hwndDlg, 0 ); + ::DestroyMenu( hMenu ); + return TRUE; + } + break; + case WM_CLOSE: + DestroyWindow( hwndDlg ); + break; + case WM_DESTROY: + hwndJabberGroupchat = NULL; + break; + } + return FALSE; +} + +void JabberGroupchatJoinRoom( const TCHAR* server, const TCHAR* room, const TCHAR* nick, const TCHAR* password ) +{ + TCHAR text[128]; + mir_sntprintf( text, SIZEOF(text), _T("%s@%s/%s"), room, server, nick ); + + JABBER_LIST_ITEM* item = JabberListAdd( LIST_CHATROOM, text ); + replaceStr( item->nick, nick ); + + int status = ( jabberStatus == ID_STATUS_INVISIBLE ) ? ID_STATUS_ONLINE : jabberStatus; + XmlNode* x = new XmlNode( "x" ); x->addAttr( "xmlns", "http://jabber.org/protocol/muc" ); + if ( password && password[0]!='\0' ) + x->addChild( "password", password ); + JabberSendPresenceTo( status, text, x ); +} + +static BOOL CALLBACK JabberGroupchatJoinDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + TCHAR text[128]; + TCHAR* p; + + switch ( msg ) { + case WM_INITDIALOG: + { + // lParam is the room JID ( room@server ) in UTF-8 + hwndJabberJoinGroupchat = hwndDlg; + TranslateDialogDefault( hwndDlg ); + if ( lParam ){ + _tcsncpy( text, ( TCHAR* )lParam, SIZEOF( text )); + if (( p = _tcschr( text, '@' )) != NULL ) { + *p = '\0'; + // Need to save room name in UTF-8 in case the room codepage is different + // from the local code page + TCHAR* room = mir_tstrdup( text ); + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG ) room ); + SetDlgItemText( hwndDlg, IDC_ROOM, room ); + SetDlgItemText( hwndDlg, IDC_SERVER, p+1 ); + } + else SetDlgItemText( hwndDlg, IDC_SERVER, text ); + } + + DBVARIANT dbv; + if ( !JGetStringT( NULL, "Nick", &dbv )) { + SetDlgItemText( hwndDlg, IDC_NICK, dbv.ptszVal ); + JFreeVariant( &dbv ); + } + else { + TCHAR* nick = JabberNickFromJID( jabberJID ); + SetDlgItemText( hwndDlg, IDC_NICK, nick ); + mir_free( nick ); + } } + return TRUE; + + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_ROOM: + if (( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE ) { + // Change in IDC_ROOM field is detected, + // invalidate the saved UTF-8 room name if any + char* str = ( char* )GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( str != NULL ) { + mir_free( str ); + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG ) NULL ); + } } + break; + case IDOK: + { + GetDlgItemText( hwndDlg, IDC_SERVER, text, SIZEOF( text )); + TCHAR* server = NEWTSTR_ALLOCA( text ), *room; + + if (( room=( TCHAR* )GetWindowLong( hwndDlg, GWL_USERDATA )) != NULL ) + room = NEWTSTR_ALLOCA( room ); + else { + GetDlgItemText( hwndDlg, IDC_ROOM, text, SIZEOF( text )); + room = NEWTSTR_ALLOCA( text ); + } + + GetDlgItemText( hwndDlg, IDC_NICK, text, SIZEOF( text )); + TCHAR* nick = NEWTSTR_ALLOCA( text ); + + GetDlgItemText( hwndDlg, IDC_PASSWORD, text, SIZEOF( text )); + TCHAR* password = NEWTSTR_ALLOCA( text ); + JabberGroupchatJoinRoom( server, room, nick, password ); + } + // fall through + case IDCANCEL: + EndDialog( hwndDlg, 0 ); + break; + } + break; + case WM_JABBER_CHECK_ONLINE: + if ( !jabberOnline ) EndDialog( hwndDlg, 0 ); + break; + case WM_DESTROY: + { + char* str = ( char* )GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( str != NULL ) + mir_free( str ); + + hwndJabberJoinGroupchat = NULL; + } + break; + } + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberGroupchatProcessPresence - handles the group chat presence packet + +static int sttGetStatusCode( XmlNode* node ) +{ + XmlNode* statusNode = JabberXmlGetChild( node, "status" ); + if ( statusNode == NULL ) + return -1; + + TCHAR* statusCode = JabberXmlGetAttrValue( statusNode, "code" ); + if ( statusCode == NULL ) + return -1; + + return _ttol( statusCode ); +} + +void sttRenameParticipantNick( JABBER_LIST_ITEM* item, TCHAR* oldNick, XmlNode *itemNode ) +{ + TCHAR* newNick = JabberXmlGetAttrValue( itemNode, "nick" ); + if ( newNick == NULL ) + return; + + for ( int i=0; i < item->resourceCount; i++ ) { + JABBER_RESOURCE_STATUS& RS = item->resource[i]; + if ( !lstrcmp( RS.resourceName, oldNick )) { + replaceStr( RS.resourceName, newNick ); + + if ( !lstrcmp( item->nick, oldNick )) { + replaceStr( item->nick, newNick ); + + HANDLE hContact = JabberHContactFromJID( item->jid ); + if ( hContact != NULL ) + JSetStringT( hContact, "MyNick", newNick ); + } + + #if defined( _UNICODE ) + char* jid = u2a( item->jid ); + char* dispNick = u2a( newNick ); + char* dispOldNick = u2a( oldNick ); + #else + char* jid = item->jid; + char* dispNick = NEWSTR_ALLOCA( newNick ); + char* dispOldNick = NEWSTR_ALLOCA( oldNick ); + #endif + + GCDEST gcd = { jabberProtoName, jid, GC_EVENT_CHUID }; + GCEVENT gce = {0}; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + gce.pszNick = dispOldNick; + gce.pszText = dispNick; + gce.time = time(0); + JCallService( MS_GC_EVENT, NULL, ( LPARAM )&gce ); + + gcd.iType = GC_EVENT_NICK; + gce.pszNick = dispOldNick; + gce.pszUID = dispNick; + gce.pszText = dispNick; + JCallService( MS_GC_EVENT, NULL, ( LPARAM )&gce ); + + #if defined( _UNICODE ) + mir_free( jid ); + mir_free( dispNick ); + mir_free( dispOldNick ); + #endif + break; +} } } + +void JabberGroupchatProcessPresence( XmlNode *node, void *userdata ) +{ + struct ThreadData *info; + XmlNode *showNode, *statusNode, *errorNode, *itemNode, *n; + TCHAR* from; + int status, newRes; + int i; + BOOL roomCreated; + + if ( !node || !node->name || strcmp( node->name, "presence" )) return; + if (( info=( struct ThreadData * ) userdata ) == NULL ) return; + if (( from=JabberXmlGetAttrValue( node, "from" )) == NULL ) return; + + TCHAR* nick = _tcschr( from, '/' ); + if ( nick == NULL || nick[1] == '\0' ) + return; + nick++; + + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_CHATROOM, from ); + if ( item == NULL ) + return; + + XmlNode* xNode = JabberXmlGetChildWithGivenAttrValue( node, "x", "xmlns", _T("http://jabber.org/protocol/muc#user")); + + TCHAR* type = JabberXmlGetAttrValue( node, "type" ); + if ( type == NULL || !_tcscmp( type, _T("available"))) { + TCHAR* room = JabberNickFromJID( from ); + if ( room == NULL ) + return; + + JabberGcLogCreate( item ); + + // Update status of room participant + status = ID_STATUS_ONLINE; + if (( showNode=JabberXmlGetChild( node, "show" )) != NULL ) { + if ( showNode->text != NULL ) { + if ( !_tcscmp( showNode->text , _T("away"))) status = ID_STATUS_AWAY; + else if ( !_tcscmp( showNode->text , _T("xa"))) status = ID_STATUS_NA; + else if ( !_tcscmp( showNode->text , _T("dnd"))) status = ID_STATUS_DND; + else if ( !_tcscmp( showNode->text , _T("chat"))) status = ID_STATUS_FREECHAT; + } } + + TCHAR* str; + if (( statusNode=JabberXmlGetChild( node, "status" ))!=NULL && statusNode->text!=NULL ) + str = statusNode->text; + else + str = NULL; + newRes = ( JabberListAddResource( LIST_CHATROOM, from, status, str ) == 0 ) ? 0 : GC_EVENT_JOIN; + + roomCreated = FALSE; + + // Check additional MUC info for this user + if ( xNode != NULL ) { + if (( itemNode=JabberXmlGetChild( xNode, "item" )) != NULL ) { + JABBER_RESOURCE_STATUS* r = item->resource; + for ( i=0; i<item->resourceCount && _tcscmp( r->resourceName, nick ); i++, r++ ); + if ( i < item->resourceCount ) { + if (( str=JabberXmlGetAttrValue( itemNode, "affiliation" )) != NULL ) { + if ( !_tcscmp( str, _T("owner"))) r->affiliation = AFFILIATION_OWNER; + else if ( !_tcscmp( str, _T("admin"))) r->affiliation = AFFILIATION_ADMIN; + else if ( !_tcscmp( str, _T("member"))) r->affiliation = AFFILIATION_MEMBER; + else if ( !_tcscmp( str, _T("outcast"))) r->affiliation = AFFILIATION_OUTCAST; + } + if (( str=JabberXmlGetAttrValue( itemNode, "role" )) != NULL ) { + JABBER_GC_ROLE newRole = r->role; + + if ( !_tcscmp( str, _T("moderator"))) newRole = ROLE_MODERATOR; + else if ( !_tcscmp( str, _T("participant"))) newRole = ROLE_PARTICIPANT; + else if ( !_tcscmp( str, _T("visitor"))) newRole = ROLE_VISITOR; + else newRole = ROLE_NONE; + + if ( newRole != r->role && r->role != ROLE_NONE ) { + JabberGcLogUpdateMemberStatus( item, nick, GC_EVENT_REMOVESTATUS, NULL ); + newRes = GC_EVENT_ADDSTATUS; + } + r->role = newRole; + } } } + + if ( sttGetStatusCode( xNode ) == 201 ) + roomCreated = TRUE; + } + + // Update groupchat log window + JabberGcLogUpdateMemberStatus( item, nick, newRes, NULL ); + + HANDLE hContact = JabberHContactFromJID( from ); + if ( hContact != NULL ) + JSetWord( hContact, "Status", status ); + + // Update room status + //if ( item->status != ID_STATUS_ONLINE ) { + // item->status = ID_STATUS_ONLINE; + // JSetWord( hContact, "Status", ( WORD )ID_STATUS_ONLINE ); + // JabberLog( "Room %s online", from ); + //} + + // Check <created/> + if ( roomCreated || + (( n=JabberXmlGetChild( node, "created" ))!=NULL && + ( str=JabberXmlGetAttrValue( n, "xmlns" ))!=NULL && + !_tcscmp( str, _T("http://jabber.org/protocol/muc#owner"))) ) { + // A new room just created by me + // Request room config + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultGetMuc ); + + XmlNodeIq iq( "get", iqId, item->jid ); + XmlNode* query = iq.addQuery( xmlnsOwner ); + JabberSend( jabberThreadInfo->s, iq ); + } + + mir_free( room ); + } + else if ( !lstrcmp( type, _T("unavailable"))) { + if ( xNode != NULL && item->nick != NULL ) { + itemNode = JabberXmlGetChild( xNode, "item" ); + XmlNode* reasonNode = JabberXmlGetChild( itemNode, "reason" ); + + if ( !lstrcmp( nick, item->nick )) { + int iStatus = sttGetStatusCode( xNode ); + switch( iStatus ) { + case 301: case 307: + JabberGcQuit( item, iStatus, reasonNode ); + break; + + case 303: + sttRenameParticipantNick( item, nick, itemNode ); + return; + } } + else { + switch( sttGetStatusCode( xNode )) { + case 303: + sttRenameParticipantNick( item, nick, itemNode ); + return; + + case 307: + JabberListRemoveResource( LIST_CHATROOM, from ); + JabberGcLogUpdateMemberStatus( item, nick, GC_EVENT_KICK, reasonNode ); + return; + } } } + + JabberListRemoveResource( LIST_CHATROOM, from ); + JabberGcLogUpdateMemberStatus( item, nick, GC_EVENT_PART, NULL ); + } + else if ( !lstrcmp( type, _T("error"))) { + errorNode = JabberXmlGetChild( node, "error" ); + TCHAR* str = JabberErrorMsg( errorNode ); + MessageBox( NULL, str, TranslateT( "Jabber Error Message" ), MB_OK|MB_SETFOREGROUND ); + //JabberListRemoveResource( LIST_CHATROOM, from ); + JabberListRemove( LIST_CHATROOM, from ); + mir_free( str ); +} } + +void strdel( char* parBuffer, int len ) +{ + char* p; + for ( p = parBuffer+len; *p != 0; p++ ) + p[ -len ] = *p; + + p[ -len ] = '\0'; +} + +void JabberGroupchatProcessMessage( XmlNode *node, void *userdata ) +{ + struct ThreadData *info; + XmlNode *n, *xNode; + TCHAR* from, *type, *p, *nick, *msgText; + JABBER_LIST_ITEM *item; + + if ( !node->name || strcmp( node->name, "message" )) return; + if (( info=( struct ThreadData * ) userdata ) == NULL ) return; + if (( from = JabberXmlGetAttrValue( node, "from" )) == NULL ) return; + if (( item = JabberListGetItemPtr( LIST_CHATROOM, from )) == NULL ) return; + + if (( type = JabberXmlGetAttrValue( node, "type" )) == NULL ) return; + if ( !lstrcmp( type, _T("error"))) + return; + + #if defined( _UNICODE ) + char* jid = u2a( item->jid ); + #else + char* jid = item->jid; + #endif + GCDEST gcd = { jabberProtoName, jid, 0 }; + + if (( n = JabberXmlGetChild( node, "subject" )) != NULL ) { + if ( n->text == NULL || n->text[0] == '\0' ) + return; + + msgText = n->text; + + gcd.iType = GC_EVENT_TOPIC; + + if ( from != NULL ) { + nick = _tcschr( from, '/' ); + if ( nick == NULL || nick[1] == '\0' ) + nick = NULL; + else + nick++; + } + else nick = NULL; + } + else { + if (( n = JabberXmlGetChild( node, "body" )) == NULL ) return; + if ( n->text == NULL ) + return; + + nick = _tcschr( from, '/' ); + if ( nick == NULL || nick[1] == '\0' ) + return; + nick++; + + msgText = n->text; + + if ( _tcsncmp( msgText, _T("/me "), 4 ) == 0 && _tcslen( msgText ) > 4 ) { + msgText += 4; + gcd.iType = GC_EVENT_ACTION; + } + else gcd.iType = GC_EVENT_MESSAGE; + } + + JabberGcLogCreate( item ); + + time_t msgTime = 0; + BOOL delivered = FALSE; + for ( int i = 1; ( xNode=JabberXmlGetNthChild( node, "x", i )) != NULL; i++ ) + if (( p=JabberXmlGetAttrValue( xNode, "xmlns" )) != NULL ) + if ( !_tcscmp( p, _T("jabber:x:delay")) && msgTime==0 ) + if (( p=JabberXmlGetAttrValue( xNode, "stamp" )) != NULL ) + msgTime = JabberIsoToUnixTime( p ); + + time_t now = time( NULL ); + if ( msgTime == 0 || msgTime > now ) + msgTime = now; + + #if defined( _UNICODE ) + char* dispNick = u2a( nick ); + char* dispMsg = u2a( msgText ); + #else + char* dispNick = nick; + char* dispMsg = msgText; + #endif + + GCEVENT gce = {0}; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + gce.pszUID = dispNick; + gce.pszNick = dispNick; + gce.bAddToLog = TRUE; + gce.time = msgTime; + gce.pszText = EscapeChatTags( dispMsg ); + gce.bIsMe = lstrcmp( nick, item->nick ) == 0; + JCallService(MS_GC_EVENT, NULL, (LPARAM)&gce); + + item->bChatActive = 2; + + if ( gcd.iType == GC_EVENT_TOPIC ) { + gce.bAddToLog = FALSE; + gcd.iType = GC_EVENT_SETSBTEXT; + JCallService(MS_GC_EVENT, NULL, (LPARAM)&gce); + } + + #if defined( _UNICODE ) + mir_free( dispNick ); + mir_free( dispMsg ); + mir_free( jid ); + #endif + mir_free( (void*)gce.pszText ); // Since we processed msgText and created a new string +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Accepting groupchat invitations + +typedef struct { + TCHAR* roomJid; + TCHAR* from; + TCHAR* reason; + TCHAR* password; +} + JABBER_GROUPCHAT_INVITE_INFO; + +static void JabberAcceptGroupchatInvite( TCHAR* roomJid, TCHAR* reason, TCHAR* password ) +{ + TCHAR room[256], *server, *p; + _tcsncpy( room, roomJid, SIZEOF( room )); + p = _tcstok( room, _T( "@" )); + server = _tcstok( NULL, _T( "@" )); + JabberGroupchatJoinRoom( server, p, reason, password ); +} + +static BOOL CALLBACK JabberGroupchatInviteAcceptDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + { + JABBER_GROUPCHAT_INVITE_INFO *inviteInfo = ( JABBER_GROUPCHAT_INVITE_INFO * ) lParam; + + TranslateDialogDefault( hwndDlg ); + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG ) inviteInfo ); + SetDlgItemText( hwndDlg, IDC_FROM, inviteInfo->from ); + SetDlgItemText( hwndDlg, IDC_ROOM, inviteInfo->roomJid ); + + if ( inviteInfo->reason != NULL ) + SetDlgItemText( hwndDlg, IDC_REASON, inviteInfo->reason ); + + TCHAR* myNick = JabberNickFromJID( jabberJID ); + SetDlgItemText( hwndDlg, IDC_NICK, myNick ); + mir_free( myNick ); + + SendMessage( hwndDlg, WM_SETICON, ICON_BIG, ( LPARAM )LoadIcon( hInst, MAKEINTRESOURCE( IDI_GROUP )) ); + } + return TRUE; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_ACCEPT: + { + JABBER_GROUPCHAT_INVITE_INFO *inviteInfo = ( JABBER_GROUPCHAT_INVITE_INFO * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + TCHAR text[128]; + GetDlgItemText( hwndDlg, IDC_NICK, text, SIZEOF( text )); + JabberAcceptGroupchatInvite( inviteInfo->roomJid, text, inviteInfo->password ); + } + // Fall through + case IDCANCEL: + case IDCLOSE: + EndDialog( hwndDlg, 0 ); + return TRUE; + } + break; + case WM_CLOSE: + EndDialog( hwndDlg, 0 ); + break; + } + + return FALSE; +} + +static void __cdecl JabberGroupchatInviteAcceptThread( JABBER_GROUPCHAT_INVITE_INFO *inviteInfo ) +{ + DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_GROUPCHAT_INVITE_ACCEPT ), NULL, JabberGroupchatInviteAcceptDlgProc, ( LPARAM )inviteInfo ); + mir_free( inviteInfo->roomJid ); + mir_free( inviteInfo->from ); + mir_free( inviteInfo->reason ); + mir_free( inviteInfo->password ); + mir_free( inviteInfo ); +} + +void JabberGroupchatProcessInvite( TCHAR* roomJid, TCHAR* from, TCHAR* reason, TCHAR* password ) +{ + if ( roomJid == NULL ) + return; + + if ( JGetByte( "AutoAcceptMUC", FALSE ) == FALSE ) { + JABBER_GROUPCHAT_INVITE_INFO* inviteInfo = ( JABBER_GROUPCHAT_INVITE_INFO * ) mir_alloc( sizeof( JABBER_GROUPCHAT_INVITE_INFO )); + inviteInfo->roomJid = mir_tstrdup( roomJid ); + inviteInfo->from = mir_tstrdup( from ); + inviteInfo->reason = mir_tstrdup( reason ); + inviteInfo->password = mir_tstrdup( password ); + JabberForkThread(( JABBER_THREAD_FUNC )JabberGroupchatInviteAcceptThread, 0, ( void* )inviteInfo ); + } + else { + TCHAR* myNick = JabberNickFromJID( jabberJID ); + JabberAcceptGroupchatInvite( roomJid, myNick, password ); + mir_free( myNick ); +} } diff --git a/miranda-wine/protocols/JabberG/jabber_iq.cpp b/miranda-wine/protocols/JabberG/jabber_iq.cpp new file mode 100644 index 0000000..4b0d90d --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_iq.cpp @@ -0,0 +1,173 @@ +/* + +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_iq.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "jabber_iq.h" +#include "jabber_xmlns.h" + +static JABBER_IQ_XMLNS_FUNC jabberXmlns[] = { + { _T("http://jabber.org/protocol/disco"), JabberXmlnsDisco, TRUE }, + { _T("jabber:iq:browse"), JabberXmlnsBrowse, FALSE } +}; + +typedef struct { + int iqId; // id to match IQ get/set with IQ result + JABBER_IQ_PROCID procId; // must be unique in the list, except for IQ_PROC_NONE which can have multiple entries + JABBER_IQ_PFUNC func; // callback function + time_t requestTime; // time the request was sent, used to remove relinquent entries +} JABBER_IQ_FUNC; + +static CRITICAL_SECTION csIqList; +static JABBER_IQ_FUNC *iqList; +static int iqCount; +static int iqAlloced; + +void JabberIqInit() +{ + InitializeCriticalSection( &csIqList ); + iqList = NULL; + iqCount = 0; + iqAlloced = 0; +} + +void JabberIqUninit() +{ + if ( iqList ) mir_free( iqList ); + iqList = NULL; + iqCount = 0; + iqAlloced = 0; + DeleteCriticalSection( &csIqList ); +} + +static void JabberIqRemove( int index ) +{ + EnterCriticalSection( &csIqList ); + if ( index>=0 && index<iqCount ) { + memmove( iqList+index, iqList+index+1, sizeof( JABBER_IQ_FUNC )*( iqCount-index-1 )); + iqCount--; + } + LeaveCriticalSection( &csIqList ); +} + +static void JabberIqExpire() +{ + int i; + time_t expire; + + EnterCriticalSection( &csIqList ); + expire = time( NULL ) - 120; // 2 minute + i = 0; + while ( i < iqCount ) { + if ( iqList[i].requestTime < expire ) + JabberIqRemove( i ); + else + i++; + } + LeaveCriticalSection( &csIqList ); +} + +JABBER_IQ_PFUNC JabberIqFetchFunc( int iqId ) +{ + int i; + JABBER_IQ_PFUNC res; + + EnterCriticalSection( &csIqList ); + JabberIqExpire(); +#ifdef _DEBUG + for ( i=0; i<iqCount; i++ ) + JabberLog( " %04d : %02d : 0x%x", iqList[i].iqId, iqList[i].procId, iqList[i].func ); +#endif + for ( i=0; i<iqCount && iqList[i].iqId!=iqId; i++ ); + if ( i < iqCount ) { + res = iqList[i].func; + JabberIqRemove( i ); + } + else { + res = ( JABBER_IQ_PFUNC ) NULL; + } + LeaveCriticalSection( &csIqList ); + return res; +} + +void JabberIqAdd( unsigned int iqId, JABBER_IQ_PROCID procId, JABBER_IQ_PFUNC func ) +{ + int i; + + EnterCriticalSection( &csIqList ); + JabberLog( "IqAdd id=%d, proc=%d, func=0x%x", iqId, procId, func ); + if ( procId == IQ_PROC_NONE ) + i = iqCount; + else + for ( i=0; i<iqCount && iqList[i].procId!=procId; i++ ); + + if ( i>=iqCount && iqCount>=iqAlloced ) { + iqAlloced = iqCount + 8; + iqList = ( JABBER_IQ_FUNC * )mir_realloc( iqList, sizeof( JABBER_IQ_FUNC )*iqAlloced ); + } + + if ( iqList != NULL ) { + iqList[i].iqId = iqId; + iqList[i].procId = procId; + iqList[i].func = func; + iqList[i].requestTime = time( NULL ); + if ( i == iqCount ) iqCount++; + } + LeaveCriticalSection( &csIqList ); +} + +JABBER_IQ_PFUNC JabberIqFetchXmlnsFunc( TCHAR* xmlns ) +{ + unsigned int len, count, i; + TCHAR* p, *q; + + if ( xmlns == NULL ) + return NULL; + + p = _tcsrchr( xmlns, '/' ); + q = _tcsrchr( xmlns, '#' ); + if ( p!=NULL && q!=NULL && q>p ) + len = q - xmlns; + else + len = _tcslen( xmlns ); + + count = sizeof( jabberXmlns ) / sizeof( jabberXmlns[0] ); + for ( i=0; i<count; i++ ) { + if ( jabberXmlns[i].allowSubNs ) { + if ( _tcslen( jabberXmlns[i].xmlns ) == len && !_tcsncmp( jabberXmlns[i].xmlns, xmlns, len )) + break; + } + else { + if ( !_tcscmp( jabberXmlns[i].xmlns, xmlns )) + break; + } + } + + if ( i < count ) + return jabberXmlns[i].func; + + return NULL; +} diff --git a/miranda-wine/protocols/JabberG/jabber_iq.h b/miranda-wine/protocols/JabberG/jabber_iq.h new file mode 100644 index 0000000..35a95e5 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_iq.h @@ -0,0 +1,87 @@ +/* + +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_iq.h,v $ +Revision : $Revision: 3703 $ +Last change on : $Date: 2006-09-05 17:54:42 +0400 (Втр, 05 Сен 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#ifndef _JABBER_IQ_H_ +#define _JABBER_IQ_H_ + +#include "jabber_xml.h" + +typedef enum { + IQ_PROC_NONE, + IQ_PROC_GETAGENTS, + IQ_PROC_GETREGISTER, + IQ_PROC_SETREGISTER, + IQ_PROC_GETVCARD, + IQ_PROC_SETVCARD, + IQ_PROC_GETSEARCH, + IQ_PROC_BROWSEROOMS, + IQ_PROC_DISCOROOMSERVER, + IQ_PROC_DISCOAGENTS +} JABBER_IQ_PROCID; + +typedef void ( *JABBER_IQ_PFUNC )( XmlNode *iqNode, void *usedata ); + +typedef struct { + TCHAR* xmlns; + JABBER_IQ_PFUNC func; + BOOL allowSubNs; // e.g. #info in disco#info +} JABBER_IQ_XMLNS_FUNC; + +void JabberIqInit(); +void JabberIqUninit(); +JABBER_IQ_PFUNC JabberIqFetchFunc( int iqId ); +void JabberIqAdd( unsigned int iqId, JABBER_IQ_PROCID procId, JABBER_IQ_PFUNC func ); +JABBER_IQ_PFUNC JabberIqFetchXmlnsFunc( TCHAR* xmlns ); + +void JabberIqResultBind( XmlNode *iqNode, void *userdata ); +void JabberIqResultBrowseRooms( XmlNode *iqNode, void *userdata ); +void JabberIqResultDiscoAgentInfo( XmlNode *iqNode, void *userdata ); +void JabberIqResultDiscoAgentItems( XmlNode *iqNode, void *userdata ); +void JabberIqResultDiscoClientInfo( XmlNode *iqNode, void *userdata ); +void JabberIqResultDiscoRoomItems( XmlNode *iqNode, void *userdata ); +void JabberIqResultExtSearch( XmlNode *iqNode, void *userdata ); +void JabberIqResultGetAgents( XmlNode *iqNode, void *userdata ); +void JabberIqResultGetAuth( XmlNode *iqNode, void *userdata ); +void JabberIqResultGetAvatar( XmlNode *iqNode, void *userdata ); +void JabberIqResultGetMuc( XmlNode *iqNode, void *userdata ); +void JabberIqResultGetRegister( XmlNode *iqNode, void *userdata ); +void JabberIqResultGetRoster( XmlNode *iqNode, void *userdata ); +void JabberIqResultGetVcard( XmlNode *iqNode, void *userdata ); +void JabberIqResultMucGetAdminList( XmlNode *iqNode, void *userdata ); +void JabberIqResultMucGetBanList( XmlNode *iqNode, void *userdata ); +void JabberIqResultMucGetMemberList( XmlNode *iqNode, void *userdata ); +void JabberIqResultMucGetModeratorList( XmlNode *iqNode, void *userdata ); +void JabberIqResultMucGetOwnerList( XmlNode *iqNode, void *userdata ); +void JabberIqResultMucGetVoiceList( XmlNode *iqNode, void *userdata ); +void JabberIqResultSession( XmlNode *iqNode, void *userdata ); +void JabberIqResultSetAuth( XmlNode *iqNode, void *userdata ); +void JabberIqResultSetPassword( XmlNode *iqNode, void *userdata ); +void JabberIqResultSetRegister( XmlNode *iqNode, void *userdata ); +void JabberIqResultSetSearch( XmlNode *iqNode, void *userdata ); +void JabberIqResultSetVcard( XmlNode *iqNode, void *userdata ); + +#endif 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( "<iq/> 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, "</stream:stream>" ); + return; + } + + if ( JabberXmlGetChild( queryNode, "resource" ) != NULL ) + query->addChild( "resource", info->resource ); + + JabberSend( info->s, iq ); + } + else if ( !lstrcmp( type, _T("error"))) { + JabberSend( info->s, "</stream:stream>" ); + + 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( "<iq/> 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, "</stream:stream>" ); + 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, "</stream:stream>" ); + 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( "<iq/> 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( "<iq/> 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; i<queryNode->numChild; 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 <grouchat/> so we will + // also treat <service>public</service> 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( "<iq/> 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( "<iq/> 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( "<iq/> 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; i<vCardNode->numChild; 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 <EMAIL/> instead of <USERID/> + 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 <COUNTRY/> instead of <CTRY/> + 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 <COUNTRY/> instead of <CTRY/> + 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 <ORGUNIT/> 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( "<iq/> 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( "<iq/> 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; i<queryNode->numChild; 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( "<iq/> 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; i<queryNode->numChild; 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( "<iq/> 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( "<iq/> 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; i<queryNode->numChild; 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( "<iq/> 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 <identity/> 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; i<queryNode->numChild; 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( "<iq/> 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; i<queryNode->numChild; 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( "<iq/> 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 ); +} diff --git a/miranda-wine/protocols/JabberG/jabber_iqid_muc.cpp b/miranda-wine/protocols/JabberG/jabber_iqid_muc.cpp new file mode 100644 index 0000000..114931d --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_iqid_muc.cpp @@ -0,0 +1,531 @@ +/* + +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_muc.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "resource.h" +#include "jabber_list.h" +#include "jabber_iq.h" +#include <commctrl.h> + +void JabberAddMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* str ); +void JabberDeleteMucListItem( JABBER_MUC_JIDLIST_INFO* jidListInfo, TCHAR* str ); +BOOL JabberEnterString( TCHAR* result, size_t resultLen ); + +void JabberIqResultBrowseRooms( XmlNode *iqNode, void *userdata ) +{ + struct ThreadData *info = ( struct ThreadData * ) userdata; + XmlNode *confNode, *roomNode; + TCHAR* type, *category, *jid, *str; + JABBER_LIST_ITEM *item; + int i, j; + + // RECVED: room list + // ACTION: refresh groupchat room list dialog + JabberLog( "<iq/> iqIdBrowseRooms" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result"))) { + JabberListRemoveList( LIST_ROOM ); + for ( i=0; i<iqNode->numChild; i++ ) { + if (( confNode=iqNode->child[i] )!=NULL && confNode->name!=NULL ) { + if ( !strcmp( confNode->name, "item" )) { + if (( category=JabberXmlGetAttrValue( confNode, "category" ))!=NULL && !lstrcmp( category, _T("conference"))) { + for ( j=0; j<confNode->numChild; j++ ) { + if (( roomNode=confNode->child[j] )!=NULL && !strcmp( roomNode->name, "item" )) { + if (( category=JabberXmlGetAttrValue( roomNode, "category" ))!=NULL && !lstrcmp( category, _T("conference"))) { + if (( jid=JabberXmlGetAttrValue( roomNode, "jid" )) != NULL ) { + item = JabberListAdd( LIST_ROOM, jid ); + if (( str=JabberXmlGetAttrValue( roomNode, "name" )) != NULL ) + item->name = mir_tstrdup( str ); + if (( str=JabberXmlGetAttrValue( roomNode, "type" )) != NULL ) + item->type = mir_tstrdup( str ); + } } } } } + } + else if ( !strcmp( confNode->name, "conference" )) { + for ( j=0; j<confNode->numChild; j++ ) { + if (( roomNode=confNode->child[j] )!=NULL && !strcmp( roomNode->name, "conference" )) { + if (( jid=JabberXmlGetAttrValue( roomNode, "jid" )) != NULL ) { + item = JabberListAdd( LIST_ROOM, jid ); + if (( str=JabberXmlGetAttrValue( roomNode, "name" )) != NULL ) + item->name = mir_tstrdup( str ); + if (( str=JabberXmlGetAttrValue( roomNode, "type" )) != NULL ) + item->type = mir_tstrdup( str ); + } } } } } } + + if ( hwndJabberGroupchat != NULL ) { + if (( jid=JabberXmlGetAttrValue( iqNode, "from" )) != NULL ) + SendMessage( hwndJabberGroupchat, WM_JABBER_REFRESH, 0, ( LPARAM )jid ); + else + SendMessage( hwndJabberGroupchat, WM_JABBER_REFRESH, 0, ( LPARAM )info->server ); + } + } +} + +void JabberSetMucConfig( XmlNode* node, void *from ) +{ + if ( jabberThreadInfo && from ) { + XmlNodeIq iq( "set", NOID, ( TCHAR* )from ); + XmlNode* query = iq.addQuery( xmlnsOwner ); + query->addChild( node ); + JabberSend( jabberThreadInfo->s, iq ); +} } + +void JabberIqResultGetMuc( XmlNode *iqNode, void *userdata ) +{ + XmlNode *queryNode, *xNode; + TCHAR *type, *from, *str; + + // RECVED: room config form + // ACTION: show the form + JabberLog( "<iq/> iqIdGetMuc" ); + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( from=JabberXmlGetAttrValue( iqNode, "from" )) == NULL ) return; + + if ( !_tcscmp( type, _T("result"))) { + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) != NULL ) { + str = JabberXmlGetAttrValue( queryNode, "xmlns" ); + if ( !lstrcmp( str, _T("http://jabber.org/protocol/muc#owner" ))) { + if (( xNode=JabberXmlGetChild( queryNode, "x" )) != NULL ) { + str = JabberXmlGetAttrValue( xNode, "xmlns" ); + if ( !lstrcmp( str, _T("jabber:x:data" ))) + JabberFormCreateDialog( xNode, _T("Jabber Conference Room Configuration"), JabberSetMucConfig, mir_tstrdup( from )); +} } } } } + +void JabberIqResultDiscoRoomItems( XmlNode *iqNode, void *userdata ) +{ + struct ThreadData *info = ( struct ThreadData * ) userdata; + XmlNode *queryNode, *itemNode; + TCHAR* type, *jid, *from; + JABBER_LIST_ITEM *item; + int i; + int iqId; + + // RECVED: room list + // ACTION: refresh groupchat room list dialog + JabberLog( "<iq/> iqIdDiscoRoomItems" ); + 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 ) { + JabberListRemoveList( LIST_ROOM ); + for ( i=0; i<queryNode->numChild; i++ ) { + if (( itemNode=queryNode->child[i] )!=NULL && itemNode->name!=NULL && !strcmp( itemNode->name, "item" )) { + if (( jid=JabberXmlGetAttrValue( itemNode, "jid" )) != NULL ) { + item = JabberListAdd( LIST_ROOM, jid ); + item->name = mir_tstrdup( JabberXmlGetAttrValue( itemNode, "name" )); + } } } } + + if ( hwndJabberGroupchat != NULL ) { + if (( jid=JabberXmlGetAttrValue( iqNode, "from" )) != NULL ) + SendMessage( hwndJabberGroupchat, WM_JABBER_REFRESH, 0, ( LPARAM )jid ); + else + SendMessage( hwndJabberGroupchat, WM_JABBER_REFRESH, 0, ( LPARAM )info->server ); + } + } + else if ( !_tcscmp( type, _T("error"))) { + // disco is not supported, try browse + iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_BROWSEROOMS, JabberIqResultBrowseRooms ); + + XmlNodeIq iq( "get", iqId, from ); + XmlNode* query = iq.addQuery( "jabber:iq:browse" ); + JabberSend( jabberThreadInfo->s, iq ); +} } + +static BOOL CALLBACK JabberMucJidListDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch( msg ) { + case WM_INITDIALOG: + { + // lParam is ( JABBER_MUC_JIDLIST_INFO * ) + LVCOLUMN lvc; + RECT rc; + HWND hwndList; + + TranslateDialogDefault( hwndDlg ); + hwndList = GetDlgItem( hwndDlg, IDC_LIST ); + GetClientRect( hwndList, &rc ); + rc.right -= GetSystemMetrics( SM_CXVSCROLL ); + lvc.mask = LVCF_WIDTH; + lvc.cx = rc.right - 20; + ListView_InsertColumn( hwndList, 0, &lvc ); + lvc.cx = 20; + ListView_InsertColumn( hwndList, 1, &lvc ); + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, lParam ); + } + return TRUE; + case WM_JABBER_REFRESH: + { + // lParam is ( JABBER_MUC_JIDLIST_INFO * ) + JABBER_MUC_JIDLIST_INFO *jidListInfo; + XmlNode *iqNode, *queryNode, *itemNode; + TCHAR* from, *jid, *localFrom; + LVITEM lvi; + HWND hwndList; + int count, i; + TCHAR title[256]; + + // Clear current GWL_USERDATA, if any + jidListInfo = ( JABBER_MUC_JIDLIST_INFO * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( jidListInfo != NULL ) { + if ( jidListInfo->roomJid != NULL ) + mir_free( jidListInfo->roomJid ); + if ( jidListInfo->iqNode != NULL ) + delete jidListInfo->iqNode; + mir_free( jidListInfo ); + } + + // Clear current displayed list + hwndList = GetDlgItem( hwndDlg, IDC_LIST ); + count = ListView_GetItemCount( hwndList ); + lvi.mask = LVIF_PARAM; + lvi.iSubItem = 0; + for ( i=0; i<count; i++ ) { + lvi.iItem = i; + if ( ListView_GetItem( hwndList, &lvi ) == TRUE ) { + if ( lvi.lParam!=( LPARAM )( -1 ) && lvi.lParam!=( LPARAM )( NULL )) { + mir_free(( void * ) lvi.lParam ); + } + } + } + ListView_DeleteAllItems( hwndList ); + + // Set new GWL_USERDATA + jidListInfo = ( JABBER_MUC_JIDLIST_INFO * ) lParam; + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG ) jidListInfo ); + + // Populate displayed list from iqNode + lstrcpyn( title, TranslateT( "JID List" ), SIZEOF( title )); + if (( jidListInfo=( JABBER_MUC_JIDLIST_INFO * ) lParam ) != NULL ) { + if (( iqNode = jidListInfo->iqNode ) != NULL ) { + if (( from = JabberXmlGetAttrValue( iqNode, "from" )) != NULL ) { + jidListInfo->roomJid = mir_tstrdup( from ); + localFrom = mir_tstrdup( from ); + mir_sntprintf( title, SIZEOF( title ), _T("%s ( %s )"), + ( jidListInfo->type==MUC_VOICELIST ) ? TranslateT( "Voice List" ) : + ( jidListInfo->type==MUC_MEMBERLIST ) ? TranslateT( "Member List" ) : + ( jidListInfo->type==MUC_MODERATORLIST ) ? TranslateT( "Moderator List" ) : + ( jidListInfo->type==MUC_BANLIST ) ? TranslateT( "Ban List" ) : + ( jidListInfo->type==MUC_ADMINLIST ) ? TranslateT( "Admin List" ) : + ( jidListInfo->type==MUC_OWNERLIST ) ? TranslateT( "Owner List" ) : + TranslateT( "JID List" ), + localFrom ); + mir_free( localFrom ); + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) != NULL ) { + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iSubItem = 0; + lvi.iItem = 0; + for ( i=0; i<queryNode->numChild; i++ ) { + if (( itemNode=queryNode->child[i] ) != NULL ) { + if (( jid=JabberXmlGetAttrValue( itemNode, "jid" )) != NULL ) { + lvi.pszText = jid; + lvi.lParam = ( LPARAM )mir_tstrdup( jid ); + ListView_InsertItem( hwndList, &lvi ); + lvi.iItem++; + } } } } } } + + lvi.mask = LVIF_PARAM; + lvi.lParam = ( LPARAM )( -1 ); + ListView_InsertItem( hwndList, &lvi ); + } + SetWindowText( hwndDlg, title ); + } + break; + case WM_NOTIFY: + if (( ( LPNMHDR )lParam )->idFrom == IDC_LIST ) { + switch (( ( LPNMHDR )lParam )->code ) { + case NM_CUSTOMDRAW: + { + NMLVCUSTOMDRAW *nm = ( NMLVCUSTOMDRAW * ) lParam; + + switch ( nm->nmcd.dwDrawStage ) { + case CDDS_PREPAINT: + case CDDS_ITEMPREPAINT: + SetWindowLong( hwndDlg, DWL_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW ); + return TRUE; + case CDDS_SUBITEM|CDDS_ITEMPREPAINT: + { + RECT rc; + HICON hIcon; + + ListView_GetSubItemRect( nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, LVIR_LABEL, &rc ); + if ( nm->iSubItem == 1 ) { + if( nm->nmcd.lItemlParam == ( LPARAM )( -1 )) + hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_ADDCONTACT ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 ); + else + hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_DELETE ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 ); + DrawIconEx( nm->nmcd.hdc, ( rc.left+rc.right-GetSystemMetrics( SM_CXSMICON ))/2, ( rc.top+rc.bottom-GetSystemMetrics( SM_CYSMICON ))/2,hIcon, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0, NULL, DI_NORMAL ); + DestroyIcon( hIcon ); + SetWindowLong( hwndDlg, DWL_MSGRESULT, CDRF_SKIPDEFAULT ); + return TRUE; + } } } } + break; + case NM_CLICK: + { + JABBER_MUC_JIDLIST_INFO *jidListInfo; + NMLISTVIEW *nm = ( NMLISTVIEW * ) lParam; + LVITEM lvi; + LVHITTESTINFO hti; + TCHAR text[128]; + + if ( nm->iSubItem < 1 ) break; + jidListInfo = ( JABBER_MUC_JIDLIST_INFO * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + hti.pt.x = ( short ) LOWORD( GetMessagePos()); + hti.pt.y = ( short ) HIWORD( GetMessagePos()); + ScreenToClient( nm->hdr.hwndFrom, &hti.pt ); + if ( ListView_SubItemHitTest( nm->hdr.hwndFrom, &hti ) == -1 ) + break; + + if ( hti.iSubItem != 1 ) + break; + + lvi.mask = LVIF_PARAM | LVIF_TEXT; + lvi.iItem = hti.iItem; + lvi.iSubItem = 0; + lvi.pszText = text; + lvi.cchTextMax = sizeof( text ); + ListView_GetItem( nm->hdr.hwndFrom, &lvi ); + if ( lvi.lParam == ( LPARAM )( -1 )) { + TCHAR szBuffer[ 1024 ]; + _tcscpy( szBuffer, jidListInfo->type2str()); + if ( !JabberEnterString( szBuffer, SIZEOF(szBuffer))) + break; + + // Trim leading and trailing whitespaces + TCHAR *p = szBuffer, *q; + for ( p = szBuffer; *p!='\0' && isspace( BYTE( *p )); p++); + for ( q = p; *q!='\0' && !isspace( BYTE( *q )); q++); + if (*q != '\0') *q = '\0'; + if (*p == '\0') + break; + + JabberAddMucListItem( jidListInfo, p ); + } + else { + //delete + TCHAR msgText[128]; + + mir_sntprintf( msgText, sizeof( msgText ), _T("%s %s?"), TranslateT( "Removing" ), text ); + if ( MessageBox( hwndDlg, msgText, jidListInfo->type2str(), MB_YESNO|MB_SETFOREGROUND ) == IDYES ) { + JabberDeleteMucListItem( jidListInfo, ( TCHAR* )lvi.lParam ); + ListView_DeleteItem( nm->hdr.hwndFrom, hti.iItem ); + } } } + break; + } + break; + } + break; +/* case WM_SETCURSOR: + if ( LOWORD( LPARAM )!= HTCLIENT ) break; + if ( GetForegroundWindow() == GetParent( hwndDlg )) { + POINT pt; + GetCursorPos( &pt ); + ScreenToClient( hwndDlg,&pt ); + SetFocus( ChildWindowFromPoint( hwndDlg,pt )); //ugly hack because listviews ignore their first click + } + break; +*/ case WM_CLOSE: + { + JABBER_MUC_JIDLIST_INFO *jidListInfo; + HWND hwndList; + int count, i; + LVITEM lvi; + + // Free lParam of the displayed list items + hwndList = GetDlgItem( hwndDlg, IDC_LIST ); + count = ListView_GetItemCount( hwndList ); + lvi.mask = LVIF_PARAM; + lvi.iSubItem = 0; + for ( i=0; i<count; i++ ) { + lvi.iItem = i; + if ( ListView_GetItem( hwndList, &lvi ) == TRUE ) { + if ( lvi.lParam!=( LPARAM )( -1 ) && lvi.lParam!=( LPARAM )( NULL )) { + mir_free(( void * ) lvi.lParam ); + } + } + } + ListView_DeleteAllItems( hwndList ); + + jidListInfo = ( JABBER_MUC_JIDLIST_INFO * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + switch ( jidListInfo->type ) { + case MUC_VOICELIST: + hwndMucVoiceList = NULL; + break; + case MUC_MEMBERLIST: + hwndMucMemberList = NULL; + break; + case MUC_MODERATORLIST: + hwndMucModeratorList = NULL; + break; + case MUC_BANLIST: + hwndMucBanList = NULL; + break; + case MUC_ADMINLIST: + hwndMucAdminList = NULL; + break; + case MUC_OWNERLIST: + hwndMucOwnerList = NULL; + break; + } + + DestroyWindow( hwndDlg ); + } + break; + case WM_DESTROY: + { + JABBER_MUC_JIDLIST_INFO *jidListInfo; + + // Clear GWL_USERDATA + jidListInfo = ( JABBER_MUC_JIDLIST_INFO * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( jidListInfo != NULL ) { + if ( jidListInfo->iqNode != NULL ) + delete jidListInfo->iqNode; + if ( jidListInfo->roomJid != NULL ) + mir_free( jidListInfo->roomJid ); + mir_free( jidListInfo ); + } + } + break; + } + return FALSE; +} + +static VOID CALLBACK JabberMucJidListCreateDialogApcProc( DWORD param ) +{ + XmlNode *iqNode, *queryNode; + TCHAR* from; + JABBER_MUC_JIDLIST_INFO *jidListInfo; + HWND *pHwndJidList; + + if (( jidListInfo=( JABBER_MUC_JIDLIST_INFO * ) param ) == NULL ) return; + if (( iqNode=jidListInfo->iqNode ) == NULL ) return; + if (( from=JabberXmlGetAttrValue( iqNode, "from" )) == NULL ) return; + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) == NULL ) return; + + switch ( jidListInfo->type ) { + case MUC_VOICELIST: + pHwndJidList = &hwndMucVoiceList; + break; + case MUC_MEMBERLIST: + pHwndJidList = &hwndMucMemberList; + break; + case MUC_MODERATORLIST: + pHwndJidList = &hwndMucModeratorList; + break; + case MUC_BANLIST: + pHwndJidList = &hwndMucBanList; + break; + case MUC_ADMINLIST: + pHwndJidList = &hwndMucAdminList; + break; + case MUC_OWNERLIST: + pHwndJidList = &hwndMucOwnerList; + break; + default: + mir_free( jidListInfo ); + return; + } + + if ( *pHwndJidList!=NULL && IsWindow( *pHwndJidList )) { + SetForegroundWindow( *pHwndJidList ); + SendMessage( *pHwndJidList, WM_JABBER_REFRESH, 0, ( LPARAM )jidListInfo ); + } + else *pHwndJidList = CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_JIDLIST ), NULL, JabberMucJidListDlgProc, ( LPARAM )jidListInfo ); +} + +static void JabberIqResultMucGetJidList( XmlNode *iqNode, JABBER_MUC_JIDLIST_TYPE listType ) +{ + TCHAR* type; + JABBER_MUC_JIDLIST_INFO *jidListInfo; + + if (( type=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + + if ( !lstrcmp( type, _T("result" ))) { + if (( jidListInfo=( JABBER_MUC_JIDLIST_INFO * ) mir_alloc( sizeof( JABBER_MUC_JIDLIST_INFO )) ) != NULL ) { + jidListInfo->type = listType; + jidListInfo->roomJid = NULL; // Set in the dialog procedure + if (( ( jidListInfo->iqNode )=JabberXmlCopyNode( iqNode )) != NULL ) { + if ( GetCurrentThreadId() != jabberMainThreadId ) + QueueUserAPC( JabberMucJidListCreateDialogApcProc, hMainThread, ( DWORD )jidListInfo ); + else + JabberMucJidListCreateDialogApcProc(( DWORD )jidListInfo ); + } + else mir_free( jidListInfo ); +} } } + +void JabberIqResultMucGetVoiceList( XmlNode *iqNode, void *userdata ) +{ + JabberLog( "<iq/> iqResultMucGetVoiceList" ); + JabberIqResultMucGetJidList( iqNode, MUC_VOICELIST ); +} + +void JabberIqResultMucGetMemberList( XmlNode *iqNode, void *userdata ) +{ + JabberLog( "<iq/> iqResultMucGetMemberList" ); + JabberIqResultMucGetJidList( iqNode, MUC_MEMBERLIST ); +} + +void JabberIqResultMucGetModeratorList( XmlNode *iqNode, void *userdata ) +{ + JabberLog( "<iq/> iqResultMucGetModeratorList" ); + JabberIqResultMucGetJidList( iqNode, MUC_MODERATORLIST ); +} + +void JabberIqResultMucGetBanList( XmlNode *iqNode, void *userdata ) +{ + JabberLog( "<iq/> iqResultMucGetBanList" ); + JabberIqResultMucGetJidList( iqNode, MUC_BANLIST ); +} + +void JabberIqResultMucGetAdminList( XmlNode *iqNode, void *userdata ) +{ + JabberLog( "<iq/> iqResultMucGetAdminList" ); + JabberIqResultMucGetJidList( iqNode, MUC_ADMINLIST ); +} + +void JabberIqResultMucGetOwnerList( XmlNode *iqNode, void *userdata ) +{ + JabberLog( "<iq/> iqResultMucGetOwnerList" ); + JabberIqResultMucGetJidList( iqNode, MUC_OWNERLIST ); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TCHAR* JABBER_MUC_JIDLIST_INFO::type2str() const +{ + switch( type ) { + case MUC_VOICELIST: return TranslateT( "Voice List" ); + case MUC_MEMBERLIST: return TranslateT( "Member List" ); + case MUC_MODERATORLIST: return TranslateT( "Moderator List" ); + case MUC_BANLIST: return TranslateT( "Ban List" ); + case MUC_ADMINLIST: return TranslateT( "Admin List" ); + case MUC_OWNERLIST: return TranslateT( "Owner List" ); + } + + return TranslateT( "JID List" ); +} diff --git a/miranda-wine/protocols/JabberG/jabber_libstr.cpp b/miranda-wine/protocols/JabberG/jabber_libstr.cpp new file mode 100644 index 0000000..b5fd916 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_libstr.cpp @@ -0,0 +1,76 @@ +/* + +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_libstr.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" + +void __stdcall replaceStr( char*& dest, const char* src ) +{ + if ( src != NULL ) { + if ( dest != NULL ) + mir_free( dest ); + dest = mir_strdup( src ); + } + else dest = NULL; +} + +void __stdcall replaceStr( WCHAR*& dest, const WCHAR* src ) +{ + if ( src != NULL ) { + if ( dest != NULL ) + mir_free( dest ); + dest = mir_wstrdup( src ); + } + else dest = NULL; +} + +char* __stdcall rtrim( char *string ) +{ + char* p = string + strlen( string ) - 1; + + while ( p >= string ) { + if ( *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r' ) + break; + + *p-- = 0; + } + return string; +} + +#if defined( _UNICODE ) +TCHAR* __stdcall rtrim( TCHAR *string ) +{ + TCHAR* p = string + _tcslen( string ) - 1; + + while ( p >= string ) { + if ( *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r' ) + break; + + *p-- = 0; + } + return string; +} +#endif diff --git a/miranda-wine/protocols/JabberG/jabber_list.cpp b/miranda-wine/protocols/JabberG/jabber_list.cpp new file mode 100644 index 0000000..e1dcfe9 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_list.cpp @@ -0,0 +1,388 @@ +/* + +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_list.cpp,v $ +Revision : $Revision: 3651 $ +Last change on : $Date: 2006-08-30 16:54:52 +0400 (Срд, 30 Авг 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "jabber_list.h" + +static int count; +static JABBER_LIST_ITEM *lists; +static CRITICAL_SECTION csLists; + +static void JabberListFreeItemInternal( JABBER_LIST_ITEM *item ); + +void JabberListInit( void ) +{ + lists = NULL; + count = 0; + InitializeCriticalSection( &csLists ); +} + +void JabberListUninit( void ) +{ + JabberListWipe(); + DeleteCriticalSection( &csLists ); +} + +void JabberListWipe( void ) +{ + int i; + + EnterCriticalSection( &csLists ); + for( i=0; i<count; i++ ) + JabberListFreeItemInternal( &( lists[i] )); + if ( lists != NULL ) { + mir_free( lists ); + lists = NULL; + } + count=0; + LeaveCriticalSection( &csLists ); +} + +static void JabberListFreeItemInternal( JABBER_LIST_ITEM *item ) +{ + int i; + + if ( item == NULL ) + return; + + if ( item->jid ) mir_free( item->jid ); + if ( item->nick ) mir_free( item->nick ); + + JABBER_RESOURCE_STATUS* r = item->resource; + for ( i=0; i<item->resourceCount; i++, r++ ) { + if ( r->resourceName ) mir_free( r->resourceName ); + if ( r->statusMessage ) mir_free( r->statusMessage ); + if ( r->software ) mir_free( r->software ); + if ( r->version ) mir_free( r->version ); + if ( r->system ) mir_free( r->system ); + } + if ( item->resource ) mir_free( item->resource ); + if ( item->statusMessage ) mir_free( item->statusMessage ); + if ( item->group ) mir_free( item->group ); + if ( item->photoFileName ) { + DeleteFileA( item->photoFileName ); + mir_free( item->photoFileName ); + } + if ( item->messageEventIdStr ) mir_free( item->messageEventIdStr ); + if ( item->name ) mir_free( item->name ); + if ( item->type ) mir_free( item->type ); + if ( item->service ) mir_free( item->service ); + if ( item->list==LIST_ROSTER && item->ft ) delete item->ft; +} + +int JabberListExist( JABBER_LIST list, const TCHAR* jid ) +{ + TCHAR szSrc[ JABBER_MAX_JID_LEN ]; + JabberStripJid( jid, szSrc, sizeof( szSrc )); + + EnterCriticalSection( &csLists ); + for ( int i=0; i<count; i++ ) { + if ( lists[i].list == list ) { + TCHAR szTempJid[ JABBER_MAX_JID_LEN ]; + if ( !_tcsicmp( szSrc, JabberStripJid( lists[i].jid, szTempJid, sizeof( szTempJid )))) { + LeaveCriticalSection( &csLists ); + return i+1; + } } } + + LeaveCriticalSection( &csLists ); + return 0; +} + +JABBER_LIST_ITEM *JabberListAdd( JABBER_LIST list, const TCHAR* jid ) +{ + TCHAR* s, *p, *q; + JABBER_LIST_ITEM *item; + + EnterCriticalSection( &csLists ); + if (( item=JabberListGetItemPtr( list, jid )) != NULL ) { + LeaveCriticalSection( &csLists ); + return item; + } + + s = mir_tstrdup( jid ); + // strip resource name if any + if (( p = _tcschr( s, '@' )) != NULL ) + if (( q = _tcschr( p, '/' )) != NULL ) + *q = '\0'; + + lists = ( JABBER_LIST_ITEM * ) mir_realloc( lists, sizeof( JABBER_LIST_ITEM )*( count+1 )); + item = &( lists[count] ); + ZeroMemory( item, sizeof( JABBER_LIST_ITEM )); + item->list = list; + item->jid = s; + item->status = ID_STATUS_OFFLINE; + item->resource = NULL; + item->resourceMode = RSMODE_LASTSEEN; + item->defaultResource = -1; + if ( list == LIST_ROSTER ) + item->cap = CLIENT_CAP_CHATSTAT; + count++; + LeaveCriticalSection( &csLists ); + + return item; +} + +void JabberListRemove( JABBER_LIST list, const TCHAR* jid ) +{ + EnterCriticalSection( &csLists ); + int i = JabberListExist( list, jid ); + if ( !i ) { + LeaveCriticalSection( &csLists ); + return; + } + i--; + JabberListFreeItemInternal( &( lists[i] )); + count--; + memmove( lists+i, lists+i+1, sizeof( JABBER_LIST_ITEM )*( count-i )); + lists = ( JABBER_LIST_ITEM * ) mir_realloc( lists, sizeof( JABBER_LIST_ITEM )*count ); + LeaveCriticalSection( &csLists ); +} + +void JabberListRemoveList( JABBER_LIST list ) +{ + int i = 0; + while (( i=JabberListFindNext( list, i )) >= 0 ) + JabberListRemoveByIndex( i ); +} + +void JabberListRemoveByIndex( int index ) +{ + EnterCriticalSection( &csLists ); + if ( index>=0 && index<count ) { + JabberListFreeItemInternal( &( lists[index] )); + count--; + memmove( lists+index, lists+index+1, sizeof( JABBER_LIST_ITEM )*( count-index )); + lists = ( JABBER_LIST_ITEM * ) mir_realloc( lists, sizeof( JABBER_LIST_ITEM )*count ); + } + LeaveCriticalSection( &csLists ); +} + +int JabberListAddResource( JABBER_LIST list, const TCHAR* jid, int status, const TCHAR* statusMessage ) +{ + int j; + const TCHAR* p, *q; + + EnterCriticalSection( &csLists ); + int i = JabberListExist( list, jid ); + if ( !i ) { + LeaveCriticalSection( &csLists ); + return 0; + } + JABBER_LIST_ITEM* LI = &lists[i-1]; + + int bIsNewResource = false; + + if (( p = _tcschr( jid, '@' )) != NULL ) { + if (( q = _tcschr( p, '/' )) != NULL ) { + const TCHAR* resource = q+1; + if ( resource[0] ) { + JABBER_RESOURCE_STATUS* r = LI->resource; + for ( j=0; j < LI->resourceCount; j++, r++ ) { + if ( !_tcscmp( r->resourceName, resource )) { + // Already exist, update status and statusMessage + r->status = status; + replaceStr( r->statusMessage, statusMessage ); + break; + } } + + if ( j >= LI->resourceCount ) { + // Not already exist, add new resource + LI->resource = ( JABBER_RESOURCE_STATUS * ) mir_realloc( LI->resource, ( LI->resourceCount+1 )*sizeof( JABBER_RESOURCE_STATUS )); + bIsNewResource = true; + r = LI->resource + LI->resourceCount++; + memset( r, 0, sizeof( JABBER_RESOURCE_STATUS )); + r->status = status; + r->affiliation = AFFILIATION_NONE; + r->role = ROLE_NONE; + r->resourceName = mir_tstrdup( resource ); + if ( statusMessage ) + r->statusMessage = mir_tstrdup( statusMessage ); + } } + } + // No resource, update the main statusMessage + else replaceStr( LI->statusMessage, statusMessage ); + } + + LeaveCriticalSection( &csLists ); + return bIsNewResource; +} + +void JabberListRemoveResource( JABBER_LIST list, const TCHAR* jid ) +{ + int j; + const TCHAR* p, *q; + + EnterCriticalSection( &csLists ); + int i = JabberListExist( list, jid ); + JABBER_LIST_ITEM* LI = &lists[i-1]; + if ( !i || LI == NULL ) { + LeaveCriticalSection( &csLists ); + return; + } + + if (( p = _tcschr( jid, '@' )) != NULL ) { + if (( q = _tcschr( p, '/' )) != NULL ) { + const TCHAR* resource = q+1; + if ( resource[0] ) { + JABBER_RESOURCE_STATUS* r = LI->resource; + for ( j=0; j < LI->resourceCount; j++, r++ ) { + if ( !_tcsicmp( r->resourceName, resource )) + break; + } + if ( j < LI->resourceCount ) { + // Found resource to be removed + if ( LI->defaultResource == j ) + LI->defaultResource = -1; + else if ( LI->defaultResource > j ) + LI->defaultResource--; + if ( r->resourceName ) mir_free( r->resourceName ); + if ( r->statusMessage ) mir_free( r->statusMessage ); + if ( r->software ) mir_free( r->software ); + if ( r->version ) mir_free( r->version ); + if ( r->system ) mir_free( r->system ); + if ( LI->resourceCount-- == 1 ) { + mir_free( r ); + LI->resource = NULL; + } + else { + memmove( r, r+1, ( LI->resourceCount-j )*sizeof( JABBER_RESOURCE_STATUS )); + LI->resource = ( JABBER_RESOURCE_STATUS * )mir_realloc( LI->resource, LI->resourceCount*sizeof( JABBER_RESOURCE_STATUS )); + } } } } } + + LeaveCriticalSection( &csLists ); +} + +TCHAR* JabberListGetBestResourceNamePtr( const TCHAR* jid ) +{ + TCHAR* res; + + EnterCriticalSection( &csLists ); + int i = JabberListExist( LIST_ROSTER, jid ); + JABBER_LIST_ITEM* LI = &lists[i-1]; + if ( !i || LI == NULL ) { + LeaveCriticalSection( &csLists ); + return NULL; + } + + if ( LI->resourceCount == 1 ) + res = LI->resource[0].resourceName; + else { + res = NULL; + if ( LI->resourceMode == RSMODE_MANUAL || LI->resourceMode == RSMODE_LASTSEEN ) { + if ( LI->defaultResource>=0 && LI->defaultResource < LI->resourceCount ) + res = LI->resource[ LI->defaultResource ].resourceName; + } } + + LeaveCriticalSection( &csLists ); + return res; +} + +TCHAR* JabberListGetBestClientResourceNamePtr( const TCHAR* jid ) +{ + EnterCriticalSection( &csLists ); + int i = JabberListExist( LIST_ROSTER, jid ); + JABBER_LIST_ITEM* LI = &lists[i-1]; + + if ( !i || LI == NULL ) { + LeaveCriticalSection( &csLists ); + return NULL; + } + + TCHAR* res = JabberListGetBestResourceNamePtr( jid ); + if ( res == NULL ) { + JABBER_RESOURCE_STATUS* r = LI->resource; + int status = ID_STATUS_OFFLINE; + res = NULL; + for ( i=0; i < LI->resourceCount; i++ ) { + int s = r[i].status; + BOOL foundBetter = FALSE; + switch ( s ) { + case ID_STATUS_FREECHAT: + foundBetter = TRUE; + break; + case ID_STATUS_ONLINE: + if ( status != ID_STATUS_FREECHAT ) + foundBetter = TRUE; + break; + case ID_STATUS_DND: + if ( status != ID_STATUS_FREECHAT && status != ID_STATUS_ONLINE ) + foundBetter = TRUE; + break; + case ID_STATUS_AWAY: + if ( status != ID_STATUS_FREECHAT && status != ID_STATUS_ONLINE && status != ID_STATUS_DND ) + foundBetter = TRUE; + break; + case ID_STATUS_NA: + if ( status != ID_STATUS_FREECHAT && status != ID_STATUS_ONLINE && status != ID_STATUS_DND && status != ID_STATUS_AWAY ) + foundBetter = TRUE; + break; + } + if ( foundBetter ) { + res = r[i].resourceName; + status = s; + } } } + + LeaveCriticalSection( &csLists ); + return res; +} + +int JabberListFindNext( JABBER_LIST list, int fromOffset ) +{ + EnterCriticalSection( &csLists ); + int i = ( fromOffset>=0 ) ? fromOffset : 0; + for( ; i<count; i++ ) + if ( lists[i].list == list ) { + LeaveCriticalSection( &csLists ); + return i; + } + LeaveCriticalSection( &csLists ); + return -1; +} + +JABBER_LIST_ITEM *JabberListGetItemPtr( JABBER_LIST list, const TCHAR* jid ) +{ + EnterCriticalSection( &csLists ); + int i = JabberListExist( list, jid ); + if ( !i ) { + LeaveCriticalSection( &csLists ); + return NULL; + } + i--; + LeaveCriticalSection( &csLists ); + return &( lists[i] ); +} + +JABBER_LIST_ITEM *JabberListGetItemPtrFromIndex( int index ) +{ + EnterCriticalSection( &csLists ); + if ( index>=0 && index<count ) { + LeaveCriticalSection( &csLists ); + return &( lists[index] ); + } + LeaveCriticalSection( &csLists ); + return NULL; +} diff --git a/miranda-wine/protocols/JabberG/jabber_list.h b/miranda-wine/protocols/JabberG/jabber_list.h new file mode 100644 index 0000000..dabd81f --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_list.h @@ -0,0 +1,174 @@ +/* + +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_list.h,v $ +Revision : $Revision: 3651 $ +Last change on : $Date: 2006-08-30 16:54:52 +0400 (Срд, 30 Авг 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#ifndef _JABBER_LIST_H_ +#define _JABBER_LIST_H_ + +typedef enum { + LIST_ROSTER, // Roster list + LIST_AGENT, // Agent list to show on the Jabber Agents dialog + LIST_CHATROOM, // Groupchat room currently joined + LIST_ROOM, // Groupchat room list to show on the Jabber groupchat dialog + LIST_FILE, // Current file transfer session + LIST_BYTE, // Bytestream sending connection + LIST_FTSEND, + LIST_FTRECV +} JABBER_LIST; + +typedef enum { + SUB_NONE, + SUB_TO, + SUB_FROM, + SUB_BOTH +} JABBER_SUBSCRIPTION; + +typedef enum { + AFFILIATION_NONE, + AFFILIATION_OUTCAST, + AFFILIATION_MEMBER, + AFFILIATION_ADMIN, + AFFILIATION_OWNER +} JABBER_GC_AFFILIATION; + +typedef enum { + ROLE_NONE, + ROLE_VISITOR, + ROLE_PARTICIPANT, + ROLE_MODERATOR +} JABBER_GC_ROLE; + +typedef enum { // initial default to RSMODE_LASTSEEN + RSMODE_SERVER, // always let server decide ( always send correspondence without resouce name ) + RSMODE_LASTSEEN, // use the last seen resource ( or let server decide if haven't seen anything yet ) + RSMODE_MANUAL // specify resource manually ( see the defaultResource field - must not be NULL ) +} JABBER_RESOURCE_MODE; + +#define CLIENT_CAP_READY ( 1<<0 ) // have already done disco#info query +#define CLIENT_CAP_SI ( 1<<1 ) // stream initiation ( si ) profile +#define CLIENT_CAP_SIFILE ( 1<<2 ) // file transfer si profile +#define CLIENT_CAP_BYTESTREAM ( 1<<3 ) // socks5 bytestream +#define CLIENT_CAP_CHATSTAT ( 1<<4 ) // http://jabber.org/protocol/chatstates support (JEP-0085) + +#define AGENT_CAP_REGISTER ( 1<<13 ) +#define AGENT_CAP_SEARCH ( 1<<14 ) +#define AGENT_CAP_GROUPCHAT ( 1<<15 ) + +#define CLIENT_CAP_FILE ( CLIENT_CAP_SI | CLIENT_CAP_SIFILE ) + +struct JABBER_RESOURCE_STATUS +{ + int status; + TCHAR* resourceName; + TCHAR* statusMessage; + TCHAR* software; + TCHAR* version; + TCHAR* system; + unsigned int cap; // 0 = haven't done disco#info yet, see CLIENT_CAP_* + JABBER_GC_AFFILIATION affiliation; + JABBER_GC_ROLE role; +}; + +struct JABBER_LIST_ITEM +{ + JABBER_LIST list; + TCHAR* jid; + + // LIST_ROSTER + // jid = jid of the contact + TCHAR* nick; + int resourceCount; + int status; // Main status, currently useful for transport where no resource information is kept. + // On normal contact, this is the same status as shown on contact list. + JABBER_RESOURCE_STATUS *resource; + int defaultResource; // index to resource[x] which is the default, negative ( -1 ) means no resource is chosen yet + JABBER_RESOURCE_MODE resourceMode; + JABBER_SUBSCRIPTION subscription; + TCHAR* statusMessage; // Status message when the update is to JID with no resource specified ( e.g. transport user ) + TCHAR* group; + char* photoFileName; + int idMsgAckPending; + TCHAR* messageEventIdStr; + BOOL wantComposingEvent; + WORD cap; // See CLIENT_CAP_* above + + // LIST_AGENT + // jid = jid of the agent + // WORD cap; // See AGENT_CAP_* above + TCHAR* name; + TCHAR* service; + + // LIST_ROOM + // jid = room JID + // char* name; // room name + TCHAR* type; // room type + + // LIST_CHATROOM + // jid = room JID + // char* nick; // my nick in this chat room ( SPECIAL: in TXT ) + // JABBER_RESOURCE_STATUS *resource; // participant nicks in this room + BOOL bChatActive; + HWND hwndGcListBan; + HWND hwndGcListAdmin; + HWND hwndGcListOwner; + + // LIST_FILE + // jid = string representation of port number + filetransfer* ft; + WORD port; + + // LIST_BYTE + // jid = string representation of port number + JABBER_BYTE_TRANSFER *jbt; + + // LIST_FTSEND + // jid = string representation of iq id + // ft = file transfer data + // jbt + + // LIST_FTRECV + // jid = string representation of stream id ( sid ) + // ft = file transfer data +}; + +void JabberListInit( void ); +void JabberListUninit( void ); +void JabberListWipe( void ); +int JabberListExist( JABBER_LIST list, const TCHAR* jid ); +JABBER_LIST_ITEM *JabberListAdd( JABBER_LIST list, const TCHAR* jid ); +void JabberListRemove( JABBER_LIST list, const TCHAR* jid ); +void JabberListRemoveList( JABBER_LIST list ); +void JabberListRemoveByIndex( int index ); +int JabberListFindNext( JABBER_LIST list, int fromOffset ); +JABBER_LIST_ITEM *JabberListGetItemPtr( JABBER_LIST list, const TCHAR* jid ); +JABBER_LIST_ITEM *JabberListGetItemPtrFromIndex( int index ); + +int JabberListAddResource( JABBER_LIST list, const TCHAR* jid, int status, const TCHAR* statusMessage ); +void JabberListRemoveResource( JABBER_LIST list, const TCHAR* jid ); +TCHAR* JabberListGetBestResourceNamePtr( const TCHAR* jid ); +TCHAR* JabberListGetBestClientResourceNamePtr( const TCHAR* jid ); + +#endif diff --git a/miranda-wine/protocols/JabberG/jabber_menu.cpp b/miranda-wine/protocols/JabberG/jabber_menu.cpp new file mode 100644 index 0000000..a25fe61 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_menu.cpp @@ -0,0 +1,281 @@ +/* + +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_menu.cpp,v $ +Revision : $Revision: 2948 $ +Last change on : $Date: 2006-05-24 23:59:47 +0400 (Срд, 24 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "jabber_list.h" +#include "resource.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// module data + +static HANDLE hMenuRequestAuth = NULL; +static HANDLE hMenuGrantAuth = NULL; +static HANDLE hMenuJoinLeave = NULL; +static HANDLE hMenuConvert = NULL; +static HANDLE hMenuRosterAdd = NULL; + +static void sttEnableMenuItem( HANDLE hMenuItem, BOOL bEnable ) +{ + CLISTMENUITEM clmi = {0}; + clmi.cbSize = sizeof( CLISTMENUITEM ); + clmi.flags = CMIM_FLAGS; + if ( !bEnable ) + clmi.flags |= CMIF_HIDDEN; + + JCallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )hMenuItem, ( LPARAM )&clmi ); +} + +int JabberMenuPrebuildContactMenu( WPARAM wParam, LPARAM lParam ) +{ + sttEnableMenuItem( hMenuRequestAuth, FALSE ); + sttEnableMenuItem( hMenuGrantAuth, FALSE ); + sttEnableMenuItem( hMenuJoinLeave, FALSE ); + sttEnableMenuItem( hMenuConvert, FALSE ); + sttEnableMenuItem( hMenuRosterAdd, FALSE ); + + HANDLE hContact; + if (( hContact=( HANDLE )wParam ) == NULL ) + return 0; + + BYTE chatRoomType = (BYTE)JGetByte( hContact, "ChatRoom", 0 ); + + if ((chatRoomType == GCW_CHATROOM) || chatRoomType == 0 ) { + DBVARIANT dbv; + if ( !JGetStringT( hContact, chatRoomType?"ChatRoomID":"jid", &dbv )) { + JFreeVariant( &dbv ); + CLISTMENUITEM clmi = { 0 }; + sttEnableMenuItem( hMenuConvert, TRUE ); + clmi.cbSize = sizeof( clmi ); + clmi.pszName = JTranslate( chatRoomType ? "&Convert to Contact" : "&Convert to Chat Room" ); + clmi.flags = CMIM_NAME | CMIM_FLAGS; + JCallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )hMenuConvert, ( LPARAM )&clmi ); + } } + + if (!jabberOnline) + return 0; + + if ( chatRoomType ) { + DBVARIANT dbv; + if ( !JGetStringT( hContact, "ChatRoomID", &dbv )) { + if ( JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal ) == NULL ) { + sttEnableMenuItem( hMenuRosterAdd, TRUE ); + } + JFreeVariant( &dbv ); + } } + + if ( chatRoomType == GCW_CHATROOM ) { + CLISTMENUITEM clmi = { 0 }; + clmi.cbSize = sizeof( clmi ); + clmi.pszName = JTranslate(( JGetWord( hContact, "Status", 0 ) == ID_STATUS_ONLINE ) ? "&Leave" : "&Join" ); + clmi.flags = CMIM_NAME | CMIM_FLAGS; + JCallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )hMenuJoinLeave, ( LPARAM )&clmi ); + return 0; + } + + DBVARIANT dbv; + if ( !JGetStringT( hContact, "jid", &dbv )) { + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal ); + JFreeVariant( &dbv ); + if ( item != NULL ) { + sttEnableMenuItem( hMenuRequestAuth, item->subscription == SUB_FROM || item->subscription == SUB_NONE ); + sttEnableMenuItem( hMenuGrantAuth, item->subscription == SUB_TO || item->subscription == SUB_NONE ); + return 0; + } } + + return 0; +} + +int JabberMenuConvertChatContact( WPARAM wParam, LPARAM lParam ) +{ + BYTE chatRoomType = (BYTE)JGetByte( (HANDLE ) wParam, "ChatRoom", 0 ); + if ((chatRoomType == GCW_CHATROOM) || chatRoomType == 0 ) { + DBVARIANT dbv; + if ( !JGetStringT( (HANDLE ) wParam, (chatRoomType == GCW_CHATROOM)?"ChatRoomID":"jid", &dbv )) { + JDeleteSetting( (HANDLE ) wParam, (chatRoomType == GCW_CHATROOM)?"ChatRoomID":"jid"); + JSetStringT( (HANDLE ) wParam, (chatRoomType != GCW_CHATROOM)?"ChatRoomID":"jid", dbv.ptszVal); + JFreeVariant( &dbv ); + JSetByte((HANDLE ) wParam, "ChatRoom", (chatRoomType == GCW_CHATROOM)?0:GCW_CHATROOM); + } } + return 0; +} + +int JabberMenuRosterAdd( WPARAM wParam, LPARAM lParam ) +{ + DBVARIANT dbv; + if ( !wParam ) return 0; // we do not add ourself to the roster. (buggy situation - should not happen) + if ( !JGetStringT( ( HANDLE ) wParam, "ChatRoomID", &dbv )) { + TCHAR *roomID = mir_tstrdup(dbv.ptszVal); + JFreeVariant( &dbv ); + if ( JabberListGetItemPtr( LIST_ROSTER, roomID ) == NULL ) { + TCHAR *nick = 0; + TCHAR *group = 0; + if ( !DBGetContactSettingTString( ( HANDLE ) wParam, "CList", "Group", &dbv ) ) { + group = mir_tstrdup(dbv.ptszVal); + JFreeVariant( &dbv ); + } + if ( !JGetStringT( ( HANDLE ) wParam, "Nick", &dbv ) ) { + nick = mir_tstrdup(dbv.ptszVal); + JFreeVariant( &dbv ); + } + JabberAddContactToRoster(roomID, nick, group, SUB_NONE); + if (nick) mir_free(nick); + if (nick) mir_free(group); + } + mir_free(roomID); + } + return 0; +} + +int JabberMenuHandleRequestAuth( WPARAM wParam, LPARAM lParam ) +{ + HANDLE hContact; + DBVARIANT dbv; + + if (( hContact=( HANDLE ) wParam )!=NULL && jabberOnline ) { + if ( !JGetStringT( hContact, "jid", &dbv )) { + XmlNode presence( "presence" ); presence.addAttr( "to", dbv.ptszVal ); presence.addAttr( "type", "subscribe" ); + JabberSend( jabberThreadInfo->s, presence ); + JFreeVariant( &dbv ); + } } + + return 0; +} + +int JabberMenuHandleGrantAuth( WPARAM wParam, LPARAM lParam ) +{ + HANDLE hContact; + DBVARIANT dbv; + + if (( hContact=( HANDLE ) wParam )!=NULL && jabberOnline ) { + if ( !JGetStringT( hContact, "jid", &dbv )) { + XmlNode presence( "presence" ); presence.addAttr( "to", dbv.ptszVal ); presence.addAttr( "type", "subscribed" ); + JabberSend( jabberThreadInfo->s, presence ); + JFreeVariant( &dbv ); + } } + + return 0; +} + +int JabberMenuJoinLeave( WPARAM wParam, LPARAM lParam ) +{ + DBVARIANT dbv, jid; + if ( JGetStringT(( HANDLE )wParam, "ChatRoomID", &jid )) + return 0; + + if ( JGetStringT(( HANDLE )wParam, "MyNick", &dbv )) + if ( JGetStringT( NULL, "Nick", &dbv )) { + JFreeVariant( &jid ); + return 0; + } + + if ( JGetWord(( HANDLE )wParam, "Status", 0 ) != ID_STATUS_ONLINE ) { + if ( !jabberChatDllPresent ) { + JabberChatDllError(); + goto LBL_Return; + } + + TCHAR* p = _tcschr( jid.ptszVal, '@' ); + if ( p == NULL ) + goto LBL_Return; + + *p++ = 0; + JabberGroupchatJoinRoom( p, jid.ptszVal, dbv.ptszVal, _T("")); + } + else { + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_CHATROOM, jid.ptszVal ); + if ( item != NULL ) + JabberGcQuit( item, 0, NULL ); + } + +LBL_Return: + JFreeVariant( &dbv ); + JFreeVariant( &jid ); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// contact menu initialization code + +void JabberMenuInit() +{ + CLISTMENUITEM mi = { 0 }; + mi.cbSize = sizeof( CLISTMENUITEM ); + + char text[ 200 ]; + strcpy( text, jabberProtoName ); + char* tDest = text + strlen( text ); + + // "Request authorization" + strcpy( tDest, "/RequestAuth" ); + CreateServiceFunction( text, JabberMenuHandleRequestAuth ); + mi.pszName = JTranslate( "Request authorization" ); + mi.position = -2000001001; + mi.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_REQUEST )); + mi.pszService = text; + mi.pszContactOwner = jabberProtoName; + hMenuRequestAuth = ( HANDLE ) JCallService( MS_CLIST_ADDCONTACTMENUITEM, 0, ( LPARAM )&mi ); + + // "Grant authorization" + strcpy( tDest, "/GrantAuth" ); + CreateServiceFunction( text, JabberMenuHandleGrantAuth ); + mi.pszName = JTranslate( "Grant authorization" ); + mi.position = -2000001000; + mi.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_GRANT )); + mi.pszService = text; + mi.pszContactOwner = jabberProtoName; + hMenuGrantAuth = ( HANDLE ) JCallService( MS_CLIST_ADDCONTACTMENUITEM, 0, ( LPARAM )&mi ); + + // "Grant authorization" + strcpy( tDest, "/JoinChat" ); + CreateServiceFunction( text, JabberMenuJoinLeave ); + mi.pszName = JTranslate( "Join chat" ); + mi.position = -2000001002; + mi.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_GROUP )); + mi.pszService = text; + mi.pszContactOwner = jabberProtoName; + hMenuJoinLeave = ( HANDLE ) JCallService( MS_CLIST_ADDCONTACTMENUITEM, 0, ( LPARAM )&mi ); + + // "Convert Chat/Contact" + strcpy( tDest, "/ConvertChatContact" ); + CreateServiceFunction( text, JabberMenuConvertChatContact ); + mi.pszName = JTranslate( "Convert" ); + mi.position = -1999901003; + mi.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_USER2ROOM )); + mi.pszService = text; + mi.pszContactOwner = jabberProtoName; + hMenuConvert = ( HANDLE ) JCallService( MS_CLIST_ADDCONTACTMENUITEM, 0, ( LPARAM )&mi ); + + // "Add to roster" + strcpy( tDest, "/AddToRoster" ); + CreateServiceFunction( text, JabberMenuRosterAdd ); + mi.pszName = JTranslate( "Add to roster" ); + mi.position = -1999901004; + mi.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_ADDROSTER )); + mi.pszService = text; + mi.pszContactOwner = jabberProtoName; + hMenuRosterAdd = ( HANDLE ) JCallService( MS_CLIST_ADDCONTACTMENUITEM, 0, ( LPARAM )&mi ); +} diff --git a/miranda-wine/protocols/JabberG/jabber_misc.cpp b/miranda-wine/protocols/JabberG/jabber_misc.cpp new file mode 100644 index 0000000..35e356e --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_misc.cpp @@ -0,0 +1,382 @@ +/* + +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_misc.cpp,v $ +Revision : $Revision: 3322 $ +Last change on : $Date: 2006-07-13 16:11:29 +0400 (Чтв, 13 Июл 2006) $ +Last change by : $Author: rainwater $ + +*/ + +#include "jabber.h" +#include "jabber_list.h" + +/////////////////////////////////////////////////////////////////////////////// +// JabberAddContactToRoster() - adds a contact to the roster + +void JabberAddContactToRoster( const TCHAR* jid, const TCHAR* nick, const TCHAR* grpName, JABBER_SUBSCRIPTION subscription ) +{ + XmlNodeIq iq( "set" ); + XmlNode* query = iq.addQuery( "jabber:iq:roster" ); + XmlNode* item = query->addChild( "item" ); item->addAttr( "name", nick ); item->addAttr( "jid", jid ); + switch( subscription ) { + case SUB_BOTH: item->addAttr( "subscription", "both" ); break; + case SUB_TO: item->addAttr( "subscription", "to" ); break; + case SUB_FROM: item->addAttr( "subscription", "from" ); break; + default: item->addAttr( "subscription", "none" ); break; + } + + if ( grpName != NULL ) + item->addChild( "group", grpName ); + JabberSend( jabberThreadInfo->s, iq ); +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberChatDllError() - missing CHAT.DLL + +void JabberChatDllError() +{ + MessageBox( NULL, + TranslateT( "CHAT plugin is required for conferences. Install it before chatting" ), + TranslateT( "Jabber Error Message" ), MB_OK|MB_SETFOREGROUND ); +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberCompareJids + +int JabberCompareJids( const TCHAR* jid1, const TCHAR* jid2 ) +{ + if ( !lstrcmpi( jid1, jid2 )) + return 0; + + // match only node@domain part + TCHAR szTempJid1[ JABBER_MAX_JID_LEN ], szTempJid2[ JABBER_MAX_JID_LEN ]; + return lstrcmpi( + JabberStripJid( jid1, szTempJid1, sizeof szTempJid1 ), + JabberStripJid( jid2, szTempJid2, sizeof szTempJid2 )); +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberContactListCreateGroup() + +static void JabberContactListCreateClistGroup( TCHAR* groupName ) +{ + char str[33]; + int i; + DBVARIANT dbv; + + for ( i=0;;i++ ) { + itoa( i, str, 10 ); + if ( DBGetContactSettingTString( NULL, "CListGroups", str, &dbv )) + break; + TCHAR* name = dbv.ptszVal; + if ( name[0]!='\0' && !_tcscmp( name+1, groupName )) { + // Already exists, no need to create + JFreeVariant( &dbv ); + return; + } + JFreeVariant( &dbv ); + } + + // Create new group with id = i ( str is the text representation of i ) + TCHAR newName[128]; + newName[0] = 1 | GROUPF_EXPANDED; + _tcsncpy( newName+1, groupName, SIZEOF( newName )-1 ); + newName[ SIZEOF( newName )-1] = '\0'; + DBWriteContactSettingTString( NULL, "CListGroups", str, newName ); + JCallService( MS_CLUI_GROUPADDED, i+1, 0 ); +} + +void JabberContactListCreateGroup( TCHAR* groupName ) +{ + TCHAR name[128], *p; + + if ( groupName==NULL || groupName[0]=='\0' || groupName[0]=='\\' ) return; + + _tcsncpy( name, groupName, SIZEOF( name )); + name[ SIZEOF( name )-1] = '\0'; + for ( p=name; *p!='\0'; p++ ) { + if ( *p == '\\' ) { + *p = '\0'; + JabberContactListCreateClistGroup( name ); + *p = '\\'; + } + } + JabberContactListCreateClistGroup( name ); +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberDBAddAuthRequest() + +void JabberDBAddAuthRequest( TCHAR* jid, TCHAR* nick ) +{ + HANDLE hContact = JabberDBCreateContact( jid, NULL, FALSE, TRUE ); + JDeleteSetting( hContact, "Hidden" ); + JSetStringT( hContact, "Nick", nick ); + + #if defined( _UNICODE ) + char* szJid = u2a( jid ); + char* szNick = u2a( nick ); + #else + char* szJid = jid; + char* szNick = nick; + #endif + + //blob is: uin( DWORD ), hContact( HANDLE ), nick( ASCIIZ ), first( ASCIIZ ), last( ASCIIZ ), email( ASCIIZ ), reason( ASCIIZ ) + //blob is: 0( DWORD ), hContact( HANDLE ), nick( ASCIIZ ), ""( ASCIIZ ), ""( ASCIIZ ), email( ASCIIZ ), ""( ASCIIZ ) + DBEVENTINFO dbei = {0}; + dbei.cbSize = sizeof( DBEVENTINFO ); + dbei.szModule = jabberProtoName; + dbei.timestamp = ( DWORD )time( NULL ); + dbei.flags = 0; + dbei.eventType = EVENTTYPE_AUTHREQUEST; + dbei.cbBlob = sizeof( DWORD )+ sizeof( HANDLE ) + strlen( szNick ) + strlen( szJid ) + 5; + PBYTE pCurBlob = dbei.pBlob = ( PBYTE ) mir_alloc( dbei.cbBlob ); + *(( PDWORD ) pCurBlob ) = 0; pCurBlob += sizeof( DWORD ); + *(( PHANDLE ) pCurBlob ) = hContact; pCurBlob += sizeof( HANDLE ); + strcpy(( char* )pCurBlob, szNick ); pCurBlob += strlen( szNick )+1; + *pCurBlob = '\0'; pCurBlob++; //firstName + *pCurBlob = '\0'; pCurBlob++; //lastName + strcpy(( char* )pCurBlob, szJid ); pCurBlob += strlen( szJid )+1; + *pCurBlob = '\0'; //reason + + JCallService( MS_DB_EVENT_ADD, ( WPARAM ) ( HANDLE ) NULL, ( LPARAM )&dbei ); + JabberLog( "Setup DBAUTHREQUEST with nick='" TCHAR_STR_PARAM "' jid='" TCHAR_STR_PARAM "'", szNick, szJid ); + + #if defined( _UNICODE ) + mir_free( szJid ); + mir_free( szNick ); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberDBCreateContact() +// jid & nick are passed in TXT + +HANDLE JabberDBCreateContact( TCHAR* jid, TCHAR* nick, BOOL temporary, BOOL stripResource ) +{ + TCHAR* s, *p, *q; + int len; + char* szProto; + + if ( jid==NULL || jid[0]=='\0' ) + return NULL; + + s = mir_tstrdup( jid ); + q = NULL; + // strip resource if present + if (( p = _tcschr( s, '@' )) != NULL ) + if (( q = _tcschr( p, '/' )) != NULL ) + *q = '\0'; + + if ( !stripResource && q!=NULL ) // so that resource is not stripped + *q = '/'; + len = _tcslen( s ); + + // We can't use JabberHContactFromJID() here because of the stripResource option + HANDLE hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) { + szProto = ( char* )JCallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 ); + if ( szProto!=NULL && !strcmp( jabberProtoName, szProto )) { + DBVARIANT dbv; + if ( !JGetStringT( hContact, "jid", &dbv )) { + p = dbv.ptszVal; + if ( p && ( int )_tcslen( p )>=len && ( p[len]=='\0'||p[len]=='/' ) && !_tcsnicmp( p, s, len )) { + JFreeVariant( &dbv ); + break; + } + JFreeVariant( &dbv ); + } } + hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM ) hContact, 0 ); + } + + if ( hContact == NULL ) { + hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_ADD, 0, 0 ); + JCallService( MS_PROTO_ADDTOCONTACT, ( WPARAM ) hContact, ( LPARAM )jabberProtoName ); + JSetStringT( hContact, "jid", s ); + if ( nick != NULL && *nick != '\0' ) + JSetStringT( hContact, "Nick", nick ); + if ( temporary ) + DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 ); + JabberLog( "Create Jabber contact jid=" TCHAR_STR_PARAM ", nick=" TCHAR_STR_PARAM, s, nick ); + } + + mir_free( s ); + return hContact; +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberGetAvatarFileName() - gets a file name for the avatar image + +void JabberGetAvatarFileName( HANDLE hContact, char* pszDest, int cbLen ) +{ + JCallService( MS_DB_GETPROFILEPATH, cbLen, LPARAM( pszDest )); + + int tPathLen = strlen( pszDest ); + tPathLen += mir_snprintf( pszDest + tPathLen, MAX_PATH - tPathLen, "\\Jabber\\" ); + CreateDirectoryA( pszDest, NULL ); + + char* szFileType; + switch( JGetByte( hContact, "AvatarType", PA_FORMAT_PNG )) { + case PA_FORMAT_JPEG: szFileType = "jpg"; break; + case PA_FORMAT_PNG: szFileType = "png"; break; + case PA_FORMAT_GIF: szFileType = "gif"; break; + case PA_FORMAT_BMP: szFileType = "bmp"; break; + } + + if ( hContact != NULL ) { + char str[ 256 ]; + DBVARIANT dbv; + if ( !JGetStringUtf( hContact, "jid", &dbv )) { + strncpy( str, dbv.pszVal, sizeof str ); + str[ sizeof(str)-1 ] = 0; + JFreeVariant( &dbv ); + } + else ltoa(( long )hContact, str, 10 ); + + char* hash = JabberSha1( str ); + mir_snprintf( pszDest + tPathLen, MAX_PATH - tPathLen, "%s.%s", hash, szFileType ); + mir_free( hash ); + } + else if ( jabberThreadInfo != NULL ) { + mir_snprintf( pszDest + tPathLen, MAX_PATH - tPathLen, TCHAR_STR_PARAM"@%s avatar.%s", jabberThreadInfo->username, jabberThreadInfo->server, szFileType ); + } + else { + DBVARIANT dbv1, dbv2; + BOOL res1 = DBGetContactSetting( NULL, jabberProtoName, "LoginName", &dbv1 ); + BOOL res2 = DBGetContactSetting( NULL, jabberProtoName, "LoginServer", &dbv2 ); + mir_snprintf( pszDest + tPathLen, MAX_PATH - tPathLen, "%s@%s avatar.%s", + res1 ? "noname" : dbv1.pszVal, + res2 ? jabberProtoName : dbv2.pszVal, + szFileType ); + if (!res1) JFreeVariant( &dbv1 ); + if (!res2) JFreeVariant( &dbv2 ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberForkThread() + +struct FORK_ARG { + HANDLE hEvent; + void ( __cdecl *threadcode )( void* ); + void *arg; +}; + +static void __cdecl forkthread_r( struct FORK_ARG *fa ) +{ + void ( *callercode )( void* ) = fa->threadcode; + void *arg = fa->arg; + JabberLog( "Thread started: %08X %d", callercode, GetCurrentThreadId()); + JCallService( MS_SYSTEM_THREAD_PUSH, 0, 0 ); + SetEvent( fa->hEvent ); + __try { + callercode( arg ); + } __finally { + JCallService( MS_SYSTEM_THREAD_POP, 0, 0 ); + } + return; +} + +ULONG JabberForkThread( void ( __cdecl *threadcode )( void* ), unsigned long stacksize, void *arg ) +{ + struct FORK_ARG fa; + fa.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + fa.threadcode = threadcode; + fa.arg = arg; + + ULONG rc = _beginthread(( JABBER_THREAD_FUNC )forkthread_r, stacksize, &fa ); + if (( unsigned long ) -1L != rc ) + WaitForSingleObject( fa.hEvent, INFINITE ); + + CloseHandle( fa.hEvent ); + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberSetServerStatus() + +void JabberSetServerStatus( int iNewStatus ) +{ + if ( !jabberConnected ) + return; + + // change status + int oldStatus = jabberStatus; + switch ( iNewStatus ) { + case ID_STATUS_ONLINE: + case ID_STATUS_NA: + case ID_STATUS_FREECHAT: + case ID_STATUS_INVISIBLE: + jabberStatus = iNewStatus; + break; + case ID_STATUS_AWAY: + case ID_STATUS_ONTHEPHONE: + case ID_STATUS_OUTTOLUNCH: + jabberStatus = ID_STATUS_AWAY; + break; + case ID_STATUS_DND: + case ID_STATUS_OCCUPIED: + jabberStatus = ID_STATUS_DND; + break; + default: + return; + } + + // send presence update + JabberSendPresence( jabberStatus ); + JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus ); +} + +// Process a string, and double all % characters, according to chat.dll's restrictions +// Returns a pointer to the new string (old one is not freed) +char* EscapeChatTags(char* pszText) +{ + int nChars = 0; + for ( char* p = pszText; ( p = strchr( p, '%' )) != NULL; p++ ) + nChars++; + + if ( nChars == 0 ) + return mir_strdup( pszText ); + + char* pszNewText = (char*)mir_alloc( strlen( pszText ) + 1 + nChars ), *s, *d; + if ( pszNewText == NULL ) + return mir_strdup( pszText ); + + for ( s = pszText, d = pszNewText; *s; s++ ) { + if ( *s == '%' ) + *d++ = '%'; + *d++ = *s; + } + *d = 0; + return pszNewText; +} + +char* UnEscapeChatTags(char* str_in) +{ + char* s = str_in, *d = str_in; + while ( *s ) { + if (( *s == '%' && s[1] == '%' ) || ( *s == '\n' && s[1] == '\n' )) + s++; + *d++ = *s++; + } + *d = 0; + return str_in; +} diff --git a/miranda-wine/protocols/JabberG/jabber_opt.cpp b/miranda-wine/protocols/JabberG/jabber_opt.cpp new file mode 100644 index 0000000..4454671 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_opt.cpp @@ -0,0 +1,724 @@ +/* + +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_opt.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 "jabber_list.h" +#include <commctrl.h> +#include "resource.h" +#include <uxtheme.h> + +extern BOOL jabberSendKeepAlive; +extern UINT jabberCodePage; + +static BOOL (WINAPI *pfnEnableThemeDialogTexture)(HANDLE, DWORD) = 0; + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberRegisterDlgProc - the dialog proc for registering new account + +#if defined( _UNICODE ) + #define STR_FORMAT _T("%s %s@%S:%d?") +#else + #define STR_FORMAT _T("%s %s@%s:%d?") +#endif + +static BOOL CALLBACK JabberRegisterDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + struct ThreadData *thread, *regInfo; + + switch ( msg ) { + case WM_INITDIALOG: + { + TranslateDialogDefault( hwndDlg ); + regInfo = ( struct ThreadData * ) lParam; + TCHAR text[256]; + mir_sntprintf( text, SIZEOF(text), STR_FORMAT, TranslateT( "Register" ), regInfo->username, regInfo->server, regInfo->port ); + SetDlgItemText( hwndDlg, IDC_REG_STATUS, text ); + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG )regInfo ); + return TRUE; + } + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDOK: + ShowWindow( GetDlgItem( hwndDlg, IDOK ), SW_HIDE ); + ShowWindow( GetDlgItem( hwndDlg, IDCANCEL ), SW_HIDE ); + ShowWindow( GetDlgItem( hwndDlg, IDC_PROGRESS_REG ), SW_SHOW ); + ShowWindow( GetDlgItem( hwndDlg, IDCANCEL2 ), SW_SHOW ); + regInfo = ( struct ThreadData * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + thread = ( struct ThreadData * ) mir_alloc( sizeof( struct ThreadData )); + memset( thread, 0, sizeof( struct ThreadData )); + thread->type = JABBER_SESSION_REGISTER; + _tcsncpy( thread->username, regInfo->username, SIZEOF( thread->username )); + strncpy( thread->password, regInfo->password, SIZEOF( thread->password )); + strncpy( thread->server, regInfo->server, SIZEOF( thread->server )); + strncpy( thread->manualHost, regInfo->manualHost, SIZEOF( thread->manualHost )); + thread->port = regInfo->port; + thread->useSSL = regInfo->useSSL; + thread->reg_hwndDlg = hwndDlg; + JabberForkThread(( JABBER_THREAD_FUNC )JabberServerThread, 0, thread ); + return TRUE; + case IDCANCEL: + case IDOK2: + EndDialog( hwndDlg, 0 ); + return TRUE; + } + break; + case WM_JABBER_REGDLG_UPDATE: // wParam=progress ( 0-100 ), lparam=status string + if (( TCHAR* )lParam == NULL ) + SetDlgItemText( hwndDlg, IDC_REG_STATUS, TranslateT( "No message" )); + else + SetDlgItemText( hwndDlg, IDC_REG_STATUS, ( TCHAR* )lParam ); + if ( wParam >= 0 ) + SendMessage( GetDlgItem( hwndDlg, IDC_PROGRESS_REG ), PBM_SETPOS, wParam, 0 ); + if ( wParam >= 100 ) { + ShowWindow( GetDlgItem( hwndDlg, IDCANCEL2 ), SW_HIDE ); + ShowWindow( GetDlgItem( hwndDlg, IDOK2 ), SW_SHOW ); + } + else SetFocus( GetDlgItem( hwndDlg, IDC_PROGRESS_REG )); + return TRUE; + } + + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberOptDlgProc - main options dialog procedure + +static HWND msgLangListBox; +static BOOL CALLBACK JabberMsgLangAdd( LPSTR str ) +{ + int i, count, index; + UINT cp; + static struct { UINT cpId; TCHAR* cpName; } cpTable[] = { + { 874, _T("Thai") }, + { 932, _T("Japanese") }, + { 936, _T("Simplified Chinese") }, + { 949, _T("Korean") }, + { 950, _T("Traditional Chinese") }, + { 1250, _T("Central European") }, + { 1251, _T("Cyrillic") }, + { 1252, _T("Latin I") }, + { 1253, _T("Greek") }, + { 1254, _T("Turkish") }, + { 1255, _T("Hebrew") }, + { 1256, _T("Arabic") }, + { 1257, _T("Baltic") }, + { 1258, _T("Vietnamese") }, + { 1361, _T("Korean ( Johab )") } + }; + + cp = atoi( str ); + count = sizeof( cpTable )/sizeof( cpTable[0] ); + for ( i=0; i<count && cpTable[i].cpId!=cp; i++ ); + if ( i < count ) { + if (( index=SendMessage( msgLangListBox, CB_ADDSTRING, 0, ( LPARAM )TranslateTS( cpTable[i].cpName )) ) >= 0 ) { + SendMessage( msgLangListBox, CB_SETITEMDATA, ( WPARAM ) index, ( LPARAM )cp ); + if ( jabberCodePage == cp ) + SendMessage( msgLangListBox, CB_SETCURSEL, ( WPARAM ) index, 0 ); + } } + + return TRUE; +} + +static LRESULT CALLBACK JabberValidateUsernameWndProc( HWND hwndEdit, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + WNDPROC oldProc = ( WNDPROC ) GetWindowLong( hwndEdit, GWL_USERDATA ); + + switch ( msg ) { + case WM_CHAR: + if ( strchr( "\"&'/:<>@", wParam&0xff ) != NULL ) + return 0; + break; + } + return CallWindowProc( oldProc, hwndEdit, msg, wParam, lParam ); +} + +static BOOL CALLBACK JabberOptDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + { + DBVARIANT dbv; + BOOL enableRegister = TRUE; + + TranslateDialogDefault( hwndDlg ); + SetDlgItemTextA( hwndDlg, IDC_SIMPLE, jabberModuleName ); + if ( !DBGetContactSetting( NULL, jabberProtoName, "LoginName", &dbv )) { + SetDlgItemTextA( hwndDlg, IDC_EDIT_USERNAME, dbv.pszVal ); + if ( !dbv.pszVal[0] ) enableRegister = FALSE; + JFreeVariant( &dbv ); + } + if ( !DBGetContactSetting( NULL, jabberProtoName, "Password", &dbv )) { + JCallService( MS_DB_CRYPT_DECODESTRING, strlen( dbv.pszVal )+1, ( LPARAM )dbv.pszVal ); + SetDlgItemTextA( hwndDlg, IDC_EDIT_PASSWORD, dbv.pszVal ); + if ( !dbv.pszVal[0] ) enableRegister = FALSE; + JFreeVariant( &dbv ); + } + if ( !DBGetContactSettingTString( NULL, jabberProtoName, "Resource", &dbv )) { + SetDlgItemText( hwndDlg, IDC_EDIT_RESOURCE, dbv.ptszVal ); + JFreeVariant( &dbv ); + } + else SetDlgItemTextA( hwndDlg, IDC_EDIT_RESOURCE, "Miranda" ); + + SendMessage( GetDlgItem( hwndDlg, IDC_PRIORITY_SPIN ), UDM_SETRANGE, 0, ( LPARAM )MAKELONG( 100, 0 )); + + char text[256]; + sprintf( text, "%d", JGetWord( NULL, "Priority", 0 )); + SetDlgItemTextA( hwndDlg, IDC_PRIORITY, text ); + CheckDlgButton( hwndDlg, IDC_SAVEPASSWORD, JGetByte( "SavePassword", TRUE )); + if ( !DBGetContactSetting( NULL, jabberProtoName, "LoginServer", &dbv )) { + SetDlgItemTextA( hwndDlg, IDC_EDIT_LOGIN_SERVER, dbv.pszVal ); + if ( !dbv.pszVal[0] ) enableRegister = FALSE; + JFreeVariant( &dbv ); + } + else SetDlgItemTextA( hwndDlg, IDC_EDIT_LOGIN_SERVER, "jabber.org" ); + + WORD port = ( WORD )JGetWord( NULL, "Port", JABBER_DEFAULT_PORT ); + SetDlgItemInt( hwndDlg, IDC_PORT, port, FALSE ); + if ( port <= 0 ) enableRegister = FALSE; + + CheckDlgButton( hwndDlg, IDC_USE_SSL, JGetByte( "UseSSL", FALSE )); + CheckDlgButton( hwndDlg, IDC_USE_TLS, JGetByte( "UseTLS", FALSE )); + EnableWindow(GetDlgItem( hwndDlg, IDC_USE_TLS ), !JGetByte( "UseSSL", FALSE )); + + EnableWindow( GetDlgItem( hwndDlg, IDC_BUTTON_REGISTER ), enableRegister ); + + if ( JGetByte( "ManualConnect", FALSE ) == TRUE ) { + CheckDlgButton( hwndDlg, IDC_MANUAL, TRUE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_HOST ), TRUE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_HOSTPORT ), TRUE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_PORT ), FALSE ); + } + if ( !DBGetContactSetting( NULL, jabberProtoName, "ManualHost", &dbv )) { + SetDlgItemTextA( hwndDlg, IDC_HOST, dbv.pszVal ); + JFreeVariant( &dbv ); + } + SetDlgItemInt( hwndDlg, IDC_HOSTPORT, JGetWord( NULL, "ManualPort", JABBER_DEFAULT_PORT ), FALSE ); + + CheckDlgButton( hwndDlg, IDC_KEEPALIVE, JGetByte( "KeepAlive", TRUE )); + CheckDlgButton( hwndDlg, IDC_ROSTER_SYNC, JGetByte( "RosterSync", FALSE )); + + if ( !DBGetContactSetting( NULL, jabberProtoName, "Jud", &dbv )) { + SetDlgItemTextA( hwndDlg, IDC_JUD, dbv.pszVal ); + JFreeVariant( &dbv ); + } + else SetDlgItemTextA( hwndDlg, IDC_JUD, "users.jabber.org" ); + + msgLangListBox = GetDlgItem( hwndDlg, IDC_MSGLANG ); + TCHAR str[ 256 ]; + mir_sntprintf( str, SIZEOF(str), _T("== %s =="), TranslateT( "System default" )); + SendMessage( msgLangListBox, CB_ADDSTRING, 0, ( LPARAM )str ); + SendMessage( msgLangListBox, CB_SETITEMDATA, 0, CP_ACP ); + SendMessage( msgLangListBox, CB_SETCURSEL, 0, 0 ); + EnumSystemCodePagesA( JabberMsgLangAdd, CP_INSTALLED ); + + WNDPROC oldProc = ( WNDPROC ) GetWindowLong( GetDlgItem( hwndDlg, IDC_EDIT_USERNAME ), GWL_WNDPROC ); + SetWindowLong( GetDlgItem( hwndDlg, IDC_EDIT_USERNAME ), GWL_USERDATA, ( LONG ) oldProc ); + SetWindowLong( GetDlgItem( hwndDlg, IDC_EDIT_USERNAME ), GWL_WNDPROC, ( LONG ) JabberValidateUsernameWndProc ); + + return TRUE; + } + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_EDIT_USERNAME: + case IDC_EDIT_PASSWORD: + case IDC_EDIT_RESOURCE: + case IDC_EDIT_LOGIN_SERVER: + case IDC_PORT: + case IDC_MANUAL: + case IDC_HOST: + case IDC_HOSTPORT: + case IDC_JUD: + case IDC_PRIORITY: + { + if ( LOWORD( wParam ) == IDC_MANUAL ) { + if ( IsDlgButtonChecked( hwndDlg, IDC_MANUAL )) { + EnableWindow( GetDlgItem( hwndDlg, IDC_HOST ), TRUE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_HOSTPORT ), TRUE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_PORT ), FALSE ); + } + else { + EnableWindow( GetDlgItem( hwndDlg, IDC_HOST ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_HOSTPORT ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_PORT ), TRUE ); + } + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + } + else { + if (( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE ) + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + } + + ThreadData regInfo; + GetDlgItemText( hwndDlg, IDC_EDIT_USERNAME, regInfo.username, SIZEOF( regInfo.username )); + GetDlgItemTextA( hwndDlg, IDC_EDIT_PASSWORD, regInfo.password, SIZEOF( regInfo.password )); + GetDlgItemTextA( hwndDlg, IDC_EDIT_LOGIN_SERVER, regInfo.server, SIZEOF( regInfo.server )); + if ( IsDlgButtonChecked( hwndDlg, IDC_MANUAL )) { + regInfo.port = ( WORD )GetDlgItemInt( hwndDlg, IDC_HOSTPORT, NULL, FALSE ); + GetDlgItemTextA( hwndDlg, IDC_HOST, regInfo.manualHost, SIZEOF( regInfo.manualHost )); + } + else { + regInfo.port = ( WORD )GetDlgItemInt( hwndDlg, IDC_PORT, NULL, FALSE ); + regInfo.manualHost[0] = '\0'; + } + if ( regInfo.username[0] && regInfo.password[0] && regInfo.server[0] && regInfo.port>0 && ( !IsDlgButtonChecked( hwndDlg, IDC_MANUAL ) || regInfo.manualHost[0] )) + EnableWindow( GetDlgItem( hwndDlg, IDC_BUTTON_REGISTER ), TRUE ); + else + EnableWindow( GetDlgItem( hwndDlg, IDC_BUTTON_REGISTER ), FALSE ); + break; + } + case IDC_LINK_PUBLIC_SERVER: + ShellExecuteA( hwndDlg, "open", "http://www.jabber.org/network", "", "", SW_SHOW ); + return TRUE; + case IDC_BUTTON_REGISTER: + { + ThreadData regInfo; + GetDlgItemText( hwndDlg, IDC_EDIT_USERNAME, regInfo.username, SIZEOF( regInfo.username )); + GetDlgItemTextA( hwndDlg, IDC_EDIT_PASSWORD, regInfo.password, SIZEOF( regInfo.password )); + GetDlgItemTextA( hwndDlg, IDC_EDIT_LOGIN_SERVER, regInfo.server, SIZEOF( regInfo.server )); + if ( IsDlgButtonChecked( hwndDlg, IDC_MANUAL )) { + GetDlgItemTextA( hwndDlg, IDC_HOST, regInfo.manualHost, SIZEOF( regInfo.manualHost )); + regInfo.port = ( WORD )GetDlgItemInt( hwndDlg, IDC_HOSTPORT, NULL, FALSE ); + } + else { + regInfo.manualHost[0] = '\0'; + regInfo.port = ( WORD )GetDlgItemInt( hwndDlg, IDC_PORT, NULL, FALSE ); + } + regInfo.useSSL = IsDlgButtonChecked( hwndDlg, IDC_USE_SSL ); + + if ( regInfo.username[0] && regInfo.password[0] && regInfo.server[0] && regInfo.port>0 && ( !IsDlgButtonChecked( hwndDlg, IDC_MANUAL ) || regInfo.manualHost[0] )) + DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_OPT_REGISTER ), hwndDlg, JabberRegisterDlgProc, ( LPARAM )®Info ); + + return TRUE; + } + case IDC_MSGLANG: + if ( HIWORD( wParam ) == CBN_SELCHANGE ) + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + break; + case IDC_USE_SSL: + if ( !IsDlgButtonChecked( hwndDlg, IDC_MANUAL )) { + if ( IsDlgButtonChecked( hwndDlg, IDC_USE_SSL )) { + EnableWindow(GetDlgItem( hwndDlg, IDC_USE_TLS ), FALSE ); + SetDlgItemInt( hwndDlg, IDC_PORT, 5223, FALSE ); + } + else { + EnableWindow(GetDlgItem( hwndDlg, IDC_USE_TLS ), TRUE ); + SetDlgItemInt( hwndDlg, IDC_PORT, 5222, FALSE ); + } } + // Fall through + case IDC_USE_TLS: + case IDC_SAVEPASSWORD: + case IDC_KEEPALIVE: + case IDC_ROSTER_SYNC: + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + break; + default: + return 0; + } + break; + case WM_NOTIFY: + if (( ( LPNMHDR ) lParam )->code == PSN_APPLY ) { + BOOL reconnectRequired = FALSE; + DBVARIANT dbv; + + char userName[256], text[256]; + TCHAR textT [256]; + GetDlgItemTextA( hwndDlg, IDC_EDIT_USERNAME, userName, sizeof( userName )); + if ( DBGetContactSetting( NULL, jabberProtoName, "LoginName", &dbv ) || strcmp( userName, dbv.pszVal )) + reconnectRequired = TRUE; + if ( dbv.pszVal != NULL ) JFreeVariant( &dbv ); + JSetString( NULL, "LoginName", userName ); + + if ( IsDlgButtonChecked( hwndDlg, IDC_SAVEPASSWORD )) { + GetDlgItemTextA( hwndDlg, IDC_EDIT_PASSWORD, text, sizeof( text )); + JCallService( MS_DB_CRYPT_ENCODESTRING, sizeof( text ), ( LPARAM )text ); + if ( DBGetContactSetting( NULL, jabberProtoName, "Password", &dbv ) || strcmp( text, dbv.pszVal )) + reconnectRequired = TRUE; + if ( dbv.pszVal != NULL ) JFreeVariant( &dbv ); + JSetString( NULL, "Password", text ); + } + else JDeleteSetting( NULL, "Password" ); + + GetDlgItemText( hwndDlg, IDC_EDIT_RESOURCE, textT, SIZEOF( textT )); + if ( !JGetStringT( NULL, "Resource", &dbv )) { + if ( _tcscmp( textT, dbv.ptszVal )) + reconnectRequired = TRUE; + JFreeVariant( &dbv ); + } + else reconnectRequired = TRUE; + JSetStringT( NULL, "Resource", textT ); + + GetDlgItemTextA( hwndDlg, IDC_PRIORITY, text, sizeof( text )); + WORD port = ( WORD )atoi( text ); + if ( port > 100 ) port = 100; + if ( port < 0 ) port = 0; + if ( JGetWord( NULL, "Priority", 0 ) != port ) + reconnectRequired = TRUE; + JSetWord( NULL, "Priority", ( WORD )port ); + + JSetByte( "SavePassword", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_SAVEPASSWORD )); + + GetDlgItemTextA( hwndDlg, IDC_EDIT_LOGIN_SERVER, text, sizeof( text )); + if ( DBGetContactSetting( NULL, jabberProtoName, "LoginServer", &dbv ) || strcmp( text, dbv.pszVal )) + reconnectRequired = TRUE; + if ( dbv.pszVal != NULL ) JFreeVariant( &dbv ); + JSetString( NULL, "LoginServer", text ); + + strcat( userName, "@" ); + strncat( userName, text, sizeof( userName )); + userName[ sizeof(userName)-1 ] = 0; + JSetString( NULL, "jid", userName ); + + port = ( WORD )GetDlgItemInt( hwndDlg, IDC_PORT, NULL, FALSE ); + if ( JGetWord( NULL, "Port", JABBER_DEFAULT_PORT ) != port ) + reconnectRequired = TRUE; + JSetWord( NULL, "Port", port ); + + JSetByte( "UseSSL", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_USE_SSL )); + JSetByte( "UseTLS", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_USE_TLS )); + + JSetByte( "ManualConnect", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_MANUAL )); + + GetDlgItemTextA( hwndDlg, IDC_HOST, text, sizeof( text )); + if ( DBGetContactSetting( NULL, jabberProtoName, "ManualHost", &dbv ) || strcmp( text, dbv.pszVal )) + reconnectRequired = TRUE; + if ( dbv.pszVal != NULL ) JFreeVariant( &dbv ); + JSetString( NULL, "ManualHost", text ); + + port = ( WORD )GetDlgItemInt( hwndDlg, IDC_HOSTPORT, NULL, FALSE ); + if ( JGetWord( NULL, "ManualPort", JABBER_DEFAULT_PORT ) != port ) + reconnectRequired = TRUE; + JSetWord( NULL, "ManualPort", port ); + + JSetByte( "KeepAlive", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_KEEPALIVE )); + jabberSendKeepAlive = IsDlgButtonChecked( hwndDlg, IDC_KEEPALIVE ); + + JSetByte( "RosterSync", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_ROSTER_SYNC )); + + GetDlgItemTextA( hwndDlg, IDC_JUD, text, sizeof( text )); + JSetString( NULL, "Jud", text ); + + int index = SendMessage( GetDlgItem( hwndDlg, IDC_MSGLANG ), CB_GETCURSEL, 0, 0 ); + if ( index >= 0 ) { + jabberCodePage = SendMessage( GetDlgItem( hwndDlg, IDC_MSGLANG ), CB_GETITEMDATA, ( WPARAM ) index, 0 ); + JSetWord( NULL, "CodePage", ( WORD )jabberCodePage ); + } + + if ( reconnectRequired && jabberConnected ) + MessageBox( hwndDlg, TranslateT( "These changes will take effect the next time you connect to the Jabber network." ), TranslateT( "Jabber Protocol Option" ), MB_OK|MB_SETFOREGROUND ); + + return TRUE; + } + break; + } + + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberAdvOptDlgProc - advanced options dialog procedure + +static BOOL CALLBACK JabberAdvOptDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + char text[256]; + BOOL bChecked; + + switch ( msg ) { + case WM_INITDIALOG: + { + TranslateDialogDefault( hwndDlg ); + + // File transfer options + BOOL bDirect = JGetByte( "BsDirect", TRUE ); + BOOL bManualDirect = JGetByte( "BsDirectManual", FALSE ); + CheckDlgButton( hwndDlg, IDC_DIRECT, bDirect ); + CheckDlgButton( hwndDlg, IDC_DIRECT_MANUAL, bManualDirect ); + + DBVARIANT dbv; + if ( !DBGetContactSetting( NULL, jabberProtoName, "BsDirectAddr", &dbv )) { + SetDlgItemTextA( hwndDlg, IDC_DIRECT_ADDR, dbv.pszVal ); + JFreeVariant( &dbv ); + } + if ( !bDirect ) + EnableWindow( GetDlgItem( hwndDlg, IDC_DIRECT_MANUAL ), FALSE ); + if ( !bDirect || !bManualDirect ) + EnableWindow( GetDlgItem( hwndDlg, IDC_DIRECT_ADDR ), FALSE ); + + BOOL bProxy = JGetByte( "BsProxy", FALSE ); + BOOL bManualProxy = JGetByte( "BsProxyManual", FALSE ); + CheckDlgButton( hwndDlg, IDC_PROXY, bProxy ); + CheckDlgButton( hwndDlg, IDC_PROXY_MANUAL, bManualProxy ); + if ( !DBGetContactSetting( NULL, jabberProtoName, "BsProxyServer", &dbv )) { + SetDlgItemTextA( hwndDlg, IDC_PROXY_ADDR, dbv.pszVal ); + JFreeVariant( &dbv ); + } + if ( !bProxy ) + EnableWindow( GetDlgItem( hwndDlg, IDC_PROXY_MANUAL ), FALSE ); + if ( !bProxy || !bManualProxy ) + EnableWindow( GetDlgItem( hwndDlg, IDC_PROXY_ADDR ), FALSE ); + + // Miscellaneous options + CheckDlgButton( hwndDlg, IDC_SHOW_TRANSPORT, JGetByte( "ShowTransport", TRUE )); + CheckDlgButton( hwndDlg, IDC_AUTO_ADD, JGetByte( "AutoAdd", TRUE )); + CheckDlgButton( hwndDlg, IDC_MSG_ACK, JGetByte( "MsgAck", FALSE )); + CheckDlgButton( hwndDlg, IDC_DISABLE_MAINMENU, JGetByte( "DisableMainMenu", FALSE )); + CheckDlgButton( hwndDlg, IDC_ENABLE_AVATARS, JGetByte( "EnableAvatars", TRUE )); + CheckDlgButton( hwndDlg, IDC_AUTO_ACCEPT_MUC, JGetByte( "AutoAcceptMUC", FALSE )); + CheckDlgButton( hwndDlg, IDC_AUTOJOIN, JGetByte( "AutoJoinConferences", FALSE )); + CheckDlgButton( hwndDlg, IDC_DISABLE_SASL, JGetByte( "Disable3920auth", FALSE )); + return TRUE; + } + case WM_COMMAND: + { + switch ( LOWORD( wParam )) { + case IDC_DIRECT_ADDR: + case IDC_PROXY_ADDR: + if (( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE ) + goto LBL_Apply; + break; + case IDC_DIRECT: + bChecked = IsDlgButtonChecked( hwndDlg, IDC_DIRECT ); + EnableWindow( GetDlgItem( hwndDlg, IDC_DIRECT_MANUAL ), bChecked ); + EnableWindow( GetDlgItem( hwndDlg, IDC_DIRECT_ADDR ), ( bChecked && IsDlgButtonChecked( hwndDlg, IDC_DIRECT_MANUAL )) ); + goto LBL_Apply; + case IDC_DIRECT_MANUAL: + bChecked = IsDlgButtonChecked( hwndDlg, IDC_DIRECT_MANUAL ); + EnableWindow( GetDlgItem( hwndDlg, IDC_DIRECT_ADDR ), bChecked ); + goto LBL_Apply; + case IDC_PROXY: + bChecked = IsDlgButtonChecked( hwndDlg, IDC_PROXY ); + EnableWindow( GetDlgItem( hwndDlg, IDC_PROXY_MANUAL ), bChecked ); + EnableWindow( GetDlgItem( hwndDlg, IDC_PROXY_ADDR ), ( bChecked && IsDlgButtonChecked( hwndDlg, IDC_PROXY_MANUAL )) ); + goto LBL_Apply; + case IDC_PROXY_MANUAL: + bChecked = IsDlgButtonChecked( hwndDlg, IDC_PROXY_MANUAL ); + EnableWindow( GetDlgItem( hwndDlg, IDC_PROXY_ADDR ), bChecked ); + default: + LBL_Apply: + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + break; + } + break; + } + case WM_NOTIFY: + if (( ( LPNMHDR ) lParam )->code == PSN_APPLY ) { + // File transfer options + JSetByte( "BsDirect", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_DIRECT )); + JSetByte( "BsDirectManual", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_DIRECT_MANUAL )); + GetDlgItemTextA( hwndDlg, IDC_DIRECT_ADDR, text, sizeof( text )); + JSetString( NULL, "BsDirectAddr", text ); + JSetByte( "BsProxy", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_PROXY )); + JSetByte( "BsProxyManual", ( BYTE ) IsDlgButtonChecked( hwndDlg, IDC_PROXY_MANUAL )); + GetDlgItemTextA( hwndDlg, IDC_PROXY_ADDR, text, sizeof( text )); + JSetString( NULL, "BsProxyAddr", text ); + + // Miscellaneous options + bChecked = IsDlgButtonChecked( hwndDlg, IDC_SHOW_TRANSPORT ); + JSetByte( "ShowTransport", ( BYTE ) bChecked ); + int index = 0; + while (( index=JabberListFindNext( LIST_ROSTER, index )) >= 0 ) { + JABBER_LIST_ITEM* item = JabberListGetItemPtrFromIndex( index ); + if ( item != NULL ) { + if ( _tcschr( item->jid, '@' ) == NULL ) { + HANDLE hContact = JabberHContactFromJID( item->jid ); + if ( hContact != NULL ) { + if ( bChecked ) { + if ( item->status != JGetWord( hContact, "Status", ID_STATUS_OFFLINE )) { + JSetWord( hContact, "Status", ( WORD )item->status ); + } } + else if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != ID_STATUS_OFFLINE ) + JSetWord( hContact, "Status", ID_STATUS_OFFLINE ); + } } } + index++; + } + + JSetByte( "AutoAdd", ( BYTE )IsDlgButtonChecked( hwndDlg, IDC_AUTO_ADD )); + JSetByte( "MsgAck", ( BYTE )IsDlgButtonChecked( hwndDlg, IDC_MSG_ACK )); + JSetByte( "DisableMainMenu", ( BYTE )IsDlgButtonChecked( hwndDlg, IDC_DISABLE_MAINMENU )); + JSetByte( "Disable3920auth", ( BYTE )IsDlgButtonChecked( hwndDlg, IDC_DISABLE_SASL )); + JSetByte( "EnableAvatars", ( BYTE )IsDlgButtonChecked( hwndDlg, IDC_ENABLE_AVATARS )); + JSetByte( "AutoAcceptMUC", ( BYTE )IsDlgButtonChecked( hwndDlg, IDC_AUTO_ACCEPT_MUC )); + JSetByte( "AutoJoinConferences", ( BYTE )IsDlgButtonChecked( hwndDlg, IDC_AUTOJOIN )); + return TRUE; + } + break; + } + + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberOptInit - initializes all options dialogs + +static HWND hwndAcc = 0, hwndAdv = 0; + +static void SetOptionsDlgToType(HWND hwnd, int iExpert) +{ + TCITEM tci; + RECT rcClient; + HWND hwndTab = GetDlgItem(hwnd, IDC_OPTIONSTAB), hwndEnum; + int iPages = 0; + + if(!hwndAcc) + hwndAcc = CreateDialog(hInst,MAKEINTRESOURCE(IDD_OPT_JABBER), hwnd, JabberOptDlgProc); + + hwndEnum = GetWindow(hwndAcc, GW_CHILD); + + while(hwndEnum) { + ShowWindow(hwndEnum, iExpert ? SW_SHOW : SW_HIDE); + hwndEnum = GetWindow(hwndEnum, GW_HWNDNEXT); + } + if(!iExpert) { + hwndEnum = GetDlgItem(hwndAcc, IDC_SIMPLE); + ShowWindow(hwndEnum, SW_SHOW); + hwndEnum = GetWindow(hwndEnum, GW_HWNDNEXT); + do { + ShowWindow(hwndEnum, SW_SHOW); + hwndEnum = GetWindow(hwndEnum, GW_HWNDNEXT); + } while(hwndEnum && hwndEnum != GetDlgItem(hwndAcc, IDC_LINK_PUBLIC_SERVER)); + } + ShowWindow(hwndEnum, SW_SHOW); + GetClientRect(hwnd, &rcClient); + TabCtrl_DeleteAllItems(hwndTab); + + tci.mask = TCIF_PARAM|TCIF_TEXT; + tci.lParam = (LPARAM)hwndAcc; + tci.pszText = TranslateT("Account"); + TabCtrl_InsertItem(hwndTab, 0, &tci); + MoveWindow((HWND)tci.lParam,5,26,rcClient.right-8,rcClient.bottom-29,1); + iPages++; + + if(!hwndAdv) + hwndAdv = CreateDialog(hInst,MAKEINTRESOURCE(IDD_OPT_JABBER2),hwnd,JabberAdvOptDlgProc); + + if(pfnEnableThemeDialogTexture) { + if(hwndAcc) + pfnEnableThemeDialogTexture(hwndAcc, ETDT_ENABLETAB); + if(hwndAdv) + pfnEnableThemeDialogTexture(hwndAdv, ETDT_ENABLETAB); + } + + ShowWindow(hwndAdv, SW_HIDE); + ShowWindow(hwndAcc, SW_SHOW); + + if(iExpert) { + tci.lParam = (LPARAM)hwndAdv; + tci.pszText = TranslateT("Advanced"); + TabCtrl_InsertItem(hwndTab, iPages++, &tci); + MoveWindow((HWND)tci.lParam,5,26,rcClient.right-8,rcClient.bottom-29,1); + } + TabCtrl_SetCurSel(hwndTab, 0); +} + +static BOOL CALLBACK OptionsDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static int iInit = TRUE; + + switch(msg) { + case WM_INITDIALOG: + { + iInit = TRUE; + int iExpert = SendMessage(GetParent(hwnd), PSM_ISEXPERT, 0, 0); + SetOptionsDlgToType(hwnd, iExpert); + iInit = FALSE; + return FALSE; + } + case WM_DESTROY: + hwndAcc = hwndAdv = 0; + break; + case PSM_CHANGED: // used so tabs dont have to call SendMessage(GetParent(GetParent(hwnd)), PSM_CHANGED, 0, 0); + if(!iInit) + SendMessage(GetParent(hwnd), PSM_CHANGED, 0, 0); + break; + case WM_NOTIFY: + switch(((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_APPLY: + { + TCITEM tci; + int i,count = TabCtrl_GetItemCount(GetDlgItem(hwnd,IDC_OPTIONSTAB)); + tci.mask = TCIF_PARAM; + for (i=0;i<count;i++) { + TabCtrl_GetItem(GetDlgItem(hwnd,IDC_OPTIONSTAB),i,&tci); + SendMessage((HWND)tci.lParam,WM_NOTIFY,0,lParam); + } + break; + } + case PSN_EXPERTCHANGED: + { + int iExpert = SendMessage(GetParent(hwnd), PSM_ISEXPERT, 0, 0); + SetOptionsDlgToType(hwnd, iExpert); + break; + } } + break; + case IDC_OPTIONSTAB: + switch (((LPNMHDR)lParam)->code) { + case TCN_SELCHANGING: + { + TCITEM tci; + tci.mask = TCIF_PARAM; + TabCtrl_GetItem(GetDlgItem(hwnd,IDC_OPTIONSTAB),TabCtrl_GetCurSel(GetDlgItem(hwnd,IDC_OPTIONSTAB)),&tci); + ShowWindow((HWND)tci.lParam,SW_HIDE); + } + break; + case TCN_SELCHANGE: + { + TCITEM tci; + tci.mask = TCIF_PARAM; + TabCtrl_GetItem(GetDlgItem(hwnd,IDC_OPTIONSTAB),TabCtrl_GetCurSel(GetDlgItem(hwnd,IDC_OPTIONSTAB)),&tci); + ShowWindow((HWND)tci.lParam,SW_SHOW); + break; + } } + break; + } + break; + } + return FALSE; +} + +int JabberOptInit( WPARAM wParam, LPARAM lParam ) +{ + OPTIONSDIALOGPAGE odp = { 0 }; + HMODULE hUxTheme = 0; + + if(IsWinVerXPPlus()) { + hUxTheme = GetModuleHandle(_T("uxtheme.dll")); + + if(hUxTheme) + pfnEnableThemeDialogTexture = (BOOL (WINAPI *)(HANDLE, DWORD))GetProcAddress(hUxTheme, "EnableThemeDialogTexture"); + } + + odp.cbSize = sizeof( odp ); + odp.hInstance = hInst; + odp.pszGroup = "Network"; + odp.pszTemplate = MAKEINTRESOURCEA( IDD_OPT_JABBERMAIN ); + odp.pszTitle = jabberModuleName; + odp.pfnDlgProc = OptionsDlgProc; + odp.flags = ODPF_BOLDGROUPS; + JCallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp ); + return 0; +} diff --git a/miranda-wine/protocols/JabberG/jabber_password.cpp b/miranda-wine/protocols/JabberG/jabber_password.cpp new file mode 100644 index 0000000..da4c4c2 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_password.cpp @@ -0,0 +1,100 @@ +/* + +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_password.cpp,v $ +Revision : $Revision: 3351 $ +Last change on : $Date: 2006-07-21 00:31:42 +0400 (Птн, 21 Июл 2006) $ +Last change by : $Author: rainwater $ + +*/ + +#include "jabber.h" +#include "jabber_iq.h" +#include "resource.h" + +static BOOL CALLBACK JabberChangePasswordDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ); + +int JabberMenuHandleChangePassword( WPARAM wParam, LPARAM lParam ) +{ + if ( IsWindow( hwndJabberChangePassword )) + SetForegroundWindow( hwndJabberChangePassword ); + else { + hwndJabberChangePassword = CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_CHANGEPASSWORD ), NULL, JabberChangePasswordDlgProc, 0 ); + } + + return 0; +} + +static BOOL CALLBACK JabberChangePasswordDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + SendMessage( hwndDlg, WM_SETICON, ICON_BIG, ( LPARAM )LoadIcon( hInst, MAKEINTRESOURCE( IDI_KEYS )) ); + TranslateDialogDefault( hwndDlg ); + if ( jabberOnline && jabberThreadInfo!=NULL ) { + TCHAR text[128]; + mir_sntprintf( text, SIZEOF( text ), _T("%s %s@") _T(TCHAR_STR_PARAM), TranslateT( "Set New Password for" ), jabberThreadInfo->username, jabberThreadInfo->server ); + SetWindowText( hwndDlg, text ); + } + return TRUE; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDOK: + if ( jabberOnline && jabberThreadInfo!=NULL ) { + char newPasswd[128], text[128]; + GetDlgItemTextA( hwndDlg, IDC_NEWPASSWD, newPasswd, SIZEOF( newPasswd )); + GetDlgItemTextA( hwndDlg, IDC_NEWPASSWD2, text, SIZEOF( text )); + if ( strcmp( newPasswd, text )) { + MessageBox( hwndDlg, TranslateT( "New password does not match." ), TranslateT( "Change Password" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND ); + break; + } + GetDlgItemTextA( hwndDlg, IDC_OLDPASSWD, text, SIZEOF( text )); + if ( strcmp( text, jabberThreadInfo->password )) { + MessageBox( hwndDlg, TranslateT( "Current password is incorrect." ), TranslateT( "Change Password" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND ); + break; + } + strncpy( jabberThreadInfo->newPassword, newPasswd, SIZEOF( jabberThreadInfo->newPassword )); + + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultSetPassword ); + + XmlNodeIq iq( "set", iqId, jabberThreadInfo->server ); + XmlNode* q = iq.addQuery( "jabber:iq:register" ); + q->addChild( "username", jabberThreadInfo->username ); + q->addChild( "password", newPasswd ); + JabberSend( jabberThreadInfo->s, iq ); + } + DestroyWindow( hwndDlg ); + break; + case IDCANCEL: + DestroyWindow( hwndDlg ); + break; + } + break; + case WM_CLOSE: + DestroyWindow( hwndDlg ); + break; + case WM_DESTROY: + hwndJabberChangePassword = NULL; + break; + } + + return FALSE; +} diff --git a/miranda-wine/protocols/JabberG/jabber_proxy.cpp b/miranda-wine/protocols/JabberG/jabber_proxy.cpp new file mode 100644 index 0000000..51fae58 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_proxy.cpp @@ -0,0 +1,154 @@ +/* + +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_proxy.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" + +int JabberHttpGatewayInit( HANDLE hConn, NETLIBOPENCONNECTION *nloc, NETLIBHTTPREQUEST *nlhr ) +{ +#ifdef NNNN + WORD wLen, wVersion, wType; + WORD wIpLen; + DWORD dwSid1, dwSid2, dwSid3, dwSid4; + BYTE response[300], *buf; + int responseBytes, recvResult; + char szSid[33], szHttpServer[256], szHttpGetUrl[300], szHttpPostUrl[300]; + NETLIBHTTPPROXYINFO nlhpi = {0}; + + for( responseBytes = 0; ; ) { + recvResult = Netlib_Recv( hConn, response + responseBytes, sizeof( response ) - responseBytes, MSG_DUMPPROXY ); + if( recvResult<=0 ) break; + responseBytes += recvResult; + if( responseBytes == sizeof( response )) + break; + } + if( responseBytes < 31 ) + { + SetLastError( ERROR_INVALID_DATA ); + return 0; + } + buf = response; + unpackWord( &buf, &wLen ); + unpackWord( &buf, &wVersion ); /* always 0x0443 */ + unpackWord( &buf, &wType ); + buf += 6; /* dunno */ + unpackDWord( &buf, &dwSid1 ); + unpackDWord( &buf, &dwSid2 ); + unpackDWord( &buf, &dwSid3 ); + unpackDWord( &buf, &dwSid4 ); + sprintf( szSid, "%08x%08x%08x%08x", dwSid1, dwSid2, dwSid3, dwSid4 ); + unpackWord( &buf, &wIpLen ); + if( responseBytes < 30 + wIpLen || wIpLen == 0 || wIpLen > sizeof( szHttpServer ) - 1 ) + { + SetLastError( ERROR_INVALID_DATA ); + return 0; + } + memcpy( szHttpServer, buf, wIpLen ); + szHttpServer[wIpLen] = '\0'; + + nlhpi.cbSize = sizeof( nlhpi ); + nlhpi.flags = NLHPIF_USEPOSTSEQUENCE; + nlhpi.szHttpGetUrl = szHttpGetUrl; + nlhpi.szHttpPostUrl = szHttpPostUrl; + nlhpi.firstPostSequence = 1; + sprintf( szHttpGetUrl, "http://%s/monitor?sid=%s", szHttpServer, szSid ); + sprintf( szHttpPostUrl, "http://%s/data?sid=%s&seq=", szHttpServer, szSid ); + return JCallService( MS_NETLIB_SETHTTPPROXYINFO, ( WPARAM )hConn, ( LPARAM )&nlhpi ); +#endif + return 1; +} + +int JabberHttpGatewayBegin( HANDLE hConn, NETLIBOPENCONNECTION *nloc ) +{ + /* + icq_packet packet; + int serverNameLen; + + serverNameLen = strlen( nloc->szHost ); + + packet.wLen = ( WORD )( serverNameLen + 4 ); + write_httphdr( &packet, HTTP_PACKETTYPE_LOGIN ); + packWord( &packet, ( WORD )serverNameLen ); + packString( &packet, nloc->szHost, ( WORD )serverNameLen ); + packWord( &packet, nloc->wPort ); + Netlib_Send( hConn, packet.pData, packet.wLen, MSG_DUMPPROXY|MSG_NOHTTPGATEWAYWRAP ); + mir_free( packet.pData ); + return 1; + */ + return 1; +} + +#if 0 +int icq_httpGatewayWrapSend( HANDLE hConn, PBYTE buf, int len, int flags, MIRANDASERVICE pfnNetlibSend ) +{ + icq_packet packet; + int sendResult; + + packet.wLen = len; + write_httphdr( &packet, HTTP_PACKETTYPE_FLAP ); + packString( &packet, buf, ( WORD )len ); + sendResult = Netlib_Send( hConn, packet.pData, packet.wLen, flags ); + mir_free( packet.pData ); + if( sendResult <= 0 ) + return sendResult; + if( sendResult < 14 ) + return 0; + return sendResult - 14; +} + +PBYTE icq_httpGatewayUnwrapRecv( NETLIBHTTPREQUEST *nlhr, PBYTE buf, int len, int *outBufLen, void *( *NetlibRealloc )( void *, size_t )) +{ + WORD wLen, wType; + PBYTE tbuf; + int i, copyBytes; + + tbuf = buf; + for( i = 0;; ) + { + if ( tbuf - buf + 2 > len ) break; + unpackWord( &tbuf, &wLen ); + if ( wLen < 12 ) break; + if ( tbuf - buf + wLen > len ) break; + tbuf += 2; /* version */ + unpackWord( &tbuf, &wType ); + tbuf += 8; /* flags & subtype */ + if ( wType == HTTP_PACKETTYPE_FLAP ) + { + copyBytes = wLen - 12; + if ( copyBytes > len - i ) + { + /* invalid data - do our best to get something out of it */ + copyBytes = len - i; + } + memcpy( buf + i, tbuf, copyBytes ); + i += copyBytes; + } + tbuf += wLen - 12; + } + *outBufLen = i; + return buf; +} +#endif diff --git a/miranda-wine/protocols/JabberG/jabber_proxy.h b/miranda-wine/protocols/JabberG/jabber_proxy.h new file mode 100644 index 0000000..02a5d9f --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_proxy.h @@ -0,0 +1,34 @@ +/* + +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_proxy.h,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#ifndef _JABBER_PROXY_H_ +#define _JABBER_PROXY_H_ + +int JabberHttpGatewayInit( HANDLE hConn, NETLIBOPENCONNECTION *nloc, NETLIBHTTPREQUEST *nlhr ); +int JabberHttpGatewayBegin( HANDLE hConn, NETLIBOPENCONNECTION *nloc ); + +#endif \ No newline at end of file diff --git a/miranda-wine/protocols/JabberG/jabber_ssl.cpp b/miranda-wine/protocols/JabberG/jabber_ssl.cpp new file mode 100644 index 0000000..5c7f23b --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_ssl.cpp @@ -0,0 +1,181 @@ +/* + +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_ssl.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#define _JABBER_SSL_C_ + +#include "jabber.h" +#include "jabber_ssl.h" + +PFN_SSL_int_void pfn_SSL_library_init; // int SSL_library_init() +PFN_SSL_pvoid_void pfn_SSLv23_client_method; // SSL_METHOD *SSLv23_client_method() +PFN_SSL_pvoid_pvoid pfn_SSL_CTX_new; // SSL_CTX *SSL_CTX_new( SSL_METHOD *method ) +PFN_SSL_void_pvoid pfn_SSL_CTX_free; // void SSL_CTX_free( SSL_CTX *ctx ); +PFN_SSL_pvoid_pvoid pfn_SSL_new; // SSL *SSL_new( SSL_CTX *ctx ) +PFN_SSL_void_pvoid pfn_SSL_free; // void SSL_free( SSL *ssl ); +PFN_SSL_int_pvoid_int pfn_SSL_set_fd; // int SSL_set_fd( SSL *ssl, int fd ); +PFN_SSL_int_pvoid pfn_SSL_connect; // int SSL_connect( SSL *ssl ); +PFN_SSL_int_pvoid_pvoid_int pfn_SSL_read; // int SSL_read( SSL *ssl, void *buffer, int bufsize ) +PFN_SSL_int_pvoid_pvoid_int pfn_SSL_write; // int SSL_write( SSL *ssl, void *buffer, int bufsize ) + +static CRITICAL_SECTION sslHandleMutex; +static JABBER_SSL_MAPPING *sslHandleList = NULL; +static int sslHandleCount = 0; + +BOOL JabberSslInit() +{ + BOOL error = FALSE; + + sslHandleList = NULL; + sslHandleCount = 0; + InitializeCriticalSection( &sslHandleMutex ); + + hLibSSL = LoadLibraryA( "SSLEAY32.DLL" ); + + if ( !hLibSSL ) + hLibSSL = LoadLibraryA( "LIBSSL32.DLL" ); + + if ( hLibSSL ) { + if (( pfn_SSL_library_init=( PFN_SSL_int_void )GetProcAddress( hLibSSL, "SSL_library_init" )) == NULL ) + error = TRUE; + if (( pfn_SSLv23_client_method=( PFN_SSL_pvoid_void )GetProcAddress( hLibSSL, "SSLv23_client_method" )) == NULL ) + error = TRUE; + if (( pfn_SSL_CTX_new=( PFN_SSL_pvoid_pvoid )GetProcAddress( hLibSSL, "SSL_CTX_new" )) == NULL ) + error = TRUE; + if (( pfn_SSL_CTX_free=( PFN_SSL_void_pvoid )GetProcAddress( hLibSSL, "SSL_CTX_free" )) == NULL ) + error = TRUE; + if (( pfn_SSL_new=( PFN_SSL_pvoid_pvoid )GetProcAddress( hLibSSL, "SSL_new" )) == NULL ) + error = TRUE; + if (( pfn_SSL_free=( PFN_SSL_void_pvoid )GetProcAddress( hLibSSL, "SSL_free" )) == NULL ) + error = TRUE; + if (( pfn_SSL_set_fd=( PFN_SSL_int_pvoid_int )GetProcAddress( hLibSSL, "SSL_set_fd" )) == NULL ) + error = TRUE; + if (( pfn_SSL_connect=( PFN_SSL_int_pvoid )GetProcAddress( hLibSSL, "SSL_connect" )) == NULL ) + error = TRUE; + if (( pfn_SSL_read=( PFN_SSL_int_pvoid_pvoid_int )GetProcAddress( hLibSSL, "SSL_read" )) == NULL ) + error = TRUE; + if (( pfn_SSL_write=( PFN_SSL_int_pvoid_pvoid_int )GetProcAddress( hLibSSL, "SSL_write" )) == NULL ) + error = TRUE; + + if ( error == TRUE ) { + FreeLibrary( hLibSSL ); + hLibSSL = NULL; + } + } + + +#ifdef _DEBUG + if ( hLibSSL ) + JabberLog( "SSL library load successful" ); + else + JabberLog( "SSL library cannot load" ); +#endif + + if ( hLibSSL ) { + pfn_SSL_library_init(); + jabberSslCtx = pfn_SSL_CTX_new( pfn_SSLv23_client_method()); + + return TRUE; + } + else + return FALSE; +} + +void JabberSslUninit() +{ + if ( hLibSSL ) { + pfn_SSL_CTX_free( jabberSslCtx ); + + JabberLog( "Free SSL library" ); + FreeLibrary( hLibSSL ); + hLibSSL = NULL; + } + + if ( sslHandleList ) mir_free( sslHandleList ); + sslHandleCount = 0; + DeleteCriticalSection( &sslHandleMutex ); +} + +int JabberSslFindHandle( HANDLE hConn ) +{ + int i; + + EnterCriticalSection( &sslHandleMutex ); + for ( i=0; i<sslHandleCount; i++ ) { + if ( sslHandleList[i].h == hConn ) { + LeaveCriticalSection( &sslHandleMutex ); + return i; + } + } + LeaveCriticalSection( &sslHandleMutex ); + return -1; +} + +PVOID JabberSslHandleToSsl( HANDLE hConn ) +{ + int i; + + EnterCriticalSection( &sslHandleMutex ); + for ( i=0; i<sslHandleCount; i++ ) { + if ( sslHandleList[i].h == hConn ) { + LeaveCriticalSection( &sslHandleMutex ); + return sslHandleList[i].ssl; + } + } + LeaveCriticalSection( &sslHandleMutex ); + return NULL; +} + +void JabberSslAddHandle( HANDLE hConn, PVOID ssl ) +{ + EnterCriticalSection( &sslHandleMutex ); + if ( JabberSslFindHandle( hConn ) >= 0 ) { + LeaveCriticalSection( &sslHandleMutex ); + return; + } + + sslHandleList = ( JABBER_SSL_MAPPING * ) mir_realloc( sslHandleList, ( sslHandleCount+1 )*sizeof( JABBER_SSL_MAPPING )); + sslHandleList[sslHandleCount].h = hConn; + sslHandleList[sslHandleCount].ssl = ssl; + sslHandleCount++; + LeaveCriticalSection( &sslHandleMutex ); +} + +void JabberSslRemoveHandle( HANDLE hConn ) +{ + int i; + + EnterCriticalSection( &sslHandleMutex ); + if (( i=JabberSslFindHandle( hConn )) < 0 ) { + LeaveCriticalSection( &sslHandleMutex ); + return; + } + + sslHandleCount--; + memmove( sslHandleList+i, sslHandleList+i+1, ( sslHandleCount-i )*sizeof( JABBER_SSL_MAPPING )); + sslHandleList = ( JABBER_SSL_MAPPING * ) mir_realloc( sslHandleList, sslHandleCount*sizeof( JABBER_SSL_MAPPING )); + LeaveCriticalSection( &sslHandleMutex ); +} diff --git a/miranda-wine/protocols/JabberG/jabber_ssl.h b/miranda-wine/protocols/JabberG/jabber_ssl.h new file mode 100644 index 0000000..5832b40 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_ssl.h @@ -0,0 +1,64 @@ +/* + +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_ssl.h,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#ifndef _JABBER_SSL_H_ +#define _JABBER_SSL_H_ + +typedef int ( *PFN_SSL_int_void ) ( void ); +typedef PVOID ( *PFN_SSL_pvoid_void ) ( void ); +typedef PVOID ( *PFN_SSL_pvoid_pvoid ) ( PVOID ); +typedef void ( *PFN_SSL_void_pvoid ) ( PVOID ); +typedef int ( *PFN_SSL_int_pvoid_int ) ( PVOID, int ); +typedef int ( *PFN_SSL_int_pvoid ) ( PVOID ); +typedef int ( *PFN_SSL_int_pvoid_pvoid_int ) ( PVOID, PVOID, int ); + +#ifndef _JABBER_SSL_C_ +extern PFN_SSL_int_void pfn_SSL_library_init; // int SSL_library_init() +extern PFN_SSL_pvoid_void pfn_SSLv23_client_method; // SSL_METHOD *SSLv23_client_method() +extern PFN_SSL_pvoid_pvoid pfn_SSL_CTX_new; // SSL_CTX *SSL_CTX_new( SSL_METHOD *method ) +extern PFN_SSL_void_pvoid pfn_SSL_CTX_free; // void SSL_CTX_free( SSL_CTX *ctx ); +extern PFN_SSL_pvoid_pvoid pfn_SSL_new; // SSL *SSL_new( SSL_CTX *ctx ) +extern PFN_SSL_void_pvoid pfn_SSL_free; // void SSL_free( SSL *ssl ); +extern PFN_SSL_int_pvoid_int pfn_SSL_set_fd; // int SSL_set_fd( SSL *ssl, int fd ); +extern PFN_SSL_int_pvoid pfn_SSL_connect; // int SSL_connect( SSL *ssl ); +extern PFN_SSL_int_pvoid_pvoid_int pfn_SSL_read; // int SSL_read( SSL *ssl, void *buffer, int bufsize ) +extern PFN_SSL_int_pvoid_pvoid_int pfn_SSL_write; // int SSL_write( SSL *ssl, void *buffer, int bufsize ) +#endif + +typedef struct { + HANDLE h; + PVOID ssl; +} JABBER_SSL_MAPPING; + +BOOL JabberSslInit(); +void JabberSslUninit(); +int JabberSslFindHandle( HANDLE hConn ); +PVOID JabberSslHandleToSsl( HANDLE hConn ); +void JabberSslAddHandle( HANDLE hConn, PVOID ssl ); +void JabberSslRemoveHandle( HANDLE hConn ); + +#endif diff --git a/miranda-wine/protocols/JabberG/jabber_std.cpp b/miranda-wine/protocols/JabberG/jabber_std.cpp new file mode 100644 index 0000000..562beda --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_std.cpp @@ -0,0 +1,171 @@ +/* +Plugin of Miranda IM for communicating with users of the MSN Messenger protocol. +Copyright ( c ) 2003-5 George Hazan. +Copyright ( c ) 2002-3 Richard Hughes ( original version ). + +Miranda IM: the mir_free icq client for MS Windows +Copyright ( C ) 2000-2002 Richard Hughes, Roland Rabien & Tristan Van de Vreede + +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_std.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" + +HANDLE __stdcall JCreateServiceFunction( + const char* szService, + MIRANDASERVICE serviceProc ) +{ + char str[ MAXMODULELABELLENGTH ]; + strcpy( str, jabberProtoName ); + strcat( str, szService ); + return CreateServiceFunction( str, serviceProc ); +} + +HANDLE __stdcall JCreateHookableEvent( + const char* szService ) +{ + char str[ MAXMODULELABELLENGTH ]; + strcpy( str, jabberProtoName ); + strcat( str, szService ); + return CreateHookableEvent( str ); +} + +#if !defined( _DEBUG ) +int __stdcall JCallService( const char* szSvcName, WPARAM wParam, LPARAM lParam ) +{ + return CallService( szSvcName, wParam, lParam ); +} +#endif + +void __stdcall JDeleteSetting( HANDLE hContact, const char* valueName ) +{ + DBDeleteContactSetting( hContact, jabberProtoName, valueName ); +} + +DWORD __stdcall JGetByte( const char* valueName, int parDefltValue ) +{ + return DBGetContactSettingByte( NULL, jabberProtoName, valueName, parDefltValue ); +} + +DWORD __stdcall JGetByte( HANDLE hContact, const char* valueName, int parDefltValue ) +{ + return DBGetContactSettingByte( hContact, jabberProtoName, valueName, parDefltValue ); +} + +char* __stdcall JGetContactName( HANDLE hContact ) +{ + return ( char* )JCallService( MS_CLIST_GETCONTACTDISPLAYNAME, WPARAM( hContact ), 0 ); +} + +DWORD __stdcall JGetDword( HANDLE hContact, const char* valueName, DWORD parDefltValue ) +{ + return DBGetContactSettingDword( hContact, jabberProtoName, valueName, parDefltValue ); +} + +int __stdcall JGetStaticString( const char* valueName, HANDLE hContact, char* dest, int dest_len ) +{ + DBVARIANT dbv; + dbv.pszVal = dest; + dbv.cchVal = dest_len; + dbv.type = DBVT_ASCIIZ; + + DBCONTACTGETSETTING sVal; + sVal.pValue = &dbv; + sVal.szModule = jabberProtoName; + sVal.szSetting = valueName; + if ( JCallService( MS_DB_CONTACT_GETSETTINGSTATIC, ( WPARAM )hContact, ( LPARAM )&sVal ) != 0 ) + return 1; + + return ( dbv.type != DBVT_ASCIIZ ); +} + +int __stdcall JGetStringUtf( HANDLE hContact, char* valueName, DBVARIANT* dbv ) +{ + return DBGetContactSettingStringUtf( hContact, jabberProtoName, valueName, dbv ); +} + +int __stdcall JGetStringT( HANDLE hContact, char* valueName, DBVARIANT* dbv ) +{ + return DBGetContactSettingTString( hContact, jabberProtoName, valueName, dbv ); +} + +WORD __stdcall JGetWord( HANDLE hContact, const char* valueName, int parDefltValue ) +{ + return DBGetContactSettingWord( hContact, jabberProtoName, valueName, parDefltValue ); +} + +void __fastcall JFreeVariant( DBVARIANT* dbv ) +{ + DBFreeVariant( dbv ); +} + +int __stdcall JSendBroadcast( HANDLE hContact, int type, int result, HANDLE hProcess, LPARAM lParam ) +{ + ACKDATA ack = {0}; + ack.cbSize = sizeof( ACKDATA ); + ack.szModule = jabberProtoName; + ack.hContact = hContact; + ack.type = type; + ack.result = result; + ack.hProcess = hProcess; + ack.lParam = lParam; + return JCallService( MS_PROTO_BROADCASTACK, 0, ( LPARAM )&ack ); +} + +DWORD __stdcall JSetByte( const char* valueName, int parValue ) +{ + return DBWriteContactSettingByte( NULL, jabberProtoName, valueName, parValue ); +} + +DWORD __stdcall JSetByte( HANDLE hContact, const char* valueName, int parValue ) +{ + return DBWriteContactSettingByte( hContact, jabberProtoName, valueName, parValue ); +} + +DWORD __stdcall JSetDword( HANDLE hContact, const char* valueName, DWORD parValue ) +{ + return DBWriteContactSettingDword( hContact, jabberProtoName, valueName, parValue ); +} + +DWORD __stdcall JSetString( HANDLE hContact, const char* valueName, const char* parValue ) +{ + return DBWriteContactSettingString( hContact, jabberProtoName, valueName, parValue ); +} + +DWORD __stdcall JSetStringT( HANDLE hContact, const char* valueName, const TCHAR* parValue ) +{ + return DBWriteContactSettingTString( hContact, jabberProtoName, valueName, parValue ); +} + +DWORD __stdcall JSetStringUtf( HANDLE hContact, const char* valueName, const char* parValue ) +{ + return DBWriteContactSettingStringUtf( hContact, jabberProtoName, valueName, parValue ); +} + +DWORD __stdcall JSetWord( HANDLE hContact, const char* valueName, int parValue ) +{ + return DBWriteContactSettingWord( hContact, jabberProtoName, valueName, parValue ); +} + +char* __stdcall JTranslate( const char* str ) +{ + return Translate( str ); +} 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; +} diff --git a/miranda-wine/protocols/JabberG/jabber_thread.cpp b/miranda-wine/protocols/JabberG/jabber_thread.cpp new file mode 100644 index 0000000..84eabb7 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_thread.cpp @@ -0,0 +1,1793 @@ +/* + +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_thread.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 <io.h> +#include <WinDNS.h> // requires Windows Platform SDK + +#include "jabber_ssl.h" +#include "jabber_list.h" +#include "jabber_iq.h" +#include "resource.h" + +// <iq/> identification number for various actions +// for JABBER_REGISTER thread +unsigned int iqIdRegGetReg; +unsigned int iqIdRegSetReg; + +static void __cdecl JabberKeepAliveThread( JABBER_SOCKET s ); +static void JabberProcessStreamOpening( XmlNode *node, void *userdata ); +static void JabberProcessStreamClosing( XmlNode *node, void *userdata ); +static void JabberProcessProtocol( XmlNode *node, void *userdata ); +static void JabberProcessMessage( XmlNode *node, void *userdata ); +static void JabberProcessPresence( XmlNode *node, void *userdata ); +static void JabberProcessIq( XmlNode *node, void *userdata ); +static void JabberProcessProceed( XmlNode *node, void *userdata ); +static void JabberProcessRegIq( XmlNode *node, void *userdata ); + +static VOID CALLBACK JabberDummyApcFunc( DWORD param ) +{ + return; +} + +static char onlinePassword[128]; +static HANDLE hEventPasswdDlg; + +static BOOL CALLBACK JabberPasswordDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + { TCHAR text[128]; + mir_sntprintf( text, SIZEOF(text), _T("%s %s"), TranslateT( "Enter password for" ), ( TCHAR* )lParam ); + SetDlgItemText( hwndDlg, IDC_JID, text ); + } + return TRUE; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDOK: + GetDlgItemTextA( hwndDlg, IDC_PASSWORD, onlinePassword, SIZEOF( onlinePassword )); + //EndDialog( hwndDlg, ( int ) onlinePassword ); + //return TRUE; + // Fall through + case IDCANCEL: + //EndDialog( hwndDlg, 0 ); + SetEvent( hEventPasswdDlg ); + DestroyWindow( hwndDlg ); + return TRUE; + } + break; + } + + return FALSE; +} + +static VOID CALLBACK JabberPasswordCreateDialogApcProc( DWORD param ) +{ + CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_PASSWORD ), NULL, JabberPasswordDlgProc, ( LPARAM )param ); +} + +static VOID CALLBACK JabberOfflineChatWindows( DWORD ) +{ + GCDEST gcd = { jabberProtoName, NULL, GC_EVENT_CONTROL }; + GCEVENT gce = { 0 }; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + CallService( MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce ); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +typedef DNS_STATUS (WINAPI *DNSQUERYA)(IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PIP4_ARRAY aipServers OPTIONAL, IN OUT PDNS_RECORD *ppQueryResults OPTIONAL, IN OUT PVOID *pReserved OPTIONAL); +typedef void (WINAPI *DNSFREELIST)(IN OUT PDNS_RECORD pRecordList, IN DNS_FREE_TYPE FreeType); + +static int xmpp_client_query( char* domain ) +{ + HINSTANCE hDnsapi = LoadLibraryA( "dnsapi.dll" ); + if ( hDnsapi == NULL ) + return 0; + + DNSQUERYA pDnsQuery = (DNSQUERYA)GetProcAddress(hDnsapi, "DnsQuery_A"); + DNSFREELIST pDnsRecordListFree = (DNSFREELIST)GetProcAddress(hDnsapi, "DnsRecordListFree"); + if ( pDnsQuery == NULL ) { + //dnsapi.dll is not the needed dnsapi ;) + FreeLibrary( hDnsapi ); + return 0; + } + + char temp[256]; + mir_snprintf( temp, SIZEOF(temp), "_xmpp-client._tcp.%s", domain ); + + DNS_RECORD *results = NULL; + DNS_STATUS status = pDnsQuery(temp, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &results, NULL); + if (FAILED(status)||!results || results[0].Data.Srv.pNameTarget == 0||results[0].wType != DNS_TYPE_SRV) { + FreeLibrary(hDnsapi); + return NULL; + } + + strncpy(domain, (char*)results[0].Data.Srv.pNameTarget, 127); + int port = results[0].Data.Srv.wPort; + pDnsRecordListFree(results, DnsFreeRecordList); + FreeLibrary(hDnsapi); + return port; +} + +static XmlState xmlState; +static char *xmlStreamToBeInitialized = 0; +static void xmlStreamInitialize(char *which){ + JabberLog("Stream will be initialized %s",which); + xmlStreamToBeInitialized = strdup(which); +} +static void xmlStreamInitializeNow(struct ThreadData *info){ + JabberLog("Stream is initializing %s",xmlStreamToBeInitialized?xmlStreamToBeInitialized:"after connect"); + if (xmlStreamToBeInitialized){ + free(xmlStreamToBeInitialized); + xmlStreamToBeInitialized = NULL; + JabberXmlDestroyState(&xmlState); + } + JabberXmlInitState( &xmlState ); + JabberXmlSetCallback( &xmlState, 1, ELEM_OPEN, JabberProcessStreamOpening, info ); + JabberXmlSetCallback( &xmlState, 1, ELEM_CLOSE, JabberProcessStreamClosing, info ); + JabberXmlSetCallback( &xmlState, 2, ELEM_CLOSE, JabberProcessProtocol, info ); + //JabberSend( info->s, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><stream:stream to=\"%s\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", TXT(info->server) ); + { XmlNode stream( "stream:stream" ); + stream.props = "<?xml version='1.0' encoding='UTF-8'?>"; + stream.addAttr( "to", info->server ); + stream.addAttr( "xmlns", "jabber:client" ); + stream.addAttr( "xmlns:stream", "http://etherx.jabber.org/streams" ); + if ( !JGetByte( "Disable3920auth", 0 )) + stream.addAttr( "version", "1.0" ); + stream.dirtyHack = true; // this is to keep the node open - do not send </stream:stream> + JabberSend( info->s, stream ); +} } + +static bool wasSaslPerformed = 0; + +void __cdecl JabberServerThread( struct ThreadData *info ) +{ + DBVARIANT dbv; + char* buffer; + int datalen; + int oldStatus; + PVOID ssl; + + JabberLog( "Thread started: type=%d", info->type ); + + wasSaslPerformed = false; + if ( info->type == JABBER_SESSION_NORMAL ) { + + // Normal server connection, we will fetch all connection parameters + // e.g. username, password, etc. from the database. + + if ( jabberThreadInfo != NULL ) { + // Will not start another connection thread if a thread is already running. + // Make APC call to the main thread. This will immediately wake the thread up + // in case it is asleep in the reconnect loop so that it will immediately + // reconnect. + QueueUserAPC( JabberDummyApcFunc, jabberThreadInfo->hThread, 0 ); + JabberLog( "Thread ended, another normal thread is running" ); + mir_free( info ); + return; + } + + jabberThreadInfo = info; + if ( streamId ) mir_free( streamId ); + streamId = NULL; + + if ( !JGetStringT( NULL, "LoginName", &dbv )) { + _tcsncpy( info->username, dbv.ptszVal, SIZEOF( info->username )-1 ); + JFreeVariant( &dbv ); + } + else { + JabberLog( "Thread ended, login name is not configured" ); + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID ); +LBL_FatalError: + jabberThreadInfo = NULL; + oldStatus = jabberStatus; + jabberStatus = ID_STATUS_OFFLINE; + JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus ); +LBL_Exit: + mir_free( info ); + return; + } + + if ( *rtrim(info->username) == '\0' ) { + JabberLog( "Thread ended, login name is not configured" ); + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID ); + goto LBL_FatalError; + } + + if ( !DBGetContactSetting( NULL, jabberProtoName, "LoginServer", &dbv )) { + strncpy( info->server, dbv.pszVal, SIZEOF( info->server )-1 ); + JFreeVariant( &dbv ); + } + else { + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK ); + JabberLog( "Thread ended, login server is not configured" ); + goto LBL_FatalError; + } + + if ( !JGetStringT( NULL, "Resource", &dbv )) { + _tcsncpy( info->resource, dbv.ptszVal, SIZEOF( info->resource )-1 ); + JFreeVariant( &dbv ); + } + else _tcscpy( info->resource, _T("Miranda")); + + TCHAR jidStr[128]; + mir_sntprintf( jidStr, SIZEOF( jidStr ), _T("%s@") _T(TCHAR_STR_PARAM) _T("/%s"), info->username, info->server, info->resource ); + _tcsncpy( info->fullJID, jidStr, SIZEOF( info->fullJID )-1 ); + + if ( JGetByte( "SavePassword", TRUE ) == FALSE ) { + mir_sntprintf( jidStr, SIZEOF( jidStr ), _T("%s@") _T(TCHAR_STR_PARAM), info->username, info->server ); + + // Ugly hack: continue logging on only the return value is &( onlinePassword[0] ) + // because if WM_QUIT while dialog box is still visible, p is returned with some + // exit code which may not be NULL. + // Should be better with modeless. + onlinePassword[0] = ( char )-1; + hEventPasswdDlg = CreateEvent( NULL, FALSE, FALSE, NULL ); + QueueUserAPC( JabberPasswordCreateDialogApcProc, hMainThread, ( DWORD )jidStr ); + WaitForSingleObject( hEventPasswdDlg, INFINITE ); + CloseHandle( hEventPasswdDlg ); + + if ( onlinePassword[0] == ( TCHAR ) -1 ) { + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID ); + JabberLog( "Thread ended, password request dialog was canceled" ); + goto LBL_FatalError; + } + strncpy( info->password, onlinePassword, SIZEOF( info->password )); + info->password[ SIZEOF( info->password )-1] = '\0'; + } + else { + if ( DBGetContactSetting( NULL, jabberProtoName, "Password", &dbv )) { + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID ); + JabberLog( "Thread ended, password is not configured" ); + goto LBL_FatalError; + } + JCallService( MS_DB_CRYPT_DECODESTRING, strlen( dbv.pszVal )+1, ( LPARAM )dbv.pszVal ); + strncpy( info->password, dbv.pszVal, SIZEOF( info->password )); + info->password[SIZEOF( info->password )-1] = '\0'; + JFreeVariant( &dbv ); + } + + if ( JGetByte( "ManualConnect", FALSE ) == TRUE ) { + if ( !DBGetContactSetting( NULL, jabberProtoName, "ManualHost", &dbv )) { + strncpy( info->manualHost, dbv.pszVal, SIZEOF( info->manualHost )); + info->manualHost[sizeof( info->manualHost )-1] = '\0'; + JFreeVariant( &dbv ); + } + info->port = JGetWord( NULL, "ManualPort", JABBER_DEFAULT_PORT ); + } + else info->port = JGetWord( NULL, "Port", JABBER_DEFAULT_PORT ); + + info->useSSL = JGetByte( "UseSSL", FALSE ); + } + + else if ( info->type == JABBER_SESSION_REGISTER ) { + // Register new user connection, all connection parameters are already filled-in. + // Multiple thread allowed, although not possible : ) + // thinking again.. multiple thread should not be allowed + info->reg_done = FALSE; + SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 25, ( LPARAM )TranslateT( "Connecting..." )); + iqIdRegGetReg = -1; + iqIdRegSetReg = -1; + } + else { + JabberLog( "Thread ended, invalid session type" ); + goto LBL_Exit; + } + + char connectHost[128]; + if ( info->manualHost[0] == 0 ) { + int port_temp; + strncpy( connectHost, info->server, SIZEOF(info->server)); + if ( port_temp = xmpp_client_query( connectHost )) { // port_temp will be > 0 if resolution is successful + JabberLog("%s%s resolved to %s:%d","_xmpp-client._tcp.",info->server,connectHost,port_temp); + if (info->port==0 || info->port==5222) + info->port = port_temp; + } + else JabberLog("%s%s not resolved", "_xmpp-client._tcp.", connectHost); + } + else strncpy( connectHost, info->manualHost, SIZEOF(connectHost)); // do not resolve if manual host is selected + + JabberLog( "Thread type=%d server='%s' port='%d'", info->type, connectHost, info->port ); + + int jabberNetworkBufferSize = 2048; + if (( buffer=( char* )mir_alloc( jabberNetworkBufferSize+1 )) == NULL ) { // +1 is for '\0' when debug logging this buffer + JabberLog( "Cannot allocate network buffer, thread ended" ); + if ( info->type == JABBER_SESSION_NORMAL ) { + oldStatus = jabberStatus; + jabberStatus = ID_STATUS_OFFLINE; + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK ); + JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus ); + jabberThreadInfo = NULL; + } + else if ( info->type == JABBER_SESSION_REGISTER ) { + SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Not enough memory" )); + } + JabberLog( "Thread ended, network buffer cannot be allocated" ); + goto LBL_Exit; + } + + info->s = JabberWsConnect( connectHost, info->port ); + if ( info->s == NULL ) { + JabberLog( "Connection failed ( %d )", WSAGetLastError()); + if ( info->type == JABBER_SESSION_NORMAL ) { + if ( jabberThreadInfo == info ) { + oldStatus = jabberStatus; + jabberStatus = ID_STATUS_OFFLINE; + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK ); + JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus ); + jabberThreadInfo = NULL; + } } + else if ( info->type == JABBER_SESSION_REGISTER ) + SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Cannot connect to the server" )); + + JabberLog( "Thread ended, connection failed" ); + mir_free( buffer ); + goto LBL_Exit; + } + + // Determine local IP + int socket = JCallService( MS_NETLIB_GETSOCKET, ( WPARAM ) info->s, 0 ); + if ( info->type==JABBER_SESSION_NORMAL && socket!=INVALID_SOCKET ) { + struct sockaddr_in saddr; + int len; + + len = sizeof( saddr ); + getsockname( socket, ( struct sockaddr * ) &saddr, &len ); + jabberLocalIP = saddr.sin_addr.S_un.S_addr; + JabberLog( "Local IP = %s", inet_ntoa( saddr.sin_addr )); + } + + BOOL sslMode = FALSE; + if ( info->useSSL ) { + JabberLog( "Intializing SSL connection" ); + if ( hLibSSL!=NULL && socket!=INVALID_SOCKET ) { + JabberLog( "SSL using socket = %d", socket ); + if (( ssl=pfn_SSL_new( jabberSslCtx )) != NULL ) { + JabberLog( "SSL create context ok" ); + if ( pfn_SSL_set_fd( ssl, socket ) > 0 ) { + JabberLog( "SSL set fd ok" ); + if ( pfn_SSL_connect( ssl ) > 0 ) { + JabberLog( "SSL negotiation ok" ); + JabberSslAddHandle( info->s, ssl ); // This make all communication on this handle use SSL + sslMode = TRUE; // Used in the receive loop below + JabberLog( "SSL enabled for handle = %d", info->s ); + } + else { + JabberLog( "SSL negotiation failed" ); + pfn_SSL_free( ssl ); + } } + else { + JabberLog( "SSL set fd failed" ); + pfn_SSL_free( ssl ); + } } } + + if ( !sslMode ) { + if ( info->type == JABBER_SESSION_NORMAL ) { + oldStatus = jabberStatus; + jabberStatus = ID_STATUS_OFFLINE; + JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus ); + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK ); + if ( jabberThreadInfo == info ) + jabberThreadInfo = NULL; + } + else if ( info->type == JABBER_SESSION_REGISTER ) { + SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Cannot connect to the server" )); + } + mir_free( buffer ); + if ( !hLibSSL ) + MessageBox( NULL, TranslateT( "The connection requires an OpenSSL library, which is not installed." ), TranslateT( "Jabber Connection Error" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND ); + JabberLog( "Thread ended, SSL connection failed" ); + goto LBL_Exit; + } } + + // User may change status to OFFLINE while we are connecting above + if ( jabberDesiredStatus!=ID_STATUS_OFFLINE || info->type==JABBER_SESSION_REGISTER ) { + + if ( info->type == JABBER_SESSION_NORMAL ) { + jabberConnected = TRUE; + int len = _tcslen( info->username ) + strlen( info->server )+1; + jabberJID = ( TCHAR* )mir_alloc( sizeof( TCHAR)*( len+1 )); + mir_sntprintf( jabberJID, len+1, _T("%s@") _T(TCHAR_STR_PARAM), info->username, info->server ); + if ( JGetByte( "KeepAlive", 1 )) + jabberSendKeepAlive = TRUE; + else + jabberSendKeepAlive = FALSE; + JabberForkThread( JabberKeepAliveThread, 0, info->s ); + } + + xmlStreamInitializeNow( info ); + + JabberLog( "Entering main recv loop" ); + datalen = 0; + + for ( ;; ) { + int recvResult, bytesParsed; + + if ( !sslMode ) if (info->useSSL) { + ssl = JabberSslHandleToSsl( info->s ); + sslMode = TRUE; + } + + if ( sslMode ) + recvResult = pfn_SSL_read( ssl, buffer+datalen, jabberNetworkBufferSize-datalen ); + else + recvResult = JabberWsRecv( info->s, buffer+datalen, jabberNetworkBufferSize-datalen ); + + JabberLog( "recvResult = %d", recvResult ); + if ( recvResult <= 0 ) + break; + datalen += recvResult; + + buffer[datalen] = '\0'; + if ( sslMode && DBGetContactSettingByte( NULL, "Netlib", "DumpRecv", TRUE ) == TRUE ) { + // Emulate netlib log feature for SSL connection + char* szLogBuffer = ( char* )mir_alloc( recvResult+128 ); + if ( szLogBuffer != NULL ) { + strcpy( szLogBuffer, "( SSL ) Data received\n" ); + memcpy( szLogBuffer+strlen( szLogBuffer ), buffer+datalen-recvResult, recvResult+1 /* also copy \0 */ ); + Netlib_Logf( hNetlibUser, "%s", szLogBuffer ); // %s to protect against when fmt tokens are in szLogBuffer causing crash + mir_free( szLogBuffer ); + } } + + bytesParsed = JabberXmlParse( &xmlState, buffer ); + JabberLog( "bytesParsed = %d", bytesParsed ); + if ( bytesParsed > 0 ) { + if ( bytesParsed < datalen ) + memmove( buffer, buffer+bytesParsed, datalen-bytesParsed ); + datalen -= bytesParsed; + } + else if ( datalen == jabberNetworkBufferSize ) { + jabberNetworkBufferSize += 65536; + JabberLog( "Increasing network buffer size to %d", jabberNetworkBufferSize ); + if (( buffer=( char* )mir_realloc( buffer, jabberNetworkBufferSize+1 )) == NULL ) { + JabberLog( "Cannot reallocate more network buffer, go offline now" ); + break; + } } + else JabberLog( "Unknown state: bytesParsed=%d, datalen=%d, jabberNetworkBufferSize=%d", bytesParsed, datalen, jabberNetworkBufferSize ); + + if (xmlStreamToBeInitialized) xmlStreamInitializeNow(info); + } + + JabberXmlDestroyState(&xmlState); + + if ( info->type == JABBER_SESSION_NORMAL ) { + jabberOnline = FALSE; + jabberConnected = FALSE; + JabberEnableMenuItems( FALSE ); + if ( hwndJabberChangePassword ) { + //DestroyWindow( hwndJabberChangePassword ); + // Since this is a different thread, simulate the click on the cancel button instead + SendMessage( hwndJabberChangePassword, WM_COMMAND, MAKEWORD( IDCANCEL, 0 ), 0 ); + } + + if ( jabberChatDllPresent ) + QueueUserAPC( JabberOfflineChatWindows, hMainThread, 0 ); + + JabberListRemoveList( LIST_CHATROOM ); + if ( hwndJabberAgents ) + SendMessage( hwndJabberAgents, WM_JABBER_CHECK_ONLINE, 0, 0 ); + if ( hwndJabberGroupchat ) + SendMessage( hwndJabberGroupchat, WM_JABBER_CHECK_ONLINE, 0, 0 ); + if ( hwndJabberJoinGroupchat ) + SendMessage( hwndJabberJoinGroupchat, WM_JABBER_CHECK_ONLINE, 0, 0 ); + + // Set status to offline + oldStatus = jabberStatus; + jabberStatus = ID_STATUS_OFFLINE; + JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus ); + + // Set all contacts to offline + HANDLE hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) { + if ( !lstrcmpA(( char* )JCallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 ), jabberProtoName )) + if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != ID_STATUS_OFFLINE ) + JSetWord( hContact, "Status", ID_STATUS_OFFLINE ); + + hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM ) hContact, 0 ); + } + + mir_free( jabberJID ); + jabberJID = NULL; + jabberLoggedInTime = 0; + JabberListWipe(); + if ( hwndJabberAgents ) { + SendMessage( hwndJabberAgents, WM_JABBER_AGENT_REFRESH, 0, ( LPARAM )"" ); + SendMessage( hwndJabberAgents, WM_JABBER_TRANSPORT_REFRESH, 0, 0 ); + } + if ( hwndJabberVcard ) + SendMessage( hwndJabberVcard, WM_JABBER_CHECK_ONLINE, 0, 0 ); + } + else if ( info->type==JABBER_SESSION_REGISTER && !info->reg_done ) { + SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Connection lost" )); + } } + else { + if ( info->type == JABBER_SESSION_NORMAL ) { + oldStatus = jabberStatus; + jabberStatus = ID_STATUS_OFFLINE; + JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus ); + } } + + Netlib_CloseHandle( info->s ); + + if ( sslMode ) { + pfn_SSL_free( ssl ); + JabberSslRemoveHandle( info->s ); + } + + JabberLog( "Thread ended: type=%d server='%s'", info->type, info->server ); + + if ( info->type==JABBER_SESSION_NORMAL && jabberThreadInfo==info ) { + if ( streamId ) mir_free( streamId ); + streamId = NULL; + jabberThreadInfo = NULL; + } + + mir_free( buffer ); + JabberLog( "Exiting ServerThread" ); + goto LBL_Exit; +} + +static void JabberIqProcessSearch( XmlNode *node, void *userdata ) +{ +} + +static void JabberPerformRegistration( ThreadData* info ) +{ + iqIdRegGetReg = JabberSerialNext(); + XmlNodeIq iq("get",iqIdRegGetReg,(char*)NULL); + XmlNode* query = iq.addQuery("jabber:iq:register"); + JabberSend(info->s,iq); + SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 50, ( LPARAM )TranslateT( "Requesting registration instruction..." )); +} + +static void JabberPerformIqAuth( ThreadData* info ) +{ + if ( info->type == JABBER_SESSION_NORMAL ) { + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultGetAuth ); + XmlNodeIq iq( "get", iqId ); + XmlNode* query = iq.addQuery( "jabber:iq:auth" ); + query->addChild( "username", info->username ); + JabberSend( info->s, iq ); + } + else if ( info->type == JABBER_SESSION_REGISTER ) + JabberPerformRegistration( info ); +} + +static void JabberProcessStreamOpening( XmlNode *node, void *userdata ) +{ + struct ThreadData *info = ( struct ThreadData * ) userdata; + TCHAR* sid; + + if ( node->name==NULL || strcmp( node->name, "stream:stream" )) + return; + + if ( info->type == JABBER_SESSION_NORMAL ) { + if (( sid=JabberXmlGetAttrValue( node, "id" )) != NULL ) { + if ( streamId ) mir_free( streamId ); + streamId = t2a( sid ); + } } + + if ( JGetByte( "Disable3920auth", 0 )) + JabberPerformIqAuth( info ); +} + +static void JabberProcessStreamClosing( XmlNode *node, void *userdata ) +{ + struct ThreadData *info = ( struct ThreadData * ) userdata; + + Netlib_CloseHandle( info->s ); + if ( node->name && !strcmp( node->name, "stream:error" ) && node->text ) + MessageBox( NULL, TranslateTS( node->text ), TranslateT( "Jabber Connection Error" ), MB_OK|MB_ICONERROR|MB_SETFOREGROUND ); +} + +static void JabberProcessFeatures( XmlNode *node, void *userdata ) +{ + struct ThreadData *info = ( struct ThreadData * ) userdata; + bool isPlainAvailable = false; + bool isMd5available = false; + bool isAuthAvailable = false; + bool isXGoogleTokenAvailable = false; + bool isRegisterAvailable = false; + bool areMechanismsDefined = false; + bool isSessionAvailable = false; + + for ( int i=0; i < node->numChild; i++ ) { + XmlNode* n = node->child[i]; + if ( !strcmp( n->name, "starttls" )) { + if ( !info->useSSL && JGetByte( "UseTLS", FALSE )) { + JabberLog( "Requesting TLS" ); + XmlNode stls( n->name ); stls.addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-tls" ); + JabberSend( info->s, stls ); + return; + } } + else if ( !strcmp( n->name, "mechanisms" )) { + areMechanismsDefined = true; + //JabberLog("%d mechanisms\n",n->numChild); + for ( int k=0; k < n->numChild; k++ ) { + XmlNode* c = n->child[k]; + if ( !strcmp( c->name, "mechanism" )) + //JabberLog("Mechanism: %s",c->text); + if ( !_tcscmp( c->text, _T("PLAIN"))) isPlainAvailable = true; + else if ( !_tcscmp( c->text, _T("DIGEST-MD5"))) isMd5available = true; + else if ( !_tcscmp( c->text, _T("X-GOOGLE-TOKEN"))) isXGoogleTokenAvailable = true; + } } + else if ( !strcmp( n->name, "register" )) isRegisterAvailable = true; + else if ( !strcmp( n->name, "auth" )) isAuthAvailable = true; + else if ( !strcmp( n->name, "session" )) isSessionAvailable = true; + } + + if ( areMechanismsDefined ) { + char *PLAIN = NULL, *mechanism = NULL; + /*if ( isMd5available ) { + mechanism = NEWSTR_ALLOCA( "DIGEST-MD5" ); + } + else */if ( isPlainAvailable ) { + char *temp = t2a(info->username); + int size = strlen(temp)*2+strlen(info->server)+strlen(info->password)+3; + char *toEncode = ( char* )alloca( size+1 ); + mir_snprintf( toEncode, size+1, "%s@%s%c%s%c%s", temp, info->server, 0, temp, 0, info->password ); + PLAIN = JabberBase64Encode( toEncode, size ); + mir_free(temp); + JabberLog( "Never publish the hash below" ); + mechanism = NEWSTR_ALLOCA( "PLAIN" ); + } + else { + if ( isAuthAvailable ) { // no known mechanisms but iq_auth is available + JabberPerformIqAuth( info ); + return; + } + + MessageBox( NULL, TranslateT("No known auth methods available. Giving up."), TranslateT( "Jabber Authentication" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND ); + JabberSend( info->s, "</stream:stream>" ); + JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD ); + return; + } + + if ( info->type == JABBER_SESSION_NORMAL ) { + XmlNode auth( "auth", PLAIN ); + auth.addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl" ); + auth.addAttr( "mechanism", mechanism ); + JabberSend(info->s,auth); + wasSaslPerformed = true; //sasl was requested, but we dont know the result + } + else if ( info->type == JABBER_SESSION_REGISTER ) + JabberPerformRegistration( info ); + else + JabberSend( info->s, "</stream:stream>" ); + if (PLAIN) mir_free(PLAIN); + return; + } + + // mechanisms are not defined. + if ( wasSaslPerformed ) { //We are already logged-in + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_NONE, JabberIqResultBind ); + XmlNodeIq iq("set",iqId); + XmlNode* bind = iq.addChild( "bind" ); bind->addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-bind" ); + bind->addChild( "resource", info->resource ); + JabberSend( info->s, iq ); + + if ( isSessionAvailable ) + info->bIsSessionAvailable = TRUE; + + return; + } + + //mechanisms not available and we are not logged in + if ( isAuthAvailable ) + JabberPerformIqAuth( info ); +} + +static void __cdecl JabberWaitAndReconnectThread( int unused ) +{ + JabberLog("Reconnecting after with new X-GOOGLE-TOKEN"); + Sleep(1000); + ThreadData* thread = ( ThreadData* ) mir_alloc( sizeof( struct ThreadData )); + ZeroMemory( thread, sizeof( struct ThreadData )); + thread->type = JABBER_SESSION_NORMAL; + thread->hThread = ( HANDLE ) JabberForkThread(( JABBER_THREAD_FUNC )JabberServerThread, 0, thread ); +} + + +static void JabberProcessFailure( XmlNode *node, void *userdata ){ +// JabberXmlDumpNode( node ); + struct ThreadData *info = ( struct ThreadData * ) userdata; + TCHAR* type; +//failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" + if (( type=JabberXmlGetAttrValue( node, "xmlns" )) == NULL ) return; + if ( !_tcscmp( type, _T("urn:ietf:params:xml:ns:xmpp-sasl") )){ + JabberSend( info->s, "</stream:stream>" ); + + TCHAR text[128]; + mir_sntprintf( text, sizeof( text ), _T("%s %s@")_T(TCHAR_STR_PARAM)_T("."), TranslateT( "Authentication failed for" ), info->username, info->server ); + 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 +} } + +static void JabberProcessError( XmlNode *node, void *userdata ) +{ + struct ThreadData *info = ( struct ThreadData * ) userdata; + TCHAR *buff; + int i; + int pos; +//failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" + if ( !node->numChild ) return; + buff = (TCHAR *)mir_alloc(1024*SIZEOF(buff)); + pos=0; + for (i=0;i<node->numChild;i++){ + pos += mir_sntprintf(buff+pos,1024-pos, + _T(TCHAR_STR_PARAM)_T(": %s\n"), + node->child[i]->name,node->child[i]->text); + if (!strcmp(node->child[i]->name,"conflict")) JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_OTHERLOCATION); + } + MessageBox( NULL, buff, TranslateT( "Jabber Error" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND ); + mir_free(buff); + JabberSend( info->s, "</stream:stream>" ); +} + +static void JabberProcessSuccess( XmlNode *node, void *userdata ) +{ + struct ThreadData *info = ( struct ThreadData * ) userdata; + TCHAR* type; +// int iqId; + // RECVED: <success ... + // ACTION: if successfully logged in, continue by requesting roster list and set my initial status + if (( type=JabberXmlGetAttrValue( node, "xmlns" )) == NULL ) return; + + if ( !_tcscmp( type, _T("urn:ietf:params:xml:ns:xmpp-sasl") )){ + DBVARIANT dbv; + + JabberLog( "Succcess: Logged-in." ); + if ( DBGetContactSetting( NULL, jabberProtoName, "Nick", &dbv )) + JSetStringT( NULL, "Nick", info->username ); + else + JFreeVariant( &dbv ); + xmlStreamInitialize( "after successful sasl" ); + } + else { + JabberLog( "Succcess: unknown action "TCHAR_STR_PARAM".",type); +} } + + +static void JabberProcessProtocol( XmlNode *node, void *userdata ) +{ + struct ThreadData *info; + + info = ( struct ThreadData * ) userdata; + if ( !strcmp( node->name, "proceed" )){ + JabberProcessProceed( node, userdata ); + return; + } + else if ( !strcmp( node->name, "stream:features" )){ + JabberProcessFeatures( node, userdata ); + } + else if ( !strcmp( node->name, "success")){ + JabberProcessSuccess( node, userdata ); + } + else if ( !strcmp( node->name, "failure")){ + JabberProcessFailure( node, userdata ); + } + else if ( !strcmp( node->name, "stream:error")){ + JabberProcessError( node, userdata ); + } + else if ( info->type == JABBER_SESSION_NORMAL ) { + if ( !strcmp( node->name, "message" )) + JabberProcessMessage( node, userdata ); + else if ( !strcmp( node->name, "presence" )) + JabberProcessPresence( node, userdata ); + else if ( !strcmp( node->name, "iq" )) + JabberProcessIq( node, userdata ); + else + JabberLog( "Invalid top-level tag ( only <message/> <presence/> and <iq/> allowed )" ); + } + else if ( info->type == JABBER_SESSION_REGISTER ) { + if ( !strcmp( node->name, "iq" )) + JabberProcessRegIq( node, userdata ); + else + JabberLog( "Invalid top-level tag ( only <iq/> allowed )" ); +} } + +static void JabberProcessProceed( XmlNode *node, void *userdata ) +{ + struct ThreadData *info; + TCHAR* type; + node = node; + if (( info=( struct ThreadData * ) userdata ) == NULL ) return; + if (( type = JabberXmlGetAttrValue( node, "xmlns" )) != NULL && !lstrcmp( type, _T("error"))) + return; + + if ( !lstrcmp( type, _T("urn:ietf:params:xml:ns:xmpp-tls" ))){ + JabberLog("Starting TLS..."); + int socket = JCallService( MS_NETLIB_GETSOCKET, ( WPARAM ) info->s, 0 ); + PVOID ssl; + if (( ssl=pfn_SSL_new( jabberSslCtx )) != NULL ) { + JabberLog( "SSL create context ok" ); + if ( pfn_SSL_set_fd( ssl, socket ) > 0 ) { + JabberLog( "SSL set fd ok" ); + if ( pfn_SSL_connect( ssl ) > 0 ) { + JabberLog( "SSL negotiation ok" ); + JabberSslAddHandle( info->s, ssl ); // This make all communication on this handle use SSL + info->useSSL = true; + JabberLog( "SSL enabled for handle = %d", info->s ); + xmlStreamInitialize( "after successful StartTLS" ); + } + else { + JabberLog( "SSL negotiation failed" ); + pfn_SSL_free( ssl ); + } } + else { + JabberLog( "SSL set fd failed" ); + pfn_SSL_free( ssl ); +} } } } + +static void JabberProcessMessage( XmlNode *node, void *userdata ) +{ + struct ThreadData *info; + XmlNode *subjectNode, *xNode, *inviteNode, *idNode, *n; + TCHAR* from, *type, *nick, *p, *idStr, *fromResource; + int id; + HANDLE hContact; + + if ( !node->name || strcmp( node->name, "message" )) return; + if (( info=( struct ThreadData * ) userdata ) == NULL ) return; + + type = JabberXmlGetAttrValue( node, "type" ); + if (( from = JabberXmlGetAttrValue( node, "from" )) == NULL ) + return; + + XmlNode* errorNode = JabberXmlGetChild( node, "error" ); + if ( errorNode != NULL || !lstrcmp( type, _T("error"))) { + //we check if is message delivery failure + if (( idStr = JabberXmlGetAttrValue( node, "id" )) != NULL ) { + if ( !_tcsncmp( idStr, _T(JABBER_IQID), strlen( JABBER_IQID )) ){ + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_ROSTER, from ); + if ( item != NULL ){ + id = _ttoi(( idStr )+strlen( JABBER_IQID )); + if ( id == item->idMsgAckPending ){ // yes, it is + char *errText = t2a(JabberErrorMsg(errorNode)); + JSendBroadcast( JabberHContactFromJID( from ), ACKTYPE_MESSAGE, ACKRESULT_FAILED, ( HANDLE ) 1, (LPARAM)errText ); + mir_free(errText); + } } } } + return; + } + + JABBER_LIST_ITEM* chatItem = JabberListGetItemPtr( LIST_CHATROOM, from ); + BOOL isChatRoomJid = ( chatItem != NULL ); + if ( isChatRoomJid && !lstrcmp( type, _T("groupchat"))) { + JabberGroupchatProcessMessage( node, userdata ); + return; + } + BOOL isRss = !lstrcmp( type, _T("headline")); + + // If message is from a stranger ( not in roster ), item is NULL + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_ROSTER, from ); + + TCHAR* szMessage = NULL; + XmlNode* bodyNode = JabberXmlGetChild( node, "body" ); + if ( bodyNode != NULL ) { + if (( subjectNode=JabberXmlGetChild( node, "subject" ))!=NULL && subjectNode->text!=NULL && subjectNode->text[0]!='\0' && !isRss ) { + p = ( TCHAR* )alloca( sizeof( TCHAR )*( _tcslen( subjectNode->text ) + _tcslen( bodyNode->text ) + 12 )); + wsprintf( p, _T("Subject: %s\r\n%s"), subjectNode->text, bodyNode->text ); + szMessage = p; + } + else szMessage = bodyNode->text; + + if (( szMessage = JabberUnixToDosT( szMessage )) == NULL ) + szMessage = mir_tstrdup( _T("")); + } + + time_t msgTime = 0; + BOOL isChatRoomInvitation = FALSE; + TCHAR* inviteRoomJid = NULL; + TCHAR* inviteFromJid = NULL; + TCHAR* inviteReason = NULL; + TCHAR* invitePassword = NULL; + BOOL delivered = FALSE, composing = FALSE; + + n = JabberXmlGetChild( node, "active" ); + if ( item != NULL && bodyNode != NULL ) { + if ( n != NULL && !lstrcmp( JabberXmlGetAttrValue( n, "xmlns" ), _T("http://jabber.org/protocol/chatstates"))) + item->cap |= CLIENT_CAP_CHATSTAT; + else + item->cap &= ~CLIENT_CAP_CHATSTAT; + } + + n = JabberXmlGetChild( node, "composing" ); + if ( n != NULL && !lstrcmp( JabberXmlGetAttrValue( n, "xmlns" ), _T("http://jabber.org/protocol/chatstates"))) + if (( hContact = JabberHContactFromJID( from )) != NULL ) + JCallService( MS_PROTO_CONTACTISTYPING, ( WPARAM ) hContact, 60 ); + + n = JabberXmlGetChild( node, "paused" ); + if ( n != NULL && !lstrcmp( JabberXmlGetAttrValue( n, "xmlns" ), _T("http://jabber.org/protocol/chatstates"))) + if (( hContact = JabberHContactFromJID( from )) != NULL ) + JCallService( MS_PROTO_CONTACTISTYPING, ( WPARAM ) hContact, PROTOTYPE_CONTACTTYPING_OFF ); + + for ( int i = 1; ( xNode = JabberXmlGetNthChild( node, "x", i )) != NULL; i++ ) { + if (( p=JabberXmlGetAttrValue( xNode, "xmlns" )) != NULL ) { + if ( !_tcscmp( p, _T("jabber:x:encrypted" ))) { + if ( xNode->text == NULL ) + return; + + TCHAR* prolog = _T("-----BEGIN PGP MESSAGE-----\r\n\r\n"); + TCHAR* epilog = _T("\r\n-----END PGP MESSAGE-----\r\n"); + TCHAR* tempstring = ( TCHAR* )alloca( sizeof( TCHAR )*( _tcslen( prolog ) + _tcslen( xNode->text ) + _tcslen( epilog ))); + _tcsncpy( tempstring, prolog, _tcslen( prolog )+1 ); + _tcsncpy(tempstring + _tcslen( prolog ), xNode->text, _tcslen( xNode->text )+1); + _tcsncpy(tempstring + _tcslen( prolog )+_tcslen(xNode->text ), epilog, _tcslen( epilog )+1); + szMessage = tempstring; + } + else if ( !_tcscmp( p, _T("jabber:x:delay")) && msgTime == 0 ) { + if (( p=JabberXmlGetAttrValue( xNode, "stamp" )) != NULL ) + msgTime = JabberIsoToUnixTime( p ); + } + else if ( !_tcscmp( p, _T("jabber:x:event"))) { + if ( bodyNode == NULL ) { + idNode = JabberXmlGetChild( xNode, "id" ); + if ( JabberXmlGetChild( xNode, "delivered" )!=NULL || JabberXmlGetChild( xNode, "offline" )!=NULL ) { + id = -1; + if ( idNode!=NULL && idNode->text!=NULL ) + if ( !_tcsncmp( idNode->text, _T(JABBER_IQID), strlen( JABBER_IQID )) ) + id = _ttoi(( idNode->text )+strlen( JABBER_IQID )); + + if ( item != NULL ) + if ( id == item->idMsgAckPending ) + JSendBroadcast( JabberHContactFromJID( from ), ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, ( HANDLE ) 1, 0 ); + } + + if ( JabberXmlGetChild( xNode, "composing" ) != NULL ) + if (( hContact = JabberHContactFromJID( from )) != NULL ) + JCallService( MS_PROTO_CONTACTISTYPING, ( WPARAM ) hContact, 60 ); + + if ( xNode->numChild==0 || ( xNode->numChild==1 && idNode != NULL )) + // Maybe a cancel to the previous composing + if (( hContact = JabberHContactFromJID( from )) != NULL ) + JCallService( MS_PROTO_CONTACTISTYPING, ( WPARAM ) hContact, PROTOTYPE_CONTACTTYPING_OFF ); + } + else { + // Check whether any event is requested + if ( !delivered && ( n=JabberXmlGetChild( xNode, "delivered" )) != NULL ) { + delivered = TRUE; + idStr = JabberXmlGetAttrValue( node, "id" ); + + XmlNode m( "message" ); m.addAttr( "to", from ); + XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:event" ); x->addChild( "delivered" ); + x->addChild( "id", ( idStr != NULL ) ? idStr : NULL ); + JabberSend( info->s, m ); + } + if ( item!=NULL && JabberXmlGetChild( xNode, "composing" ) != NULL ) { + composing = TRUE; + if ( item->messageEventIdStr ) + mir_free( item->messageEventIdStr ); + idStr = JabberXmlGetAttrValue( node, "id" ); + item->messageEventIdStr = ( idStr==NULL )?NULL:mir_tstrdup( idStr ); + } } + } + else if ( !_tcscmp( p, _T("jabber:x:oob")) && isRss) { + XmlNode* rssUrlNode; + if ( (rssUrlNode = JabberXmlGetNthChild( xNode, "url", 1 )) != NULL) { + p = ( TCHAR* )alloca( sizeof(TCHAR)*( _tcslen( subjectNode->text ) + _tcslen( bodyNode->text ) + _tcslen( rssUrlNode->text ) + 14 )); + wsprintf( p, _T("Subject: %s\r\n%s\r\n%s"), subjectNode->text, rssUrlNode->text, bodyNode->text ); + szMessage = p; + } + } + else if ( !_tcscmp( p, _T("http://jabber.org/protocol/muc#user"))) { + if (( inviteNode=JabberXmlGetChild( xNode, "invite" )) != NULL ) { + inviteFromJid = JabberXmlGetAttrValue( inviteNode, "from" ); + if (( n=JabberXmlGetChild( inviteNode, "reason" )) != NULL ) + inviteReason = n->text; + } + + if (( n=JabberXmlGetChild( xNode, "password" )) != NULL ) + invitePassword = n->text; + } + else if ( !_tcscmp( p, _T("jabber:x:conference"))) { + inviteRoomJid = JabberXmlGetAttrValue( xNode, "jid" ); + if ( inviteReason == NULL ) + inviteReason = xNode->text; + isChatRoomInvitation = TRUE; + } } } + + if ( isChatRoomInvitation ) { + if ( inviteRoomJid != NULL ) + JabberGroupchatProcessInvite( inviteRoomJid, inviteFromJid, inviteReason, invitePassword ); + return; + } + + if ( bodyNode != NULL ) { + if ( bodyNode->text == NULL ) + return; + + WCHAR* wszMessage; + char* szAnsiMsg; + int cbAnsiLen, cbWideLen; + + #if defined( _UNICODE ) + wszMessage = szMessage; cbWideLen = wcslen( szMessage ); + cbAnsiLen = WideCharToMultiByte( CP_ACP, 0, wszMessage, cbWideLen, NULL, 0, NULL, NULL ); + szAnsiMsg = ( char* )alloca( cbAnsiLen+1 ); + WideCharToMultiByte( CP_ACP, 0, wszMessage, cbWideLen, szAnsiMsg, cbAnsiLen, NULL, NULL ); + szAnsiMsg[ cbAnsiLen ] = 0; + #else + szAnsiMsg = szMessage; cbAnsiLen = strlen( szMessage ); + cbWideLen = MultiByteToWideChar( CP_ACP, 0, szAnsiMsg, cbAnsiLen, NULL, 0 ); + wszMessage = ( WCHAR* )alloca( sizeof(WCHAR)*( cbWideLen+1 )); + MultiByteToWideChar( CP_ACP, 0, szAnsiMsg, cbAnsiLen, wszMessage, cbWideLen ); + wszMessage[ cbWideLen ] = 0; + #endif + + char* buf = ( char* )alloca( cbAnsiLen+1 + (cbWideLen+1)*sizeof( WCHAR )); + memcpy( buf, szAnsiMsg, cbAnsiLen+1 ); + memcpy( buf + cbAnsiLen + 1, wszMessage, (cbWideLen+1)*sizeof( WCHAR )); + + HANDLE hContact = JabberHContactFromJID( from ); + + if ( item != NULL ) { + item->wantComposingEvent = composing; + if ( hContact != NULL ) + JCallService( MS_PROTO_CONTACTISTYPING, ( WPARAM ) hContact, PROTOTYPE_CONTACTTYPING_OFF ); + + if ( item->resourceMode==RSMODE_LASTSEEN && ( fromResource = _tcschr( from, '/' ))!=NULL ) { + fromResource++; + if ( *fromResource != '\0' ) { + for ( int i=0; i<item->resourceCount; i++ ) { + if ( !lstrcmp( item->resource[i].resourceName, fromResource )) { + item->defaultResource = i; + break; + } } } } } + + if ( hContact == NULL ) { + // Create a temporary contact + if ( isChatRoomJid ) { + if (( p = _tcschr( from, '/' ))!=NULL && p[1]!='\0' ) + p++; + else + p = from; + hContact = JabberDBCreateContact( from, p, TRUE, FALSE ); + + for ( int i=0; i < chatItem->resourceCount; i++ ) { + if ( !lstrcmp( chatItem->resource[i].resourceName, p )) { + JSetWord( hContact, "Status", chatItem->resource[i].status ); + break; + } } + } + else { + nick = JabberNickFromJID( from ); + hContact = JabberDBCreateContact( from, nick, TRUE, TRUE ); + mir_free( nick ); + } } + + time_t now = time( NULL ); + if ( msgTime == 0 || now - jabberLoggedInTime > 60 ) + msgTime = now; + + PROTORECVEVENT recv; + recv.flags = PREF_UNICODE; + recv.timestamp = ( DWORD )msgTime; + recv.szMessage = buf; + recv.lParam = 0; + + CCSDATA ccs; + ccs.hContact = hContact; + ccs.wParam = 0; + ccs.szProtoService = PSR_MESSAGE; + ccs.lParam = ( LPARAM )&recv; + JCallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs ); + + mir_free( szMessage ); +} } + +static void JabberProcessPresence( XmlNode *node, void *userdata ) +{ + struct ThreadData *info; + HANDLE hContact; + XmlNode *showNode, *statusNode; + JABBER_LIST_ITEM *item; + TCHAR* from, *nick, *show; + int i; + TCHAR* p; + + if ( !node || !node->name || strcmp( node->name, "presence" )) return; + if (( info=( struct ThreadData * ) userdata ) == NULL ) return; + if (( from = JabberXmlGetAttrValue( node, "from" )) == NULL ) return; + + if ( JabberListExist( LIST_CHATROOM, from )) { + JabberGroupchatProcessPresence( node, userdata ); + return; + } + + TCHAR* type = JabberXmlGetAttrValue( node, "type" ); + if ( type == NULL || !_tcscmp( type, _T("available"))) { + if (( nick=JabberNickFromJID( from )) == NULL ) + return; + if (( hContact = JabberHContactFromJID( from )) == NULL ) + hContact = JabberDBCreateContact( from, nick, FALSE, TRUE ); + if ( !JabberListExist( LIST_ROSTER, from )) { + JabberLog("Receive presence online from "TCHAR_STR_PARAM" ( who is not in my roster )", from ); + JabberListAdd( LIST_ROSTER, from ); + } + int status = ID_STATUS_ONLINE; + if (( showNode = JabberXmlGetChild( node, "show" )) != NULL ) { + if (( show = showNode->text ) != NULL ) { + if ( !_tcscmp( show, _T("away"))) status = ID_STATUS_AWAY; + else if ( !_tcscmp( show, _T("xa"))) status = ID_STATUS_NA; + else if ( !_tcscmp( show, _T("dnd"))) status = ID_STATUS_DND; + else if ( !_tcscmp( show, _T("chat"))) status = ID_STATUS_FREECHAT; + } } + + // Send version query if this is the new resource + if (( p = _tcschr( from, '@' )) != NULL ) { + if (( p = _tcschr( p, '/' ))!=NULL && p[1]!='\0' ) { + p++; + if (( item = JabberListGetItemPtr( LIST_ROSTER, from )) != NULL ) { + JABBER_RESOURCE_STATUS *r = item->resource; + for ( i=0; i < item->resourceCount && lstrcmp( r->resourceName, p ); i++, r++ ); + if ( i >= item->resourceCount || ( r->version == NULL && r->system == NULL && r->software == NULL )) { + XmlNodeIq iq( "get", NOID, from ); + XmlNode* query = iq.addQuery( "jabber:iq:version" ); + JabberSend( info->s, iq ); + } } } } + + if (( statusNode = JabberXmlGetChild( node, "status" )) != NULL && statusNode->text != NULL ) + p = mir_tstrdup( statusNode->text ); + else + p = NULL; + JabberListAddResource( LIST_ROSTER, from, status, p ); + if ( p ) { + DBWriteContactSettingTString( hContact, "CList", "StatusMsg", p ); + mir_free( p ); + } + else DBDeleteContactSetting( hContact, "CList", "StatusMsg" ); + + // Determine status to show for the contact + if (( item=JabberListGetItemPtr( LIST_ROSTER, from )) != NULL ) { + for ( i=0; i < item->resourceCount; i++ ) + status = JabberCombineStatus( status, item->resource[i].status ); + item->status = status; + } + + if ( _tcschr( from, '@' )!=NULL || JGetByte( "ShowTransport", TRUE )==TRUE ) + if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != status ) + JSetWord( hContact, "Status", ( WORD )status ); + + if ( _tcschr( from, '@' )==NULL && hwndJabberAgents ) + SendMessage( hwndJabberAgents, WM_JABBER_TRANSPORT_REFRESH, 0, 0 ); + JabberLog( TCHAR_STR_PARAM " ( " TCHAR_STR_PARAM " ) online, set contact status to %s", nick, from, JCallService(MS_CLIST_GETSTATUSMODEDESCRIPTION,(WPARAM)status,0 )); + mir_free( nick ); + + XmlNode* xNode; + BOOL hasXAvatar = false; + if (JGetByte( "EnableAvatars", TRUE )){ + JabberLog( "Avatar enabled" ); + for ( int i = 1; ( xNode=JabberXmlGetNthChild( node, "x", i )) != NULL; i++ ) { + if ( !lstrcmp( JabberXmlGetAttrValue( xNode, "xmlns" ), _T("jabber:x:avatar"))) { + if (( xNode = JabberXmlGetChild( xNode, "hash" )) != NULL && xNode->text != NULL ) { + JDeleteSetting(hContact,"AvatarXVcard"); + JabberLog( "AvatarXVcard deleted" ); + JSetStringT( hContact, "AvatarHash", xNode->text ); + hasXAvatar = true; + DBVARIANT dbv = {0}; + int result = JGetStringT( hContact, "AvatarSaved", &dbv ); + if ( !result || lstrcmp( dbv.ptszVal, xNode->text )) { + JabberLog( "Avatar was changed" ); + JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, NULL ); + } else JabberLog( "Not broadcasting avatar changed" ); + if ( !result ) JFreeVariant( &dbv ); + } } } + if (!hasXAvatar){ //no jabber:x:avatar. try vcard-temp:x:update + JabberLog( "Not hasXAvatar" ); + for ( int i = 1; ( xNode=JabberXmlGetNthChild( node, "x", i )) != NULL; i++ ) { + if ( !lstrcmp( JabberXmlGetAttrValue( xNode, "xmlns" ), _T("vcard-temp:x:update"))) { + if (( xNode = JabberXmlGetChild( xNode, "photo" )) != NULL && xNode->text != NULL ) { + JSetByte(hContact,"AvatarXVcard",1); + JabberLog( "AvatarXVcard set" ); + JSetStringT( hContact, "AvatarHash", xNode->text ); + DBVARIANT dbv = {0}; + int result = JGetStringT( hContact, "AvatarSaved", &dbv ); + if ( !result || lstrcmp( dbv.ptszVal, xNode->text )) { + JabberLog( "Avatar was changed. Using vcard-temp:x:update" ); + JSendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, NULL ); + } JabberLog( "Not broadcasting avatar changed" ); + if ( !result ) JFreeVariant( &dbv ); + } } } } } + return; + } + + if ( !_tcscmp( type, _T("unavailable"))) { + if ( !JabberListExist( LIST_ROSTER, from )) { + JabberLog( "Receive presence offline from " TCHAR_STR_PARAM " ( who is not in my roster )", from ); + JabberListAdd( LIST_ROSTER, from ); + } + else JabberListRemoveResource( LIST_ROSTER, from ); + + int status = ID_STATUS_OFFLINE; + if (( statusNode = JabberXmlGetChild( node, "status" )) != NULL ) { + if ( JGetByte( "OfflineAsInvisible", FALSE ) == TRUE ) + status = ID_STATUS_INVISIBLE; + + if (( hContact = JabberHContactFromJID( from )) != NULL) { + if ( statusNode->text ) + DBWriteContactSettingTString(hContact, "CList", "StatusMsg", statusNode->text ); + else + DBDeleteContactSetting(hContact, "CList", "StatusMsg"); + } } + + if (( item=JabberListGetItemPtr( LIST_ROSTER, from )) != NULL ) { + // Determine status to show for the contact based on the remaining resources + status = ID_STATUS_OFFLINE; + for ( i=0; i < item->resourceCount; i++ ) + status = JabberCombineStatus( status, item->resource[i].status ); + item->status = status; + } + if (( hContact=JabberHContactFromJID( from )) != NULL ) { + if ( _tcschr( from, '@' )!=NULL || JGetByte( "ShowTransport", TRUE )==TRUE ) + if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != status ) + JSetWord( hContact, "Status", ( WORD )status ); + + JabberLog( TCHAR_STR_PARAM " offline, set contact status to %d", from, status ); + } + if ( _tcschr( from, '@' )==NULL && hwndJabberAgents ) + SendMessage( hwndJabberAgents, WM_JABBER_TRANSPORT_REFRESH, 0, 0 ); + return; + } + + if ( !_tcscmp( type, _T("subscribe"))) { + if ( _tcschr( from, '@' ) == NULL ) { + // automatically send authorization allowed to agent/transport + XmlNode p( "presence" ); p.addAttr( "to", from ); p.addAttr( "type", "subscribed" ); + JabberSend( info->s, p ); + } + else if (( nick=JabberNickFromJID( from )) != NULL ) { + JabberLog( TCHAR_STR_PARAM " ( " TCHAR_STR_PARAM " ) requests authorization", nick, from ); + JabberDBAddAuthRequest( from, nick ); + mir_free( nick ); + } + return; + } + + if ( !_tcscmp( type, _T("subscribed"))) { + if (( item=JabberListGetItemPtr( LIST_ROSTER, from )) != NULL ) { + if ( item->subscription == SUB_FROM ) item->subscription = SUB_BOTH; + else if ( item->subscription == SUB_NONE ) { + item->subscription = SUB_TO; + if ( hwndJabberAgents && _tcschr( from, '@' )==NULL ) + SendMessage( hwndJabberAgents, WM_JABBER_TRANSPORT_REFRESH, 0, 0 ); +} } } } + +///////////////////////////////////////////////////////////////////////////////////////// +// Handles various <iq... requests + +static void JabberProcessIqVersion( TCHAR* idStr, XmlNode* node ) +{ + TCHAR* from; + if (( from=JabberXmlGetAttrValue( node, "from" )) == NULL ) + return; + + char* version = JabberGetVersionText(); + TCHAR* os = NULL; + + OSVERSIONINFO osvi = { 0 }; + osvi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); + if ( GetVersionEx( &osvi )) { + switch ( osvi.dwPlatformId ) { + case VER_PLATFORM_WIN32_NT: + if ( osvi.dwMajorVersion == 5 ) { + if ( osvi.dwMinorVersion == 2 ) os = TranslateT( "Windows Server 2003" ); + else if ( osvi.dwMinorVersion == 1 ) os = TranslateT( "Windows XP" ); + else if ( osvi.dwMinorVersion == 0 ) os = TranslateT( "Windows 2000" ); + } + else if ( osvi.dwMajorVersion <= 4 ) { + os = TranslateT( "Windows NT" ); + } + break; + case VER_PLATFORM_WIN32_WINDOWS: + if ( osvi.dwMajorVersion == 4 ) { + if ( osvi.dwMinorVersion == 0 ) os = TranslateT( "Windows 95" ); + if ( osvi.dwMinorVersion == 10 ) os = TranslateT( "Windows 98" ); + if ( osvi.dwMinorVersion == 90 ) os = TranslateT( "Windows ME" ); + } + break; + } } + + if ( os == NULL ) os = TranslateT( "Windows" ); + + char mversion[100]; + strcpy( mversion, "Miranda IM " ); + JCallService( MS_SYSTEM_GETVERSIONTEXT, sizeof( mversion )-12, ( LPARAM )&mversion[11] ); + + XmlNodeIq iq( "result", idStr, from ); + XmlNode* query = iq.addQuery( "jabber:iq:version" ); + query->addChild( "name", mversion ); query->addChild( "version", version ); query->addChild( "os", os ); + JabberSend( jabberThreadInfo->s, iq ); + + if ( version ) mir_free( version ); +} + +static void JabberProcessIqTime( TCHAR* idStr, XmlNode* node ) //added by Rion (jep-0090) +{ + TCHAR* from; + struct tm *gmt; + time_t ltime; + char stime[20],*dtime; + if (( from=JabberXmlGetAttrValue( node, "from" )) == NULL ) + return; + + _tzset(); + time( <ime ); + gmt = gmtime( <ime ); + sprintf (stime,"%.4i%.2i%.2iT%.2i:%.2i:%.2i",gmt->tm_year+1900,gmt->tm_mon,gmt->tm_mday,gmt->tm_hour,gmt->tm_min,gmt->tm_sec); + dtime = ctime(<ime); + dtime[24]=0; + + XmlNodeIq iq( "result", idStr, from ); + XmlNode* query = iq.addQuery( "jabber:iq:time" ); + query->addChild( "utc", stime ); query->addChild( "tz", _tzname[1] ); query->addChild( "display", dtime ); + JabberSend( jabberThreadInfo->s, iq ); +} + +static void JabberProcessIqAvatar( TCHAR* idStr, XmlNode* node ) +{ + if ( !JGetByte( "EnableAvatars", TRUE )) + return; + + TCHAR* from; + if (( from = JabberXmlGetAttrValue( node, "from" )) == NULL ) + return; + + int pictureType = JGetByte( "AvatarType", PA_FORMAT_UNKNOWN ); + if ( pictureType == PA_FORMAT_UNKNOWN ) + return; + + char* szMimeType; + switch( pictureType ) { + case PA_FORMAT_JPEG: szMimeType = "image/jpeg"; break; + case PA_FORMAT_GIF: szMimeType = "image/gif"; break; + case PA_FORMAT_PNG: szMimeType = "image/png"; break; + case PA_FORMAT_BMP: szMimeType = "image/bmp"; break; + default: return; + } + + char szFileName[ MAX_PATH ]; + JabberGetAvatarFileName( NULL, szFileName, MAX_PATH ); + + FILE* in = fopen( szFileName, "rb" ); + if ( in == NULL ) + return; + + long bytes = filelength( fileno( in )); + char* buffer = ( char* )mir_alloc( bytes*4/3 + bytes + 1000 ); + if ( buffer == NULL ) { + fclose( in ); + return; + } + + fread( buffer, bytes, 1, in ); + fclose( in ); + + char* str = JabberBase64Encode( buffer, bytes ); + XmlNodeIq iq( "result", idStr, from ); + XmlNode* query = iq.addQuery( "jabber:iq:avatar" ); + XmlNode* data = query->addChild( "data", str ); data->addAttr( "mimetype", szMimeType ); + JabberSend( jabberThreadInfo->s, iq ); + mir_free( str ); + mir_free( buffer ); +} + +static void JabberProcessIqResultVersion( TCHAR* type, XmlNode* node, XmlNode* queryNode ) +{ + TCHAR* from = JabberXmlGetAttrValue( node, "from" ); + if ( from == NULL ) return; + + JABBER_LIST_ITEM *item = JabberListGetItemPtr( LIST_ROSTER, from ); + if ( item == NULL ) return; + + JABBER_RESOURCE_STATUS *r = item->resource; + if ( r == NULL ) return; + + TCHAR* p = _tcschr( from, '/' ); + if ( p == NULL ) return; + if ( *++p == '\0' ) return; + + int i; + for ( i=0; i<item->resourceCount && _tcscmp( r->resourceName, p ); i++, r++ ); + if ( i >= item->resourceCount ) + return; + + HANDLE hContact = JabberHContactFromJID( from ); + if ( hContact == NULL ) + return; + + if ( !lstrcmp( type, _T("error"))) { + if ( r->resourceName != NULL ) + JSetStringT( hContact, "MirVer", r->resourceName ); + return; + } + + XmlNode* n; + if ( r->software ) mir_free( r->software ); + if (( n=JabberXmlGetChild( queryNode, "name" ))!=NULL && n->text ) { + if (( hContact=JabberHContactFromJID( item->jid )) != NULL ) { + if (( p = _tcsstr( n->text, _T("Miranda IM"))) != NULL ) + JSetStringT( hContact, "MirVer", p ); + else + JSetStringT( hContact, "MirVer", n->text ); + } + r->software = mir_tstrdup( n->text ); + } + else r->software = NULL; + if ( r->version ) mir_free( r->version ); + if (( n=JabberXmlGetChild( queryNode, "version" ))!=NULL && n->text ) + r->version = mir_tstrdup( n->text ); + else + r->version = NULL; + if ( r->system ) mir_free( r->system ); + if (( n=JabberXmlGetChild( queryNode, "os" ))!=NULL && n->text ) + r->system = mir_tstrdup( n->text ); + else + r->system = NULL; +} + +static void JabberProcessIq( XmlNode *node, void *userdata ) +{ + struct ThreadData *info; + HANDLE hContact; + XmlNode *queryNode, *siNode, *n; + TCHAR* from, *type, *jid, *nick; + TCHAR* xmlns, *profile; + TCHAR* idStr, *str, *p, *q; + TCHAR text[256]; + int id; + int i; + JABBER_IQ_PFUNC pfunc; + + if ( !node->name || strcmp( node->name, "iq" )) return; + if (( info=( struct ThreadData * ) userdata ) == NULL ) return; + if (( type=JabberXmlGetAttrValue( node, "type" )) == NULL ) return; + + id = -1; + if (( idStr=JabberXmlGetAttrValue( node, "id" )) != NULL ) + if ( !_tcsncmp( idStr, _T(JABBER_IQID), strlen( JABBER_IQID )) ) + id = _ttoi( idStr+strlen( JABBER_IQID )); + + queryNode = JabberXmlGetChild( node, "query" ); + xmlns = JabberXmlGetAttrValue( queryNode, "xmlns" ); + + ///////////////////////////////////////////////////////////////////////// + // MATCH BY ID + ///////////////////////////////////////////////////////////////////////// + + if (( pfunc=JabberIqFetchFunc( id )) != NULL ) { + JabberLog( "Handling iq request for id=%d", id ); + pfunc( node, userdata ); + } + + ///////////////////////////////////////////////////////////////////////// + // MORE GENERAL ROUTINES, WHEN ID DOES NOT MATCH + ///////////////////////////////////////////////////////////////////////// + + else if (( pfunc=JabberIqFetchXmlnsFunc( xmlns )) != NULL ) { + JabberLog( "Handling iq request for xmlns = " TCHAR_STR_PARAM, xmlns ); + pfunc( node, userdata ); + } + + // RECVED: <iq type='set'><query ... + else if ( !_tcscmp( type, _T("set")) && queryNode!=NULL && xmlns != NULL ) { + + // RECVED: roster push + // ACTION: similar to iqIdGetRoster above + if ( !_tcscmp( xmlns, _T("jabber:iq:roster"))) { + XmlNode *itemNode, *groupNode; + JABBER_LIST_ITEM *item; + TCHAR* name; + + JabberLog( "<iq/> Got roster push, query has %d children", queryNode->numChild ); + for ( i=0; i<queryNode->numChild; i++ ) { + itemNode = queryNode->child[i]; + if ( strcmp( itemNode->name, "item" ) != 0 ) + continue; + if (( jid = JabberXmlGetAttrValue( itemNode, "jid" )) == NULL ) + continue; + if (( str = JabberXmlGetAttrValue( itemNode, "subscription" )) == NULL ) + continue; + + // we will not add new account when subscription=remove + if ( !_tcscmp( str, _T("to")) || !_tcscmp( str, _T("both")) || !_tcscmp( str, _T("from")) || !_tcscmp( str, _T("none"))) { + if (( name=JabberXmlGetAttrValue( itemNode, "name" )) != NULL ) + nick = mir_tstrdup( name ); + else + nick = JabberNickFromJID( jid ); + + if ( nick != NULL ) { + if (( item=JabberListAdd( LIST_ROSTER, jid )) != NULL ) { + if ( item->nick ) mir_free( item->nick ); + item->nick = nick; + + if ( item->group ) mir_free( item->group ); + if (( groupNode=JabberXmlGetChild( itemNode, "group" ))!=NULL && groupNode->text!=NULL ) + item->group = mir_tstrdup( groupNode->text ); + else + item->group = NULL; + + if (( hContact=JabberHContactFromJID( jid )) == NULL ) { + // Received roster has a new JID. + // Add the jid ( with empty resource ) to Miranda contact list. + hContact = JabberDBCreateContact( jid, nick, FALSE, TRUE ); + } + else JSetStringT( hContact, "jid", jid ); + + DBVARIANT dbnick; + if ( !JGetStringT( hContact, "Nick", &dbnick )) { + if ( _tcscmp( nick, dbnick.ptszVal ) != 0 ) + DBWriteContactSettingTString( hContact, "CList", "MyHandle", nick ); + else + DBDeleteContactSetting( hContact, "CList", "MyHandle" ); + JFreeVariant( &dbnick ); + } + else DBWriteContactSettingTString( hContact, "CList", "MyHandle", nick ); + + if ( item->group != NULL ) { + JabberContactListCreateGroup( item->group ); + DBWriteContactSettingTString( hContact, "CList", "Group", item->group ); + } + else DBDeleteContactSetting( hContact, "CList", "Group" ); + } + else mir_free( nick ); + } } + + if (( item=JabberListGetItemPtr( LIST_ROSTER, jid )) != NULL ) { + if ( !_tcscmp( str, _T("both"))) item->subscription = SUB_BOTH; + else if ( !_tcscmp( str, _T("to"))) item->subscription = SUB_TO; + else if ( !_tcscmp( str, _T("from"))) item->subscription = SUB_FROM; + else item->subscription = SUB_NONE; + JabberLog( "Roster push for jid=" TCHAR_STR_PARAM ", set subscription to %s", jid, str ); + // subscription = remove is to remove from roster list + // but we will just set the contact to offline and not actually + // remove, so that history will be retained. + if ( !_tcscmp( str, _T("remove"))) { + if (( hContact=JabberHContactFromJID( jid )) != NULL ) { + if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != ID_STATUS_OFFLINE ) + JSetWord( hContact, "Status", ID_STATUS_OFFLINE ); + JabberListRemove( LIST_ROSTER, jid ); + } } + else if ( JGetByte( hContact, "ChatRoom", 0 )) + DBDeleteContactSetting( hContact, "CList", "Hidden" ); + } } + + if ( hwndJabberAgents ) + SendMessage( hwndJabberAgents, WM_JABBER_TRANSPORT_REFRESH, 0, 0 ); + } + + // RECVED: file transfer request + // ACTION: notify Miranda throuch CHAINRECV + else if ( !_tcscmp( xmlns, _T("jabber:iq:oob"))) { + if (( jid=JabberXmlGetAttrValue( node, "from" ))!=NULL && ( n=JabberXmlGetChild( queryNode, "url" ))!=NULL && n->text!=NULL ) { + str = n->text; // URL of the file to get + filetransfer* ft = new filetransfer; + ft->std.totalFiles = 1; + ft->jid = mir_tstrdup( jid ); + ft->std.hContact = JabberHContactFromJID( jid ); + ft->type = FT_OOB; + ft->httpHostName = NULL; + ft->httpPort = 80; + ft->httpPath = NULL; + // Parse the URL + if ( !_tcsnicmp( str, _T("http://"), 7 )) { + p = str + 7; + if (( q = _tcschr( p, '/' )) != NULL ) { + if ( q-p < SIZEOF( text )) { + _tcsncpy( text, p, q-p ); + text[q-p] = '\0'; + if (( p = _tcschr( text, ':' )) != NULL ) { + ft->httpPort = ( WORD )_ttoi( p+1 ); + *p = '\0'; + } + ft->httpHostName = t2a( text ); + ft->httpPath = t2a( ++q ); + } } } + + if (( str=JabberXmlGetAttrValue( node, "id" )) != NULL ) + ft->iqId = mir_tstrdup( str ); + + if ( ft->httpHostName && ft->httpPath ) { + CCSDATA ccs; + PROTORECVEVENT pre; + char* szBlob, *desc; + + JabberLog( "Host=%s Port=%d Path=%s", ft->httpHostName, ft->httpPort, ft->httpPath ); + if (( n=JabberXmlGetChild( queryNode, "desc" ))!=NULL && n->text!=NULL ) + desc = t2a( n->text ); + else + desc = mir_strdup( "" ); + + if ( desc != NULL ) { + char* str; + JabberLog( "description = %s", desc ); + if (( str = strrchr( ft->httpPath, '/' )) != NULL ) + str++; + else + str = ft->httpPath; + str = mir_strdup( str ); + JabberHttpUrlDecode( str ); + szBlob = ( char* )mir_alloc( sizeof( DWORD )+ strlen( str ) + strlen( desc ) + 2 ); + *(( PDWORD ) szBlob ) = ( DWORD )ft; + strcpy( szBlob + sizeof( DWORD ), str ); + strcpy( szBlob + sizeof( DWORD )+ strlen( str ) + 1, desc ); + pre.flags = 0; + pre.timestamp = time( NULL ); + pre.szMessage = szBlob; + pre.lParam = 0; + ccs.szProtoService = PSR_FILE; + ccs.hContact = ft->std.hContact; + ccs.wParam = 0; + ccs.lParam = ( LPARAM )⪯ + JCallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs ); + mir_free( szBlob ); + mir_free( str ); + mir_free( desc ); + } + } + else { + // reject + XmlNodeIq iq( "error", idStr, ft->jid ); + XmlNode* e = iq.addChild( "error", "File transfer refused" ); e->addAttr( "code", 406 ); + JabberSend( jabberThreadInfo->s, iq ); + delete ft; + } } } + + // RECVED: bytestream initiation request + // ACTION: check for any stream negotiation that is pending ( now only file transfer is handled ) + else if ( !_tcscmp( xmlns, _T("http://jabber.org/protocol/bytestreams"))) + JabberFtHandleBytestreamRequest( node ); + } + // RECVED: <iq type='get'><query ... + else if ( !_tcscmp( type, _T("get")) && queryNode!=NULL && xmlns != NULL ) { + + // RECVED: software version query + // ACTION: return my software version + if ( !_tcscmp( xmlns, _T("jabber:iq:version"))) + JabberProcessIqVersion( idStr, node ); + else if ( !_tcscmp( xmlns, _T("jabber:iq:avatar"))) + JabberProcessIqAvatar( idStr, node ); + else if ( !_tcscmp( xmlns, _T("jabber:iq:time"))) + JabberProcessIqTime( idStr, node ); + } + // RECVED: <iq type='result'><query ... + else if ( !_tcscmp( type, _T("result")) && queryNode != NULL && xmlns != NULL ) { + + // RECVED: software version result + // ACTION: update version information for the specified jid/resource + if ( !_tcscmp( xmlns, _T("jabber:iq:version"))) + JabberProcessIqResultVersion( type, node, queryNode ); + } + // RECVED: <iq type='set'><si xmlns='http://jabber.org/protocol/si' ... + else if ( !_tcscmp( type, _T("set")) && ( siNode=JabberXmlGetChildWithGivenAttrValue( node, "si", "xmlns", _T("http://jabber.org/protocol/si")))!=NULL && ( profile=JabberXmlGetAttrValue( siNode, "profile" ))!=NULL ) { + + // RECVED: file transfer request + // ACTION: notify Miranda throuch CHAINRECV + if ( !_tcscmp( profile, _T("http://jabber.org/protocol/si/profile/file-transfer" ))) { + JabberFtHandleSiRequest( node ); + } + // RECVED: unknown profile + // ACTION: reply with bad-profile + else { + if (( from=JabberXmlGetAttrValue( node, "from" )) != NULL ) { + idStr = JabberXmlGetAttrValue( node, "id" ); + + XmlNodeIq iq( "error", idStr, from ); + XmlNode* error = iq.addChild( "error" ); error->addAttr( "code", "400" ); error->addAttr( "type", "cancel" ); + XmlNode* brq = error->addChild( "bad-request" ); brq->addAttr( "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas" ); + XmlNode* bp = error->addChild( "bad-profile" ); brq->addAttr( "xmlns", "http://jabber.org/protocol/si" ); + JabberSend( jabberThreadInfo->s, iq ); + } } + } + // RECVED: <iq type='error'> ... + else if ( !_tcscmp( type, _T("error"))) { + if ( !lstrcmp( xmlns, _T("jabber:iq:version"))) { + JabberProcessIqResultVersion( type, node, queryNode ); + return; + } + JabberLog( "XXX on entry" ); + // Check for file transfer deny by comparing idStr with ft->iqId + i = 0; + while (( i=JabberListFindNext( LIST_FILE, i )) >= 0 ) { + JABBER_LIST_ITEM *item = JabberListGetItemPtrFromIndex( i ); + if ( item->ft != NULL && item->ft->state == FT_CONNECTING && !_tcscmp( idStr, item->ft->iqId )) { + JabberLog( "Denying file sending request" ); + item->ft->state = FT_DENIED; + if ( item->ft->hFileEvent != NULL ) + SetEvent( item->ft->hFileEvent ); // Simulate the termination of file server connection + } + i++; +} } } + +static void JabberProcessRegIq( XmlNode *node, void *userdata ) +{ + struct ThreadData *info; + XmlNode *errorNode; + TCHAR *type, *str; + + if ( !node->name || strcmp( node->name, "iq" )) return; + if (( info=( struct ThreadData * ) userdata ) == NULL ) return; + if (( type=JabberXmlGetAttrValue( node, "type" )) == NULL ) return; + + unsigned int id = -1; + if (( str=JabberXmlGetAttrValue( node, "id" )) != NULL ) + if ( !_tcsncmp( str, _T(JABBER_IQID), strlen( JABBER_IQID )) ) + id = _ttoi( str + strlen( JABBER_IQID )); + + if ( !_tcscmp( type, _T("result"))) { + + // RECVED: result of the request for registration mechanism + // ACTION: send account registration information + if ( id == iqIdRegGetReg ) { + iqIdRegSetReg = JabberSerialNext(); + + XmlNodeIq iq( "set", iqIdRegSetReg ); + XmlNode* query = iq.addQuery( "jabber:iq:register" ); + query->addChild( "password", info->password ); + query->addChild( "username", info->username ); + JabberSend( info->s, iq ); + + SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 75, ( LPARAM )TranslateT( "Sending registration information..." )); + } + // RECVED: result of the registration process + // ACTION: account registration successful + else if ( id == iqIdRegSetReg ) { + JabberSend( info->s, "</stream:stream>" ); + SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Registration successful" )); + info->reg_done = TRUE; + } } + + else if ( !_tcscmp( type, _T("error"))) { + errorNode = JabberXmlGetChild( node, "error" ); + str = JabberErrorMsg( errorNode ); + SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )str ); + mir_free( str ); + info->reg_done = TRUE; + JabberSend( info->s, "</stream:stream>" ); +} } + +static void __cdecl JabberKeepAliveThread( JABBER_SOCKET s ) +{ + NETLIBSELECT nls = {0}; + + nls.cbSize = sizeof( NETLIBSELECT ); + nls.dwTimeout = 60000; // 60000 millisecond ( 1 minute ) + nls.hExceptConns[0] = s; + for ( ;; ) { + if ( JCallService( MS_NETLIB_SELECT, 0, ( LPARAM )&nls ) != 0 ) + break; + if ( jabberSendKeepAlive ) + JabberSend( s, " \t " ); + } + JabberLog( "Exiting KeepAliveThread" ); +} diff --git a/miranda-wine/protocols/JabberG/jabber_userinfo.cpp b/miranda-wine/protocols/JabberG/jabber_userinfo.cpp new file mode 100644 index 0000000..f792448 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_userinfo.cpp @@ -0,0 +1,525 @@ +/* + +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_userinfo.cpp,v $ +Revision : $Revision: 3016 $ +Last change on : $Date: 2006-06-04 23:07:43 +0400 (Вск, 04 Июн 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" + +#include <commctrl.h> +#include "jabber_list.h" +#include "resource.h" +#include "sha1.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberUserInfoDlgProc - main user info dialog + +static BOOL CALLBACK JabberUserInfoDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + // lParam is hContact + TranslateDialogDefault( hwndDlg ); + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG )( HANDLE ) lParam ); + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 ); + return TRUE; + case WM_JABBER_REFRESH: + { + DBVARIANT dbv; + JABBER_LIST_ITEM *item; + JABBER_RESOURCE_STATUS *r; + + HWND hwndList = GetDlgItem( hwndDlg, IDC_INFO_RESOURCE ); + SendMessage( hwndList, LB_RESETCONTENT, 0, 0 ); + SetDlgItemTextA( hwndDlg, IDC_INFO_JID, "" ); + SetDlgItemTextA( hwndDlg, IDC_SUBSCRIPTION, "" ); + SetDlgItemText( hwndDlg, IDC_SOFTWARE, TranslateT( "<click resource to view>" )); + SetDlgItemText( hwndDlg, IDC_VERSION, TranslateT( "<click resource to view>" )); + SetDlgItemText( hwndDlg, IDC_SYSTEM, TranslateT( "<click resource to view>" )); + EnableWindow( GetDlgItem( hwndDlg, IDC_SOFTWARE ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_VERSION ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_SYSTEM ), FALSE ); + + HANDLE hContact = ( HANDLE ) GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( !JGetStringT( hContact, "jid", &dbv )) { + SetDlgItemText( hwndDlg, IDC_INFO_JID, dbv.ptszVal ); + + if ( jabberOnline ) { + if (( item=JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal )) != NULL ) { + if (( r=item->resource ) != NULL ) { + int count = item->resourceCount; + for ( int i=0; i<count; i++ ) { + int index = SendMessage( hwndList, LB_ADDSTRING, 0, ( LPARAM )r[i].resourceName ); + SendMessage( hwndList, LB_SETITEMDATA, index, ( LPARAM )r[i].resourceName ); + } } + + switch ( item->subscription ) { + case SUB_BOTH: + SetDlgItemText( hwndDlg, IDC_SUBSCRIPTION, TranslateT( "both" )); + break; + case SUB_TO: + SetDlgItemText( hwndDlg, IDC_SUBSCRIPTION, TranslateT( "to" )); + break; + case SUB_FROM: + SetDlgItemText( hwndDlg, IDC_SUBSCRIPTION, TranslateT( "from" )); + break; + default: + SetDlgItemText( hwndDlg, IDC_SUBSCRIPTION, TranslateT( "none" )); + break; + } } + else SetDlgItemText( hwndDlg, IDC_SUBSCRIPTION, TranslateT( "none ( not on roster )" )); + } + else EnableWindow( hwndList, FALSE ); + JFreeVariant( &dbv ); + } } + break; + case WM_NOTIFY: + switch (( ( LPNMHDR )lParam )->idFrom ) { + case 0: + switch (( ( LPNMHDR )lParam )->code ) { + case PSN_INFOCHANGED: + { + HANDLE hContact = ( HANDLE ) (( LPPSHNOTIFY ) lParam )->lParam; + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, ( LPARAM )hContact ); + } + break; + } + break; + } + break; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_INFO_RESOURCE: + switch ( HIWORD( wParam )) { + case LBN_SELCHANGE: + { + HWND hwndList = GetDlgItem( hwndDlg, IDC_INFO_RESOURCE ); + HANDLE hContact = ( HANDLE ) GetWindowLong( hwndDlg, GWL_USERDATA ); + + DBVARIANT dbv; + if ( !JGetStringT( hContact, "jid", &dbv )) { + TCHAR* jid = dbv.ptszVal; + int nItem = SendMessage( hwndList, LB_GETCURSEL, 0, 0 ); + TCHAR* szResource = ( TCHAR* )SendMessage( hwndList, LB_GETITEMDATA, ( WPARAM ) nItem, 0 ); + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_ROSTER, jid ); + JABBER_RESOURCE_STATUS *r; + + if ( szResource != ( TCHAR* )LB_ERR && item != NULL && ( r=item->resource ) != NULL ) { + int i; + for ( i=0; i < item->resourceCount && _tcscmp( r[i].resourceName, szResource ); i++ ); + if ( i < item->resourceCount ) { + if ( r[i].software != NULL ) { + SetDlgItemText( hwndDlg, IDC_SOFTWARE, r[i].software ); + EnableWindow( GetDlgItem( hwndDlg, IDC_SOFTWARE ), TRUE ); + } + else { + SetDlgItemText( hwndDlg, IDC_SOFTWARE, TranslateT( "<not specified>" )); + EnableWindow( GetDlgItem( hwndDlg, IDC_SOFTWARE ), FALSE ); + } + if ( r[i].version != NULL ) { + SetDlgItemText( hwndDlg, IDC_VERSION, r[i].version ); + EnableWindow( GetDlgItem( hwndDlg, IDC_VERSION ), TRUE ); + } + else { + SetDlgItemText( hwndDlg, IDC_VERSION, TranslateT( "<not specified>" )); + EnableWindow( GetDlgItem( hwndDlg, IDC_VERSION ), FALSE ); + } + if ( r[i].system != NULL ) { + SetDlgItemText( hwndDlg, IDC_SYSTEM, r[i].system ); + EnableWindow( GetDlgItem( hwndDlg, IDC_SYSTEM ), TRUE ); + } + else { + SetDlgItemText( hwndDlg, IDC_SYSTEM, TranslateT( "<not specified>" )); + EnableWindow( GetDlgItem( hwndDlg, IDC_SYSTEM ), FALSE ); + } } } + JFreeVariant( &dbv ); + } } } + break; + } + break; + } + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberUserPhotoDlgProc - Jabber photo dialog + +typedef struct { + HANDLE hContact; + HBITMAP hBitmap; +} USER_PHOTO_INFO; + +static BOOL CALLBACK JabberUserPhotoDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + USER_PHOTO_INFO *photoInfo; + + photoInfo = ( USER_PHOTO_INFO * ) GetWindowLong( hwndDlg, GWL_USERDATA ); + + switch ( msg ) { + case WM_INITDIALOG: + // lParam is hContact + TranslateDialogDefault( hwndDlg ); + photoInfo = ( USER_PHOTO_INFO * ) mir_alloc( sizeof( USER_PHOTO_INFO )); + photoInfo->hContact = ( HANDLE ) lParam; + photoInfo->hBitmap = NULL; + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG ) photoInfo ); + SendMessage( GetDlgItem( hwndDlg, IDC_SAVE ), BM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadImage( hInst, MAKEINTRESOURCE( IDI_SAVE ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 )); + ShowWindow( GetDlgItem( hwndDlg, IDC_LOAD ), SW_HIDE ); + ShowWindow( GetDlgItem( hwndDlg, IDC_DELETE ), SW_HIDE ); + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 ); + return TRUE; + case WM_NOTIFY: + switch (( ( LPNMHDR )lParam )->idFrom ) { + case 0: + switch (( ( LPNMHDR )lParam )->code ) { + case PSN_INFOCHANGED: + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 ); + break; + } + break; + } + break; + case WM_JABBER_REFRESH: + { + JABBER_LIST_ITEM *item; + DBVARIANT dbv; + + if ( photoInfo->hBitmap ) { + DeleteObject( photoInfo->hBitmap ); + photoInfo->hBitmap = NULL; + } + ShowWindow( GetDlgItem( hwndDlg, IDC_SAVE ), SW_HIDE ); + if ( !JGetStringT( photoInfo->hContact, "jid", &dbv )) { + TCHAR* jid = dbv.ptszVal; + if (( item=JabberListGetItemPtr( LIST_ROSTER, jid )) != NULL ) { + if ( item->photoFileName ) { + JabberLog( "Showing picture from %s", item->photoFileName ); + photoInfo->hBitmap = ( HBITMAP ) JCallService( MS_UTILS_LOADBITMAP, 0, ( LPARAM )item->photoFileName ); + ShowWindow( GetDlgItem( hwndDlg, IDC_SAVE ), SW_SHOW ); + } + } + JFreeVariant( &dbv ); + } + InvalidateRect( hwndDlg, NULL, TRUE ); + UpdateWindow( hwndDlg ); + } + break; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_SAVE: + { + DBVARIANT dbv; + JABBER_LIST_ITEM *item; + HANDLE hFile; + OPENFILENAMEA ofn; + static char szFilter[512]; + unsigned char buffer[3]; + char szFileName[_MAX_PATH]; + DWORD n; + + if ( JGetStringT( photoInfo->hContact, "jid", &dbv )) + break; + + TCHAR* jid = dbv.ptszVal; + if (( item=JabberListGetItemPtr( LIST_ROSTER, jid )) != NULL ) { + if (( hFile=CreateFileA( item->photoFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) != INVALID_HANDLE_VALUE ) { + if ( ReadFile( hFile, buffer, 3, &n, NULL ) && n==3 ) { + if ( !strncmp(( char* )buffer, "BM", 2 )) { + mir_snprintf( szFilter, sizeof( szFilter ), "BMP %s ( *.bmp )", JTranslate( "format" )); + n = strlen( szFilter ); + strncpy( szFilter+n+1, "*.BMP", sizeof( szFilter )-n-2 ); + } + else if ( !strncmp(( char* )buffer, "GIF", 3 )) { + mir_snprintf( szFilter, sizeof( szFilter ), "GIF %s ( *.gif )", JTranslate( "format" )); + n = strlen( szFilter ); + strncpy( szFilter+n+1, "*.GIF", sizeof( szFilter )-n-2 ); + } + else if ( buffer[0]==0xff && buffer[1]==0xd8 && buffer[2]==0xff ) { + mir_snprintf( szFilter, sizeof( szFilter ), "JPEG %s ( *.jpg;*.jpeg )", JTranslate( "format" )); + n = strlen( szFilter ); + strncpy( szFilter+n+1, "*.JPG;*.JPEG", sizeof( szFilter )-n-2 ); + } + else { + mir_snprintf( szFilter, sizeof( szFilter ), "%s ( *.* )", JTranslate( "Unknown format" )); + n = strlen( szFilter ); + strncpy( szFilter+n+1, "*.*", sizeof( szFilter )-n-2 ); + } + szFilter[sizeof( szFilter )-1] = '\0'; + + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = hwndDlg; + ofn.hInstance = NULL; + ofn.lpstrFilter = szFilter; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 0; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = _MAX_PATH; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = NULL; + ofn.Flags = OFN_OVERWRITEPROMPT; + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = NULL; + ofn.lCustData = 0L; + ofn.lpfnHook = NULL; + ofn.lpTemplateName = NULL; + szFileName[0] = '\0'; + if ( GetSaveFileNameA( &ofn )) { + JabberLog( "File selected is %s", szFileName ); + CopyFileA( item->photoFileName, szFileName, FALSE ); + } + } + CloseHandle( hFile ); + } + } + JFreeVariant( &dbv ); + + } + break; + } + break; + case WM_PAINT: + if ( !jabberOnline ) + SetDlgItemText( hwndDlg, IDC_CANVAS, TranslateT( "<Photo not available while offline>" )); + else if ( !photoInfo->hBitmap ) + SetDlgItemText( hwndDlg, IDC_CANVAS, TranslateT( "<No photo>" )); + else { + BITMAP bm; + POINT ptSize, ptOrg, pt, ptFitSize; + RECT rect; + + SetDlgItemTextA( hwndDlg, IDC_CANVAS, "" ); + HBITMAP hBitmap = photoInfo->hBitmap; + HWND hwndCanvas = GetDlgItem( hwndDlg, IDC_CANVAS ); + HDC hdcCanvas = GetDC( hwndCanvas ); + HDC hdcMem = CreateCompatibleDC( hdcCanvas ); + SelectObject( hdcMem, hBitmap ); + SetMapMode( hdcMem, GetMapMode( hdcCanvas )); + GetObject( hBitmap, sizeof( BITMAP ), ( LPVOID ) &bm ); + ptSize.x = bm.bmWidth; + ptSize.y = bm.bmHeight; + DPtoLP( hdcCanvas, &ptSize, 1 ); + ptOrg.x = ptOrg.y = 0; + DPtoLP( hdcMem, &ptOrg, 1 ); + GetClientRect( hwndCanvas, &rect ); + InvalidateRect( hwndCanvas, NULL, TRUE ); + UpdateWindow( hwndCanvas ); + if ( ptSize.x<=rect.right && ptSize.y<=rect.bottom ) { + pt.x = ( rect.right - ptSize.x )/2; + pt.y = ( rect.bottom - ptSize.y )/2; + BitBlt( hdcCanvas, pt.x, pt.y, ptSize.x, ptSize.y, hdcMem, ptOrg.x, ptOrg.y, SRCCOPY ); + } + else { + if (( ( float )( ptSize.x-rect.right ))/ptSize.x > (( float )( ptSize.y-rect.bottom ))/ptSize.y ) { + ptFitSize.x = rect.right; + ptFitSize.y = ( ptSize.y*rect.right )/ptSize.x; + pt.x = 0; + pt.y = ( rect.bottom - ptFitSize.y )/2; + } + else { + ptFitSize.x = ( ptSize.x*rect.bottom )/ptSize.y; + ptFitSize.y = rect.bottom; + pt.x = ( rect.right - ptFitSize.x )/2; + pt.y = 0; + } + SetStretchBltMode( hdcCanvas, COLORONCOLOR ); + StretchBlt( hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY ); + } + DeleteDC( hdcMem ); + } + break; + case WM_DESTROY: + if ( photoInfo->hBitmap ) { + JabberLog( "Delete bitmap" ); + DeleteObject( photoInfo->hBitmap ); + } + if ( photoInfo ) mir_free( photoInfo ); + break; + } + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberSetAvatarDlgProc - avatar options dialog procedure + +static HBITMAP hAvatar; +static char szFileName[ MAX_PATH ]; + +static void sttSaveAvatar() +{ + char tFileName[ MAX_PATH ]; + JabberGetAvatarFileName( NULL, tFileName, sizeof tFileName ); + + if ( CopyFileA( szFileName, tFileName, FALSE ) == FALSE ) { + JabberLog( "Copy failed with error %d", GetLastError() ); + return; + } + + SHA1Context ctx; + uint8_t digest[20]; + SHA1Reset( &ctx ); + + FILE* in = fopen( tFileName, "rb" ); + if ( in == NULL ) + return; + + char buf[ 512 ]; + bool bIsFirst = true; + int pictureType; + while( !feof( in )) { + int bytes = fread( buf, 1, sizeof buf, in ); + if ( bIsFirst ) { + pictureType = JabberGetPictureType( buf ); + bIsFirst = false; + } + SHA1Input( &ctx, ( const unsigned __int8* )buf, bytes ); + } + fclose( in ); + + if ( pictureType == PA_FORMAT_UNKNOWN ) + return; + + SHA1Result( &ctx, digest ); + for ( int i=0; i<20; i++ ) + sprintf( buf+( i<<1 ), "%02x", digest[i] ); + JSetString( NULL, "AvatarHash", buf ); + JSetByte( "AvatarType", pictureType ); + + JabberGetAvatarFileName( NULL, szFileName, MAX_PATH ); + if ( strcmp( szFileName, tFileName )) + MoveFileA( tFileName, szFileName ); +} + +static BOOL CALLBACK JabberSetAvatarDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + { + hAvatar = NULL; + szFileName[0] = 0; + + BOOL tValue = JGetByte( "EnableAvatars", 1 ); + CheckDlgButton( hwndDlg, IDC_ENABLE_AVATARS, tValue ); + if ( tValue ) { + char szAvatar[ MAX_PATH ]; + JabberGetAvatarFileName( NULL, szAvatar, sizeof szAvatar ); + hAvatar = (HBITMAP)CallService(MS_UTILS_LOADBITMAP, 0, (WPARAM)szAvatar ); + if ( hAvatar ) + SendDlgItemMessage(hwndDlg, IDC_AVATAR, STM_SETIMAGE, IMAGE_BITMAP, (WPARAM)hAvatar ); + } } + + return TRUE; + + case WM_COMMAND: + if ( HIWORD( wParam ) == BN_CLICKED ) + switch( LOWORD( wParam )) { + case IDC_SETAVATAR: + if ( JabberEnterBitmapName( szFileName ) == ERROR_SUCCESS ) { + HBITMAP hBitmap = (HBITMAP)CallService(MS_UTILS_LOADBITMAP, 0, (WPARAM)szFileName ); + if ( hBitmap != NULL ) { + hAvatar = hBitmap; + hBitmap = ( HBITMAP )SendDlgItemMessage(hwndDlg, IDC_AVATAR, STM_SETIMAGE, IMAGE_BITMAP, (WPARAM)hBitmap ); + if ( hBitmap ) + DeleteObject( hBitmap ); + + sttSaveAvatar(); + if ( jabberConnected ) + JabberSendPresence( jabberDesiredStatus ); + RedrawWindow(GetDlgItem(hwndDlg, IDC_AVATAR), NULL, NULL, RDW_INVALIDATE); + } } + break; + + case IDC_DELETEAVATAR: + char tFileName[ MAX_PATH ]; + JabberGetAvatarFileName( NULL, tFileName, sizeof tFileName ); + DeleteFileA( tFileName ); + JDeleteSetting( NULL, "AvatarHash" ); + JDeleteSetting( NULL, "AvatarType" ); + + DeleteObject( hAvatar ); hAvatar = NULL; + HBITMAP hBitmap = (HBITMAP)SendDlgItemMessage(hwndDlg, IDC_AVATAR, STM_SETIMAGE, IMAGE_BITMAP, (WPARAM)NULL ); + if ( hBitmap ) + DeleteObject( hBitmap ); + + if ( jabberConnected ) + JabberSendPresence( jabberDesiredStatus ); + break; + } + break; + + case WM_DESTROY: + if ( hAvatar ) + DeleteObject( hAvatar ); + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberUserInfoInit - initializes user info option dialogs + +int JabberUserInfoInit( WPARAM wParam, LPARAM lParam ) +{ + if ( !JCallService( MS_PROTO_ISPROTOCOLLOADED, 0, ( LPARAM )jabberProtoName )) + return 0; + + OPTIONSDIALOGPAGE odp = {0}; + odp.cbSize = sizeof( odp ); + odp.hInstance = hInst; + + HANDLE hContact = ( HANDLE )lParam; + if ( hContact == NULL ) { + if ( JGetByte( "EnableAvatars", TRUE )) { + char szTitle[256]; + mir_snprintf( szTitle, sizeof( szTitle ), "%s %s", jabberProtoName, JTranslate( "Avatar" )); + + odp.pfnDlgProc = JabberSetAvatarDlgProc; + odp.position = 2000000001; + odp.pszTemplate = MAKEINTRESOURCEA( IDD_OPT_SETAVATAR ); + odp.pszTitle = szTitle; + JCallService( MS_USERINFO_ADDPAGE, wParam, ( LPARAM )&odp ); + } + return 0; + } + + char* szProto = ( char* )JCallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 ); + if ( szProto != NULL && !strcmp( szProto, jabberProtoName )) { + odp.pfnDlgProc = JabberUserInfoDlgProc; + odp.position = -2000000000; + odp.pszTemplate = MAKEINTRESOURCEA( IDD_INFO_JABBER ); + odp.pszTitle = jabberModuleName; + JCallService( MS_USERINFO_ADDPAGE, wParam, ( LPARAM )&odp ); + + odp.pfnDlgProc = JabberUserPhotoDlgProc; + odp.position = 2000000000; + odp.pszTemplate = MAKEINTRESOURCEA( IDD_VCARD_PHOTO ); + odp.pszTitle = JTranslate( "Photo" ); + JCallService( MS_USERINFO_ADDPAGE, wParam, ( LPARAM )&odp ); + } + return 0; +} diff --git a/miranda-wine/protocols/JabberG/jabber_util.cpp b/miranda-wine/protocols/JabberG/jabber_util.cpp new file mode 100644 index 0000000..d725f98 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_util.cpp @@ -0,0 +1,1133 @@ +/* + +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_util.cpp,v $ +Revision : $Revision: 3667 $ +Last change on : $Date: 2006-08-31 18:10:18 +0400 (Чтв, 31 Авг 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include "jabber_ssl.h" +#include "jabber_list.h" +#include "sha1.h" + +extern CRITICAL_SECTION mutex; +extern UINT jabberCodePage; + +static CRITICAL_SECTION serialMutex; +static unsigned int serial; + +void __stdcall JabberSerialInit( void ) +{ + InitializeCriticalSection( &serialMutex ); + serial = 0; +} + +void __stdcall JabberSerialUninit( void ) +{ + DeleteCriticalSection( &serialMutex ); +} + +unsigned int __stdcall JabberSerialNext( void ) +{ + unsigned int ret; + + EnterCriticalSection( &serialMutex ); + ret = serial; + serial++; + LeaveCriticalSection( &serialMutex ); + return ret; +} + +void __stdcall JabberLog( const char* fmt, ... ) +{ + va_list vararg; + va_start( vararg, fmt ); + char* str = ( char* )alloca( 32000 ); + mir_vsnprintf( str, 32000, fmt, vararg ); + va_end( vararg ); + + JCallService( MS_NETLIB_LOG, ( WPARAM )hNetlibUser, ( LPARAM )str ); +} + +// Caution: DO NOT use JabberSend() to send binary ( non-string ) data +int __stdcall JabberSend( HANDLE hConn, XmlNode& node ) +{ + char* str = node.getText(); + int size = strlen( str ), result; + + EnterCriticalSection( &mutex ); + + PVOID ssl; + if (( ssl=JabberSslHandleToSsl( hConn )) != NULL ) { + if ( DBGetContactSettingByte( NULL, "Netlib", "DumpSent", TRUE ) == TRUE ) { + char* szLogBuffer = ( char* )alloca( size+32 ); + strcpy( szLogBuffer, "( SSL ) Data sent\n" ); + memcpy( szLogBuffer+strlen( szLogBuffer ), str, size+1 ); // also copy \0 + Netlib_Logf( hNetlibUser, "%s", szLogBuffer ); // %s to protect against when fmt tokens are in szLogBuffer causing crash + } + + result = pfn_SSL_write( ssl, str, size ); + } + else result = JabberWsSend( hConn, str, size ); + LeaveCriticalSection( &mutex ); + + mir_free( str ); + return result; +} + +int __stdcall JabberSend( HANDLE hConn, const char* fmt, ... ) +{ + int result; + + EnterCriticalSection( &mutex ); + + va_list vararg; + va_start( vararg,fmt ); + int size = 512; + char* str = ( char* )mir_alloc( size ); + while ( _vsnprintf( str, size, fmt, vararg ) == -1 ) { + size += 512; + str = ( char* )mir_realloc( str, size ); + } + va_end( vararg ); + + size = strlen( str ); + PVOID ssl; + if (( ssl=JabberSslHandleToSsl( hConn )) != NULL ) { + if ( DBGetContactSettingByte( NULL, "Netlib", "DumpSent", TRUE ) == TRUE ) { + char* szLogBuffer = ( char* )alloca( size+32 ); + strcpy( szLogBuffer, "( SSL ) Data sent\n" ); + memcpy( szLogBuffer+strlen( szLogBuffer ), str, size+1 ); // also copy \0 + Netlib_Logf( hNetlibUser, "%s", szLogBuffer ); // %s to protect against when fmt tokens are in szLogBuffer causing crash + } + + result = pfn_SSL_write( ssl, str, size ); + } + else result = JabberWsSend( hConn, str, size ); + LeaveCriticalSection( &mutex ); + + mir_free( str ); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberHContactFromJID - looks for the HCONTACT with required JID + +HANDLE __stdcall JabberHContactFromJID( const TCHAR* jid ) +{ + if ( jid == NULL ) + return ( HANDLE )NULL; + + JABBER_LIST_ITEM* item = JabberListGetItemPtr( LIST_CHATROOM, jid ); + + HANDLE hContactMatched = NULL; + HANDLE hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) { + char* szProto = ( char* )JCallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 ); + if ( szProto != NULL && !strcmp( jabberProtoName, szProto )) { + DBVARIANT dbv; + int result = JGetStringT( hContact, "jid", &dbv ); + if ( result ) + result = JGetStringT( hContact, "ChatRoomID", &dbv ); + + if ( !result ) { + int result; + if ( item != NULL ) + result = lstrcmpi( jid, dbv.ptszVal ); + else + result = JabberCompareJids( jid, dbv.ptszVal ); + JFreeVariant( &dbv ); + if ( !result ) { + hContactMatched = hContact; + break; + } } } + + hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM ) hContact, 0 ); + } + + return hContactMatched; +} + +TCHAR* __stdcall JabberNickFromJID( const TCHAR* jid ) +{ + const TCHAR* p; + TCHAR* nick; + + if (( p = _tcschr( jid, '@' )) == NULL ) + p = _tcschr( jid, '/' ); + + if ( p != NULL ) { + if (( nick=( TCHAR* )mir_alloc( sizeof(TCHAR)*( int( p-jid )+1 ))) != NULL ) { + _tcsncpy( nick, jid, p-jid ); + nick[p-jid] = '\0'; + } + } + else nick = mir_tstrdup( jid ); + return nick; +} + +char* __stdcall JabberUrlDecode( char* str ) +{ + char* p, *q; + + if ( str == NULL ) + return NULL; + + for ( p=q=str; *p!='\0'; p++,q++ ) { + if ( *p == '&' ) { + if ( !strncmp( p, "&", 5 )) { *q = '&'; p += 4; } + else if ( !strncmp( p, "'", 6 )) { *q = '\''; p += 5; } + else if ( !strncmp( p, ">", 4 )) { *q = '>'; p += 3; } + else if ( !strncmp( p, "<", 4 )) { *q = '<'; p += 3; } + else if ( !strncmp( p, """, 6 )) { *q = '"'; p += 5; } + else { *q = *p; } + } + else { + *q = *p; + } + } + *q = '\0'; + return str; +} + +void __stdcall JabberUrlDecodeW( WCHAR* str ) +{ + if ( str == NULL ) + return; + + WCHAR* p, *q; + for ( p=q=str; *p!='\0'; p++,q++ ) { + if ( *p == '&' ) { + if ( !wcsncmp( p, L"&", 5 )) { *q = '&'; p += 4; } + else if ( !wcsncmp( p, L"'", 6 )) { *q = '\''; p += 5; } + else if ( !wcsncmp( p, L">", 4 )) { *q = '>'; p += 3; } + else if ( !wcsncmp( p, L"<", 4 )) { *q = '<'; p += 3; } + else if ( !wcsncmp( p, L""", 6 )) { *q = '"'; p += 5; } + else { *q = *p; } + } + else { + *q = *p; + } + } + *q = '\0'; +} + +char* __stdcall JabberUrlEncode( const char* str ) +{ + char* s, *p, *q; + int c; + + if ( str == NULL ) + return NULL; + + for ( c=0,p=( char* )str; *p!='\0'; p++ ) { + switch ( *p ) { + case '&': c += 5; break; + case '\'': c += 6; break; + case '>': c += 4; break; + case '<': c += 4; break; + case '"': c += 6; break; + default: c++; break; + } + } + if (( s=( char* )mir_alloc( c+1 )) != NULL ) { + for ( p=( char* )str,q=s; *p!='\0'; p++ ) { + switch ( *p ) { + case '&': strcpy( q, "&" ); q += 5; break; + case '\'': strcpy( q, "'" ); q += 6; break; + case '>': strcpy( q, ">" ); q += 4; break; + case '<': strcpy( q, "<" ); q += 4; break; + case '"': strcpy( q, """ ); q += 6; break; + default: *q = *p; q++; break; + } + } + *q = '\0'; + } + + return s; +} + +static void __stdcall sttUtf8Decode( const BYTE* str, wchar_t* tempBuf ) +{ + wchar_t* d = tempBuf; + BYTE* s = ( BYTE* )str; + + while( *s ) + { + if (( *s & 0x80 ) == 0 ) { + *d++ = *s++; + continue; + } + + if (( s[0] & 0xE0 ) == 0xE0 && ( s[1] & 0xC0 ) == 0x80 && ( s[2] & 0xC0 ) == 0x80 ) { + *d++ = (( WORD )( s[0] & 0x0F ) << 12 ) + ( WORD )(( s[1] & 0x3F ) << 6 ) + ( WORD )( s[2] & 0x3F ); + s += 3; + continue; + } + + if (( s[0] & 0xE0 ) == 0xC0 && ( s[1] & 0xC0 ) == 0x80 ) { + *d++ = ( WORD )(( s[0] & 0x1F ) << 6 ) + ( WORD )( s[1] & 0x3F ); + s += 2; + continue; + } + + *d++ = *s++; + } + + *d = 0; +} + + +char* __stdcall JabberUtf8Decode( char* str, WCHAR** ucs2 ) +{ + if ( str == NULL ) + return NULL; + + int len = strlen( str ); + if ( len < 2 ) { + if ( ucs2 != NULL ) { + *ucs2 = ( wchar_t* )mir_alloc(( len+1 )*sizeof( wchar_t )); + MultiByteToWideChar( CP_ACP, 0, str, len, *ucs2, len ); + ( *ucs2 )[ len ] = 0; + } + return str; + } + + wchar_t* tempBuf = ( wchar_t* )alloca(( len+1 )*sizeof( wchar_t )); + sttUtf8Decode(( BYTE* )str, tempBuf ); + + if ( ucs2 != NULL ) { + int fullLen = ( len+1 )*sizeof( wchar_t ); + *ucs2 = ( wchar_t* )mir_alloc( fullLen ); + memcpy( *ucs2, tempBuf, fullLen ); + } + + WideCharToMultiByte( CP_ACP, 0, tempBuf, -1, str, len, NULL, NULL ); + return str; +} + +char* __stdcall JabberUtf8EncodeW( const WCHAR* wstr ) +{ + const WCHAR* w; + + // Convert unicode to utf8 + int len = 0; + for ( w = wstr; *w; w++ ) { + if ( *w < 0x0080 ) len++; + else if ( *w < 0x0800 ) len += 2; + else len += 3; + } + + unsigned char* szOut = ( unsigned char* )mir_alloc( len+1 ); + if ( szOut == NULL ) + return NULL; + + int i = 0; + for ( w = wstr; *w; w++ ) { + if ( *w < 0x0080 ) + szOut[i++] = ( unsigned char ) *w; + else if ( *w < 0x0800 ) { + szOut[i++] = 0xc0 | (( *w ) >> 6 ); + szOut[i++] = 0x80 | (( *w ) & 0x3f ); + } + else { + szOut[i++] = 0xe0 | (( *w ) >> 12 ); + szOut[i++] = 0x80 | (( ( *w ) >> 6 ) & 0x3f ); + szOut[i++] = 0x80 | (( *w ) & 0x3f ); + } } + + szOut[ i ] = '\0'; + return ( char* )szOut; +} + +void __stdcall JabberUtfToTchar( const char* pszValue, size_t cbLen, LPTSTR& dest ) +{ + char* pszCopy = ( char* )alloca( cbLen+1 ); + memcpy( pszCopy, pszValue, cbLen ); + pszCopy[ cbLen ] = 0; + + JabberUrlDecode( pszCopy ); + + #if defined( _UNICODE ) + JabberUtf8Decode( pszCopy, &dest ); + #else + JabberUtf8Decode( pszCopy, NULL ); + dest = mir_strdup( pszCopy ); + #endif +} + +char* __stdcall JabberUtf8Encode( const char* str ) +{ + if ( str == NULL ) + return NULL; + + // Convert local codepage to unicode + int len = strlen( str ); + WCHAR* wszTemp = ( WCHAR* )alloca( sizeof( WCHAR )*( len+1 )); + MultiByteToWideChar( jabberCodePage, 0, str, -1, wszTemp, len+1 ); + + return JabberUtf8EncodeW( wszTemp ); +} + +char* __stdcall JabberSha1( char* str ) +{ + SHA1Context sha; + uint8_t digest[20]; + char* result; + int i; + + if ( str == NULL ) + return NULL; + + SHA1Reset( &sha ); + SHA1Input( &sha, ( const unsigned __int8* )str, strlen( str )); + SHA1Result( &sha, digest ); + if (( result=( char* )mir_alloc( 41 )) == NULL ) + return NULL; + + for ( i=0; i<20; i++ ) + sprintf( result+( i<<1 ), "%02x", digest[i] ); + return result; +} + +char* __stdcall JabberUnixToDos( const char* str ) +{ + char* p, *q, *res; + int extra; + + if ( str==NULL || str[0]=='\0' ) + return NULL; + + extra = 0; + for ( p=( char* )str; *p!='\0'; p++ ) { + if ( *p == '\n' ) + extra++; + } + if (( res=( char* )mir_alloc( strlen( str )+extra+1 )) != NULL ) { + for ( p=( char* )str,q=res; *p!='\0'; p++,q++ ) { + if ( *p == '\n' ) { + *q = '\r'; + q++; + } + *q = *p; + } + *q = '\0'; + } + return res; +} + +WCHAR* __stdcall JabberUnixToDosW( const WCHAR* str ) +{ + if ( str==NULL || str[0]=='\0' ) + return NULL; + + const WCHAR* p; + WCHAR* q, *res; + int extra = 0; + + for ( p = str; *p!='\0'; p++ ) { + if ( *p == '\n' ) + extra++; + } + if (( res = ( WCHAR* )mir_alloc( sizeof( WCHAR )*( wcslen( str ) + extra + 1 )) ) != NULL ) { + for ( p = str,q=res; *p!='\0'; p++,q++ ) { + if ( *p == '\n' ) { + *q = '\r'; + q++; + } + *q = *p; + } + *q = '\0'; + } + return res; +} + +char* __stdcall JabberHttpUrlEncode( const char* str ) +{ + unsigned char* p, *q, *res; + + if ( str == NULL ) return NULL; + res = ( BYTE* ) mir_alloc( 3*strlen( str ) + 1 ); + for ( p=( BYTE* )str,q=res; *p!='\0'; p++,q++ ) { + if (( *p>='A' && *p<='Z' ) || ( *p>='a' && *p<='z' ) || ( *p>='0' && *p<='9' ) || strchr( "$-_.+!*'(),", *p )!=NULL ) { + *q = *p; + } + else { + sprintf(( char* )q, "%%%02X", *p ); + q += 2; + } + } + *q = '\0'; + return ( char* )res; +} + +void __stdcall JabberHttpUrlDecode( char* str ) +{ + unsigned char* p, *q; + unsigned int code; + + if ( str == NULL ) return; + for ( p=q=( BYTE* )str; *p!='\0'; p++,q++ ) { + if ( *p=='%' && *( p+1 )!='\0' && isxdigit( *( p+1 )) && *( p+2 )!='\0' && isxdigit( *( p+2 )) ) { + sscanf(( char* )p+1, "%2x", &code ); + *q = ( unsigned char ) code; + p += 2; + } + else { + *q = *p; + } } + + *q = '\0'; +} + +int __stdcall JabberCombineStatus( int status1, int status2 ) +{ + // Combine according to the following priority ( high to low ) + // ID_STATUS_FREECHAT + // ID_STATUS_ONLINE + // ID_STATUS_DND + // ID_STATUS_AWAY + // ID_STATUS_NA + // ID_STATUS_INVISIBLE ( valid only for TLEN_PLUGIN ) + // ID_STATUS_OFFLINE + // other ID_STATUS in random order ( actually return status1 ) + if ( status1==ID_STATUS_FREECHAT || status2==ID_STATUS_FREECHAT ) + return ID_STATUS_FREECHAT; + if ( status1==ID_STATUS_ONLINE || status2==ID_STATUS_ONLINE ) + return ID_STATUS_ONLINE; + if ( status1==ID_STATUS_DND || status2==ID_STATUS_DND ) + return ID_STATUS_DND; + if ( status1==ID_STATUS_AWAY || status2==ID_STATUS_AWAY ) + return ID_STATUS_AWAY; + if ( status1==ID_STATUS_NA || status2==ID_STATUS_NA ) + return ID_STATUS_NA; + if ( status1==ID_STATUS_INVISIBLE || status2==ID_STATUS_INVISIBLE ) + return ID_STATUS_INVISIBLE; + if ( status1==ID_STATUS_OFFLINE || status2==ID_STATUS_OFFLINE ) + return ID_STATUS_OFFLINE; + return status1; +} + +struct tagErrorCodeToStr { + int code; + TCHAR* str; +} +static JabberErrorCodeToStrMapping[] = { + { JABBER_ERROR_REDIRECT, _T("Redirect") }, + { JABBER_ERROR_BAD_REQUEST, _T("Bad request") }, + { JABBER_ERROR_UNAUTHORIZED, _T("Unauthorized") }, + { JABBER_ERROR_PAYMENT_REQUIRED, _T("Payment required") }, + { JABBER_ERROR_FORBIDDEN, _T("Forbidden") }, + { JABBER_ERROR_NOT_FOUND, _T("Not found") }, + { JABBER_ERROR_NOT_ALLOWED, _T("Not allowed") }, + { JABBER_ERROR_NOT_ACCEPTABLE, _T("Not acceptable") }, + { JABBER_ERROR_REGISTRATION_REQUIRED, _T("Registration required") }, + { JABBER_ERROR_REQUEST_TIMEOUT, _T("Request timeout") }, + { JABBER_ERROR_CONFLICT, _T("Conflict") }, + { JABBER_ERROR_INTERNAL_SERVER_ERROR, _T("Internal server error") }, + { JABBER_ERROR_NOT_IMPLEMENTED, _T("Not implemented") }, + { JABBER_ERROR_REMOTE_SERVER_ERROR, _T("Remote server error") }, + { JABBER_ERROR_SERVICE_UNAVAILABLE, _T("Service unavailable") }, + { JABBER_ERROR_REMOTE_SERVER_TIMEOUT, _T("Remote server timeout") }, + { -1, _T("Unknown error") } +}; + +TCHAR* __stdcall JabberErrorStr( int errorCode ) +{ + int i; + + for ( i=0; JabberErrorCodeToStrMapping[i].code!=-1 && JabberErrorCodeToStrMapping[i].code!=errorCode; i++ ); + return JabberErrorCodeToStrMapping[i].str; +} + +TCHAR* __stdcall JabberErrorMsg( XmlNode *errorNode ) +{ + TCHAR* errorStr, *str; + int errorCode; + + errorStr = ( TCHAR* )mir_alloc( 256 * sizeof( TCHAR )); + if ( errorNode == NULL ) { + mir_sntprintf( errorStr, 256, _T("%s -1: %s"), TranslateT( "Error" ), TranslateT( "Unknown error message" )); + return errorStr; + } + + errorCode = -1; + if (( str=JabberXmlGetAttrValue( errorNode, "code" )) != NULL ) + errorCode = _ttoi( str ); + if (( str=errorNode->text ) != NULL ) + mir_sntprintf( errorStr, 256, _T("%s %d: %s\r\n%s"), TranslateT( "Error" ), errorCode, TranslateTS( JabberErrorStr( errorCode )), str ); + else + mir_sntprintf( errorStr, 256, _T("%s %d: %s"), TranslateT( "Error" ), errorCode, TranslateTS( JabberErrorStr( errorCode )) ); + return errorStr; +} + +void __stdcall JabberSendVisibleInvisiblePresence( BOOL invisible ) +{ + if ( !jabberOnline ) return; + + for ( int i = 0; ( i=JabberListFindNext( LIST_ROSTER, i )) >= 0; i++ ) { + JABBER_LIST_ITEM *item = JabberListGetItemPtrFromIndex( i ); + if ( item == NULL ) + continue; + + HANDLE hContact = JabberHContactFromJID( item->jid ); + if ( hContact == NULL ) + continue; + + WORD apparentMode = JGetWord( hContact, "ApparentMode", 0 ); + if ( invisible==TRUE && apparentMode==ID_STATUS_OFFLINE ) { + XmlNode p( "presence" ); p.addAttr( "to", item->jid ); p.addAttr( "type", "invisible" ); + JabberSend( jabberThreadInfo->s, p ); + } + else if ( invisible==FALSE && apparentMode==ID_STATUS_ONLINE ) + JabberSendPresenceTo( jabberStatus, item->jid, NULL ); +} } + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberTextEncodeW - prepare a string for transmission + +char* __stdcall JabberTextEncode( const char* str ) +{ + if ( str == NULL ) + return NULL; + + char* s1 = JabberUrlEncode( str ); + if ( s1 == NULL ) + return NULL; + + // Convert invalid control characters to space + if ( *s1 ) { + char* p, *q; + + for ( p = s1; *p != '\0'; p++ ) + if ( *p > 0 && *p < 0x20 && *p != 0x09 && *p != 0x0a && *p != 0x0d ) + *p = ( char )0x20; + + for ( p = q = s1; *p!='\0'; p++ ) { + if ( *p != '\r' ) { + *q = *p; + q++; + } } + + *q = '\0'; + } + + char* s2 = JabberUtf8Encode( s1 ); + mir_free( s1 ); + return s2; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberTextEncodeW - prepare a string for transmission + +char* __stdcall JabberTextEncodeW( const wchar_t* str ) +{ + if ( str == NULL ) + return NULL; + + const wchar_t *s; + int resLen = 1; + + for ( s = str; *s; s++ ) { + switch( *s ) { + case '\r': continue; + case '&': resLen += 5; break; + case '\'': resLen += 6; break; + case '>': + case '<': resLen += 6; break; + case '\"': resLen += 6; break; + default: resLen++; break; + } } + + wchar_t* tmp = ( wchar_t* )alloca( resLen * sizeof( wchar_t )), *d; + + for ( s = str, d = tmp; *s; s++ ) { + switch( *s ) { + case '\r': continue; + case '&': wcscpy( d, L"&" ); d += 5; break; + case '\'': wcscpy( d, L"'" ); d += 6; break; + case '>': wcscpy( d, L">" ); d += 4; break; + case '<': wcscpy( d, L"<" ); d += 4; break; + case '\"': wcscpy( d, L""" ); d += 6; break; + default: + if ( *s > 0 && *s < 0x20 && *s != 0x09 && *s != 0x0a && *s != 0x0d ) + *d++ = ' '; + else + *d++ = *s; + } } + + *d = 0; + + return JabberUtf8EncodeW( tmp ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberTextDecode - retrieve a text from the encoded string + +char* __stdcall JabberTextDecode( const char* str ) +{ + if ( str == NULL ) + return NULL; + + char* s1 = ( char* )alloca( strlen( str )+1 ), *s2; + strcpy( s1, str ); + + JabberUtf8Decode( s1, NULL ); + JabberUrlDecode( s1 ); + if (( s2 = JabberUnixToDos( s1 )) == NULL ) + return NULL; + + return s2; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberBase64Encode + +static char b64table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +char* __stdcall JabberBase64Encode( const char* buffer, int bufferLen ) +{ + if ( buffer==NULL || bufferLen<=0 ) + return NULL; + + char* res = (char*)mir_alloc(((( bufferLen+2 )*4 )/3 ) + 1); + if ( res == NULL ) + return NULL; + + unsigned char igroup[3]; + int nGroups = 0; + char *r = res; + const char* peob = buffer + bufferLen; + for ( const char* p = buffer; p < peob; ) { + igroup[ 0 ] = igroup[ 1 ] = igroup[ 2 ] = 0; + int n; + for ( n=0; n<3; n++ ) { + if ( p >= peob ) break; + igroup[n] = ( unsigned char ) *p; + p++; + } + + if ( n > 0 ) { + r[0] = b64table[ igroup[0]>>2 ]; + r[1] = b64table[ (( igroup[0]&3 )<<4 ) | ( igroup[1]>>4 ) ]; + r[2] = b64table[ (( igroup[1]&0xf )<<2 ) | ( igroup[2]>>6 ) ]; + r[3] = b64table[ igroup[2]&0x3f ]; + + if ( n < 3 ) { + r[3] = '='; + if ( n < 2 ) + r[2] = '='; + } + r += 4; + } } + + *r = '\0'; + + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberBase64Decode + +static unsigned char b64rtable[256]; + +char* __stdcall JabberBase64Decode( const TCHAR* str, int *resultLen ) +{ + char* res; + unsigned char* r, igroup[4], a[4]; + int n, num, count; + + if ( str==NULL || resultLen==NULL ) return NULL; + if (( res=( char* )mir_alloc(( ( _tcslen( str )+3 )/4 )*3 )) == NULL ) return NULL; + + for ( n=0; n<256; n++ ) + b64rtable[n] = ( unsigned char ) 0x80; + for ( n=0; n<26; n++ ) + b64rtable['A'+n] = n; + for ( n=0; n<26; n++ ) + b64rtable['a'+n] = n + 26; + for ( n=0; n<10; n++ ) + b64rtable['0'+n] = n + 52; + b64rtable['+'] = 62; + b64rtable['/'] = 63; + b64rtable['='] = 0; + count = 0; + for ( r=( unsigned char* )res; *str != '\0'; ) { + for ( n=0; n<4; n++ ) { + if ( BYTE(*str) == '\r' || BYTE(*str) == '\n' ) { + n--; str++; + continue; + } + + if ( BYTE(*str)=='\0' ) { + if ( n == 0 ) + goto LBL_Exit; + mir_free( res ); + return NULL; + } + + if ( b64rtable[BYTE(*str)]==0x80 ) { + mir_free( res ); + return NULL; + } + + a[n] = BYTE(*str); + igroup[n] = b64rtable[BYTE(*str)]; + str++; + } + r[0] = igroup[0]<<2 | igroup[1]>>4; + r[1] = igroup[1]<<4 | igroup[2]>>2; + r[2] = igroup[2]<<6 | igroup[3]; + r += 3; + num = ( a[2]=='='?1:( a[3]=='='?2:3 )); + count += num; + if ( num < 3 ) break; + } +LBL_Exit: + *resultLen = count; + return res; +} + +char* __stdcall JabberGetVersionText() +{ + char filename[MAX_PATH], *fileVersion, *res; + DWORD unused; + DWORD verInfoSize; + UINT blockSize; + PVOID pVerInfo; + + GetModuleFileNameA( hInst, filename, sizeof( filename )); + verInfoSize = GetFileVersionInfoSizeA( filename, &unused ); + if (( pVerInfo=mir_alloc( verInfoSize )) != NULL ) { + GetFileVersionInfoA( filename, 0, verInfoSize, pVerInfo ); + VerQueryValueA( pVerInfo, "\\StringFileInfo\\040904b0\\FileVersion", ( LPVOID* )&fileVersion, &blockSize ); + if ( strstr( fileVersion, "cvs" )) { + res = ( char* )mir_alloc( strlen( fileVersion ) + strlen( __DATE__ ) + 2 ); + sprintf( res, "%s %s", fileVersion, __DATE__ ); + } + else { + res = mir_strdup( fileVersion ); + } + mir_free( pVerInfo ); + return res; + } + return NULL; +} + +time_t __stdcall JabberIsoToUnixTime( TCHAR* stamp ) +{ + struct tm timestamp; + TCHAR date[9]; + TCHAR* p; + int i, y; + time_t t; + + if ( stamp == NULL ) return ( time_t ) 0; + + p = stamp; + + // Get the date part + for ( i=0; *p!='\0' && i<8 && isdigit( *p ); p++,i++ ) + date[i] = *p; + + // Parse year + if ( i == 6 ) { + // 2-digit year ( 1970-2069 ) + y = ( date[0]-'0' )*10 + ( date[1]-'0' ); + if ( y < 70 ) y += 100; + } + else if ( i == 8 ) { + // 4-digit year + y = ( date[0]-'0' )*1000 + ( date[1]-'0' )*100 + ( date[2]-'0' )*10 + date[3]-'0'; + y -= 1900; + } + else + return ( time_t ) 0; + timestamp.tm_year = y; + // Parse month + timestamp.tm_mon = ( date[i-4]-'0' )*10 + date[i-3]-'0' - 1; + // Parse date + timestamp.tm_mday = ( date[i-2]-'0' )*10 + date[i-1]-'0'; + + // Skip any date/time delimiter + for ( ; *p!='\0' && !isdigit( *p ); p++ ); + + // Parse time + if ( _stscanf( p, _T("%d:%d:%d"), ×tamp.tm_hour, ×tamp.tm_min, ×tamp.tm_sec ) != 3 ) + return ( time_t ) 0; + + timestamp.tm_isdst = 0; // DST is already present in _timezone below + t = mktime( ×tamp ); + + _tzset(); + t -= _timezone; + + if ( t >= 0 ) + return t; + else + return ( time_t ) 0; +} + +struct MyCountryListEntry +{ + int id; + TCHAR* szName; +} +static extraCtry[] = +{ + { 1, _T("United States") }, + { 1, _T("United States of America") }, + { 1, _T("US") }, + { 44, _T("England") } +}; + +int __stdcall JabberCountryNameToId( TCHAR* ctry ) +{ + int ctryCount, i; + MyCountryListEntry *ctryList; + + // Check for some common strings not present in the country list + ctryCount = sizeof( extraCtry )/sizeof( extraCtry[0] ); + for ( i=0; i<ctryCount && _tcsicmp( extraCtry[i].szName, ctry ); i++ ); + if ( i < ctryCount ) + return extraCtry[i].id; + + // Check Miranda country list + JCallService( MS_UTILS_GETCOUNTRYLIST, ( WPARAM ) &ctryCount, ( LPARAM )&ctryList ); + for ( i=0; i<ctryCount && _tcsicmp( ctryList[i].szName, ctry ); i++ ); + if ( i < ctryCount ) + return ctryList[i].id; + else + return 0xffff; // Unknown +} + +void __stdcall JabberSendPresenceTo( int status, TCHAR* to, XmlNode* extra ) +{ + if ( !jabberOnline ) return; + + // Send <presence/> update for status ( we won't handle ID_STATUS_OFFLINE here ) + // Note: jabberModeMsg is already encoded using JabberTextEncode() + EnterCriticalSection( &modeMsgMutex ); + + char szPriority[40]; + itoa( JGetWord( NULL, "Priority", 0 ), szPriority, 10 ); + + XmlNode p( "presence" ); p.addChild( "priority", szPriority ); + if ( to != NULL ) + p.addAttr( "to", to ); + + if ( extra ) + p.addChild( extra ); + + if ( JGetByte( "EnableAvatars", TRUE )) { + char hashValue[ 50 ]; + if ( !JGetStaticString( "AvatarHash", NULL, hashValue, sizeof hashValue )) { + XmlNode* x = p.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:avatar" ); + x->addChild( "hash", hashValue ); + + x = p.addChild( "x" ); x->addAttr( "xmlns", "vcard-temp:x:update" ); + x->addChild( "photo", hashValue ); + } } + + switch ( status ) { + case ID_STATUS_ONLINE: + if ( modeMsgs.szOnline ) + p.addChild( "status", modeMsgs.szOnline ); + break; + case ID_STATUS_INVISIBLE: + p.addAttr( "type", "invisible" ); + break; + case ID_STATUS_AWAY: + case ID_STATUS_ONTHEPHONE: + case ID_STATUS_OUTTOLUNCH: + p.addChild( "show", "away" ); + if ( modeMsgs.szAway ) + p.addChild( "status", modeMsgs.szAway ); + break; + case ID_STATUS_NA: + p.addChild( "show", "xa" ); + if ( modeMsgs.szNa ) + p.addChild( "status", modeMsgs.szNa ); + break; + case ID_STATUS_DND: + case ID_STATUS_OCCUPIED: + p.addChild( "show", "dnd" ); + if ( modeMsgs.szDnd ) + p.addChild( "status", modeMsgs.szDnd ); + break; + case ID_STATUS_FREECHAT: + p.addChild( "show", "chat" ); + if ( modeMsgs.szFreechat ) + p.addChild( "status", modeMsgs.szFreechat ); + break; + default: + // Should not reach here + break; + } + JabberSend( jabberThreadInfo->s, p ); + LeaveCriticalSection( &modeMsgMutex ); +} + +void __stdcall JabberSendPresence( int status ) +{ + JabberSendPresenceTo( status, NULL, NULL ); + JabberSendVisibleInvisiblePresence( status == ID_STATUS_INVISIBLE ); + + // Also update status in all chatrooms + for ( int i = 0; ( i=JabberListFindNext( LIST_CHATROOM, i )) >= 0; i++ ) { + JABBER_LIST_ITEM *item = JabberListGetItemPtrFromIndex( i ); + if ( item != NULL ) + JabberSendPresenceTo( status, item->jid, NULL ); +} } + +void __stdcall JabberStringAppend( char* *str, int *sizeAlloced, const char* fmt, ... ) +{ + va_list vararg; + char* p; + int size, len; + + if ( str == NULL ) return; + + if ( *str==NULL || *sizeAlloced<=0 ) { + *sizeAlloced = size = 2048; + *str = ( char* )mir_alloc( size ); + len = 0; + } + else { + len = strlen( *str ); + size = *sizeAlloced - strlen( *str ); + } + + p = *str + len; + va_start( vararg, fmt ); + while ( _vsnprintf( p, size, fmt, vararg ) == -1 ) { + size += 2048; + ( *sizeAlloced ) += 2048; + *str = ( char* )mir_realloc( *str, *sizeAlloced ); + p = *str + len; + } + va_end( vararg ); +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberGetClientJID - adds a resource postfix to a JID + +TCHAR* __stdcall JabberGetClientJID( const TCHAR* jid, TCHAR* dest, size_t destLen ) +{ + if ( jid == NULL ) + return NULL; + + size_t len = _tcslen( jid ); + if ( len >= destLen ) + len = destLen-1; + + _tcsncpy( dest, jid, len ); + dest[ len ] = '\0'; + + TCHAR* p = _tcschr( dest, '/' ); + if ( p == NULL ) { + TCHAR* resource = JabberListGetBestResourceNamePtr( jid ); + if ( resource != NULL ) + mir_sntprintf( dest+len, destLen-len-1, _T("/%s"), resource ); + } + + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// +// JabberStripJid - strips a resource postfix from a JID + +TCHAR* __stdcall JabberStripJid( const TCHAR* jid, TCHAR* dest, size_t destLen ) +{ + if ( jid == NULL ) + *dest = 0; + else { + size_t len = _tcslen( jid ); + if ( len >= destLen ) + len = destLen-1; + + memcpy( dest, jid, len * sizeof( TCHAR )); + dest[ len ] = 0; + + TCHAR* p = _tcschr( dest, '/' ); + if ( p != NULL ) + *p = 0; + } + + return dest; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberGetPictureType - tries to autodetect the picture type from the buffer + +int __stdcall JabberGetPictureType( const char* buf ) +{ + if ( buf != NULL ) { + if ( memcmp( buf, "GIF89", 5 ) == 0 ) return PA_FORMAT_GIF; + if ( memcmp( buf, "\x89PNG", 4 ) == 0 ) return PA_FORMAT_PNG; + if ( memcmp( buf, "BM", 2 ) == 0 ) return PA_FORMAT_BMP; + if ( memcmp( buf+6, "JFIF", 4 ) == 0 ) return PA_FORMAT_JPEG; + } + + return PA_FORMAT_UNKNOWN; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Unicode functions + +char* t2a( const TCHAR* src ) +{ + #if defined( _UNICODE ) + return u2a( src ); + #else + return mir_strdup( src ); + #endif +} + +char* u2a( const wchar_t* src ) +{ + int codepage = CallService( MS_LANGPACK_GETCODEPAGE, 0, 0 ); + + int cbLen = WideCharToMultiByte( codepage, 0, src, -1, NULL, 0, NULL, NULL ); + char* result = ( char* )mir_alloc( cbLen+1 ); + if ( result == NULL ) + return NULL; + + WideCharToMultiByte( codepage, 0, src, -1, result, cbLen, NULL, NULL ); + result[ cbLen ] = 0; + return result; +} + +wchar_t* a2u( const char* src ) +{ + int codepage = CallService( MS_LANGPACK_GETCODEPAGE, 0, 0 ); + + int cbLen = MultiByteToWideChar( codepage, 0, src, -1, NULL, 0 ); + wchar_t* result = ( wchar_t* )mir_alloc( sizeof( wchar_t )*(cbLen+1)); + if ( result == NULL ) + return NULL; + + MultiByteToWideChar( codepage, 0, src, -1, result, cbLen ); + result[ cbLen ] = 0; + return result; +} diff --git a/miranda-wine/protocols/JabberG/jabber_vcard.cpp b/miranda-wine/protocols/JabberG/jabber_vcard.cpp new file mode 100644 index 0000000..0f551eb --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_vcard.cpp @@ -0,0 +1,1179 @@ +/* + +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_vcard.cpp,v $ +Revision : $Revision: 3016 $ +Last change on : $Date: 2006-06-04 23:07:43 +0400 (Вск, 04 Июн 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" +#include <sys/stat.h> +#include <fcntl.h> +#include <io.h> +#include <commctrl.h> +#include "jabber_iq.h" +#include "resource.h" + +extern char* jabberVcardPhotoFileName; +extern char* jabberVcardPhotoType; + +///////////////////////////////////////////////////////////////////////////////////////// + +static BOOL CALLBACK JabberVcardDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ); + +int JabberMenuHandleVcard( WPARAM wParam, LPARAM lParam ) +{ + if ( IsWindow( hwndJabberVcard )) + SetForegroundWindow( hwndJabberVcard ); + else { + hwndJabberVcard = CreateDialogParam( hInst, MAKEINTRESOURCE( IDD_VCARD ), NULL, JabberVcardDlgProc, ( LPARAM )NULL ); + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int JabberSendGetVcard( const TCHAR* jid ) +{ + int iqId = JabberSerialNext(); + JabberIqAdd( iqId, ( jid == jabberJID ) ? IQ_PROC_GETVCARD : IQ_PROC_NONE, JabberIqResultGetVcard ); + + XmlNodeIq iq( "get", iqId, jid ); + XmlNode* vs = iq.addChild( "vCard" ); vs->addAttr( "xmlns", "vcard-temp" ); + vs->addAttr( "prodid", "-//HandGen//NONSGML vGen v1.0//EN" ); vs->addAttr( "version", "2.0" ); + JabberSend( jabberThreadInfo->s, iq ); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +typedef struct { + HWND hwnd; + int dlgId; + DLGPROC dlgProc; +} VcardPage; + +typedef struct { + int pageCount; + int currentPage; + RECT rectTab; + VcardPage *page; + BOOL changed; + int updateAnimFrame; + TCHAR* szUpdating; + BOOL animating; +} VcardTab; + +static void SetDialogField( HWND hwndDlg, int nDlgItem, char* key ) +{ + DBVARIANT dbv; + + if ( !DBGetContactSettingTString( NULL, jabberProtoName, key, &dbv )) { + SetDlgItemText( hwndDlg, nDlgItem, dbv.ptszVal ); + JFreeVariant( &dbv ); + } + else SetDlgItemTextA( hwndDlg, nDlgItem, "" ); +} + +static BOOL CALLBACK PersonalDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + SendMessage( GetDlgItem( hwndDlg, IDC_GENDER ), CB_ADDSTRING, 0, ( LPARAM )TranslateT( "Male" )); + SendMessage( GetDlgItem( hwndDlg, IDC_GENDER ), CB_ADDSTRING, 0, ( LPARAM )TranslateT( "Female" )); + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 ); + return TRUE; + case WM_JABBER_REFRESH: + SetDialogField( hwndDlg, IDC_FULLNAME, "FullName" ); + SetDialogField( hwndDlg, IDC_NICKNAME, "Nick" ); + SetDialogField( hwndDlg, IDC_FIRSTNAME, "FirstName" ); + SetDialogField( hwndDlg, IDC_MIDDLE, "MiddleName" ); + SetDialogField( hwndDlg, IDC_LASTNAME, "LastName" ); + SetDialogField( hwndDlg, IDC_BIRTH, "BirthDate" ); + SetDialogField( hwndDlg, IDC_GENDER, "GenderString" ); + SetDialogField( hwndDlg, IDC_OCCUPATION, "Role" ); + SetDialogField( hwndDlg, IDC_HOMEPAGE, "Homepage" ); + break; + case WM_COMMAND: + if (( ( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE ) || + (( HWND )lParam==GetDlgItem( hwndDlg, IDC_GENDER ) && ( HIWORD( wParam )==CBN_EDITCHANGE||HIWORD( wParam )==CBN_SELCHANGE )) ) + SendMessage( GetParent( hwndDlg ), WM_JABBER_CHANGED, 0, 0 ); + break; + } + return FALSE; +} + +static BOOL CALLBACK HomeDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 ); + return TRUE; + case WM_JABBER_REFRESH: + SetDialogField( hwndDlg, IDC_ADDRESS1, "Street" ); + SetDialogField( hwndDlg, IDC_ADDRESS2, "Street2" ); + SetDialogField( hwndDlg, IDC_CITY, "City" ); + SetDialogField( hwndDlg, IDC_STATE, "State" ); + SetDialogField( hwndDlg, IDC_ZIP, "ZIP" ); + SetDialogField( hwndDlg, IDC_COUNTRY, "CountryName" ); + break; + case WM_COMMAND: + if (( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE ) + SendMessage( GetParent( hwndDlg ), WM_JABBER_CHANGED, 0, 0 ); + break; + } + return FALSE; +} + +static BOOL CALLBACK WorkDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 ); + return TRUE; + case WM_JABBER_REFRESH: + SetDialogField( hwndDlg, IDC_COMPANY, "Company" ); + SetDialogField( hwndDlg, IDC_DEPARTMENT, "CompanyDepartment" ); + SetDialogField( hwndDlg, IDC_TITLE, "CompanyPosition" ); + SetDialogField( hwndDlg, IDC_ADDRESS1, "CompanyStreet" ); + SetDialogField( hwndDlg, IDC_ADDRESS2, "CompanyStreet2" ); + SetDialogField( hwndDlg, IDC_CITY, "CompanyCity" ); + SetDialogField( hwndDlg, IDC_STATE, "CompanyState" ); + SetDialogField( hwndDlg, IDC_ZIP, "CompanyZIP" ); + SetDialogField( hwndDlg, IDC_COUNTRY, "CompanyCountryName" ); + break; + case WM_COMMAND: + if (( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE ) + SendMessage( GetParent( hwndDlg ), WM_JABBER_CHANGED, 0, 0 ); + break; + } + return FALSE; +} + +static char szPhotoFileName[MAX_PATH]; +static char szPhotoType[33]; +static BOOL bPhotoChanged; + +static BOOL CALLBACK PhotoDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + static HBITMAP hBitmap; + char szTempPath[MAX_PATH], szTempFileName[MAX_PATH]; + + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + hBitmap = NULL; + SendMessage( GetDlgItem( hwndDlg, IDC_LOAD ), BM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadImage( hInst, MAKEINTRESOURCE( IDI_OPEN ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 )); + SendMessage( GetDlgItem( hwndDlg, IDC_DELETE ), BM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadImage( hInst, MAKEINTRESOURCE( IDI_DELETE ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 )); + ShowWindow( GetDlgItem( hwndDlg, IDC_SAVE ), SW_HIDE ); + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 ); + return TRUE; + case WM_JABBER_REFRESH: + if ( hBitmap ) { + DeleteObject( hBitmap ); + hBitmap = NULL; + DeleteFileA( szPhotoFileName ); + szPhotoFileName[0] = '\0'; + } + EnableWindow( GetDlgItem( hwndDlg, IDC_DELETE ), FALSE ); + if ( jabberVcardPhotoFileName ) { + if ( GetTempPathA( sizeof( szTempPath ), szTempPath ) <= 0 ) + strcpy( szTempPath, ".\\" ); + if ( GetTempFileNameA( szTempPath, "jab", 0, szTempFileName ) > 0 ) { + JabberLog( "Temp file = %s", szTempFileName ); + if ( CopyFileA( jabberVcardPhotoFileName, szTempFileName, FALSE ) == TRUE ) { + if (( hBitmap=( HBITMAP ) JCallService( MS_UTILS_LOADBITMAP, 0, ( LPARAM )szTempFileName )) != NULL ) { + strcpy( szPhotoFileName, szTempFileName ); + EnableWindow( GetDlgItem( hwndDlg, IDC_DELETE ), TRUE ); + } + else DeleteFileA( szTempFileName ); + } + else DeleteFileA( szTempFileName ); + } + } + bPhotoChanged = FALSE; + InvalidateRect( hwndDlg, NULL, TRUE ); + UpdateWindow( hwndDlg ); + break; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_LOAD: + { + OPENFILENAMEA ofn = {0}; + static char szFilter[512]; + char szFileName[_MAX_PATH]; + char* p; + int n; + +// JCallService( MS_UTILS_GETBITMAPFILTERSTRINGS, ( WPARAM ) sizeof( szFilter ), ( LPARAM )szFilter ); + p = szFilter; + n = sizeof( szFilter ); + strncpy( p, JTranslate( "All Bitmaps" ), n ); n = sizeof( szFilter )-strlen( szFilter ); + strncat( p, " ( *.bmp;*.jpg;*.jpeg;*.gif )", n ); n = sizeof( szFilter )-strlen( szFilter ); + p += strlen( p )+1; n = sizeof( szFilter )-( p-szFilter ); + strncpy( p, "*.BMP;*.JPG;*.JPEG;*.GIF", n ); + szFilter[512-1] = '\0'; + + + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = hwndDlg; + ofn.lpstrFilter = szFilter; + ofn.lpstrCustomFilter = NULL; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = _MAX_PATH; + ofn.Flags = OFN_FILEMUSTEXIST; + szFileName[0] = '\0'; + if ( GetOpenFileNameA( &ofn )) { + struct _stat st; + HBITMAP hNewBitmap; + + JabberLog( "File selected is %s", szFileName ); + if ( _stat( szFileName, &st )<0 || st.st_size>40*1024 ) { + MessageBox( hwndDlg, TranslateT( "Only JPG, GIF, and BMP image files smaller than 40 KB are supported." ), TranslateT( "Jabber vCard" ), MB_OK|MB_SETFOREGROUND ); + break; + } + if ( GetTempPathA( sizeof( szTempPath ), szTempPath ) <= 0 ) + strcpy( szTempPath, ".\\" ); + if ( GetTempFileNameA( szTempPath, "jab", 0, szTempFileName ) > 0 ) { + JabberLog( "Temp file = %s", szTempFileName ); + if ( CopyFileA( szFileName, szTempFileName, FALSE ) == TRUE ) { + if (( hNewBitmap=( HBITMAP ) JCallService( MS_UTILS_LOADBITMAP, 0, ( LPARAM )szTempFileName )) != NULL ) { + if ( hBitmap ) { + DeleteObject( hBitmap ); + DeleteFileA( szPhotoFileName ); + } + if (( p=strrchr( szFileName, '.' )) != NULL ) { + if ( !stricmp( p, ".bmp" )) + strcpy( szPhotoType, "image/bmp" ); + else if ( !stricmp( p, ".gif" )) + strcpy( szPhotoType, "image/gif" ); + else + strcpy( szPhotoType, "image/jpeg" ); + } + else + strcpy( szPhotoType, "image/jpeg" ); + hBitmap = hNewBitmap; + strcpy( szPhotoFileName, szTempFileName ); + bPhotoChanged = TRUE; + EnableWindow( GetDlgItem( hwndDlg, IDC_DELETE ), TRUE ); + InvalidateRect( hwndDlg, NULL, TRUE ); + UpdateWindow( hwndDlg ); + SendMessage( GetParent( hwndDlg ), WM_JABBER_CHANGED, 0, 0 ); + } + else { + DeleteFileA( szTempFileName ); + } + } + else + DeleteFileA( szTempFileName ); + } + } + } + break; + case IDC_DELETE: + if ( hBitmap ) { + DeleteObject( hBitmap ); + hBitmap = NULL; + DeleteFileA( szPhotoFileName ); + szPhotoFileName[0] = '\0'; + bPhotoChanged = TRUE; + EnableWindow( GetDlgItem( hwndDlg, IDC_DELETE ), FALSE ); + InvalidateRect( hwndDlg, NULL, TRUE ); + UpdateWindow( hwndDlg ); + SendMessage( GetParent( hwndDlg ), WM_JABBER_CHANGED, 0, 0 ); + } + break; + } + break; + case WM_PAINT: + if ( hBitmap ) { + BITMAP bm; + HDC hdcMem; + HWND hwndCanvas; + HDC hdcCanvas; + POINT ptSize, ptOrg, pt, ptFitSize; + RECT rect; + + hwndCanvas = GetDlgItem( hwndDlg, IDC_CANVAS ); + hdcCanvas = GetDC( hwndCanvas ); + hdcMem = CreateCompatibleDC( hdcCanvas ); + SelectObject( hdcMem, hBitmap ); + SetMapMode( hdcMem, GetMapMode( hdcCanvas )); + GetObject( hBitmap, sizeof( BITMAP ), ( LPVOID ) &bm ); + ptSize.x = bm.bmWidth; + ptSize.y = bm.bmHeight; + DPtoLP( hdcCanvas, &ptSize, 1 ); + ptOrg.x = ptOrg.y = 0; + DPtoLP( hdcMem, &ptOrg, 1 ); + GetClientRect( hwndCanvas, &rect ); + InvalidateRect( hwndCanvas, NULL, TRUE ); + UpdateWindow( hwndCanvas ); + if ( ptSize.x<=rect.right && ptSize.y<=rect.bottom ) { + pt.x = ( rect.right - ptSize.x )/2; + pt.y = ( rect.bottom - ptSize.y )/2; + BitBlt( hdcCanvas, pt.x, pt.y, ptSize.x, ptSize.y, hdcMem, ptOrg.x, ptOrg.y, SRCCOPY ); + } + else { + if (( ( float )( ptSize.x-rect.right ))/ptSize.x > (( float )( ptSize.y-rect.bottom ))/ptSize.y ) { + ptFitSize.x = rect.right; + ptFitSize.y = ( ptSize.y*rect.right )/ptSize.x; + pt.x = 0; + pt.y = ( rect.bottom - ptFitSize.y )/2; + } + else { + ptFitSize.x = ( ptSize.x*rect.bottom )/ptSize.y; + ptFitSize.y = rect.bottom; + pt.x = ( rect.right - ptFitSize.x )/2; + pt.y = 0; + } + SetStretchBltMode( hdcCanvas, COLORONCOLOR ); + StretchBlt( hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY ); + } + DeleteDC( hdcMem ); + } + break; + case WM_DESTROY: + if ( hBitmap ) { + JabberLog( "Delete bitmap" ); + DeleteObject( hBitmap ); + DeleteFileA( szPhotoFileName ); + szPhotoFileName[0] = '\0'; + } + break; + } + return FALSE; +} + +static BOOL CALLBACK NoteDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + SendMessage( hwndDlg, WM_JABBER_REFRESH, 0, 0 ); + return TRUE; + case WM_JABBER_REFRESH: + SetDialogField( hwndDlg, IDC_DESC, "About" ); + break; + case WM_COMMAND: + if (( HWND )lParam==GetFocus() && HIWORD( wParam )==EN_CHANGE ) + SendMessage( GetParent( hwndDlg ), WM_JABBER_CHANGED, 0, 0 ); + break; + } + return FALSE; +} + +static BOOL CALLBACK EditEmailDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + SetWindowLong( hwndDlg, GWL_USERDATA, lParam ); + if ( lParam >= 0 ) { + DBVARIANT dbv; + char idstr[33]; + WORD nFlag; + + SetWindowText( hwndDlg, TranslateT( "Jabber vCard: Edit Email Address" )); + wsprintfA( idstr, "e-mail%d", lParam ); + if ( !DBGetContactSetting( NULL, jabberProtoName, idstr, &dbv )) { + SetDlgItemTextA( hwndDlg, IDC_EMAIL, dbv.pszVal ); + JFreeVariant( &dbv ); + wsprintfA( idstr, "e-mailFlag%d", lParam ); + nFlag = DBGetContactSettingWord( NULL, jabberProtoName, idstr, 0 ); + if ( nFlag & JABBER_VCEMAIL_HOME ) CheckDlgButton( hwndDlg, IDC_HOME, TRUE ); + if ( nFlag & JABBER_VCEMAIL_WORK ) CheckDlgButton( hwndDlg, IDC_WORK, TRUE ); + if ( nFlag & JABBER_VCEMAIL_INTERNET ) CheckDlgButton( hwndDlg, IDC_INTERNET, TRUE ); + if ( nFlag & JABBER_VCEMAIL_X400 ) CheckDlgButton( hwndDlg, IDC_X400, TRUE ); + } + } + return TRUE; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDOK: + { + TCHAR text[128]; + char idstr[33]; + int id = ( int ) GetWindowLong( hwndDlg, GWL_USERDATA ); + DBVARIANT dbv; + WORD nFlag; + + if ( id < 0 ) { + for ( id=0;;id++ ) { + mir_snprintf( idstr, SIZEOF(idstr), "e-mail%d", id ); + if ( DBGetContactSetting( NULL, jabberProtoName, idstr, &dbv )) break; + JFreeVariant( &dbv ); + } } + GetDlgItemText( hwndDlg, IDC_EMAIL, text, SIZEOF( text )); + mir_snprintf( idstr, SIZEOF(idstr), "e-mail%d", id ); + JSetStringT( NULL, idstr, text ); + + nFlag = 0; + if ( IsDlgButtonChecked( hwndDlg, IDC_HOME )) nFlag |= JABBER_VCEMAIL_HOME; + if ( IsDlgButtonChecked( hwndDlg, IDC_WORK )) nFlag |= JABBER_VCEMAIL_WORK; + if ( IsDlgButtonChecked( hwndDlg, IDC_INTERNET )) nFlag |= JABBER_VCEMAIL_INTERNET; + if ( IsDlgButtonChecked( hwndDlg, IDC_X400 )) nFlag |= JABBER_VCEMAIL_X400; + mir_snprintf( idstr, SIZEOF(idstr), "e-mailFlag%d", id ); + JSetWord( NULL, idstr, nFlag ); + } + // fall through + case IDCANCEL: + EndDialog( hwndDlg, wParam ); + break; + } + } + return FALSE; +} + +static BOOL CALLBACK EditPhoneDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + SetWindowLong( hwndDlg, GWL_USERDATA, lParam ); + if ( lParam >= 0 ) { + DBVARIANT dbv; + char idstr[33]; + WORD nFlag; + + SetWindowText( hwndDlg, TranslateT( "Jabber vCard: Edit Phone Number" )); + wsprintfA( idstr, "Phone%d", lParam ); + if ( !DBGetContactSetting( NULL, jabberProtoName, idstr, &dbv )) { + SetDlgItemTextA( hwndDlg, IDC_PHONE, dbv.pszVal ); + JFreeVariant( &dbv ); + wsprintfA( idstr, "PhoneFlag%d", lParam ); + nFlag = JGetWord( NULL, idstr, 0 ); + if ( nFlag & JABBER_VCTEL_HOME ) CheckDlgButton( hwndDlg, IDC_HOME, TRUE ); + if ( nFlag & JABBER_VCTEL_WORK ) CheckDlgButton( hwndDlg, IDC_WORK, TRUE ); + if ( nFlag & JABBER_VCTEL_VOICE ) CheckDlgButton( hwndDlg, IDC_VOICE, TRUE ); + if ( nFlag & JABBER_VCTEL_FAX ) CheckDlgButton( hwndDlg, IDC_FAX, TRUE ); + if ( nFlag & JABBER_VCTEL_PAGER ) CheckDlgButton( hwndDlg, IDC_PAGER, TRUE ); + if ( nFlag & JABBER_VCTEL_MSG ) CheckDlgButton( hwndDlg, IDC_MSG, TRUE ); + if ( nFlag & JABBER_VCTEL_CELL ) CheckDlgButton( hwndDlg, IDC_CELL, TRUE ); + if ( nFlag & JABBER_VCTEL_VIDEO ) CheckDlgButton( hwndDlg, IDC_VIDEO, TRUE ); + if ( nFlag & JABBER_VCTEL_BBS ) CheckDlgButton( hwndDlg, IDC_BBS, TRUE ); + if ( nFlag & JABBER_VCTEL_MODEM ) CheckDlgButton( hwndDlg, IDC_MODEM, TRUE ); + if ( nFlag & JABBER_VCTEL_ISDN ) CheckDlgButton( hwndDlg, IDC_ISDN, TRUE ); + if ( nFlag & JABBER_VCTEL_PCS ) CheckDlgButton( hwndDlg, IDC_PCS, TRUE ); + } + } + return TRUE; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDOK: + { + char text[128]; + char idstr[33]; + int id = ( int ) GetWindowLong( hwndDlg, GWL_USERDATA ); + DBVARIANT dbv; + WORD nFlag; + + if ( id < 0 ) { + for ( id=0;;id++ ) { + wsprintfA( idstr, "Phone%d", id ); + if ( DBGetContactSetting( NULL, jabberProtoName, idstr, &dbv )) break; + JFreeVariant( &dbv ); + } + } + GetDlgItemTextA( hwndDlg, IDC_PHONE, text, SIZEOF( text )); + wsprintfA( idstr, "Phone%d", id ); + JSetString( NULL, idstr, text ); + nFlag = 0; + if ( IsDlgButtonChecked( hwndDlg, IDC_HOME )) nFlag |= JABBER_VCTEL_HOME; + if ( IsDlgButtonChecked( hwndDlg, IDC_WORK )) nFlag |= JABBER_VCTEL_WORK; + if ( IsDlgButtonChecked( hwndDlg, IDC_VOICE )) nFlag |= JABBER_VCTEL_VOICE; + if ( IsDlgButtonChecked( hwndDlg, IDC_FAX )) nFlag |= JABBER_VCTEL_FAX; + if ( IsDlgButtonChecked( hwndDlg, IDC_PAGER )) nFlag |= JABBER_VCTEL_PAGER; + if ( IsDlgButtonChecked( hwndDlg, IDC_MSG )) nFlag |= JABBER_VCTEL_MSG; + if ( IsDlgButtonChecked( hwndDlg, IDC_CELL )) nFlag |= JABBER_VCTEL_CELL; + if ( IsDlgButtonChecked( hwndDlg, IDC_VIDEO )) nFlag |= JABBER_VCTEL_VIDEO; + if ( IsDlgButtonChecked( hwndDlg, IDC_BBS )) nFlag |= JABBER_VCTEL_BBS; + if ( IsDlgButtonChecked( hwndDlg, IDC_MODEM )) nFlag |= JABBER_VCTEL_MODEM; + if ( IsDlgButtonChecked( hwndDlg, IDC_ISDN )) nFlag |= JABBER_VCTEL_ISDN; + if ( IsDlgButtonChecked( hwndDlg, IDC_PCS )) nFlag |= JABBER_VCTEL_PCS; + wsprintfA( idstr, "PhoneFlag%d", id ); + JSetWord( NULL, idstr, nFlag ); + } + // fall through + case IDCANCEL: + EndDialog( hwndDlg, wParam ); + break; + } + } + return FALSE; +} + +#define M_REMAKELISTS ( WM_USER+1 ) +static BOOL CALLBACK ContactDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch( msg ) { + case WM_INITDIALOG: + { + LVCOLUMN lvc; + RECT rc; + + TranslateDialogDefault( hwndDlg ); + GetClientRect( GetDlgItem( hwndDlg,IDC_EMAILS ), &rc ); + rc.right -= GetSystemMetrics( SM_CXVSCROLL ); + lvc.mask = LVCF_WIDTH; + lvc.cx = 30; + ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_EMAILS ), 0, &lvc ); + ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_PHONES ), 0, &lvc ); + lvc.cx = rc.right - 30 - 40; + ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_EMAILS ), 1, &lvc ); + ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_PHONES ), 1, &lvc ); + lvc.cx = 20; + ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_EMAILS ), 2, &lvc ); + ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_EMAILS ), 3, &lvc ); + ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_PHONES ), 2, &lvc ); + ListView_InsertColumn( GetDlgItem( hwndDlg,IDC_PHONES ), 3, &lvc ); + SendMessage( hwndDlg, M_REMAKELISTS, 0, 0 ); + } + return TRUE; + case M_REMAKELISTS: + { + LVITEM lvi; + int i; + char idstr[33]; + TCHAR number[20]; + DBVARIANT dbv; + + //e-mails + ListView_DeleteAllItems( GetDlgItem( hwndDlg, IDC_EMAILS )); + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iSubItem = 0; + lvi.iItem = 0; + for ( i=0;;i++ ) { + wsprintfA( idstr, "e-mail%d", i ); + if ( DBGetContactSettingTString( NULL, jabberProtoName, idstr, &dbv )) break; + wsprintf( number, _T("%d"), i+1 ); + lvi.pszText = number; + lvi.lParam = ( LPARAM )i; + ListView_InsertItem( GetDlgItem( hwndDlg, IDC_EMAILS ), &lvi ); + ListView_SetItemText( GetDlgItem( hwndDlg, IDC_EMAILS ), lvi.iItem, 1, dbv.ptszVal ); + JFreeVariant( &dbv ); + lvi.iItem++; + } + lvi.mask = LVIF_PARAM; + lvi.lParam = ( LPARAM )( -1 ); + ListView_InsertItem( GetDlgItem( hwndDlg, IDC_EMAILS ), &lvi ); + //phones + ListView_DeleteAllItems( GetDlgItem( hwndDlg, IDC_PHONES )); + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iSubItem = 0; + lvi.iItem = 0; + for ( i=0;;i++ ) { + wsprintfA( idstr, "Phone%d", i ); + if ( DBGetContactSettingTString( NULL, jabberProtoName, idstr, &dbv )) break; + wsprintf( number, _T("%d"), i+1 ); + lvi.pszText = number; + lvi.lParam = ( LPARAM )i; + ListView_InsertItem( GetDlgItem( hwndDlg, IDC_PHONES ), &lvi ); + ListView_SetItemText( GetDlgItem( hwndDlg, IDC_PHONES ), lvi.iItem, 1, dbv.ptszVal ); + JFreeVariant( &dbv ); + lvi.iItem++; + } + lvi.mask = LVIF_PARAM; + lvi.lParam = ( LPARAM )( -1 ); + ListView_InsertItem( GetDlgItem( hwndDlg, IDC_PHONES ), &lvi ); + } + break; + case WM_NOTIFY: + switch (( ( LPNMHDR )lParam )->idFrom ) { + case IDC_EMAILS: + case IDC_PHONES: + switch (( ( LPNMHDR )lParam )->code ) { + case NM_CUSTOMDRAW: + { + NMLVCUSTOMDRAW *nm = ( NMLVCUSTOMDRAW * ) lParam; + + switch ( nm->nmcd.dwDrawStage ) { + case CDDS_PREPAINT: + case CDDS_ITEMPREPAINT: + SetWindowLong( hwndDlg, DWL_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW ); + return TRUE; + case CDDS_SUBITEM|CDDS_ITEMPREPAINT: + { + RECT rc; + HICON hIcon; + + ListView_GetSubItemRect( nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, LVIR_LABEL, &rc ); + if( nm->nmcd.lItemlParam==( LPARAM )( -1 ) && nm->iSubItem==3 ) + hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_ADDCONTACT ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 ); + else if ( nm->iSubItem==2 && nm->nmcd.lItemlParam!=( LPARAM )( -1 )) + hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_EDIT ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 ); + else if ( nm->iSubItem==3 && nm->nmcd.lItemlParam!=( LPARAM )( -1 )) + hIcon = ( HICON )LoadImage( hInst, MAKEINTRESOURCE( IDI_DELETE ), IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0 ); + else break; + DrawIconEx( nm->nmcd.hdc, ( rc.left+rc.right-GetSystemMetrics( SM_CXSMICON ))/2, ( rc.top+rc.bottom-GetSystemMetrics( SM_CYSMICON ))/2,hIcon, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), 0, NULL, DI_NORMAL ); + DestroyIcon( hIcon ); + SetWindowLong( hwndDlg, DWL_MSGRESULT, CDRF_SKIPDEFAULT ); + } + return TRUE; + } + } + break; + case NM_CLICK: + { + NMLISTVIEW *nm = ( NMLISTVIEW * ) lParam; + LVITEM lvi; + const char* szIdTemplate = nm->hdr.idFrom==IDC_PHONES?"Phone%d":"e-mail%d"; + const char* szFlagTemplate = nm->hdr.idFrom==IDC_PHONES?"PhoneFlag%d":"e-mailFlag%d"; + LVHITTESTINFO hti; + + if ( nm->iSubItem < 2 ) break; + hti.pt.x = ( short ) LOWORD( GetMessagePos()); + hti.pt.y = ( short ) HIWORD( GetMessagePos()); + ScreenToClient( nm->hdr.hwndFrom, &hti.pt ); + if ( ListView_SubItemHitTest( nm->hdr.hwndFrom, &hti ) == -1 ) break; + lvi.mask = LVIF_PARAM; + lvi.iItem = hti.iItem; + lvi.iSubItem = 0; + ListView_GetItem( nm->hdr.hwndFrom, &lvi ); + if ( lvi.lParam == ( LPARAM )( -1 )) { + if ( hti.iSubItem == 3 ) { + //add + if ( DialogBoxParam( hInst, MAKEINTRESOURCE( nm->hdr.idFrom==IDC_PHONES?IDD_VCARD_ADDPHONE:IDD_VCARD_ADDEMAIL ), hwndDlg, nm->hdr.idFrom==IDC_PHONES?EditPhoneDlgProc:EditEmailDlgProc, ( LPARAM )( -1 )) != IDOK ) + break; + SendMessage( hwndDlg, M_REMAKELISTS, 0, 0 ); + SendMessage( GetParent( hwndDlg ), WM_JABBER_CHANGED, 0, 0 ); + } + } + else { + if ( hti.iSubItem == 3 ) { + //delete + int i; + char idstr[33]; + DBVARIANT dbv; + + for( i=lvi.lParam;;i++ ) { + WORD nFlag; + + wsprintfA( idstr, szIdTemplate, i+1 ); + if ( DBGetContactSetting( NULL, jabberProtoName, idstr, &dbv )) break; + wsprintfA( idstr,szIdTemplate,i ); + JSetString( NULL, idstr, dbv.pszVal ); + wsprintfA( idstr, szFlagTemplate, i+1 ); + JFreeVariant( &dbv ); + nFlag = JGetWord( NULL, idstr, 0 ); + wsprintfA( idstr, szFlagTemplate, i ); + JSetWord( NULL, idstr, nFlag ); + } + wsprintfA( idstr, szIdTemplate, i ); + JDeleteSetting( NULL, idstr ); + wsprintfA( idstr, szFlagTemplate, i ); + JDeleteSetting( NULL, idstr ); + SendMessage( hwndDlg, M_REMAKELISTS, 0, 0 ); + SendMessage( GetParent( hwndDlg ), WM_JABBER_CHANGED, 0, 0 ); + } + else if ( hti.iSubItem == 2 ) { + //edit + if ( DialogBoxParam( hInst, MAKEINTRESOURCE( nm->hdr.idFrom==IDC_PHONES?IDD_VCARD_ADDPHONE:IDD_VCARD_ADDEMAIL ), hwndDlg, nm->hdr.idFrom==IDC_PHONES?EditPhoneDlgProc:EditEmailDlgProc, lvi.lParam ) != IDOK ) + break; + SendMessage( hwndDlg,M_REMAKELISTS,0,0 ); + SendMessage( GetParent( hwndDlg ), WM_JABBER_CHANGED, 0, 0 ); + } + } + } + break; + } + break; + } + break; + case WM_SETCURSOR: + if ( LOWORD( lParam ) != HTCLIENT ) break; + if ( GetForegroundWindow() == GetParent( hwndDlg )) { + POINT pt; + GetCursorPos( &pt ); + ScreenToClient( hwndDlg,&pt ); + SetFocus( ChildWindowFromPoint( hwndDlg,pt )); //ugly hack because listviews ignore their first click + } + break; + } + return FALSE; +} + +static void SaveVcardToDB( VcardTab *dat ) +{ + HWND hwndPage; + TCHAR text[2048]; + + if ( dat==NULL || dat->page==NULL ) return; + + // Page 0: Personal + if (( hwndPage=dat->page[0].hwnd ) != NULL ) { + GetDlgItemText( hwndPage, IDC_FULLNAME, text, SIZEOF( text )); + JSetStringT( NULL, "FullName", text ); + GetDlgItemText( hwndPage, IDC_NICKNAME, text, SIZEOF( text )); + JSetStringT( NULL, "Nick", text ); + GetDlgItemText( hwndPage, IDC_FIRSTNAME, text, SIZEOF( text )); + JSetStringT( NULL, "FirstName", text ); + GetDlgItemText( hwndPage, IDC_MIDDLE, text, SIZEOF( text )); + JSetStringT( NULL, "MiddleName", text ); + GetDlgItemText( hwndPage, IDC_LASTNAME, text, SIZEOF( text )); + JSetStringT( NULL, "LastName", text ); + GetDlgItemText( hwndPage, IDC_BIRTH, text, SIZEOF( text )); + JSetStringT( NULL, "BirthDate", text ); + GetDlgItemText( hwndPage, IDC_GENDER, text, SIZEOF( text )); + JSetStringT( NULL, "GenderString", text ); + GetDlgItemText( hwndPage, IDC_OCCUPATION, text, SIZEOF( text )); + JSetStringT( NULL, "Role", text ); + GetDlgItemText( hwndPage, IDC_HOMEPAGE, text, SIZEOF( text )); + JSetStringT( NULL, "Homepage", text ); + } + // Page 1: Contacts + // no need to save, always in sync with the DB + // Page 2: Home + if (( hwndPage=dat->page[2].hwnd ) != NULL ) { + GetDlgItemText( hwndPage, IDC_ADDRESS1, text, SIZEOF( text )); + JSetStringT( NULL, "Street", text ); + GetDlgItemText( hwndPage, IDC_ADDRESS2, text, SIZEOF( text )); + JSetStringT( NULL, "Street2", text ); + GetDlgItemText( hwndPage, IDC_CITY, text, SIZEOF( text )); + JSetStringT( NULL, "City", text ); + GetDlgItemText( hwndPage, IDC_STATE, text, SIZEOF( text )); + JSetStringT( NULL, "State", text ); + GetDlgItemText( hwndPage, IDC_ZIP, text, SIZEOF( text )); + JSetStringT( NULL, "ZIP", text ); + GetDlgItemText( hwndPage, IDC_COUNTRY, text, SIZEOF( text )); + JSetStringT( NULL, "CountryName", text ); + } + // Page 3: Work + if (( hwndPage=dat->page[3].hwnd ) != NULL ) { + GetDlgItemText( hwndPage, IDC_COMPANY, text, SIZEOF( text )); + JSetStringT( NULL, "Company", text ); + GetDlgItemText( hwndPage, IDC_DEPARTMENT, text, SIZEOF( text )); + JSetStringT( NULL, "CompanyDepartment", text ); + GetDlgItemText( hwndPage, IDC_TITLE, text, SIZEOF( text )); + JSetStringT( NULL, "CompanyPosition", text ); + GetDlgItemText( hwndPage, IDC_ADDRESS1, text, SIZEOF( text )); + JSetStringT( NULL, "CompanyStreet", text ); + GetDlgItemText( hwndPage, IDC_ADDRESS2, text, SIZEOF( text )); + JSetStringT( NULL, "CompanyStreet2", text ); + GetDlgItemText( hwndPage, IDC_CITY, text, SIZEOF( text )); + JSetStringT( NULL, "CompanyCity", text ); + GetDlgItemText( hwndPage, IDC_STATE, text, SIZEOF( text )); + JSetStringT( NULL, "CompanyState", text ); + GetDlgItemText( hwndPage, IDC_ZIP, text, SIZEOF( text )); + JSetStringT( NULL, "CompanyZIP", text ); + GetDlgItemText( hwndPage, IDC_COUNTRY, text, SIZEOF( text )); + JSetStringT( NULL, "CompanyCountryName", text ); + } + // Page 4: Photo + // not saved in the database + // Page 5: Note + if (( hwndPage=dat->page[5].hwnd ) != NULL ) { + GetDlgItemText( hwndPage, IDC_DESC, text, SIZEOF( text )); + JSetStringT( NULL, "About", text ); + } +} + +static void AppendVcardFromDB( XmlNode* n, char* tag, char* key ) +{ + if ( n == NULL || tag == NULL || key == NULL ) + return; + + DBVARIANT dbv; + if ( DBGetContactSettingTString( NULL, jabberProtoName, key, &dbv )) + n->addChild( tag ); + else { + n->addChild( tag, dbv.ptszVal ); + JFreeVariant( &dbv ); +} } + +static void SetServerVcard() +{ + DBVARIANT dbv; + int iqId; + char *szFileName, *szFileType; + int i; + char idstr[33]; + WORD nFlag; + + iqId = JabberSerialNext(); + JabberIqAdd( iqId, IQ_PROC_SETVCARD, JabberIqResultSetVcard ); + + XmlNodeIq iq( "set", iqId ); + XmlNode* v = iq.addChild( "vCard" ); v->addAttr( "xmlns", "vcard-temp" ); + + AppendVcardFromDB( v, "FN", "FullName" ); + + XmlNode* n = v->addChild( "N" ); + AppendVcardFromDB( n, "GIVEN", "FirstName" ); + AppendVcardFromDB( n, "MIDDLE", "MiddleName" ); + AppendVcardFromDB( n, "FAMILY", "LastName" ); + + AppendVcardFromDB( v, "NICKNAME", "Nick" ); + AppendVcardFromDB( v, "BDAY", "BirthDate" ); + AppendVcardFromDB( v, "GENDER", "GenderString" ); + + for ( i=0;;i++ ) { + wsprintfA( idstr, "e-mail%d", i ); + if ( DBGetContactSettingTString( NULL, jabberProtoName, idstr, &dbv )) + break; + + XmlNode* e = v->addChild( "EMAIL", dbv.ptszVal ); + JFreeVariant( &dbv ); + AppendVcardFromDB( e, "USERID", idstr ); + + wsprintfA( idstr, "e-mailFlag%d", i ); + nFlag = DBGetContactSettingWord( NULL, jabberProtoName, idstr, 0 ); + if ( nFlag & JABBER_VCEMAIL_HOME ) e->addChild( "HOME" ); + if ( nFlag & JABBER_VCEMAIL_WORK ) e->addChild( "WORK" ); + if ( nFlag & JABBER_VCEMAIL_INTERNET ) e->addChild( "INTERNET" ); + if ( nFlag & JABBER_VCEMAIL_X400 ) e->addChild( "X400" ); + } + + n = v->addChild( "ADR" ); + n->addChild( "HOME" ); + AppendVcardFromDB( n, "STREET", "Street" ); + AppendVcardFromDB( n, "EXTADR", "Street2" ); + AppendVcardFromDB( n, "EXTADD", "Street2" ); // for compatibility with client using old vcard format + AppendVcardFromDB( n, "LOCALITY", "City" ); + AppendVcardFromDB( n, "REGION", "State" ); + AppendVcardFromDB( n, "PCODE", "ZIP" ); + AppendVcardFromDB( n, "CTRY", "CountryName" ); + AppendVcardFromDB( n, "COUNTRY", "CountryName" ); // for compatibility with client using old vcard format + + n = v->addChild( "ADR" ); + n->addChild( "WORK" ); + AppendVcardFromDB( n, "STREET", "CompanyStreet" ); + AppendVcardFromDB( n, "EXTADR", "CompanyStreet2" ); + AppendVcardFromDB( n, "EXTADD", "CompanyStreet2" ); // for compatibility with client using old vcard format + AppendVcardFromDB( n, "LOCALITY", "CompanyCity" ); + AppendVcardFromDB( n, "REGION", "CompanyState" ); + AppendVcardFromDB( n, "PCODE", "CompanyZIP" ); + AppendVcardFromDB( n, "CTRY", "CompanyCountryName" ); + AppendVcardFromDB( n, "COUNTRY", "CompanyCountryName" ); // for compatibility with client using old vcard format + + n = v->addChild( "ORG" ); + AppendVcardFromDB( n, "ORGNAME", "Company" ); + AppendVcardFromDB( n, "ORGUNIT", "CompanyDepartment" ); + + AppendVcardFromDB( v, "TITLE", "CompanyPosition" ); + AppendVcardFromDB( v, "ROLE", "Role" ); + AppendVcardFromDB( v, "URL", "Homepage" ); + AppendVcardFromDB( v, "DESC", "About" ); + + for ( i=0;;i++ ) { + wsprintfA( idstr, "Phone%d", i ); + if ( DBGetContactSettingTString( NULL, jabberProtoName, idstr, &dbv )) break; + JFreeVariant( &dbv ); + + n = v->addChild( "TEL" ); + AppendVcardFromDB( n, "NUMBER", idstr ); + + wsprintfA( idstr, "PhoneFlag%d", i ); + nFlag = JGetWord( NULL, idstr, 0 ); + if ( nFlag & JABBER_VCTEL_HOME ) n->addChild( "HOME" ); + if ( nFlag & JABBER_VCTEL_WORK ) n->addChild( "WORK" ); + if ( nFlag & JABBER_VCTEL_VOICE ) n->addChild( "VOICE" ); + if ( nFlag & JABBER_VCTEL_FAX ) n->addChild( "FAX" ); + if ( nFlag & JABBER_VCTEL_PAGER ) n->addChild( "PAGER" ); + if ( nFlag & JABBER_VCTEL_MSG ) n->addChild( "MSG" ); + if ( nFlag & JABBER_VCTEL_CELL ) n->addChild( "CELL" ); + if ( nFlag & JABBER_VCTEL_VIDEO ) n->addChild( "VIDEO" ); + if ( nFlag & JABBER_VCTEL_BBS ) n->addChild( "BBS" ); + if ( nFlag & JABBER_VCTEL_MODEM ) n->addChild( "MODEM" ); + if ( nFlag & JABBER_VCTEL_ISDN ) n->addChild( "ISDN" ); + if ( nFlag & JABBER_VCTEL_PCS ) n->addChild( "PCS" ); + } + + if ( bPhotoChanged ) { + if ( szPhotoFileName[0] ) { + szFileName = szPhotoFileName; + szFileType = szPhotoType; + } + else szFileName = NULL; + } + else { + szFileName = jabberVcardPhotoFileName; + szFileType = jabberVcardPhotoType; + } + + // Set photo element, also update the global jabberVcardPhotoFileName to reflect the update + JabberLog( "Before update, jabberVcardPhotoFileName = %s", jabberVcardPhotoFileName ); + if ( szFileName == NULL ) { + v->addChild( "PHOTO" ); + if ( jabberVcardPhotoFileName ) { + DeleteFileA( jabberVcardPhotoFileName ); + mir_free( jabberVcardPhotoFileName ); + jabberVcardPhotoFileName = NULL; + } } + else { + char szTempPath[MAX_PATH], szTempFileName[MAX_PATH]; + HANDLE hFile; + struct _stat st; + char* buffer, *str; + DWORD nRead; + + JabberLog( "Saving picture from %s", szFileName ); + if ( _stat( szFileName, &st ) >= 0 ) { + // Note the FILE_SHARE_READ attribute so that the CopyFile can succeed + if (( hFile=CreateFileA( szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) != INVALID_HANDLE_VALUE ) { + if (( buffer=( char* )mir_alloc( st.st_size )) != NULL ) { + if ( ReadFile( hFile, buffer, st.st_size, &nRead, NULL )) { + if (( str=JabberBase64Encode( buffer, nRead )) != NULL ) { + n = v->addChild( "PHOTO" ); + if ( szFileType ) { + n->addChild( "TYPE", szFileType ); + JabberLog( "File type sent is %s", szFileType ); + } + else { + n->addChild( "TYPE", "image/jpeg" ); + JabberLog( "File type sent is default to image/jpge" ); + } + + n->addChild( "BINVAL", str ); + mir_free( str ); + + if ( szFileName != jabberVcardPhotoFileName ) { + if ( jabberVcardPhotoFileName ) { + DeleteFileA( jabberVcardPhotoFileName ); + mir_free( jabberVcardPhotoFileName ); + jabberVcardPhotoFileName = NULL; + if ( jabberVcardPhotoType ) { + mir_free( jabberVcardPhotoType ); + jabberVcardPhotoType = NULL; + } } + + if ( GetTempPathA( sizeof( szTempPath ), szTempPath ) <= 0 ) + strcpy( szTempPath, ".\\" ); + if ( GetTempFileNameA( szTempPath, "jab", 0, szTempFileName ) > 0 ) { + JabberLog( "New global file is %s", szTempFileName ); + if ( CopyFileA( szFileName, szTempFileName, FALSE ) == TRUE ) { + jabberVcardPhotoFileName = mir_strdup( szTempFileName ); + if ( jabberVcardPhotoType ) mir_free( jabberVcardPhotoType ); + if ( szFileType ) + jabberVcardPhotoType = mir_strdup( szFileType ); + else + jabberVcardPhotoType = NULL; + } + else DeleteFileA( szTempFileName ); + } } } } + mir_free( buffer ); + } + CloseHandle( hFile ); + } } } + + JabberSend( jabberThreadInfo->s, iq ); +} + +static void ThemeDialogBackground( HWND hwnd ) { + if ( IsWinVerXPPlus()) { + static HMODULE hThemeAPI = NULL; + if ( !hThemeAPI ) hThemeAPI = GetModuleHandleA( "uxtheme" ); + if ( hThemeAPI ) { + HRESULT ( STDAPICALLTYPE *MyEnableThemeDialogTexture )( HWND,DWORD ) = ( HRESULT ( STDAPICALLTYPE* )( HWND,DWORD ))GetProcAddress( hThemeAPI,"EnableThemeDialogTexture" ); + if ( MyEnableThemeDialogTexture ) + MyEnableThemeDialogTexture( hwnd,0x00000002|0x00000004 ); //0x00000002|0x00000004=ETDT_ENABLETAB +} } } + +static BOOL CALLBACK JabberVcardDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + VcardTab* dat = ( VcardTab* ) GetWindowLong( hwndDlg, GWL_USERDATA ); + + switch ( msg ) { + case WM_INITDIALOG: + { + SendMessage( hwndDlg, WM_SETICON, ICON_BIG, ( LPARAM )LoadIcon( hInst, MAKEINTRESOURCE( IDI_VCARD )) ); + TranslateDialogDefault( hwndDlg ); + EnableWindow( GetDlgItem( hwndDlg, IDC_UPDATE ), jabberOnline ); + + dat = ( VcardTab * ) mir_alloc( sizeof( VcardTab )); + memset( dat, 0, sizeof( VcardTab )); + dat->pageCount = 6; + dat->currentPage = 0; + dat->changed = FALSE; + dat->updateAnimFrame = 0; + dat->animating = FALSE; + dat->page = ( VcardPage * ) mir_alloc( dat->pageCount * sizeof( VcardPage )); + memset( dat->page, 0, dat->pageCount * sizeof( VcardPage )); + + HWND hwndTabs = GetDlgItem( hwndDlg, IDC_TABS ); + + TCITEM tci = { 0 }; + tci.mask = TCIF_TEXT; + // Page 0: Personal + dat->page[0].dlgId = IDD_VCARD_PERSONAL; + dat->page[0].dlgProc = PersonalDlgProc; + tci.pszText = TranslateT( "Personal" ); + TabCtrl_InsertItem( hwndTabs, 0, &tci ); + // Page 1: Contacts + dat->page[1].dlgId = IDD_VCARD_CONTACT; + dat->page[1].dlgProc = ContactDlgProc; + tci.pszText = TranslateT( "Contacts" ); + TabCtrl_InsertItem( hwndTabs, 1, &tci ); + // Page 2: Home + dat->page[2].dlgId = IDD_VCARD_HOME; + dat->page[2].dlgProc = HomeDlgProc; + tci.pszText = TranslateT( "Home" ); + TabCtrl_InsertItem( hwndTabs, 2, &tci ); + // Page 3: Work + dat->page[3].dlgId = IDD_VCARD_WORK; + dat->page[3].dlgProc = WorkDlgProc; + tci.pszText = TranslateT( "Work" ); + TabCtrl_InsertItem( hwndTabs, 3, &tci ); + // Page 4: Photo + dat->page[4].dlgId = IDD_VCARD_PHOTO; + dat->page[4].dlgProc = PhotoDlgProc; + tci.pszText = TranslateT( "Photo" ); + TabCtrl_InsertItem( hwndTabs, 4, &tci ); + // Page 5: Note + dat->page[5].dlgId = IDD_VCARD_NOTE; + dat->page[5].dlgProc = NoteDlgProc; + tci.pszText = TranslateT( "Note" ); + TabCtrl_InsertItem( hwndTabs, 5, &tci ); + + GetWindowRect( hwndTabs, &( dat->rectTab )); + TabCtrl_AdjustRect( hwndTabs, FALSE, &( dat->rectTab )); + { POINT pt = {0,0}; + ClientToScreen( hwndDlg, &pt ); + OffsetRect( &( dat->rectTab ), -pt.x, -pt.y ); + } + + TabCtrl_SetCurSel( hwndTabs, dat->currentPage ); + dat->page[dat->currentPage].hwnd = CreateDialogParam( hInst, MAKEINTRESOURCE( dat->page[dat->currentPage].dlgId ), hwndDlg, dat->page[dat->currentPage].dlgProc, 0 ); + ThemeDialogBackground( dat->page[dat->currentPage].hwnd ); + SetWindowPos( dat->page[dat->currentPage].hwnd, HWND_TOP, dat->rectTab.left, dat->rectTab.top, 0, 0, SWP_NOSIZE ); + ShowWindow( dat->page[dat->currentPage].hwnd, SW_SHOW ); + + SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG ) dat ); + + bPhotoChanged = FALSE; + szPhotoFileName[0] = '\0'; + + if ( jabberOnline ) SendMessage( hwndDlg, WM_COMMAND, IDC_UPDATE, 0 ); + return TRUE; + } + case WM_CTLCOLORSTATIC: + switch (GetDlgCtrlID((HWND)lParam)) { + case IDC_WHITERECT: + case IDC_LOGO: + case IDC_NAME: + case IDC_DESCRIPTION: + SetBkColor(( HDC ) wParam, RGB( 255, 255, 255 )); + return ( BOOL ) GetStockObject( WHITE_BRUSH ); + + case IDC_UPDATING: + SetBkColor(( HDC )wParam, GetSysColor( COLOR_3DFACE )); + return ( BOOL ) GetSysColorBrush( COLOR_3DFACE ); + } + break; + + case WM_NOTIFY: + switch ( wParam ) { + case IDC_TABS: + switch (( ( LPNMHDR ) lParam )->code ) { + case TCN_SELCHANGE: + if ( dat->currentPage>=0 && dat->page[dat->currentPage].hwnd!=NULL ) + ShowWindow( dat->page[dat->currentPage].hwnd, SW_HIDE ); + dat->currentPage = TabCtrl_GetCurSel( GetDlgItem( hwndDlg, IDC_TABS )); + if ( dat->currentPage >= 0 ) { + if ( dat->page[dat->currentPage].hwnd == NULL ) { + dat->page[dat->currentPage].hwnd = CreateDialogParam( hInst, MAKEINTRESOURCE( dat->page[dat->currentPage].dlgId ), hwndDlg, dat->page[dat->currentPage].dlgProc, 0 ); + ThemeDialogBackground( dat->page[dat->currentPage].hwnd ); + SetWindowPos( dat->page[dat->currentPage].hwnd, HWND_TOP, dat->rectTab.left, dat->rectTab.top, 0, 0, SWP_NOSIZE ); + } + ShowWindow( dat->page[dat->currentPage].hwnd, SW_SHOW ); + } + break; + } + break; + } + break; + case WM_JABBER_CHANGED: + dat->changed = TRUE; + EnableWindow( GetDlgItem( hwndDlg, IDC_SAVE ), jabberOnline ); + break; + case WM_COMMAND: + switch ( LOWORD( wParam )) { + case IDC_UPDATE: + EnableWindow( GetDlgItem( hwndDlg,IDC_UPDATE ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg,IDC_SAVE ), FALSE ); + dat->szUpdating = TranslateT( "Updating" ); + SetDlgItemText( hwndDlg, IDC_UPDATING, dat->szUpdating ); + ShowWindow( GetDlgItem( hwndDlg, IDC_UPDATING ), SW_SHOW ); + JabberSendGetVcard( jabberJID ); + dat->animating = TRUE; + SetTimer( hwndDlg, 1, 200, NULL ); + break; + case IDC_SAVE: + EnableWindow( GetDlgItem( hwndDlg,IDC_UPDATE ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg,IDC_SAVE ), FALSE ); + dat->szUpdating = TranslateT( "Saving" ); + SetDlgItemText( hwndDlg, IDC_UPDATING, dat->szUpdating ); + ShowWindow( GetDlgItem( hwndDlg, IDC_UPDATING ), SW_SHOW ); + dat->animating = TRUE; + SetTimer( hwndDlg, 1, 200, NULL ); + SaveVcardToDB( dat ); + SetServerVcard(); + break; + case IDCLOSE: + DestroyWindow( hwndDlg ); + break; + } + break; + case WM_TIMER: + { TCHAR str[128]; + mir_sntprintf( str, SIZEOF(str), _T("%.*s%s%.*s"), dat->updateAnimFrame%5, _T("...."), dat->szUpdating, dat->updateAnimFrame%5, _T("....")); + SetDlgItemText( hwndDlg, IDC_UPDATING, str ); + if (( ++dat->updateAnimFrame ) >= 5 ) dat->updateAnimFrame = 0; + } + break; + case WM_JABBER_CHECK_ONLINE: + EnableWindow( GetDlgItem( hwndDlg, IDC_UPDATE ), jabberOnline ); + if ( dat->changed ) + EnableWindow( GetDlgItem( hwndDlg, IDC_SAVE ), jabberOnline ); + break; + case WM_JABBER_REFRESH: + if ( dat->animating ) { + KillTimer( hwndDlg, 1 ); + dat->animating = FALSE; + ShowWindow( GetDlgItem( hwndDlg, IDC_UPDATING ), FALSE ); + } + { for ( int i=0; i<dat->pageCount; i++ ) + if ( dat->page[i].hwnd != NULL ) + SendMessage( dat->page[i].hwnd, WM_JABBER_REFRESH, 0, 0 ); + } + dat->changed = FALSE; + EnableWindow( GetDlgItem( hwndDlg, IDC_UPDATE ), TRUE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_SAVE ), FALSE ); + break; + case WM_CLOSE: + DestroyWindow( hwndDlg ); + break; + case WM_DESTROY: + hwndJabberVcard = NULL; + for ( int i=0; i<dat->pageCount; i++ ) + if ( dat->page[i].hwnd != NULL ) + DestroyWindow( dat->page[i].hwnd ); + + if ( dat->page ) mir_free( dat->page ); + if ( dat ) mir_free( dat ); + break; + } + return FALSE; +} diff --git a/miranda-wine/protocols/JabberG/jabber_ws.cpp b/miranda-wine/protocols/JabberG/jabber_ws.cpp new file mode 100644 index 0000000..a2bd1d1 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_ws.cpp @@ -0,0 +1,92 @@ +/* + +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_ws.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" + +BOOL JabberWsInit( void ) +{ + NETLIBUSER nlu = {0}; + char name[128]; + + sprintf( name, "%s %s", jabberModuleName, JTranslate( "connection" )); + + nlu.cbSize = sizeof( nlu ); + nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS; // | NUF_HTTPGATEWAY; + nlu.szDescriptiveName = name; + nlu.szSettingsModule = jabberProtoName; + //nlu.szHttpGatewayHello = "http://http.proxy.icq.com/hello"; + //nlu.szHttpGatewayUserAgent = "Mozilla/4.08 [en] ( WinNT; U ;Nav )"; + //nlu.pfnHttpGatewayInit = JabberHttpGatewayInit; + //nlu.pfnHttpGatewayBegin = JabberHttpGatewayBegin; + //nlu.pfnHttpGatewayWrapSend = JabberHttpGatewayWrapSend; + //nlu.pfnHttpGatewayUnwrapRecv = JabberHttpGatewayUnwrapRecv; + hNetlibUser = ( HANDLE ) JCallService( MS_NETLIB_REGISTERUSER, 0, ( LPARAM )&nlu ); + + return ( hNetlibUser!=NULL )?TRUE:FALSE; +} + +void JabberWsUninit( void ) +{ + Netlib_CloseHandle( hNetlibUser ); + hNetlibUser = NULL; +} + +JABBER_SOCKET JabberWsConnect( char* host, WORD port ) +{ + NETLIBOPENCONNECTION nloc = { 0 }; + nloc.cbSize = sizeof( nloc ); + nloc.szHost = host; + nloc.wPort = port; + return ( HANDLE )JCallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) hNetlibUser, ( LPARAM )&nloc ); +} + +int JabberWsSend( JABBER_SOCKET hConn, char* data, int datalen ) +{ + int len; + + if (( len=Netlib_Send( hConn, data, datalen, MSG_DUMPASTEXT ))==SOCKET_ERROR || len!=datalen ) { + JabberLog( "Netlib_Send() failed, error=%d", WSAGetLastError()); + return FALSE; + } + return TRUE; +} + +int JabberWsRecv( JABBER_SOCKET hConn, char* data, long datalen ) +{ + int ret; + + ret = Netlib_Recv( hConn, data, datalen, MSG_DUMPASTEXT ); + if( ret == SOCKET_ERROR ) { + JabberLog( "Netlib_Recv() failed, error=%d", WSAGetLastError()); + return 0; + } + if( ret == 0 ) { + JabberLog( "Connection closed gracefully" ); + return 0; + } + return ret; +} diff --git a/miranda-wine/protocols/JabberG/jabber_xml.cpp b/miranda-wine/protocols/JabberG/jabber_xml.cpp new file mode 100644 index 0000000..d3bd155 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_xml.cpp @@ -0,0 +1,786 @@ +/* + +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_xml.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" + +static BOOL JabberXmlProcessElem( XmlState *xmlState, XmlElemType elemType, char* elemText, char* elemAttr ); +static void JabberXmlRemoveChild( XmlNode *node, XmlNode *child ); + +void JabberXmlInitState( XmlState *xmlState ) +{ + if ( xmlState == NULL ) return; + xmlState->root.name = NULL; + xmlState->root.depth = 0; + xmlState->root.numAttr = 0; + xmlState->root.maxNumAttr = 0; + xmlState->root.attr = NULL; + xmlState->root.numChild = 0; + xmlState->root.maxNumChild = 0; + xmlState->root.child = NULL; + xmlState->root.text = NULL; + xmlState->root.state = NODE_OPEN; + xmlState->callback1_open = NULL; + xmlState->callback1_close = NULL; + xmlState->callback2_open = NULL; + xmlState->callback2_close = NULL; + xmlState->userdata1_open = NULL; + xmlState->userdata1_close = NULL; + xmlState->userdata2_open = NULL; + xmlState->userdata2_close = NULL; +} + +void JabberXmlDestroyState( XmlState *xmlState ) +{ + int i; + XmlNode *node; + + if ( xmlState == NULL ) return; + // Note: cannot use JabberXmlFreeNode() to mir_free xmlState->root + // because it will do mir_free( xmlState->root ) which is not freeable. + node = &( xmlState->root ); + + // Free all children first + for ( i=0; i<node->numChild; i++ ) + delete node->child[i]; + if ( node->child ) mir_free( node->child ); + + // Free all attributes + for ( i=0; i<node->numAttr; i++ ) + delete node->attr[i]; + if ( node->attr ) mir_free( node->attr ); + + // Free string field + if ( node->text ) mir_free( node->text ); + if ( node->name ) mir_free( node->name ); + + memset( xmlState, 0, sizeof( XmlState )); +} + +BOOL JabberXmlSetCallback( XmlState *xmlState, int depth, XmlElemType type, JABBER_XML_CALLBACK callback, void *userdata ) +{ + if ( depth==1 && type==ELEM_OPEN ) { + xmlState->callback1_open = callback; + xmlState->userdata1_open = userdata; + } + else if ( depth==1 && type==ELEM_CLOSE ) { + xmlState->callback1_close = callback; + xmlState->userdata1_close = userdata; + } + else if ( depth==2 && type==ELEM_OPEN ) { + xmlState->callback2_open = callback; + xmlState->userdata2_open = userdata; + } + else if ( depth==2 && type==ELEM_CLOSE ) { + xmlState->callback2_close = callback; + xmlState->userdata2_close = userdata; + } + else + return FALSE; + + return TRUE; +} + +#define TAG_MAX_LEN 50 +#define ATTR_MAX_LEN 1024 + +static char* skipSpaces( char* p, int* num = NULL ) +{ + int i; + + for ( i=0; *p != 0 && isspace( BYTE( *p )); i++ ) + p++; + + if ( num != NULL ) + num += i; + return p; +} + +static char* findClose( char* p ) +{ + while ( *p != 0 ) { + switch( *p ) { + case '>': return p; + + case '\'': + case '\"': + p = strchr( p+1, *p ); + if ( p == NULL ) + return NULL; + } + + p++; + } + + return NULL; +} + +int JabberXmlParse( XmlState *xmlState, char* buffer ) +{ + char* r; + int num = 0; + char tag[TAG_MAX_LEN]; + char attr[ATTR_MAX_LEN]; + XmlElemType elemType; + + char* p = skipSpaces( buffer, &num ); + + while ( *p != 0 ) { + // found starting bracket + if ( *p == '<' ) { + if ( memcmp( p, "<!--", 4 ) == 0 ) { + char* q = strstr( p+4, "-->" ); + if ( q == NULL ) + break; + + p = q+3; + continue; + } + + char* q = findClose( p+1 ); + if ( q == NULL ) + break; + + // found closing bracket + for ( r=p+1; *r!='>' && *r!=' ' && *r!='\t'; r++ ); + if ( r-( p+1 ) > TAG_MAX_LEN ) { + JabberLog( "TAG_MAX_LEN too small, ignore current tag" ); + } + else { + if ( *( p+1 ) == '/' ) { // closing tag + strncpy( tag, p+2, r-( p+2 )); + tag[r-( p+2 )] = '\0'; + elemType = ELEM_CLOSE; + } + else { + if ( *( r-1 ) == '/' ) { // single open/close tag + strncpy( tag, p+1, r-( p+1 )-1 ); + tag[r-( p+1 )-1] = '\0'; + elemType = ELEM_OPENCLOSE; + } + else { + strncpy( tag, p+1, r-( p+1 )); + tag[r-( p+1 )] = '\0'; + elemType = ELEM_OPEN; + } + } + for ( ;r<q && ( *r==' ' || *r=='\t' ); r++ ); + if ( q-r > ATTR_MAX_LEN ) { + JabberLog( "ATTR_MAX_LEN too small, ignore current tag" ); + } + else { + strncpy( attr, r, q-r ); + if (( q-r )>0 && attr[q-r-1]=='/' ) { + attr[q-r-1] = '\0'; + elemType = ELEM_OPENCLOSE; + } + else + attr[q-r] = '\0'; + JabberXmlProcessElem( xmlState, elemType, tag, attr ); + } + } + num += ( q-p+1 ); + p = q + 1; + if ( elemType==ELEM_CLOSE || elemType==ELEM_OPENCLOSE ) + p = skipSpaces( p, &num ); // Skip whitespaces after end tags + } + else { // found inner text + char* q = strchr( p+1, '<' ); + if ( q == NULL ) + break; + + // found starting bracket of the next element + char* str = ( char* )mir_alloc( q-p+1 ); + strncpy( str, p, q-p ); + str[q-p] = '\0'; + JabberXmlProcessElem( xmlState, ELEM_TEXT, str, NULL ); + mir_free( str ); + num += ( q-p ); + p = q; + } } + + return num; +} + +static void JabberXmlParseAttr( XmlNode *node, char* text ) +{ + char* kstart, *vstart; + int klen, vlen; + char* p; + XmlAttr *a; + + if ( node==NULL || text==NULL || strlen( text )<=0 ) + return; + + for ( p=text;; ) { + + // Skip leading whitespaces + p = skipSpaces( p ); + if ( *p == '\0' ) + break; + + // Fetch key + kstart = p; + for ( ;*p!='\0' && *p!='=' && *p!=' ' && *p!='\t'; p++ ); + klen = p-kstart; + + if ( node->numAttr >= node->maxNumAttr ) { + node->maxNumAttr = node->numAttr + 20; + node->attr = ( XmlAttr ** ) mir_realloc( node->attr, node->maxNumAttr*sizeof( XmlAttr * )); + } + a = node->attr[node->numAttr] = new XmlAttr(); + node->numAttr++; + + // Skip possible whitespaces between key and '=' + p = skipSpaces( p ); + + if ( *p == '\0' ) { + a->name = ( char* )mir_alloc( klen+1 ); + strncpy( a->name, kstart, klen ); + a->name[klen] = '\0'; + a->value = mir_tstrdup( _T("")); + break; + } + + if ( *p != '=' ) { + a->name = ( char* )mir_alloc( klen+1 ); + strncpy( a->name, kstart, klen ); + a->name[klen] = '\0'; + a->value = mir_tstrdup( _T("")); + continue; + } + + // Found '=' + p++; + + // Skip possible whitespaces between '=' and value + p = skipSpaces( p ); + + if ( *p == '\0' ) { + a->name = ( char* )mir_alloc( klen+1 ); + strncpy( a->name, kstart, klen ); + a->name[klen] = '\0'; + a->value = mir_tstrdup( _T("")); + break; + } + + // Fetch value + if ( *p=='\'' || *p=='"' ) { + p++; + vstart = p; + for ( ;*p!='\0' && *p!=*( vstart-1 ); p++ ); + vlen = p-vstart; + if ( *p != '\0' ) p++; + } + else { + vstart = p; + for ( ;*p!='\0' && *p!=' ' && *p!='\t'; p++ ); + vlen = p-vstart; + } + + a->name = ( char* )mir_alloc( klen+1 ); + strncpy( a->name, kstart, klen ); + a->name[klen] = '\0'; + + JabberUtfToTchar( vstart, vlen, a->value ); + } +} + +static BOOL JabberXmlProcessElem( XmlState *xmlState, XmlElemType elemType, char* elemText, char* elemAttr ) +{ + XmlNode *node, *parentNode, *n; + BOOL activateCallback = FALSE; + char* text, *attr; + + if ( elemText == NULL ) return FALSE; + + if ( elemType==ELEM_OPEN && !strcmp( elemText, "?xml" )) { + JabberLog( "XML: skip <?xml> tag" ); + return TRUE; + } + + // Find active node + node = &( xmlState->root ); + parentNode = NULL; + while ( node->numChild>0 && node->child[node->numChild-1]->state==NODE_OPEN ) { + parentNode = node; + node = node->child[node->numChild-1]; + } + + if ( node->state != NODE_OPEN ) return FALSE; + + text = NEWSTR_ALLOCA( elemText ); + + if ( elemAttr ) + attr = NEWSTR_ALLOCA( elemAttr ); + else + attr = NULL; + + switch ( elemType ) { + case ELEM_OPEN: + if ( node->numChild >= node->maxNumChild ) { + node->maxNumChild = node->numChild + 20; + node->child = ( XmlNode ** ) mir_realloc( node->child, node->maxNumChild*sizeof( XmlNode * )); + } + n = node->child[node->numChild] = new XmlNode(text); + node->numChild++; + n->depth = node->depth + 1; + n->state = NODE_OPEN; + n->numChild = n->maxNumChild = 0; + n->child = NULL; + n->numAttr = n->maxNumAttr = 0; + n->attr = NULL; + JabberXmlParseAttr( n, attr ); + n->text = NULL; + if ( n->depth==1 && xmlState->callback1_open!=NULL ) + ( *( xmlState->callback1_open ))( n, xmlState->userdata1_open ); + if ( n->depth==2 && xmlState->callback2_open!=NULL ) + ( *xmlState->callback2_open )( n, xmlState->userdata2_open ); + break; + case ELEM_OPENCLOSE: + if ( node->numChild >= node->maxNumChild ) { + node->maxNumChild = node->numChild + 20; + node->child = ( XmlNode ** ) mir_realloc( node->child, node->maxNumChild*sizeof( XmlNode * )); + } + n = node->child[node->numChild] = new XmlNode( text ); + node->numChild++; + n->depth = node->depth + 1; + n->state = NODE_CLOSE; + n->numChild = n->maxNumAttr = 0; + n->child = NULL; + n->numAttr = n->maxNumAttr = 0; + n->attr = NULL; + JabberXmlParseAttr( n, attr ); + n->text = NULL; + if ( n->depth==1 && xmlState->callback1_close!=NULL ) { + ( *( xmlState->callback1_close ))( n, xmlState->userdata1_close ); + JabberXmlRemoveChild( node, n ); + } + if ( n->depth==2 && xmlState->callback2_close!=NULL ) { + ( *xmlState->callback2_close )( n, xmlState->userdata2_close ); + JabberXmlRemoveChild( node, n ); + } + break; + case ELEM_CLOSE: + if ( node->name!=NULL && !strcmp( node->name, text )) { + node->state = NODE_CLOSE; + if ( node->depth==1 && xmlState->callback1_close!=NULL ) { + ( *( xmlState->callback1_close ))( node, xmlState->userdata1_close ); + JabberXmlRemoveChild( parentNode, node ); + } + else if ( node->depth==2 && xmlState->callback2_close!=NULL ) { + ( *xmlState->callback2_close )( node, xmlState->userdata2_close ); + JabberXmlRemoveChild( parentNode, node ); + } } + else { + JabberLog( "XML: Closing </%s> without opening tag", text ); + return FALSE; + } + break; + case ELEM_TEXT: + JabberUtfToTchar( text, strlen( text ), node->text ); + break; + default: + return FALSE; + } + + return TRUE; +} + +TCHAR* JabberXmlGetAttrValue( XmlNode *node, char* key ) +{ + if ( node==NULL || node->numAttr<=0 || key==NULL || strlen( key )<=0 ) + return NULL; + + for ( int i=0; i<node->numAttr; i++ ) + if ( !lstrcmpA( key, node->attr[i]->name )) + return node->attr[i]->value; + + return NULL; +} + +XmlNode *JabberXmlGetChild( XmlNode *node, char* tag ) +{ + return JabberXmlGetNthChild( node, tag, 1 ); +} + +XmlNode *JabberXmlGetNthChild( XmlNode *node, char* tag, int nth ) +{ + int i, num; + + if ( node==NULL || node->numChild<=0 || tag==NULL || strlen( tag )<=0 || nth<1 ) + return NULL; + num = 1; + for ( i=0; i<node->numChild; i++ ) { + if ( node->child[i]->name && !strcmp( tag, node->child[i]->name )) { + if ( num == nth ) { + return node->child[i]; + } + num++; + } + } + return NULL; +} + +XmlNode *JabberXmlGetChildWithGivenAttrValue( XmlNode *node, char* tag, char* attrKey, TCHAR* attrValue ) +{ + if ( node==NULL || node->numChild<=0 || tag==NULL || strlen( tag )<=0 || attrKey==NULL || strlen( attrKey )<=0 || attrValue==NULL || lstrlen( attrValue )<=0 ) + return NULL; + + TCHAR* str; + for ( int i=0; i<node->numChild; i++ ) + if ( node->child[i]->name && !strcmp( tag, node->child[i]->name )) + if (( str=JabberXmlGetAttrValue( node->child[i], attrKey )) != NULL ) + if ( !lstrcmp( str, attrValue )) + return node->child[i]; + + return NULL; +} + +static void JabberXmlRemoveChild( XmlNode *node, XmlNode *child ) +{ + int i; + + if ( node==NULL || child==NULL || node->numChild<=0 ) return; + for ( i=0; i<node->numChild; i++ ) { + if ( node->child[i] == child ) + break; + } + if ( i < node->numChild ) { + for ( ++i; i<node->numChild; i++ ) + node->child[i-1] = node->child[i]; + node->numChild--; + delete child; + } +} + +XmlNode *JabberXmlCopyNode( XmlNode *node ) +{ + if ( node == NULL ) + return NULL; + + XmlNode *n = new XmlNode( node->name ); + // Copy attributes + if ( node->numAttr > 0 ) { + n->attr = ( XmlAttr ** ) mir_alloc( node->numAttr*sizeof( XmlAttr * )); + for ( int i=0; i < node->numAttr; i++ ) { + n->attr[i] = new XmlAttr; + if ( node->attr[i]->name ) + n->attr[i]->name = mir_strdup( node->attr[i]->name ); + if ( node->attr[i]->value ) + n->attr[i]->value = mir_tstrdup( node->attr[i]->value ); + } + } + else + n->attr = NULL; + // Recursively copy children + if ( node->numChild > 0 ) { + n->child = ( XmlNode ** ) mir_alloc( node->numChild*sizeof( XmlNode * )); + for ( int i=0; i<node->numChild; i++ ) + n->child[i] = JabberXmlCopyNode( node->child[i] ); + } + else + n->child = NULL; + // Copy other fields + n->numAttr = node->numAttr; + n->maxNumAttr = node->numAttr; + n->numChild = node->numChild; + n->maxNumChild = node->numChild; + n->depth = node->depth; + n->state = node->state; + n->text = ( node->text )?mir_tstrdup( node->text ):NULL; + return n; +} + +XmlNode *JabberXmlAddChild( XmlNode *n, char* name ) +{ + if ( n==NULL || name==NULL ) + return NULL; + + XmlNode* result = new XmlNode( name ); + if ( result == NULL ) + return NULL; + + return n->addChild( result ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// XmlNodeIq class members + +XmlNodeIq::XmlNodeIq( const char* type, int id, const TCHAR* to ) : + XmlNode( "iq" ) +{ + if ( type != NULL ) addAttr( "type", type ); + if ( to != NULL ) addAttr( "to", to ); + if ( id != NOID ) addAttrID( id ); +} + +XmlNodeIq::XmlNodeIq( const char* type, const TCHAR* idStr, const TCHAR* to ) : + XmlNode( "iq" ) +{ + if ( type != NULL ) addAttr( "type", type ); + if ( to != NULL ) addAttr( "to", to ); + if ( idStr != NULL ) addAttr( "id", idStr ); +} + +#if defined( _UNICODE ) +XmlNodeIq::XmlNodeIq( const char* type, int id, const char* to ) : + XmlNode( "iq" ) +{ + if ( type != NULL ) addAttr( "type", type ); + if ( to != NULL ) addAttr( "to", to ); + if ( id != NOID ) addAttrID( id ); +} +#endif + +///////////////////////////////////////////////////////////////////////////////////////// +// XmlNode class members + +XmlNode::XmlNode( const char* pszName ) +{ + memset( this, 0, sizeof( XmlNode )); + name = mir_strdup( pszName ); +} + +XmlNode::XmlNode( const char* pszName, const TCHAR* ptszText ) +{ + memset( this, 0, sizeof( XmlNode )); + name = mir_strdup( pszName ); + #if defined( _UNICODE ) + sendText = JabberTextEncodeW( ptszText ); + #else + sendText = JabberTextEncode( ptszText ); + #endif +} + +#if defined( _UNICODE ) +XmlNode::XmlNode( const char* pszName, const char* ptszText ) +{ + memset( this, 0, sizeof( XmlNode )); + name = mir_strdup( pszName ); + sendText = JabberTextEncode( ptszText ); +} +#endif + +XmlNode::~XmlNode() +{ + if ( this == NULL ) return; + + // Free all children first + int i; + for ( i=0; i < numChild; i++ ) + delete child[i]; + if ( child ) mir_free( child ); + + // Free all attributes + for ( i=0; i < numAttr; i++ ) + delete attr[i]; + if ( attr ) mir_free( attr ); + + // Free string field + if ( text ) mir_free( text ); + if ( name ) mir_free( name ); +} + +XmlAttr* XmlNode::addAttr( XmlAttr* a ) +{ + if ( this == NULL || a == NULL ) + return NULL; + + int i = numAttr++; + attr = ( XmlAttr ** ) mir_realloc( attr, sizeof( XmlAttr * ) * numAttr ); + attr[i] = a; + return a; +} + +XmlAttr* XmlNode::addAttr( const char* pszName, const TCHAR* ptszValue ) +{ + return addAttr( new XmlAttr( pszName, ptszValue )); +} + +#if defined( _UNICODE ) +XmlAttr* XmlNode::addAttr( const char* pszName, const char* pszValue ) +{ + return addAttr( new XmlAttr( pszName, pszValue )); +} +#endif + +XmlAttr* XmlNode::addAttr( const char* pszName, int value ) +{ + if ( this == NULL ) + return NULL; + + TCHAR buf[ 40 ]; + _itot( value, buf, 10 ); + return addAttr( new XmlAttr( pszName, buf )); +} + +XmlAttr* XmlNode::addAttrID( int id ) +{ + if ( this == NULL ) + return NULL; + + TCHAR text[ 100 ]; + mir_sntprintf( text, SIZEOF(text), _T("mir_%d"), id ); + return addAttr( new XmlAttr( "id", text )); +} + +XmlNode* XmlNode::addChild( XmlNode* pNode ) +{ + if ( this == NULL || pNode == NULL ) + return NULL; + + int i = numChild++; + child = ( XmlNode ** ) mir_realloc( child, sizeof( XmlNode * ) * numChild ); + child[i] = pNode; + pNode->depth = depth+1; + return pNode; +} + +XmlNode* XmlNode::addChild( const char* pszName ) +{ + return addChild( new XmlNode( pszName )); +} + +XmlNode* XmlNode::addChild( const char* pszName, const TCHAR* ptszValue ) +{ + return addChild( new XmlNode( pszName, ptszValue )); +} + +#if defined( _UNICODE ) +XmlNode* XmlNode::addChild( const char* pszName, const char* pszValue ) +{ + return addChild( new XmlNode( pszName, pszValue )); +} +#endif + +XmlNode* XmlNode::addQuery( const char* szNameSpace ) +{ + XmlNode* n = addChild( "query" ); + if ( n ) + n->addAttr( "xmlns", szNameSpace ); + return n; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// text extraction routines + +static char* sttCopyNode( const XmlNode* n, char* dest ) +{ + if ( n->props ) { + lstrcpyA( dest, n->props ); dest += lstrlenA( n->props ); + } + + *dest++ = '<'; + lstrcpyA( dest, n->name ); dest += lstrlenA( n->name ); + + for ( int i=0; i < n->numAttr; i++ ) { + *dest++ = ' '; + lstrcpyA( dest, n->attr[i]->name ); dest += lstrlenA( n->attr[i]->name ); + *dest++ = '='; + *dest++ = '\''; + lstrcpyA( dest, n->attr[i]->sendValue ); dest += lstrlenA( n->attr[i]->sendValue ); + *dest++ = '\''; + } + + if ( n->numChild != 0 || n->sendText != NULL ) + *dest++ = '>'; + + if ( n->sendText != NULL ) { + lstrcpyA( dest, n->sendText ); dest += lstrlenA( n->sendText ); + } + + if ( n->numChild != 0 ) + for ( int i=0; i < n->numChild; i++ ) + dest = sttCopyNode( n->child[i], dest ); + + if ( n->numChild != 0 || n->sendText != NULL ) { + *dest++ = '<'; + *dest++ = '/'; + lstrcpyA( dest, n->name ); dest += lstrlenA( n->name ); + } + else if ( !n->dirtyHack ) *dest++ = '/'; + + *dest++ = '>'; + *dest = 0; + return dest; +} + +char* XmlNode::getText() const +{ + int cbLen = getTextLen(); + char* result = ( char* )mir_alloc( cbLen+1 ); + if ( result == NULL ) + return NULL; + + sttCopyNode( this, result ); + return result; +} + +int XmlNode::getTextLen() const +{ + int result = 10 + lstrlenA( props ) + lstrlenA( name )*2 + lstrlenA( sendText ); + + for ( int i=0; i < numAttr; i++ ) + result += lstrlenA( attr[i]->name ) + lstrlenA( attr[i]->sendValue ) + 4; + + for ( int j=0; j < numChild; j++ ) + result += child[j]->getTextLen(); + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// XmlAttr class members + +XmlAttr::XmlAttr() : + name( NULL ), value( NULL ) +{ +} + +XmlAttr::XmlAttr( const char* pszName, const TCHAR* ptszValue ) +{ + name = mir_strdup( pszName ); + #if defined( _UNICODE ) + sendValue = JabberTextEncodeW( ptszValue ); + #else + sendValue = JabberTextEncode( ptszValue ); + #endif +} + +#if defined( _UNICODE ) +XmlAttr::XmlAttr( const char* pszName, const char* ptszValue ) +{ + name = mir_strdup( pszName ); + sendValue = JabberTextEncode( ptszValue ); +} +#endif + +XmlAttr::~XmlAttr() +{ + if ( name != NULL ) mir_free( name ); + if ( value != NULL ) mir_free( value ); +} diff --git a/miranda-wine/protocols/JabberG/jabber_xml.h b/miranda-wine/protocols/JabberG/jabber_xml.h new file mode 100644 index 0000000..50e3268 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_xml.h @@ -0,0 +1,152 @@ +/* + +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_xml.h,v $ +Revision : $Revision: 3691 $ +Last change on : $Date: 2006-09-04 10:04:06 +0400 (Пнд, 04 Сен 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#ifndef _JABBER_XML_H_ +#define _JABBER_XML_H_ + +#define NOID (-1) + +typedef enum { ELEM_OPEN, ELEM_CLOSE, ELEM_OPENCLOSE, ELEM_TEXT } XmlElemType; +typedef enum { NODE_OPEN, NODE_CLOSE } XmlNodeType; + +struct XmlAttr +{ + XmlAttr(); + XmlAttr( const char* pszName, const TCHAR* ptszValue ); + #if defined( _UNICODE ) + XmlAttr( const char* pszName, const char* ptszValue ); + #endif + ~XmlAttr(); + + char* name; + union { + TCHAR* value; + char* sendValue; + }; +}; + +struct XmlNode +{ + XmlNode( const char* name ); + XmlNode( const char* pszName, const TCHAR* ptszText ); + #if defined( _UNICODE ) + XmlNode( const char* pszName, const char* ptszText ); + #endif + ~XmlNode(); + + XmlAttr* addAttr( XmlAttr* ); + XmlAttr* addAttr( const char* pszName, const TCHAR* ptszValue ); + #if defined( _UNICODE ) + XmlAttr* addAttr( const char* pszName, const char* pszValue ); + #endif + XmlAttr* addAttr( const char* pszName, int value ); + XmlAttr* addAttrID( int id ); + + XmlNode* addChild( XmlNode* ); + XmlNode* addChild( const char* pszName ); + XmlNode* addChild( const char* pszName, const TCHAR* ptszValue ); + #if defined( _UNICODE ) + XmlNode* addChild( const char* pszName, const char* pszValue ); + #endif + + XmlNode* addQuery( const char* szNameSpace ); + + int getTextLen() const; + char* getText() const; + + int depth; // depth of the current node ( 1=root ) + char* name; // tag name of the current node + union { + TCHAR* text; + char* sendText; + }; + int numAttr; // number of attributes + int maxNumAttr; // internal use ( num of slots currently allocated to attr ) + XmlAttr **attr; // attribute list + int numChild; // number of direct child nodes + int maxNumChild; // internal use ( num of slots currently allocated to child ) + XmlNode **child; // child node list + XmlNodeType state; // internal use by parser + char* props; + boolean dirtyHack; // to allow generator to issue the unclosed tag +}; + +struct XmlNodeIq : public XmlNode +{ + XmlNodeIq( const char* type, int id = NOID, const TCHAR* to = NULL ); + XmlNodeIq( const char* type, const TCHAR* idStr, const TCHAR* to ); + #if defined( _UNICODE ) + XmlNodeIq( const char* type, int id, const char* to ); + #endif +}; + +typedef void ( *JABBER_XML_CALLBACK )( XmlNode*, void* ); + +struct XmlState +{ + XmlState() : root(NULL) {} + + XmlNode root; // root is the document ( depth = 0 ); + // callback for depth=n element on opening/closing + JABBER_XML_CALLBACK callback1_open; + JABBER_XML_CALLBACK callback1_close; + JABBER_XML_CALLBACK callback2_open; + JABBER_XML_CALLBACK callback2_close; + void *userdata1_open; + void *userdata1_close; + void *userdata2_open; + void *userdata2_close; +}; + +void JabberXmlInitState( XmlState *xmlState ); +void JabberXmlDestroyState( XmlState *xmlState ); +BOOL JabberXmlSetCallback( XmlState *xmlState, int depth, XmlElemType type, void ( *callback )(), void *userdata ); +int JabberXmlParse( XmlState *xmlState, char* buffer ); +TCHAR* JabberXmlGetAttrValue( XmlNode *node, char* key ); +XmlNode *JabberXmlGetChild( XmlNode *node, char* tag ); +XmlNode *JabberXmlGetNthChild( XmlNode *node, char* tag, int nth ); +XmlNode *JabberXmlGetChildWithGivenAttrValue( XmlNode *node, char* tag, char* attrKey, TCHAR* attrValue ); +void JabberXmlDumpAll( XmlState *xmlState ); +void JabberXmlDumpNode( XmlNode *node ); +XmlNode *JabberXmlCopyNode( XmlNode *node ); +BOOL JabberXmlSetCallback( XmlState *xmlState, int depth, XmlElemType type, JABBER_XML_CALLBACK callback, void *userdata ); + +XmlNode *JabberXmlCreateNode( char* name ); +void JabberXmlAddAttr( XmlNode *n, char* name, char* value ); +XmlNode *JabberXmlAddChild( XmlNode *n, char* name ); + +inline XmlNode& operator+( XmlNode& n1, XmlNode& n2 ) +{ n1.addChild( &n2 ); + return n1; +} + +inline XmlNode& operator+( XmlNode& n, XmlAttr& a ) +{ n.addAttr( &a ); + return n; +} + +#endif diff --git a/miranda-wine/protocols/JabberG/jabber_xmlns.cpp b/miranda-wine/protocols/JabberG/jabber_xmlns.cpp new file mode 100644 index 0000000..2ddcd25 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_xmlns.cpp @@ -0,0 +1,107 @@ +/* + +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_xmlns.cpp,v $ +Revision : $Revision: 3651 $ +Last change on : $Date: 2006-08-30 16:54:52 +0400 (Срд, 30 Авг 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#include "jabber.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberXmlnsBrowse + +void JabberXmlnsBrowse( XmlNode *iqNode, void *userdata ) +{ + XmlNode *queryNode; + TCHAR *xmlns, *iqFrom, *iqType; + + if ( iqNode == NULL ) return; + if (( iqFrom=JabberXmlGetAttrValue( iqNode, "from" )) == NULL ) return; + if (( iqType=JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( queryNode=JabberXmlGetChild( iqNode, "query" )) == NULL ) return; + if (( xmlns=JabberXmlGetAttrValue( queryNode, "xmlns" )) == NULL ) return; + + if ( !_tcscmp( iqType, _T("get"))) { + XmlNodeIq iq( "result", JabberXmlGetAttrValue( iqNode, "id" ), iqFrom ); + XmlNode* user = iq.addChild( "user" ); user->addAttr( "jid", jabberJID ); user->addAttr( "type", "client" ); user->addAttr( "xmlns", xmlns ); + user->addChild( "ns", "http://jabber.org/protocol/disco#info" ); + user->addChild( "ns", "http://jabber.org/protocol/muc" ); + user->addChild( "ns", "jabber:iq:agents" ); + user->addChild( "ns", "jabber:iq:browse" ); + user->addChild( "ns", "jabber:iq:oob" ); + user->addChild( "ns", "jabber:iq:version" ); + user->addChild( "ns", "jabber:x:data" ); + user->addChild( "ns", "jabber:x:event" ); + user->addChild( "ns", "vcard-temp" ); + JabberSend( jabberThreadInfo->s, iq ); +} } + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberXmlnsDisco + +static void sttAddFeature( XmlNode* n, char* text ) +{ + XmlNode* f = n->addChild( "feature" ); f->addAttr( "var", text ); +} + +void JabberXmlnsDisco( XmlNode *iqNode, void *userdata ) +{ + XmlNode *queryNode; + TCHAR *xmlns, *p, *discoType; + TCHAR *iqFrom, *iqType; + + if ( iqNode == NULL ) return; + if (( iqFrom = JabberXmlGetAttrValue( iqNode, "from" )) == NULL ) return; + if (( iqType = JabberXmlGetAttrValue( iqNode, "type" )) == NULL ) return; + if (( queryNode = JabberXmlGetChild( iqNode, "query" )) == NULL ) return; + if (( xmlns = JabberXmlGetAttrValue( queryNode, "xmlns" )) == NULL ) return; + + p = _tcsrchr( xmlns, '/' ); + discoType = _tcsrchr( xmlns, '#' ); + + if ( p==NULL || discoType==NULL || discoType < p ) + return; + + if ( !_tcscmp( iqType, _T("get"))) { + XmlNodeIq iq( "result", JabberXmlGetAttrValue( iqNode, "id" ), iqFrom ); + + if ( !_tcscmp( discoType, _T("#info"))) { + XmlNode* query = iq.addChild( "query" ); query->addAttr( "xmlns", xmlns ); + XmlNode* ident = query->addChild( "identity" ); ident->addAttr( "category", "user" ); + ident->addAttr( "type", "client" ); ident->addAttr( "name", "Miranda" ); + sttAddFeature( query, "http://jabber.org/protocol/disco#info" ); + sttAddFeature( query, "http://jabber.org/protocol/muc" ); + sttAddFeature( query, "http://jabber.org/protocol/si" ); + sttAddFeature( query, "http://jabber.org/protocol/si/profile/file-transfer" ); + sttAddFeature( query, "http://jabber.org/protocol/bytestreams" ); + sttAddFeature( query, "http://jabber.org/protocol/chatstates" ); + sttAddFeature( query, "jabber:iq:agents" ); + sttAddFeature( query, "jabber:iq:browse" ); + sttAddFeature( query, "jabber:iq:oob" ); + sttAddFeature( query, "jabber:iq:version" ); + sttAddFeature( query, "jabber:x:data" ); + sttAddFeature( query, "jabber:x:event" ); + sttAddFeature( query, "vcard-temp" ); + } + JabberSend( jabberThreadInfo->s, iq ); +} } diff --git a/miranda-wine/protocols/JabberG/jabber_xmlns.h b/miranda-wine/protocols/JabberG/jabber_xmlns.h new file mode 100644 index 0000000..e0bfd49 --- /dev/null +++ b/miranda-wine/protocols/JabberG/jabber_xmlns.h @@ -0,0 +1,34 @@ +/* + +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_xmlns.h,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +#ifndef _JABBER_XMLNS_H_ +#define _JABBER_XMLNS_H_ + +void JabberXmlnsBrowse( XmlNode *iqNode, void *userdata ); +void JabberXmlnsDisco( XmlNode *iqNode, void *userdata ); + +#endif diff --git a/miranda-wine/protocols/JabberG/msvc6.rc b/miranda-wine/protocols/JabberG/msvc6.rc new file mode 100644 index 0000000..da0ebe9 --- /dev/null +++ b/miranda-wine/protocols/JabberG/msvc6.rc @@ -0,0 +1,2 @@ +#include "jabber.rc" +#include "version.rc" diff --git a/miranda-wine/protocols/JabberG/resource.h b/miranda-wine/protocols/JabberG/resource.h new file mode 100644 index 0000000..42469ef --- /dev/null +++ b/miranda-wine/protocols/JabberG/resource.h @@ -0,0 +1,232 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by jabber.rc +// +#define IDCANCEL2 3 +#define IDD_OPT_JABBER 101 +#define IDI_JABBER 102 +#define IDD_INFO_JABBER 103 +#define IDD_OPT_REGISTER 105 +#define IDD_AGENTS 106 +#define IDD_FORM 107 +#define IDI_ADDROSTER 108 +#define IDI_USER2ROOM 109 +#define IDD_PASSWORD 111 +#define IDI_TLEN 121 +#define IDI_ADDCONTACT 122 +#define IDI_DELETE 123 +#define IDI_EDIT 124 +#define IDD_VCARD 125 +#define IDD_VCARD_HOME 126 +#define IDD_VCARD_PERSONAL 127 +#define IDD_VCARD_WORK 128 +#define IDD_VCARD_CONTACT 129 +#define IDD_VCARD_ADDEMAIL 130 +#define IDD_VCARD_ADDPHONE 131 +#define IDI_OPEN 131 +#define IDD_VCARD_PHOTO 132 +#define IDD_VCARD_NOTE 133 +#define IDD_CHANGEPASSWORD 136 +#define IDD_OPT_JABBERMAIN 139 +#define IDD_OPT_JABBER2 140 +#define IDI_REQUEST 141 +#define IDD_GROUPCHAT 141 +#define IDI_GRANT 142 +#define IDI_KEYS 144 +#define IDI_GROUP 147 +#define IDD_GROUPCHAT_JOIN 148 +#define IDI_AGENTS 154 +#define IDI_VCARD 155 +#define IDI_WRITE 162 +#define IDI_SAVE 166 +#define IDD_GROUPCHAT_INPUT 167 +#define IDD_ADVSEARCH_TLEN 169 +#define IDD_JIDLIST 171 +#define IDD_AGENT_MANUAL_REGISTER 182 +#define IDD_GROUPCHAT_INVITE 183 +#define IDD_GROUPCHAT_INVITE_ACCEPT 184 +#define IDD_OPT_JABBER3 185 +#define IDD_OPT_SETAVATAR 185 +#define IDC_EDIT_USERNAME 1000 +#define IDC_SAVE 1000 +#define IDC_EDIT_PASSWORD 1001 +#define IDC_BUTTON_REGISTER 1002 +#define IDC_EDIT_LOGIN_SERVER 1003 +#define IDC_PORT 1004 +#define IDC_EDIT_RESOURCE 1005 +#define IDC_INFO_JID 1007 +#define IDC_INFO_RESOURCE 1008 +#define IDC_LINK_PUBLIC_SERVER 1009 +#define IDC_NAME 1009 +#define IDC_PROGRESS_REG 1011 +#define IDOK2 1012 +#define IDC_AGENT_TRANSPORT 1015 +#define IDC_AGENT_REGISTER 1016 +#define IDC_AGENT_LOGON 1017 +#define IDC_AGENT_UNREGISTER 1018 +#define IDC_AGENT_SERVER 1019 +#define IDC_AGENT_LOGOFF 1020 +#define IDC_AGENT_LIST 1021 +#define IDC_AGENT_SEARCH 1022 +#define IDC_SUBMIT 1023 +#define IDC_LOGO 1024 +#define IDC_AGENT_BROWSE 1029 +#define IDC_INSTRUCTION 1030 +#define IDC_DESCRIPTION 1031 +#define IDC_FRAME 1037 +#define IDC_FRAME_TEXT 1038 +#define IDC_SUBSCRIPTION 1039 +#define IDC_SIMPLE 1041 +#define IDC_KEEPALIVE 1042 +#define IDC_HOST 1043 +#define IDC_HOSTPORT 1044 +#define IDC_USE_SSL 1045 +#define IDC_MANUAL 1046 +#define IDC_RESOURCE_T 1047 +#define IDC_SAVEPASSWORD 1048 +#define IDC_MSGLANG 1049 +#define IDC_PASSWORD 1050 +#define IDC_JID 1051 +#define IDC_NEWPASSWD2 1052 +#define IDC_ROSTER_SYNC 1052 +#define IDC_OLDPASSWD 1053 +#define IDC_ADDRESS1 1056 +#define IDC_ADDRESS2 1057 +#define IDC_CITY 1058 +#define IDC_STATE 1059 +#define IDC_COUNTRY 1060 +#define IDC_FULLNAME 1061 +#define IDC_ZIP 1061 +#define IDC_NICKNAME 1062 +#define IDC_COMPANY 1062 +#define IDC_FIRSTNAME 1063 +#define IDC_DEPARTMENT 1063 +#define IDC_LASTNAME 1064 +#define IDC_BIRTH 1065 +#define IDC_OCCUPATION 1066 +#define IDC_HOMEPAGE 1067 +#define IDC_AVATAR 1068 +#define IDC_ENABLE_AVATARS 1069 +#define IDC_SETAVATAR 1070 +#define IDC_DELETEAVATAR 1071 +#define IDC_MIDDLE 1072 +#define IDC_EMAIL 1073 +#define IDC_HOME 1074 +#define IDC_INTERNET 1075 +#define IDC_X400 1076 +#define IDC_WORK 1077 +#define IDC_PHONE 1078 +#define IDC_CELL 1079 +#define IDC_VIDEO 1080 +#define IDC_BBS 1081 +#define IDC_MODEM 1082 +#define IDC_PAGER 1083 +#define IDC_MSG 1084 +#define IDC_ISDN 1085 +#define IDC_PCS 1086 +#define IDC_VOICE 1087 +#define IDC_FAX 1088 +#define IDC_TITLE 1089 +#define IDC_DESC 1090 +#define IDC_DELETE 1092 +#define IDC_LOAD 1093 +#define IDC_CANVAS 1094 +#define IDC_OPTIONSTAB 1095 +#define IDC_GENDER 1096 +#define IDC_JUD 1097 +#define IDC_JUD_LABEL 1098 +#define IDC_MSGLANG_LABEL 1099 +#define IDC_SOFTWARE 1101 +#define IDC_VERSION 1102 +#define IDC_SYSTEM 1103 +#define IDC_PRIORITY_SPIN 1104 +#define IDC_PRIORITY 1105 +#define IDC_PRIORITY_LABEL 1106 +#define IDC_NEWPASSWD 1107 +#define IDC_PASS_SERVER 1112 +#define IDC_PROXY_ADDR 1112 +#define IDC_PASS_PORT 1113 +#define IDC_EXT_ADDRESS 1114 +#define IDC_DIRECT_ADDR 1114 +#define IDC_SHOW_TRANSPORT 1115 +#define IDC_AUTO_ADD 1116 +#define IDC_PASS_SERVER_LABEL 1117 +#define IDC_EXT_ADDRESS_LABEL 1118 +#define IDC_PASS_PORT_LABEL 1119 +#define IDC_USE_PASS 1120 +#define IDC_PROXY 1120 +#define IDC_USE_EXT_ADDRESS 1121 +#define IDC_DIRECT_MANUAL 1121 +#define IDC_REG_STATUS 1122 +#define IDC_MSG_ACK 1122 +#define IDC_JOIN 1123 +#define IDC_DIRECT 1123 +#define IDC_ROOM 1124 +#define IDC_PROXY_MANUAL 1124 +#define IDC_SERVER 1125 +#define IDC_BROWSE 1126 +#define IDC_VSCROLL 1128 +#define IDC_NICK 1129 +#define IDC_EDIT 1131 +#define IDC_LIST 1133 +#define IDC_HSPLIT 1134 +#define IDC_LOG 1136 +#define IDC_VSPLIT 1137 +#define IDC_SET 1140 +#define IDC_TABS 1141 +#define IDC_TOPIC 1141 +#define IDC_FONT 1143 +#define IDC_ENTER 1144 +#define IDC_CTRLENTER 1145 +#define IDC_FONTSHOW 1146 +#define IDC_FLASH 1148 +#define IDC_AGEFROM 1152 +#define IDC_AGETO 1153 +#define IDC_LOOKFOR 1154 +#define IDC_SCHOOL 1155 +#define IDC_TIME 1156 +#define IDC_DATE 1157 +#define IDC_MANUAL_REGISTER 1167 +#define IDC_REASON 1171 +#define IDC_USER 1172 +#define IDC_INVITE 1173 +#define IDC_ACCEPT 1174 +#define IDC_FROM 1175 +#define IDC_AUTOJOIN 1176 +#define IDC_DISABLE_MAINMENU 1178 +#define IDC_USE_TLS 1179 +#define IDC_AUTO_ACCEPT_MUC 1180 +#define IDC_DISABLE_SASL 1182 +#define IDC_WHITERECT 1221 +#define IDC_UPDATING 1231 +#define IDC_EMAILS 1306 +#define IDC_PHONES 1308 +#define IDC_UPDATE 1313 +#define IDC_STATUS 1414 +#define IDC_PLAN 1415 +#define IDC_PERSONALGROUP 1434 +#define IDC_EXTRAGROUP 1436 +#define IDM_MESSAGE 10002 +#define IDM_CLEAR 10003 +#define IDM_VOICE 10005 +#define IDM_BAN 10006 +#define IDM_ADMIN 10007 +#define IDM_OWNER 10008 +#define IDM_CONFIG 10009 +#define IDM_KICK 10011 +#define IDM_NICK 10012 +#define IDM_MODERATOR 10013 +#define IDM_DESTROY 10014 +#define IDM_MEMBER 10015 +#define IDM_INVITE 10016 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 185 +#define _APS_NEXT_COMMAND_VALUE 40017 +#define _APS_NEXT_CONTROL_VALUE 1183 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/miranda-wine/protocols/JabberG/sdk/m_smileyadd.h b/miranda-wine/protocols/JabberG/sdk/m_smileyadd.h new file mode 100644 index 0000000..83b2dbb --- /dev/null +++ b/miranda-wine/protocols/JabberG/sdk/m_smileyadd.h @@ -0,0 +1,78 @@ +/* +Miranda SmileyAdd Plugin +Plugin support header file +Copyright ( C ) 2003 Rein-Peter de Boer ( peacow ) + +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. +*/ + + +//replace smiley tags in a rich edit control... +//wParam = ( WPARAM ) 0; not used +//lParam = ( LPARAM )( SMADD_RICHEDIT* ) &smre; //pointer to SmAddRicheditStructure +//return: TRUE if replacement succeeded, FALSE if not ( disable by user? ). +typedef struct +{ + int cbSize; //size of the structure + HWND hwndRichEditControl; //handle to the rich edit control + CHARRANGE* rangeToReplace; //same meaning as for normal Richedit use ( NULL = replaceall ) + char* Protocolname; //protocol to use... if you have defined a protocol, u can + //use your own protocol name. Smiley add wil automatically + //select the smileypack that is defined for your protocol. + //Or, use "Standard" for standard smiley set. Or "ICQ", "MSN" + //if you prefer those icons. + //If not found or NULL: "Standard" will be used + } SMADD_RICHEDIT; + +//new version from smileyadd 1.2 +typedef struct +{ + int cbSize; //size of the structure + HWND hwndRichEditControl; //handle to the rich edit control + CHARRANGE* rangeToReplace; //same meaning as for normal Richedit use ( NULL = replaceall ) + char* Protocolname; //protocol to use... if you have defined a protocol, u can + //use your own protocol name. Smiley add wil automatically + //select the smileypack that is defined for your protocol. + //Or, use "Standard" for standard smiley set. Or "ICQ", "MSN" + //if you prefer those icons. + //If not found or NULL: "Standard" will be used + BOOL useSounds; //NOT IMPLEMENTED YET, set to FALSE + BOOL disableRedraw; //If true then you have to restore scrollbars, selection + //etc and redraw yourself + //everything will be screwed up and not restored. + +} SMADD_RICHEDIT2; + +#define MS_SMILEYADD_REPLACESMILEYS "SmileyAdd/ReplaceSmileys" + + + + +//replace smiley tags in a rich edit control... +//wParam = ( WPARAM ) 0; not used +//lParam = ( LPARAM )( SMADD_GETICON* ) &smgi; //pointer to SmAddRicheditStructure +//return: TRUE if found, FALSE if not +//NOTE: the +typedef struct +{ + int cbSize; //same as in SMADD_RICHEDIT + char* Protocolname; // " " + char* SmileySequence; //character string containing the smiley + HICON SmileyIcon; //RETURN VALUE: this is filled with the icon handle... + //do not destroy! + int Smileylength; //length of the smiley that is found. +} SMADD_GETICON; +#define MS_SMILEYADD_GETSMILEYICON "SmileyAdd/GetSmileyIcon" + diff --git a/miranda-wine/protocols/JabberG/sha1.cpp b/miranda-wine/protocols/JabberG/sha1.cpp new file mode 100644 index 0000000..107c4cd --- /dev/null +++ b/miranda-wine/protocols/JabberG/sha1.cpp @@ -0,0 +1,446 @@ +/* + +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/sha1.cpp,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +/* + + Copyright ( C ) The Internet Society ( 2001 ). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +*/ + +/* + * sha1.c + * + * Description: + * This file implements the Secure Hashing Algorithm 1 as + * defined in FIPS PUB 180-1 published April 17, 1995. + * + * The SHA-1, produces a 160-bit message digest for a given + * data stream. It should take about 2**n steps to find a + * message with the same digest as a given message and + * 2**( n/2 ) to find any two messages with the same digest, + * when n is the digest size in bits. Therefore, this + * algorithm can serve as a means of providing a + * "fingerprint" for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code + * uses <stdint.h> ( included via "sha1.h" to define 32 and 8 + * bit unsigned integer types. If your C compiler does not + * support 32 bit unsigned integers, this code is not + * appropriate. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated + * for messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is + * a multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift( bits,word ) \ + (( ( word ) << ( bits )) | (( word ) >> ( 32-( bits )) )) + +/* Local Function Prototyptes */ +void SHA1PadMessage( SHA1Context * ); +void SHA1ProcessMessageBlock( SHA1Context * ); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Reset( SHA1Context *context ) +{ + if ( !context ) + { + return shaNull; + } + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Result( SHA1Context *context, + uint8_t Message_Digest[SHA1HashSize] ) +{ + int i; + + if ( !context || !Message_Digest ) + { + return shaNull; + } + + if ( context->Corrupted ) + { + return context->Corrupted; + } + + if ( !context->Computed ) + { + SHA1PadMessage( context ); + for( i=0; i<64; ++i ) + { + /* message may be sensitive, clear it out */ + context->Message_Block[i] = 0; + } + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; + + } + + for( i = 0; i < SHA1HashSize; ++i ) + { + Message_Digest[i] = ( uint8_t ) ( context->Intermediate_Hash[i>>2] + >> 8 * ( 3 - ( i & 0x03 )) ); + } + + return shaSuccess; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int SHA1Input( SHA1Context *context, + const uint8_t *message_array, + unsigned length ) +{ + if ( !length ) + { + return shaSuccess; + } + + if ( !context || !message_array ) + { + return shaNull; + } + + if ( context->Computed ) + { + context->Corrupted = shaStateError; + + return shaStateError; + } + + if ( context->Corrupted ) + { + return context->Corrupted; + } + while( length-- && !context->Corrupted ) + { + context->Message_Block[context->Message_Block_Index++] = + ( *message_array & 0xFF ); + + context->Length_Low += 8; + if ( context->Length_Low == 0 ) + { + context->Length_High++; + if ( context->Length_High == 0 ) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if ( context->Message_Block_Index == 64 ) + { + SHA1ProcessMessageBlock( context ); + } + + message_array++; + } + + return shaSuccess; +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + * + * + */ +void SHA1ProcessMessageBlock( SHA1Context *context ) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for( t = 0; t < 16; t++ ) + { + W[t] = context->Message_Block[t * 4] << 24; + W[t] |= context->Message_Block[t * 4 + 1] << 16; + W[t] |= context->Message_Block[t * 4 + 2] << 8; + W[t] |= context->Message_Block[t * 4 + 3]; + } + + for( t = 16; t < 80; t++ ) + { + W[t] = SHA1CircularShift( 1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16] ); + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for( t = 0; t < 20; t++ ) + { + temp = SHA1CircularShift( 5,A ) + + (( B & C ) | (( ~B ) & D )) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift( 30,B ); + + B = A; + A = temp; + } + + for( t = 20; t < 40; t++ ) + { + temp = SHA1CircularShift( 5,A ) + ( B ^ C ^ D ) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift( 30,B ); + B = A; + A = temp; + } + + for( t = 40; t < 60; t++ ) + { + temp = SHA1CircularShift( 5,A ) + + (( B & C ) | ( B & D ) | ( C & D )) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift( 30,B ); + B = A; + A = temp; + } + + for( t = 60; t < 80; t++ ) + { + temp = SHA1CircularShift( 5,A ) + ( B ^ C ^ D ) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift( 30,B ); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * ProcessMessageBlock: [in] + * The appropriate SHA*ProcessMessageBlock function + * Returns: + * Nothing. + * + */ + +void SHA1PadMessage( SHA1Context *context ) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if ( context->Message_Block_Index > 55 ) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while( context->Message_Block_Index < 64 ) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock( context ); + + while( context->Message_Block_Index < 56 ) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while( context->Message_Block_Index < 56 ) + { + + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = ( uint8_t ) ( context->Length_High >> 24 ); + context->Message_Block[57] = ( uint8_t ) ( context->Length_High >> 16 ); + context->Message_Block[58] = ( uint8_t ) ( context->Length_High >> 8 ); + context->Message_Block[59] = ( uint8_t ) ( context->Length_High ); + context->Message_Block[60] = ( uint8_t ) ( context->Length_Low >> 24 ); + context->Message_Block[61] = ( uint8_t ) ( context->Length_Low >> 16 ); + context->Message_Block[62] = ( uint8_t ) ( context->Length_Low >> 8 ); + context->Message_Block[63] = ( uint8_t ) ( context->Length_Low ); + + SHA1ProcessMessageBlock( context ); +} diff --git a/miranda-wine/protocols/JabberG/sha1.h b/miranda-wine/protocols/JabberG/sha1.h new file mode 100644 index 0000000..b7a96ae --- /dev/null +++ b/miranda-wine/protocols/JabberG/sha1.h @@ -0,0 +1,133 @@ +/* + +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/sha1.h,v $ +Revision : $Revision: 2866 $ +Last change on : $Date: 2006-05-16 20:39:40 +0400 (Втр, 16 Май 2006) $ +Last change by : $Author: ghazan $ + +*/ + +/* + + Copyright ( C ) The Internet Society ( 2001 ). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +*/ + +/* + * sha1.h + * + * Description: + * This is the header file for code which implements the Secure + * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + * April 17, 1995. + * + * Many of the variable names in this code, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +//#include <stdint.h> +typedef unsigned __int32 uint32_t; +typedef unsigned __int8 uint8_t; +typedef __int32 int_least16_t; +/* + * If you do not have the ISO standard stdint.h header file, then you + * must typdef the following: + * name meaning + * uint32_t unsigned 32 bit integer + * uint8_t unsigned 8 bit integer ( i.e., unsigned char ) + * int_least16_t integer of >= 16 bits + * + */ + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; +#endif +#define SHA1HashSize 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct SHA1Context +{ + uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + /* Index into message block array */ + int_least16_t Message_Block_Index; + uint8_t Message_Block[64]; /* 512-bit message blocks */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corrupted? */ +} SHA1Context; + +/* + * Function Prototypes + */ + +int SHA1Reset( SHA1Context * ); +int SHA1Input( SHA1Context *, + const uint8_t *, + unsigned int ); +int SHA1Result( SHA1Context *, + uint8_t Message_Digest[SHA1HashSize] ); + +#endif diff --git a/miranda-wine/protocols/JabberG/version.h b/miranda-wine/protocols/JabberG/version.h new file mode 100644 index 0000000..def2e71 --- /dev/null +++ b/miranda-wine/protocols/JabberG/version.h @@ -0,0 +1,3 @@ +#define __FILEVERSION_STRING 0,6,0,1 +#define __VERSION_STRING "0.6.0.1" +#define __VERSION_DWORD 0x00060001 diff --git a/miranda-wine/protocols/JabberG/version.rc b/miranda-wine/protocols/JabberG/version.rc new file mode 100644 index 0000000..6ca3961 --- /dev/null +++ b/miranda-wine/protocols/JabberG/version.rc @@ -0,0 +1,52 @@ +#include "version.h" +#include "winres.h" + +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page( 1252) +#endif //_WIN32 + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION __FILEVERSION_STRING + PRODUCTVERSION __FILEVERSION_STRING + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "Miranda\0" + VALUE "FileDescription", "Jabber Protocol Plugin for Miranda IM\0" + VALUE "FileVersion", __VERSION_STRING + VALUE "InternalName", "jabber\0" + VALUE "LegalCopyright", "Copyright ( c) 2002-04 Santithorn Bunchua, 2005 George Hazan \0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "jabber.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Jabber Protocol Plugin for Miranda IM\0" + VALUE "ProductVersion", __VERSION_STRING + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC -- cgit v1.2.3