From 48540940b6c28bb4378abfeb500ec45a625b37b6 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 15 May 2012 10:38:20 +0000 Subject: initial commit git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- src/modules/protocols/protoaccs.cpp | 638 +++++++++++++++++++ src/modules/protocols/protochains.cpp | 274 +++++++++ src/modules/protocols/protocols.cpp | 844 +++++++++++++++++++++++++ src/modules/protocols/protodir.cpp | 248 ++++++++ src/modules/protocols/protoint.cpp | 271 +++++++++ src/modules/protocols/protoopts.cpp | 1084 +++++++++++++++++++++++++++++++++ 6 files changed, 3359 insertions(+) create mode 100644 src/modules/protocols/protoaccs.cpp create mode 100644 src/modules/protocols/protochains.cpp create mode 100644 src/modules/protocols/protocols.cpp create mode 100644 src/modules/protocols/protodir.cpp create mode 100644 src/modules/protocols/protoint.cpp create mode 100644 src/modules/protocols/protoopts.cpp (limited to 'src/modules/protocols') diff --git a/src/modules/protocols/protoaccs.cpp b/src/modules/protocols/protoaccs.cpp new file mode 100644 index 0000000000..b4a2b64f4f --- /dev/null +++ b/src/modules/protocols/protoaccs.cpp @@ -0,0 +1,638 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2009 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +#include "../clist/clc.h" + +bool CheckProtocolOrder(void); +void BuildProtoMenus(); + +static BOOL bModuleInitialized = FALSE; + +static int CompareAccounts( const PROTOACCOUNT* p1, const PROTOACCOUNT* p2 ) +{ + return lstrcmpA( p1->szModuleName, p2->szModuleName ); +} + +LIST accounts( 10, CompareAccounts ); + +///////////////////////////////////////////////////////////////////////////////////////// + +static int EnumDbModules(const char *szModuleName, DWORD ofsModuleName, LPARAM lParam) +{ + DBVARIANT dbv; + if ( !DBGetContactSettingString( NULL, szModuleName, "AM_BaseProto", &dbv )) { + if (!Proto_GetAccount( szModuleName )) { + PROTOACCOUNT* pa = ( PROTOACCOUNT* )mir_calloc( sizeof( PROTOACCOUNT )); + pa->cbSize = sizeof( *pa ); + pa->type = PROTOTYPE_PROTOCOL; + pa->szModuleName = mir_strdup( szModuleName ); + pa->szProtoName = mir_strdup( dbv.pszVal ); + pa->tszAccountName = mir_a2t( szModuleName ); + pa->bIsVisible = TRUE; + pa->bIsEnabled = FALSE; + pa->iOrder = accounts.getCount(); + accounts.insert( pa ); + } + DBFreeVariant( &dbv ); + } + return 0; +} + +void LoadDbAccounts(void) +{ + DBVARIANT dbv; + int ver = DBGetContactSettingDword( NULL, "Protocols", "PrVer", -1 ); + int count = DBGetContactSettingDword( NULL, "Protocols", "ProtoCount", 0 ), i; + + for ( i=0; i < count; i++ ) { + char buf[10]; + _itoa( i, buf, 10 ); + if ( DBGetContactSettingString( NULL, "Protocols", buf, &dbv )) + continue; + + PROTOACCOUNT* pa = ( PROTOACCOUNT* )mir_calloc( sizeof( PROTOACCOUNT )); + if ( pa == NULL ) { + DBFreeVariant( &dbv ); + continue; + } + pa->cbSize = sizeof( *pa ); + pa->type = PROTOTYPE_PROTOCOL; + pa->szModuleName = mir_strdup( dbv.pszVal ); + DBFreeVariant( &dbv ); + + _itoa( OFFSET_VISIBLE+i, buf, 10 ); + pa->bIsVisible = DBGetContactSettingDword( NULL, "Protocols", buf, 1 ); + + _itoa( OFFSET_PROTOPOS+i, buf, 10 ); + pa->iOrder = DBGetContactSettingDword( NULL, "Protocols", buf, 1 ); + + if ( ver >= 4 ) { + DBFreeVariant( &dbv ); + _itoa( OFFSET_NAME+i, buf, 10 ); + if ( !DBGetContactSettingTString( NULL, "Protocols", buf, &dbv )) { + pa->tszAccountName = mir_tstrdup( dbv.ptszVal ); + DBFreeVariant( &dbv ); + } + + _itoa( OFFSET_ENABLED+i, buf, 10 ); + pa->bIsEnabled = DBGetContactSettingDword( NULL, "Protocols", buf, 1 ); + + if ( !DBGetContactSettingString( NULL, pa->szModuleName, "AM_BaseProto", &dbv )) { + pa->szProtoName = mir_strdup( dbv.pszVal ); + DBFreeVariant( &dbv ); + } + } + else pa->bIsEnabled = TRUE; + + if ( !pa->szProtoName ) { + pa->szProtoName = mir_strdup( pa->szModuleName ); + DBWriteContactSettingString( NULL, pa->szModuleName, "AM_BaseProto", pa->szProtoName ); + } + + if ( !pa->tszAccountName ) + pa->tszAccountName = mir_a2t( pa->szModuleName ); + + accounts.insert( pa ); + } + + if (CheckProtocolOrder()) WriteDbAccounts(); + + int anum = accounts.getCount(); + CallService(MS_DB_MODULES_ENUM, 0, (LPARAM)EnumDbModules); + if (anum != accounts.getCount()) WriteDbAccounts(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int arrlen; + char **pszSettingName; +} + enumDB_ProtoProcParam; + +static int enumDB_ProtoProc( const char* szSetting, LPARAM lParam ) +{ + if ( szSetting ) { + enumDB_ProtoProcParam* p = ( enumDB_ProtoProcParam* )lParam; + + p->arrlen++; + p->pszSettingName = ( char** )mir_realloc( p->pszSettingName, p->arrlen*sizeof( char* )); + p->pszSettingName[ p->arrlen-1 ] = mir_strdup( szSetting ); + } + return 0; +} + +void WriteDbAccounts() +{ + int i; + + // enum all old settings to delete + enumDB_ProtoProcParam param = { 0, NULL }; + + DBCONTACTENUMSETTINGS dbces; + dbces.pfnEnumProc = enumDB_ProtoProc; + dbces.szModule = "Protocols"; + dbces.ofsSettings = 0; + dbces.lParam = ( LPARAM )¶m; + CallService( MS_DB_CONTACT_ENUMSETTINGS, 0, ( LPARAM )&dbces ); + + // delete all settings + if ( param.arrlen ) { + int i; + for ( i=0; i < param.arrlen; i++ ) { + DBDeleteContactSetting( 0, "Protocols", param.pszSettingName[i] ); + mir_free( param.pszSettingName[i] ); + } + mir_free( param.pszSettingName ); + } + + // write new data + for ( i=0; i < accounts.getCount(); i++ ) { + PROTOACCOUNT* pa = accounts[i]; + + char buf[ 20 ]; + _itoa( i, buf, 10 ); + DBWriteContactSettingString( NULL, "Protocols", buf, pa->szModuleName ); + + _itoa( OFFSET_PROTOPOS+i, buf, 10 ); + DBWriteContactSettingDword( NULL, "Protocols", buf, pa->iOrder ); + + _itoa( OFFSET_VISIBLE+i, buf, 10 ); + DBWriteContactSettingDword( NULL, "Protocols", buf, pa->bIsVisible ); + + _itoa( OFFSET_ENABLED+i, buf, 10 ); + DBWriteContactSettingDword( NULL, "Protocols", buf, pa->bIsEnabled ); + + _itoa( OFFSET_NAME+i, buf, 10 ); + DBWriteContactSettingTString( NULL, "Protocols", buf, pa->tszAccountName ); + } + + DBDeleteContactSetting( 0, "Protocols", "ProtoCount" ); + DBWriteContactSettingDword( 0, "Protocols", "ProtoCount", accounts.getCount() ); + DBWriteContactSettingDword( 0, "Protocols", "PrVer", 4 ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +static int OnContactDeleted(WPARAM wParam, LPARAM lParam) +{ + const HANDLE hContact = (HANDLE)wParam; + if (hContact) + { + PROTOACCOUNT* pa = Proto_GetAccount(hContact); + + if (Proto_IsAccountEnabled(pa) && pa->ppro) + pa->ppro->OnEvent(EV_PROTO_ONCONTACTDELETED, wParam, lParam); + } + return 0; +} + +static int OnDbSettingsChanged(WPARAM wParam, LPARAM lParam) +{ + const HANDLE hContact = (HANDLE)wParam; + if (hContact) + { + PROTOACCOUNT* pa = Proto_GetAccount(hContact); + if (Proto_IsAccountEnabled(pa) && pa->ppro) + pa->ppro->OnEvent(EV_PROTO_DBSETTINGSCHANGED, wParam, lParam); + } + return 0; +} + +static int InitializeStaticAccounts( WPARAM, LPARAM ) +{ + int count = 0; + + for ( int i = 0; i < accounts.getCount(); i++ ) { + PROTOACCOUNT* pa = accounts[i]; + if ( !pa->ppro || !Proto_IsAccountEnabled( pa )) + continue; + + pa->ppro->OnEvent( EV_PROTO_ONLOAD, 0, 0 ); + + if ( !pa->bOldProto ) + count++; + } + + BuildProtoMenus(); + + if ( count == 0 && !DBGetContactSettingByte( NULL, "FirstRun", "AccManager", 0 )) { + DBWriteContactSettingByte( NULL, "FirstRun", "AccManager", 1 ); + CallService( MS_PROTO_SHOWACCMGR, 0, 0 ); + } + return 0; +} + +static int UninitializeStaticAccounts( WPARAM, LPARAM ) +{ + for ( int i = 0; i < accounts.getCount(); i++ ) { + PROTOACCOUNT* pa = accounts[i]; + if ( !pa->ppro || !Proto_IsAccountEnabled( pa )) + continue; + + pa->ppro->OnEvent( EV_PROTO_ONREADYTOEXIT, 0, 0 ); + pa->ppro->OnEvent( EV_PROTO_ONEXIT, 0, 0 ); + } + return 0; +} + +int LoadAccountsModule( void ) +{ + int i; + + bModuleInitialized = TRUE; + + for ( i = 0; i < accounts.getCount(); i++ ) { + PROTOACCOUNT* pa = accounts[i]; + pa->bDynDisabled = !Proto_IsProtocolLoaded( pa->szProtoName ); + if ( pa->ppro ) + continue; + + if (!Proto_IsAccountEnabled( pa )) { + pa->type = PROTOTYPE_DISPROTO; + continue; + } + + if ( !ActivateAccount( pa )) { + pa->bDynDisabled = TRUE; + pa->type = PROTOTYPE_DISPROTO; + } } + + HookEvent( ME_SYSTEM_MODULESLOADED, InitializeStaticAccounts ); + HookEvent( ME_SYSTEM_PRESHUTDOWN, UninitializeStaticAccounts ); + HookEvent( ME_DB_CONTACT_DELETED, OnContactDeleted ); + HookEvent( ME_DB_CONTACT_SETTINGCHANGED, OnDbSettingsChanged ); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static INT_PTR stub1( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ return ( INT_PTR )ppi->AddToList( wParam, (PROTOSEARCHRESULT*)lParam ); +} + +static INT_PTR stub2( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ return ( INT_PTR )ppi->AddToListByEvent( HIWORD(wParam), LOWORD(wParam), (HANDLE)lParam ); +} + +static INT_PTR stub3( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM ) +{ return ( INT_PTR )ppi->Authorize(( HANDLE )wParam ); +} + +static INT_PTR stub4( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, StrConvT(( const char* )lParam )); +} + +static INT_PTR stub7( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ return ( INT_PTR )ppi->ChangeInfo( wParam, ( void* )lParam ); +} + +static INT_PTR stub11( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ PROTOFILERESUME* pfr = ( PROTOFILERESUME* )lParam; + return ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action, (const PROTOCHAR**)&pfr->szFilename ); +} + +static INT_PTR stub12( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ return ( INT_PTR )ppi->GetCaps( wParam, (HANDLE)lParam ); +} + +static INT_PTR stub13( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM ) +{ return ( INT_PTR )ppi->GetIcon( wParam ); +} + +static INT_PTR stub15( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam ) +{ return ( INT_PTR )ppi->SearchBasic( StrConvT(( char* )lParam )); +} + +static INT_PTR stub16( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam ) +{ return ( INT_PTR )ppi->SearchByEmail( StrConvT(( char* )lParam )); +} + +static INT_PTR stub17( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam ) +{ PROTOSEARCHBYNAME* psbn = ( PROTOSEARCHBYNAME* )lParam; + return ( INT_PTR )ppi->SearchByName( StrConvT(( char* )psbn->pszNick ), + StrConvT(( char* )psbn->pszFirstName ), StrConvT(( char* )psbn->pszLastName )); +} + +static INT_PTR stub18( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam ) +{ return ( INT_PTR )ppi->SearchAdvanced(( HWND )lParam ); +} + +static INT_PTR stub19( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam ) +{ return ( INT_PTR )ppi->CreateExtendedSearchUI (( HWND )lParam ); +} + +static INT_PTR stub22( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam ) +{ CCSDATA *ccs = ( CCSDATA* )lParam; + ppi->RecvMsg( ccs->hContact, ( PROTORECVEVENT* )ccs->lParam ); + return 0; +} + +static INT_PTR stub29( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM ) +{ return ( INT_PTR )ppi->SetStatus( wParam ); +} + +static INT_PTR stub33( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ return ( INT_PTR )ppi->SetAwayMsg( wParam, StrConvT(( const char* )lParam )); +} + +static INT_PTR stub41( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ lstrcpynA(( char* )lParam, ppi->m_szModuleName, wParam ); + return 0; +} + +static INT_PTR stub42( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ return ppi->m_iStatus; +} + +#ifdef _UNICODE + +static INT_PTR stub43( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ + PROTO_AVATAR_INFORMATION* p = ( PROTO_AVATAR_INFORMATION* )lParam; + + PROTO_AVATAR_INFORMATIONW tmp = { 0 }; + tmp.cbSize = sizeof( tmp ); + tmp.hContact = p->hContact; + int result = CallProtoService( ppi->m_szModuleName, PS_GETAVATARINFOW, wParam, ( LPARAM )&tmp ); + + p->format = tmp.format; + + wchar_t filename[MAX_PATH]; + wcscpy(filename, tmp.filename); + GetShortPathNameW(tmp.filename, filename, SIZEOF(filename)); + + WideCharToMultiByte( CP_ACP, 0, filename, -1, p->filename, MAX_PATH, 0, 0 ); + return result; +} + +static INT_PTR stub44( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ + wchar_t* buf = ( wchar_t* )_alloca( sizeof(wchar_t) * (lParam + 1)); + int result = CallProtoService( ppi->m_szModuleName, PS_GETMYAVATARW, WPARAM( buf ), lParam ); + if ( result == 0 ) + { + wchar_t* filename = ( wchar_t* )_alloca( sizeof(wchar_t) * (lParam + 1)); + wcscpy(filename, buf); + GetShortPathNameW(buf, filename, lParam + 1); + + WideCharToMultiByte( CP_ACP, 0, filename, -1, ( char* )wParam, lParam, 0, 0 ); + } + + return result; +} + +static INT_PTR stub45( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam ) +{ + return CallProtoService( ppi->m_szModuleName, PS_SETMYAVATARW, wParam, ( LPARAM )( LPCTSTR )StrConvT(( char* )lParam )); +} + +#endif + +static HANDLE CreateProtoServiceEx( const char* szModule, const char* szService, MIRANDASERVICEOBJ pFunc, void* param ) +{ + char tmp[100]; + mir_snprintf( tmp, sizeof( tmp ), "%s%s", szModule, szService ); + return CreateServiceFunctionObj( tmp, pFunc, param ); +} + +BOOL ActivateAccount( PROTOACCOUNT* pa ) +{ + PROTO_INTERFACE* ppi; + PROTOCOLDESCRIPTOR* ppd = Proto_IsProtocolLoaded( pa->szProtoName ); + if ( ppd == NULL ) + return FALSE; + + if ( ppd->fnInit == NULL ) + return FALSE; + + ppi = ppd->fnInit( pa->szModuleName, pa->tszAccountName ); + if ( ppi == NULL ) + return FALSE; + + pa->type = PROTOTYPE_PROTOCOL; + pa->ppro = ppi; + ppi->m_iDesiredStatus = ppi->m_iStatus = ID_STATUS_OFFLINE; + CreateProtoServiceEx( pa->szModuleName, PS_ADDTOLIST, (MIRANDASERVICEOBJ)stub1, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_ADDTOLISTBYEVENT, (MIRANDASERVICEOBJ)stub2, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_AUTHALLOW, (MIRANDASERVICEOBJ)stub3, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_AUTHDENY, (MIRANDASERVICEOBJ)stub4, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_CHANGEINFO, (MIRANDASERVICEOBJ)stub7, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_FILERESUME, (MIRANDASERVICEOBJ)stub11, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_GETCAPS, (MIRANDASERVICEOBJ)stub12, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_LOADICON, (MIRANDASERVICEOBJ)stub13, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_BASICSEARCH, (MIRANDASERVICEOBJ)stub15, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_SEARCHBYEMAIL, (MIRANDASERVICEOBJ)stub16, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_SEARCHBYNAME, (MIRANDASERVICEOBJ)stub17, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_SEARCHBYADVANCED, (MIRANDASERVICEOBJ)stub18, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_CREATEADVSEARCHUI, (MIRANDASERVICEOBJ)stub19, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PSR_MESSAGE, (MIRANDASERVICEOBJ)stub22, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_SETSTATUS, (MIRANDASERVICEOBJ)stub29, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_SETAWAYMSG, (MIRANDASERVICEOBJ)stub33, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_GETNAME, (MIRANDASERVICEOBJ)stub41, pa->ppro ); + CreateProtoServiceEx( pa->szModuleName, PS_GETSTATUS, (MIRANDASERVICEOBJ)stub42, pa->ppro ); + +#ifdef _UNICODE + char szServiceName[ 200 ]; + mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_GETAVATARINFO ); + if ( !ServiceExists( szServiceName )) { + mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_GETAVATARINFOW ); + if ( ServiceExists( szServiceName )) + CreateProtoServiceEx( pa->szModuleName, PS_GETAVATARINFO, (MIRANDASERVICEOBJ)stub43, pa->ppro ); + } + + mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_GETMYAVATAR ); + if ( !ServiceExists( szServiceName )) { + mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_GETMYAVATARW ); + if ( ServiceExists( szServiceName )) + CreateProtoServiceEx( pa->szModuleName, PS_GETMYAVATAR, (MIRANDASERVICEOBJ)stub44, pa->ppro ); + } + + mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_SETMYAVATAR ); + if ( !ServiceExists( szServiceName )) { + mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_SETMYAVATARW ); + if ( ServiceExists( szServiceName )) + CreateProtoServiceEx( pa->szModuleName, PS_SETMYAVATAR, (MIRANDASERVICEOBJ)stub45, pa->ppro ); + } + #endif + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct DeactivationThreadParam +{ + tagPROTO_INTERFACE* ppro; + pfnUninitProto fnUninit; + bool bIsDynamic; + bool bErase; +}; + +pfnUninitProto GetProtocolDestructor( char* szProto ); + +static int DeactivationThread( DeactivationThreadParam* param ) +{ + tagPROTO_INTERFACE* p = ( tagPROTO_INTERFACE* )param->ppro; + p->SetStatus(ID_STATUS_OFFLINE); + + char * szModuleName = NEWSTR_ALLOCA(p->m_szModuleName); + + if ( param->bIsDynamic ) { + p->OnEvent( EV_PROTO_ONREADYTOEXIT, 0, 0 ); + p->OnEvent( EV_PROTO_ONEXIT, 0, 0 ); + } + + KillObjectThreads( p ); // waits for them before terminating + KillObjectEventHooks( p ); // untie an object from the outside world + + if ( param->bErase ) + p->OnEvent( EV_PROTO_ONERASE, 0, 0 ); + + if ( param->fnUninit ) + param->fnUninit( p ); + + KillObjectServices( p ); + + if ( param->bErase ) + EraseAccount( szModuleName ); + + delete param; + return 0; +} + +void DeactivateAccount( PROTOACCOUNT* pa, bool bIsDynamic, bool bErase ) +{ + if ( pa->ppro == NULL ) { + if ( bErase ) + EraseAccount( pa->szModuleName ); + return; + } + + if ( pa->hwndAccMgrUI ) { + DestroyWindow(pa->hwndAccMgrUI); + pa->hwndAccMgrUI = NULL; + pa->bAccMgrUIChanged = FALSE; + } + + DeactivationThreadParam* param = new DeactivationThreadParam; + param->ppro = pa->ppro; + param->fnUninit = GetProtocolDestructor( pa->szProtoName ); + param->bIsDynamic = bIsDynamic; + param->bErase = bErase; + pa->ppro = NULL; + pa->type = PROTOTYPE_DISPROTO; + if ( bIsDynamic ) + mir_forkthread(( pThreadFunc )DeactivationThread, param ); + else + DeactivationThread( param ); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void EraseAccount( const char* pszModuleName ) +{ + DBVARIANT dbv; + DBCONTACTGETSETTING dbcgs; + char szProtoName[32]; + + dbcgs.pValue = &dbv; + dbcgs.szModule = "Protocol"; + dbcgs.szSetting = "p"; + + // remove protocol contacts first + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) { + HANDLE h1 = hContact; + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM )h1, 0 ); + + dbv.type = DBVT_ASCIIZ; + dbv.pszVal = szProtoName; + dbv.cchVal = SIZEOF(szProtoName); + + if ( CallService( MS_DB_CONTACT_GETSETTINGSTATIC, ( WPARAM )h1, ( LPARAM )&dbcgs )) + continue; + + if ( !lstrcmpA( szProtoName, pszModuleName )) + CallService( MS_DB_CONTACT_DELETE, ( WPARAM )h1, 0 ); + } + + // remove all protocol settings + CallService( MS_DB_MODULE_DELETE, 0, ( LPARAM )pszModuleName ); +} + + +///////////////////////////////////////////////////////////////////////////////////////// + +void UnloadAccount( PROTOACCOUNT* pa, bool bIsDynamic, bool bErase ) +{ + DeactivateAccount( pa, bIsDynamic, bErase ); + + mir_free( pa->tszAccountName ); + mir_free( pa->szProtoName ); + // szModuleName should be freed only on a program's exit. + // otherwise many plugins dependand on static protocol names will crash! + // do NOT fix this 'leak', please + if ( !bIsDynamic ) { + mir_free( pa->szModuleName ); + mir_free( pa ); + } +} + +void UnloadAccountsModule() +{ + int i; + + if ( !bModuleInitialized ) return; + + for( i=accounts.getCount()-1; i >= 0; i-- ) { + PROTOACCOUNT* pa = accounts[ i ]; + UnloadAccount( pa, false, false ); + accounts.remove(i); + } + + accounts.destroy(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void BuildProtoMenus() +{ + for ( int i = 0; i < accounts.getCount(); i++ ) { + PROTOACCOUNT* pa = accounts[ i ]; + if ( cli.pfnGetProtocolVisibility( pa->szModuleName ) == 0 ) + continue; + + if ( pa->ppro ) + pa->ppro->OnEvent( EV_PROTO_ONMENU, 0, 0 ); + } +} + +void RebuildProtoMenus( int iNewValue ) +{ + DBWriteContactSettingByte( NULL, "CList", "MoveProtoMenus", iNewValue ); + + RebuildMenuOrder(); + BuildProtoMenus(); +} diff --git a/src/modules/protocols/protochains.cpp b/src/modules/protocols/protochains.cpp new file mode 100644 index 0000000000..f7f3729806 --- /dev/null +++ b/src/modules/protocols/protochains.cpp @@ -0,0 +1,274 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2009 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include + +//Protocol chain is list of integers "0".."n", with network protocol named "p" +INT_PTR Proto_CallContactService(WPARAM wParam,LPARAM lParam) +//note that this is ChainSend() too, due to a quirk of function definitions +{ + CCSDATA *ccs=(CCSDATA*)lParam; + int i; + char str[10]; + DBVARIANT dbv; + INT_PTR ret; + PROTOACCOUNT* pa; + + if ( wParam == (WPARAM)(-1)) + return 1; + + for ( i = wParam;; i++ ) { + _itoa( i, str, 10 ); + if ( DBGetContactSettingString( ccs->hContact, "_Filter", str, &dbv )) + break; + + if (( ret = CallProtoService( dbv.pszVal, ccs->szProtoService, i+1, lParam )) != CALLSERVICE_NOTFOUND ) { + //chain was started, exit + mir_free( dbv.pszVal ); + return ret; + } + mir_free( dbv.pszVal ); + } + if ( DBGetContactSettingString( ccs->hContact, "Protocol", "p", &dbv )) + return 1; + + pa = Proto_GetAccount( dbv.pszVal ); + if ( pa == NULL || pa->ppro == NULL ) + ret = 1; + else { + if ( pa->bOldProto ) + ret = CallProtoServiceInt( ccs->hContact, dbv.pszVal, ccs->szProtoService, (WPARAM)(-1), ( LPARAM)ccs ); + else + ret = CallProtoServiceInt( ccs->hContact, dbv.pszVal, ccs->szProtoService, ccs->wParam, ccs->lParam ); + if ( ret == CALLSERVICE_NOTFOUND ) + ret = 1; + } + + mir_free(dbv.pszVal); + return ret; +} + +static INT_PTR CallRecvChain(WPARAM wParam,LPARAM lParam) +{ + CCSDATA *ccs=(CCSDATA*)lParam; + int i; + INT_PTR ret; + char str[10]; + DBVARIANT dbv; + PROTOACCOUNT* pa; + + if ( wParam == (WPARAM)(-1)) return 1; //shouldn't happen - sanity check + if ( wParam == 0 ) { //begin processing by finding end of chain + for( ;;wParam++ ) { + _itoa( wParam, str, 10 ); + if ( DBGetContactSettingString( ccs->hContact, "_Filter", str, &dbv )) + break; + mir_free(dbv.pszVal); + } + } + else wParam--; + + for ( i = wParam-1; i >= 0; i-- ) { + _itoa( i, str, 10 ); + if ( DBGetContactSettingString( ccs->hContact, "_Filter", str, &dbv )) //never happens + return 1; + + if (( ret = CallProtoService( dbv.pszVal, ccs->szProtoService, i+1, lParam )) != CALLSERVICE_NOTFOUND ) { + //chain was started, exit + mir_free( dbv.pszVal ); + return ret; + } + mir_free( dbv.pszVal ); + } + + //end of chain, call network protocol again + if ( DBGetContactSettingString( ccs->hContact, "Protocol", "p", &dbv )) + return 1; + + pa = Proto_GetAccount( dbv.pszVal ); + if ( pa == NULL || pa->ppro == NULL ) + ret = 1; + else { + if ( pa->bOldProto ) + ret = CallProtoServiceInt( ccs->hContact, dbv.pszVal, ccs->szProtoService, (WPARAM)(-1), ( LPARAM)ccs ); + else + ret = CallProtoServiceInt( ccs->hContact, dbv.pszVal, ccs->szProtoService, ccs->wParam, ccs->lParam ); + if ( ret == CALLSERVICE_NOTFOUND ) + ret = 1; + } + + mir_free( dbv.pszVal ); + return ret; +} + +static INT_PTR Proto_ChainRecv(WPARAM wParam,LPARAM lParam) +{ + /* this will switch threads just like before */ + return CallServiceSync(MS_PROTO_CHAINRECV "ThreadSafe",wParam,lParam); +} + +PROTOACCOUNT* __fastcall Proto_GetAccount(HANDLE hContact) +{ + DBVARIANT dbv; + DBCONTACTGETSETTING dbcgs; + char name[32]; + + dbv.type = DBVT_ASCIIZ; + dbv.pszVal = name; + dbv.cchVal = SIZEOF(name); + dbcgs.pValue = &dbv; + dbcgs.szModule = "Protocol"; + dbcgs.szSetting = "p"; + if (CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM)hContact, (LPARAM)&dbcgs)) + return 0; + + return Proto_GetAccount((char* )dbv.pszVal); +} + +static INT_PTR Proto_GetContactBaseProto(WPARAM wParam, LPARAM) +{ + PROTOACCOUNT* pa = Proto_GetAccount((HANDLE)wParam); + return (INT_PTR)(Proto_IsAccountEnabled( pa ) ? pa->szModuleName : NULL); +} + +static INT_PTR Proto_GetContactBaseAccount(WPARAM wParam, LPARAM) +{ + PROTOACCOUNT* pa = Proto_GetAccount((HANDLE)wParam); + return (INT_PTR)(pa ? pa->szModuleName : NULL); +} + +static INT_PTR Proto_IsProtoOnContact(WPARAM wParam,LPARAM lParam) +{ + int i; + char str[10]; + DBVARIANT dbv; + + if (!lParam) return 0; + + if(!DBGetContactSettingString((HANDLE)wParam,"Protocol","p",&dbv)) { + if(!_stricmp((char*)lParam,dbv.pszVal)) { + mir_free(dbv.pszVal); + return -1; + } + mir_free(dbv.pszVal); + } + for(i=0;;i++) { + _itoa(i,str,10); + if(DBGetContactSettingString((HANDLE)wParam,"_Filter",str,&dbv)) break; + if(!strcmp((char*)lParam,dbv.pszVal)) { + mir_free(dbv.pszVal); + return i+1; + } + mir_free(dbv.pszVal); + } + return 0; +} + +static INT_PTR Proto_AddToContact(WPARAM wParam,LPARAM lParam) +{ + PROTOCOLDESCRIPTOR *pd,*pdCompare; + + pd = Proto_IsProtocolLoaded(( char* )lParam ); + if ( pd == NULL ) { + PROTOACCOUNT* pa = Proto_GetAccount(( char* )lParam ); + if ( pa ) { + DBWriteContactSettingString((HANDLE)wParam,"Protocol","p",(char*)lParam); + return 0; + } + return 1; + } + + if ( pd->type == PROTOTYPE_PROTOCOL ) { + DBWriteContactSettingString((HANDLE)wParam,"Protocol","p",(char*)lParam); + return 0; + } + if(Proto_IsProtoOnContact(wParam,lParam)) return 1; + { /* v:0.3.3 + PROTO FILTERS ARE NOW KEPT IN THEIR OWN DB MODULE! */ + int i; + char str[10],*lastProto; + DBVARIANT dbv; + + for(i=0;;i++) { + _itoa(i,str,10); + if(DBGetContactSettingString((HANDLE)wParam,"_Filter",str,&dbv)) break; + pdCompare = Proto_IsProtocolLoaded(( char* )dbv.pszVal ); + mir_free(dbv.pszVal); + if(pdCompare==NULL) continue; + if(pd->type > pdCompare->type) break; + } + //put the new module at position i + lastProto=mir_strdup((char*)lParam); + for(;;i++) { + _itoa(i,str,10); + if(DBGetContactSettingString((HANDLE)wParam,"_Filter",str,&dbv)) { + DBWriteContactSettingString((HANDLE)wParam,"_Filter",str,lastProto); + mir_free(lastProto); + break; + } + DBWriteContactSettingString((HANDLE)wParam,"_Filter",str,lastProto); + mir_free(lastProto); + lastProto=dbv.pszVal; + } + } + return 0; +} + +static INT_PTR Proto_RemoveFromContact(WPARAM wParam,LPARAM lParam) +{ + int i; + DBVARIANT dbv; + char str[10]; + + i = Proto_IsProtoOnContact(wParam,lParam); + if(!i) return 1; + if(i==-1) + DBDeleteContactSetting((HANDLE)wParam,"Protocol","p"); + else { + for(i--;;i++) { //we have to decrease i, as Proto_IsOnContact returns +1 more number than read from database + _itoa(i+1,str,10); + if(0!=DBGetContactSettingString((HANDLE)wParam,"_Filter",str,&dbv)) { + _itoa(i,str,10); + DBDeleteContactSetting((HANDLE)wParam,"_Filter",str); + break; + } + _itoa(i,str,10); + DBWriteContactSettingString((HANDLE)wParam,"_Filter",str,dbv.pszVal); + mir_free(dbv.pszVal); + } + } + return 0; +} + +int LoadProtoChains(void) +{ + CreateServiceFunction(MS_PROTO_CALLCONTACTSERVICE,Proto_CallContactService); + CreateServiceFunction(MS_PROTO_CHAINSEND,Proto_CallContactService); + CreateServiceFunction(MS_PROTO_CHAINRECV,Proto_ChainRecv); + CreateServiceFunction(MS_PROTO_CHAINRECV "ThreadSafe",CallRecvChain); + CreateServiceFunction(MS_PROTO_GETCONTACTBASEPROTO,Proto_GetContactBaseProto); + CreateServiceFunction(MS_PROTO_GETCONTACTBASEACCOUNT,Proto_GetContactBaseAccount); + CreateServiceFunction(MS_PROTO_ISPROTOONCONTACT,Proto_IsProtoOnContact); + CreateServiceFunction(MS_PROTO_ADDTOCONTACT,Proto_AddToContact); + CreateServiceFunction(MS_PROTO_REMOVEFROMCONTACT,Proto_RemoveFromContact); + return 0; +} diff --git a/src/modules/protocols/protocols.cpp b/src/modules/protocols/protocols.cpp new file mode 100644 index 0000000000..60b53aeaf4 --- /dev/null +++ b/src/modules/protocols/protocols.cpp @@ -0,0 +1,844 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2009 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" + +int LoadProtoChains(void); +int LoadProtoOptions( void ); + +HANDLE hAccListChanged; +static HANDLE hAckEvent,hTypeEvent; +static BOOL bModuleInitialized = FALSE; + +typedef struct +{ + const char* name; + int id; +} + TServiceListItem; + +static int CompareServiceItems( const TServiceListItem* p1, const TServiceListItem* p2 ) +{ return strcmp( p1->name, p2->name ); +} + +static LIST serviceItems( 10, CompareServiceItems ); + +//------------------------------------------------------------------------------------ + +static int CompareProtos( const PROTOCOLDESCRIPTOR* p1, const PROTOCOLDESCRIPTOR* p2 ) +{ return strcmp( p1->szName, p2->szName ); +} + +static LIST protos( 10, CompareProtos ); + +static INT_PTR Proto_BroadcastAck(WPARAM wParam, LPARAM lParam) +{ +#ifdef _UNICODE + ACKDATA *ack = (ACKDATA*)lParam; + if (ack && ack->type == ACKTYPE_AVATAR && ack->hProcess) + { + PROTO_AVATAR_INFORMATION* ai = (PROTO_AVATAR_INFORMATION*)ack->hProcess; + if (ai->cbSize == sizeof(PROTO_AVATAR_INFORMATION)) + { + PROTO_AVATAR_INFORMATIONW aiw = { sizeof(aiw), ai->hContact, ai->format }; + MultiByteToWideChar(CP_ACP, 0, ai->filename, -1, aiw.filename, SIZEOF(aiw.filename)); + + ack->hProcess = &aiw; + } + } +#endif + + return NotifyEventHooks(hAckEvent, wParam, lParam); +} + +INT_PTR __fastcall MyCallProtoService( const char *szModule, const char *szService, WPARAM wParam, LPARAM lParam ); +void FreeFilesMatrix( TCHAR ***files ); + +PROTOCOLDESCRIPTOR* __fastcall Proto_IsProtocolLoaded( const char* szProtoName ) +{ + if ( szProtoName ) { + PROTOCOLDESCRIPTOR tmp; + tmp.szName = ( char* )szProtoName; + return protos.find( &tmp ); + } + return NULL; +} + +INT_PTR srvProto_IsLoaded(WPARAM, LPARAM lParam) +{ + return (INT_PTR)Proto_GetAccount(( char* )lParam ); +} + +INT_PTR Proto_EnumProtocols(WPARAM wParam,LPARAM lParam) +{ + *( int* )wParam = protos.getCount(); + *( PROTOCOLDESCRIPTOR*** )lParam = protos.getArray(); + return 0; +} + +static PROTO_INTERFACE* defInitProto( const char* szModuleName, const TCHAR* ) +{ + return AddDefaultAccount( szModuleName ); +} + +static INT_PTR Proto_RegisterModule(WPARAM, LPARAM lParam) +{ + PROTOCOLDESCRIPTOR* pd = ( PROTOCOLDESCRIPTOR* )lParam, *p; + if ( pd->cbSize != sizeof( PROTOCOLDESCRIPTOR ) && pd->cbSize != PROTOCOLDESCRIPTOR_V3_SIZE ) + return 1; + + p = ( PROTOCOLDESCRIPTOR* )mir_alloc( sizeof( PROTOCOLDESCRIPTOR )); + if ( !p ) + return 2; + + if ( pd->cbSize == PROTOCOLDESCRIPTOR_V3_SIZE ) { + memset( p, 0, sizeof( PROTOCOLDESCRIPTOR )); + p->cbSize = PROTOCOLDESCRIPTOR_V3_SIZE; + p->type = pd->type; + if ( p->type == PROTOTYPE_PROTOCOL ) { + // let's create a new container + PROTO_INTERFACE* ppi = AddDefaultAccount( pd->szName ); + if ( ppi ) { + PROTOACCOUNT* pa = Proto_GetAccount( pd->szName ); + if ( pa == NULL ) { + pa = (PROTOACCOUNT*)mir_calloc( sizeof( PROTOACCOUNT )); + pa->cbSize = sizeof(PROTOACCOUNT); + pa->type = PROTOTYPE_PROTOCOL; + pa->szModuleName = mir_strdup( pd->szName ); + pa->szProtoName = mir_strdup( pd->szName ); + pa->tszAccountName = mir_a2t( pd->szName ); + pa->bIsVisible = pa->bIsEnabled = TRUE; + pa->iOrder = accounts.getCount(); + accounts.insert( pa ); + } + pa->bOldProto = TRUE; + pa->ppro = ppi; + p->fnInit = defInitProto; + p->fnUninit = FreeDefaultAccount; + } + } + } + else *p = *pd; + p->szName = mir_strdup( pd->szName ); + protos.insert( p ); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Basic core services + +static INT_PTR Proto_RecvFile(WPARAM,LPARAM lParam) +{ + CCSDATA* ccs = ( CCSDATA* )lParam; + PROTORECVEVENT* pre = ( PROTORECVEVENT* )ccs->lParam; + char* szFile = pre->szMessage + sizeof( DWORD ); + char* szDescr = szFile + strlen( szFile ) + 1; + + // Suppress the standard event filter + if ( pre->lParam != NULL ) + *( DWORD* )pre->szMessage = 0; + + DBEVENTINFO dbei = { 0 }; + dbei.cbSize = sizeof( dbei ); + dbei.szModule = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + dbei.timestamp = pre->timestamp; + dbei.flags = ( pre->flags & PREF_CREATEREAD ) ? DBEF_READ : 0; + dbei.flags |= ( pre->flags & PREF_UTF ) ? DBEF_UTF : 0; + dbei.eventType = EVENTTYPE_FILE; + dbei.cbBlob = (DWORD)(sizeof( DWORD ) + strlen( szFile ) + strlen( szDescr ) + 2); + dbei.pBlob = ( PBYTE )pre->szMessage; + HANDLE hdbe = ( HANDLE )CallService( MS_DB_EVENT_ADD, ( WPARAM )ccs->hContact, ( LPARAM )&dbei ); + + if ( pre->lParam != NULL ) + PushFileEvent( ccs->hContact, hdbe, pre->lParam ); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void sttRecvCreateBlob( DBEVENTINFO& dbei, int fileCount, char** pszFiles, char* szDescr ) +{ + dbei.cbBlob = sizeof( DWORD ); + { + for ( int i=0; i < fileCount; i++ ) + dbei.cbBlob += lstrlenA( pszFiles[i] ) + 1; + } + + dbei.cbBlob += lstrlenA( szDescr ) + 1; + + if (( dbei.pBlob = ( BYTE* )mir_alloc( dbei.cbBlob )) == 0 ) + return; + + *( DWORD* )dbei.pBlob = 0; + BYTE* p = dbei.pBlob + sizeof( DWORD ); + for ( int i=0; i < fileCount; i++ ) { + strcpy(( char* )p, pszFiles[i] ); + p += lstrlenA( pszFiles[i] ) + 1; + } + strcpy(( char* )p, ( szDescr == NULL ) ? "" : szDescr ); +} + +static INT_PTR Proto_RecvFileT(WPARAM,LPARAM lParam) +{ + CCSDATA* ccs = ( CCSDATA* )lParam; + PROTORECVFILET* pre = ( PROTORECVFILET* )ccs->lParam; + if ( pre->fileCount == 0 ) + return 0; + + DBEVENTINFO dbei = { 0 }; + dbei.cbSize = sizeof( dbei ); + dbei.szModule = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + dbei.timestamp = pre->timestamp; + dbei.flags = ( pre->flags & PREF_CREATEREAD ) ? DBEF_READ : 0; + dbei.eventType = EVENTTYPE_FILE; + + char** pszFiles = ( char** )alloca( pre->fileCount * sizeof(char*)); + { + for ( int i=0; i < pre->fileCount; i++ ) + pszFiles[i] = Utf8EncodeT( pre->ptszFiles[i] ); + } + char* szDescr = Utf8EncodeT( pre->tszDescription ); + dbei.flags |= DBEF_UTF; + sttRecvCreateBlob( dbei, pre->fileCount, pszFiles, szDescr ); + { + for ( int i=0; i < pre->fileCount; i++ ) + mir_free( pszFiles[i] ); + } + mir_free( szDescr ); + + HANDLE hdbe = ( HANDLE )CallService( MS_DB_EVENT_ADD, ( WPARAM )ccs->hContact, ( LPARAM )&dbei ); + + PushFileEvent( ccs->hContact, hdbe, pre->lParam ); + mir_free( dbei.pBlob ); + return 0; +} + +static INT_PTR Proto_RecvMessage(WPARAM,LPARAM lParam) +{ + CCSDATA *ccs = ( CCSDATA* )lParam; + PROTORECVEVENT *pre = ( PROTORECVEVENT* )ccs->lParam; + + if (pre->szMessage == NULL) return NULL; + + DBEVENTINFO dbei = { 0 }; + dbei.cbSize = sizeof( dbei ); + dbei.szModule = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + dbei.timestamp = pre->timestamp; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = (DWORD)strlen( pre->szMessage ) + 1; + if ( pre->flags & PREF_CREATEREAD ) + dbei.flags |= DBEF_READ; + if ( pre->flags & PREF_UTF ) + dbei.flags |= DBEF_UTF; + if ( pre->flags & PREF_UNICODE ) + dbei.cbBlob += sizeof( wchar_t )*( (DWORD)wcslen(( wchar_t* )&pre->szMessage[dbei.cbBlob+1] )+1 ); + + dbei.pBlob = ( PBYTE ) pre->szMessage; + return CallService( MS_DB_EVENT_ADD, ( WPARAM ) ccs->hContact, ( LPARAM )&dbei ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// User Typing Notification services + +static int Proto_ValidTypingContact(HANDLE hContact, char *szProto) +{ + if ( !hContact || !szProto ) + return 0; + + return ( CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_4,0) & PF4_SUPPORTTYPING ) ? 1 : 0; +} + +static INT_PTR Proto_SelfIsTyping(WPARAM wParam,LPARAM lParam) +{ + if ( lParam == PROTOTYPE_SELFTYPING_OFF || lParam == PROTOTYPE_SELFTYPING_ON ) { + char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, wParam, 0 ); + if ( !szProto ) + return 0; + + if ( Proto_ValidTypingContact(( HANDLE )wParam, szProto )) + CallProtoService( szProto, PSS_USERISTYPING, wParam, lParam ); + } + + return 0; +} + +static INT_PTR Proto_ContactIsTyping(WPARAM wParam,LPARAM lParam) +{ + int type = (int)lParam; + char *szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, wParam, 0 ); + if ( !szProto ) + return 0; + + if ( CallService( MS_IGNORE_ISIGNORED, wParam, IGNOREEVENT_TYPINGNOTIFY )) + return 0; + + if ( type < PROTOTYPE_CONTACTTYPING_OFF ) + return 0; + + if ( Proto_ValidTypingContact(( HANDLE )wParam, szProto )) + NotifyEventHooks( hTypeEvent, wParam, lParam ); + + return 0; +} + +void Proto_SetStatus(const char* szProto, unsigned status) +{ + if (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND) + { + TCHAR* awayMsg = (TCHAR* )CallService(MS_AWAYMSG_GETSTATUSMSGW, (WPARAM) status, (LPARAM) szProto); + if ((INT_PTR)awayMsg == CALLSERVICE_NOTFOUND) + { + char* awayMsgA = (char*)CallService(MS_AWAYMSG_GETSTATUSMSG, (WPARAM) status, (LPARAM) szProto); + if ((INT_PTR)awayMsgA != CALLSERVICE_NOTFOUND) + { + awayMsg = mir_a2t(awayMsgA); + mir_free(awayMsgA); + } + } + if ((INT_PTR)awayMsg != CALLSERVICE_NOTFOUND) + { + CallProtoService(szProto, PS_SETAWAYMSGT, status, (LPARAM) awayMsg); + mir_free(awayMsg); + } } + CallProtoService(szProto, PS_SETSTATUS, status, 0); +} + +#ifdef _UNICODE +char** __fastcall Proto_FilesMatrixA( wchar_t **files ) +{ + if ( files == NULL ) return NULL; + + int count = 0; + while( files[ count++ ] ); + + char** filesA = ( char** )mir_alloc( count * sizeof( char* )); + for( int i = 0; i < count; ++i ) + filesA[ i ] = mir_u2a( files[ i ] ); + + return filesA; +} + +static wchar_t** __fastcall Proto_FilesMatrixU( char **files ) +{ + if ( files == NULL ) return NULL; + + int count = 0; + while( files[ count++ ] ); + + wchar_t** filesU = ( wchar_t** )mir_alloc( count * sizeof( wchar_t* )); + for( int i = 0; i < count; ++i ) + filesU[ i ] = mir_a2u( files[ i ] ); + + return filesU; +} +#endif + +///////////////////////////////////////////////////////////////////////////////////////// +// 0.8.0+ - accounts + +PROTOACCOUNT* __fastcall Proto_GetAccount( const char* accName ) +{ + int idx; + PROTOACCOUNT temp; + temp.szModuleName = ( char* )accName; + if (( idx = accounts.getIndex( &temp )) == -1 ) + return NULL; + + return accounts[idx]; +} + +static INT_PTR srvProto_GetAccount(WPARAM, LPARAM lParam) +{ + return ( INT_PTR )Proto_GetAccount(( char* )lParam ); +} + +static INT_PTR Proto_EnumAccounts(WPARAM wParam, LPARAM lParam) +{ + *( int* )wParam = accounts.getCount(); + *( PROTOACCOUNT*** )lParam = accounts.getArray(); + return 0; +} + +bool __fastcall Proto_IsAccountEnabled( PROTOACCOUNT* pa ) +{ + return pa && (( pa->bIsEnabled && !pa->bDynDisabled ) || pa->bOldProto ); +} + +static INT_PTR srvProto_IsAccountEnabled(WPARAM, LPARAM lParam) +{ + return ( INT_PTR )Proto_IsAccountEnabled(( PROTOACCOUNT* )lParam); +} + +bool __fastcall Proto_IsAccountLocked( PROTOACCOUNT* pa ) +{ + return pa && DBGetContactSettingByte(NULL, pa->szModuleName, "LockMainStatus", 0) != 0; +} + +static INT_PTR srvProto_IsAccountLocked(WPARAM, LPARAM lParam) +{ + return ( INT_PTR )Proto_IsAccountLocked( Proto_GetAccount(( char* )lParam )); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR CallProtoServiceInt( HANDLE hContact, const char *szModule, const char *szService, WPARAM wParam, LPARAM lParam ) +{ + PROTOACCOUNT* pa = Proto_GetAccount( szModule ); + if ( pa && !pa->bOldProto ) { + PROTO_INTERFACE* ppi; + if (( ppi = pa->ppro ) == NULL ) + return CALLSERVICE_NOTFOUND; + else { + TServiceListItem *item = serviceItems.find(( TServiceListItem* )&szService ); + if ( item ) { + switch( item->id ) { + case 1: +#ifdef _UNICODE + if ( ppi->m_iVersion > 1 || !((( PROTOSEARCHRESULT* )lParam)->flags & PSR_UNICODE)) + return ( INT_PTR )ppi->AddToList( wParam, (PROTOSEARCHRESULT*)lParam ); + else { + PROTOSEARCHRESULT *psr = ( PROTOSEARCHRESULT* )lParam; + PROTOSEARCHRESULT *psra =( PROTOSEARCHRESULT* )mir_alloc( psr->cbSize ); + memcpy( psra, psr, psr->cbSize ); + psra->nick = ( PROTOCHAR* )mir_u2a( psr->nick ); + psra->firstName = ( PROTOCHAR* )mir_u2a( psr->firstName ); + psra->lastName = ( PROTOCHAR* )mir_u2a( psr->lastName ); + psra->email = ( PROTOCHAR* )mir_u2a( psr->email ); + + INT_PTR res = ( INT_PTR )ppi->AddToList( wParam, psra ); + + mir_free( psra->nick ); + mir_free( psra->firstName ); + mir_free( psra->lastName ); + mir_free( psra->email ); + mir_free( psra ); + + return res; + } +#else + return ( INT_PTR )ppi->AddToList( wParam, (PROTOSEARCHRESULT*)lParam ); +#endif + case 2: return ( INT_PTR )ppi->AddToListByEvent( LOWORD(wParam), HIWORD(wParam), (HANDLE)lParam ); + case 3: return ( INT_PTR )ppi->Authorize( ( HANDLE )wParam ); + case 4: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, StrConvT(( char* )lParam )); + else + return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, ( PROTOCHAR* )lParam ); + case 5: return ( INT_PTR )ppi->AuthRecv( hContact, ( PROTORECVEVENT* )lParam ); + case 6: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->AuthRequest( hContact, StrConvT(( char* )lParam )); + else + return ( INT_PTR )ppi->AuthRequest( hContact, ( PROTOCHAR* )lParam ); + case 7: return ( INT_PTR )ppi->ChangeInfo( wParam, ( void* )lParam ); + case 8: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->FileAllow( hContact, ( HANDLE )wParam, StrConvT(( char* )lParam )); + else + return ( INT_PTR )ppi->FileAllow( hContact, ( HANDLE )wParam, ( PROTOCHAR* )lParam ); + case 9: return ( INT_PTR )ppi->FileCancel( hContact, ( HANDLE )wParam ); + case 10: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->FileDeny( hContact, ( HANDLE )wParam, StrConvT(( char* )lParam )); + else + return ( INT_PTR )ppi->FileDeny( hContact, ( HANDLE )wParam, ( PROTOCHAR* )lParam ); + case 11: { + PROTOFILERESUME* pfr = ( PROTOFILERESUME* )lParam; +#ifdef _UNICODE + if ( ppi->m_iVersion > 1 ) { + PROTOCHAR* szFname = mir_a2t(( char* )pfr->szFilename ); + INT_PTR res = ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action, + ( const PROTOCHAR** )&szFname); + mir_free(( PROTOCHAR* )pfr->szFilename ); + pfr->szFilename = ( PROTOCHAR* )mir_t2a( szFname ); mir_free( szFname ); + } + else +#endif + return ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action, + ( const PROTOCHAR** )&pfr->szFilename ); + } + case 12: return ( INT_PTR )ppi->GetCaps( wParam, (HANDLE)lParam ); + case 13: return ( INT_PTR )ppi->GetIcon( wParam ); + case 14: return ( INT_PTR )ppi->GetInfo( hContact, wParam );; + case 15: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->SearchBasic( StrConvT(( char* )lParam )); + else + return ( INT_PTR )ppi->SearchBasic(( TCHAR* )lParam ); + case 16: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->SearchByEmail( StrConvT(( char* )lParam )); + else + return ( INT_PTR )ppi->SearchByEmail(( TCHAR* )lParam ); + case 17: { + PROTOSEARCHBYNAME* psbn = ( PROTOSEARCHBYNAME* )lParam; + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->SearchByName( StrConvT(( char* )psbn->pszNick ), + StrConvT(( char* )psbn->pszFirstName ), StrConvT(( char* )psbn->pszLastName )); + else + return ( INT_PTR )ppi->SearchByName( psbn->pszNick, psbn->pszFirstName, psbn->pszLastName ); + } + case 18: return ( INT_PTR )ppi->SearchAdvanced( ( HWND )lParam ); + case 19: return ( INT_PTR )ppi->CreateExtendedSearchUI ( ( HWND )lParam ); + case 20: return ( INT_PTR )ppi->RecvContacts( hContact, ( PROTORECVEVENT* )lParam ); + case 21: return ( INT_PTR )ppi->RecvFile( hContact, ( PROTOFILEEVENT* )lParam ); + case 22: return ( INT_PTR )ppi->RecvMsg( hContact, ( PROTORECVEVENT* )lParam ); + case 23: return ( INT_PTR )ppi->RecvUrl( hContact, ( PROTORECVEVENT* )lParam ); + case 24: return ( INT_PTR )ppi->SendContacts( hContact, LOWORD( wParam ), HIWORD( wParam ), + ( HANDLE* )lParam ); + case 25: +#ifdef _UNICODE + if ( ppi->m_iVersion > 1 ) { + TCHAR** files = Proto_FilesMatrixU(( char** )lParam ); + INT_PTR res = ( INT_PTR )ppi->SendFile( hContact, StrConvT(( char* )wParam ), ( TCHAR** )files ); + if ( res == 0 ) FreeFilesMatrix( &files ); + return res; + } + else +#endif + return ( INT_PTR )ppi->SendFile( hContact, ( TCHAR* )wParam, ( TCHAR** )lParam ); + case 26: return ( INT_PTR )ppi->SendMsg( hContact, wParam, ( const char* )lParam ); + case 27: return ( INT_PTR )ppi->SendUrl( hContact, wParam, ( const char* )lParam ); + case 28: return ( INT_PTR )ppi->SetApparentMode( hContact, wParam ); + case 29: return ( INT_PTR )ppi->SetStatus( wParam ); + case 30: return ( INT_PTR )ppi->GetAwayMsg( hContact ); + case 31: return ( INT_PTR )ppi->RecvAwayMsg( hContact, wParam, ( PROTORECVEVENT* )lParam ); + case 32: return ( INT_PTR )ppi->SendAwayMsg( hContact, ( HANDLE )wParam, ( const char* )lParam ); + case 33: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->SetAwayMsg( wParam, StrConvT(( char* )lParam )); + else + return ( INT_PTR )ppi->SetAwayMsg( wParam, ( TCHAR* )lParam ); + case 34: return ( INT_PTR )ppi->UserIsTyping( ( HANDLE )wParam, lParam ); + case 35: lstrcpynA(( char* )lParam, ppi->m_szModuleName, wParam ); return 0; + case 36: return ppi->m_iStatus; + +#ifdef _UNICODE + case 100: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->SetAwayMsg( wParam, ( TCHAR* )lParam ); + else + return ( INT_PTR )ppi->SetAwayMsg( wParam, StrConvA(( TCHAR* )lParam )); + case 102: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->SendFile( hContact, ( TCHAR* )wParam, ( TCHAR** )lParam ); + else { + char** files = Proto_FilesMatrixA(( TCHAR** )lParam ); + INT_PTR res = ( INT_PTR )ppi->SendFile( hContact, StrConvA(( TCHAR* )wParam ), ( TCHAR** )files ); + if ( res == 0 ) FreeFilesMatrix(( TCHAR*** )&files ); + return res; + } + case 103: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->FileAllow( hContact, ( HANDLE )wParam, ( TCHAR* )lParam ); + else + return ( INT_PTR )ppi->FileAllow( hContact, ( HANDLE )wParam, StrConvA(( TCHAR* )lParam )); + case 104: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->FileDeny( hContact, ( HANDLE )wParam, ( TCHAR* )lParam ); + else + return ( INT_PTR )ppi->FileDeny( hContact, ( HANDLE )wParam, StrConvA(( TCHAR* )lParam )); + case 105: { + PROTOFILERESUME* pfr = ( PROTOFILERESUME* )lParam; + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action, + ( const PROTOCHAR** )&pfr->szFilename ); + else { + char* szFname = mir_t2a( pfr->szFilename ); + INT_PTR res = ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action, + ( const PROTOCHAR** )&szFname); + mir_free( szFname ); + } } + case 106: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->AuthRequest( hContact, ( const TCHAR* )lParam ); + else + return ( INT_PTR )ppi->AuthRequest( hContact, StrConvA(( const TCHAR* )lParam )); + case 107: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, ( const TCHAR* )lParam ); + else + return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, StrConvA(( const TCHAR* )lParam )); + case 108: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->SearchBasic(( const TCHAR* )lParam ); + else + return ( INT_PTR )ppi->SearchBasic(StrConvA(( const TCHAR* )lParam )); + case 109: { + PROTOSEARCHBYNAME* psbn = ( PROTOSEARCHBYNAME* )lParam; + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->SearchByName( psbn->pszNick, psbn->pszFirstName, psbn->pszLastName ); + else + return ( INT_PTR )ppi->SearchByName( StrConvA(( TCHAR* )psbn->pszNick ), + StrConvA(( TCHAR* )psbn->pszFirstName ), StrConvA(( TCHAR* )psbn->pszLastName )); + } + case 110: + if ( ppi->m_iVersion > 1 ) + return ( INT_PTR )ppi->SearchByEmail(( const TCHAR* )lParam ); + else + return ( INT_PTR )ppi->SearchByEmail(StrConvA(( const TCHAR* )lParam )); +#endif + } } } } + +#ifdef _UNICODE + if ( strcmp( szService, PS_ADDTOLIST ) == 0 ) { + PROTOSEARCHRESULT *psr = ( PROTOSEARCHRESULT* )lParam; + PROTOSEARCHRESULT *psra =( PROTOSEARCHRESULT* )mir_alloc( psr->cbSize ); + memcpy( psra, psr, psr->cbSize ); + psra->nick = ( PROTOCHAR* )mir_u2a( psr->nick ); + psra->firstName = ( PROTOCHAR* )mir_u2a( psr->firstName ); + psra->lastName = ( PROTOCHAR* )mir_u2a( psr->lastName ); + psra->email = ( PROTOCHAR* )mir_u2a( psr->email ); + + INT_PTR res = MyCallProtoService( szModule, szService, wParam, ( LPARAM )psra ); + + mir_free( psra->nick ); + mir_free( psra->firstName ); + mir_free( psra->lastName ); + mir_free( psra->email ); + mir_free( psra ); + + return res; + } +#endif + + INT_PTR res = MyCallProtoService( szModule, szService, wParam, lParam ); + +#ifdef _UNICODE + if ( res == CALLSERVICE_NOTFOUND && pa && pa->bOldProto && pa->ppro && strchr( szService, 'W' )) { + TServiceListItem *item = serviceItems.find(( TServiceListItem* )&szService ); + if ( !item ) return res; + + switch( item->id ) { + case 100: + return ( INT_PTR )pa->ppro->SetAwayMsg( wParam, ( TCHAR* )lParam ); + case 102: { + CCSDATA *ccs = ( CCSDATA* )lParam; + return ( INT_PTR )pa->ppro->SendFile( ccs->hContact, ( TCHAR* )ccs->wParam, ( TCHAR** )ccs->lParam ); + } + case 103: { + CCSDATA *ccs = ( CCSDATA* )lParam; + return ( INT_PTR )pa->ppro->FileAllow( ccs->hContact, ( HANDLE )ccs->wParam, ( TCHAR* )ccs->lParam ); + } + case 104: { + CCSDATA *ccs = ( CCSDATA* )lParam; + return ( INT_PTR )pa->ppro->FileDeny( ccs->hContact, ( HANDLE )ccs->wParam, ( TCHAR* )ccs->lParam ); + } + case 105: { + PROTOFILERESUME* pfr = ( PROTOFILERESUME* )lParam; + return ( INT_PTR )pa->ppro->FileResume(( HANDLE )wParam, &pfr->action, &pfr->szFilename ); + } + case 106: { + CCSDATA *ccs = ( CCSDATA* )lParam; + return ( INT_PTR )pa->ppro->AuthRequest( ccs->hContact, ( const TCHAR* )ccs->lParam ); + } + case 107: + return ( INT_PTR )pa->ppro->AuthDeny(( HANDLE )wParam, ( const TCHAR* )lParam ); + case 108: + return ( INT_PTR )pa->ppro->SearchBasic(( const TCHAR* )lParam ); + case 109: { + PROTOSEARCHBYNAME* psbn = ( PROTOSEARCHBYNAME* )lParam; + return ( INT_PTR )pa->ppro->SearchByName( psbn->pszNick, psbn->pszFirstName, psbn->pszLastName ); + } + case 110: + return ( INT_PTR )pa->ppro->SearchByEmail(( const TCHAR* )lParam ); + } } +#endif + + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR CallContactService( HANDLE hContact, const char *szProtoService, WPARAM wParam, LPARAM lParam ) +{ + int i; + DBVARIANT dbv; + INT_PTR ret; + PROTOACCOUNT* pa; + CCSDATA ccs = { hContact, szProtoService, wParam, lParam }; + + for ( i = 0;; i++ ) { + char str[10]; + _itoa( i, str, 10 ); + if ( DBGetContactSettingString( hContact, "_Filter", str, &dbv )) + break; + + if (( ret = CallProtoServiceInt( hContact, dbv.pszVal, szProtoService, i+1, ( LPARAM)&ccs )) != CALLSERVICE_NOTFOUND ) { + //chain was started, exit + mir_free( dbv.pszVal ); + return ret; + } + mir_free( dbv.pszVal ); + } + if ( DBGetContactSettingString( hContact, "Protocol", "p", &dbv )) + return 1; + + pa = Proto_GetAccount( dbv.pszVal ); + if ( pa == NULL || pa->ppro == NULL ) + ret = 1; + else { + if ( pa->bOldProto ) + ret = CallProtoServiceInt( hContact, dbv.pszVal, szProtoService, (WPARAM)(-1), ( LPARAM)&ccs ); + else + ret = CallProtoServiceInt( hContact, dbv.pszVal, szProtoService, wParam, lParam ); + if ( ret == CALLSERVICE_NOTFOUND ) + ret = 1; + } + + mir_free( dbv.pszVal ); + return ret; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void InsertServiceListItem( int id, const char* szName ) +{ + TServiceListItem* p = ( TServiceListItem* )mir_alloc( sizeof( TServiceListItem )); + p->id = id; + p->name = szName; + serviceItems.insert( p ); +} + +int LoadProtocolsModule(void) +{ + bModuleInitialized = TRUE; + + if ( LoadProtoChains() ) + return 1; + + InsertServiceListItem( 1, PS_ADDTOLIST ); + InsertServiceListItem( 2, PS_ADDTOLISTBYEVENT ); + InsertServiceListItem( 3, PS_AUTHALLOW ); + InsertServiceListItem( 4, PS_AUTHDENY ); + InsertServiceListItem( 5, PSR_AUTH ); + InsertServiceListItem( 6, PSS_AUTHREQUEST ); + InsertServiceListItem( 7, PS_CHANGEINFO ); + InsertServiceListItem( 8, PSS_FILEALLOW ); + InsertServiceListItem( 9, PSS_FILECANCEL ); + InsertServiceListItem( 10, PSS_FILEDENY ); + InsertServiceListItem( 11, PS_FILERESUME ); + InsertServiceListItem( 12, PS_GETCAPS ); + InsertServiceListItem( 13, PS_LOADICON ); + InsertServiceListItem( 14, PSS_GETINFO ); + InsertServiceListItem( 15, PS_BASICSEARCH ); + InsertServiceListItem( 16, PS_SEARCHBYEMAIL ); + InsertServiceListItem( 17, PS_SEARCHBYNAME ); + InsertServiceListItem( 18, PS_SEARCHBYADVANCED ); + InsertServiceListItem( 19, PS_CREATEADVSEARCHUI ); + InsertServiceListItem( 20, PSR_CONTACTS ); + InsertServiceListItem( 21, PSR_FILE ); + InsertServiceListItem( 22, PSR_MESSAGE ); + InsertServiceListItem( 23, PSR_URL ); + InsertServiceListItem( 24, PSS_CONTACTS ); + InsertServiceListItem( 25, PSS_FILE ); + InsertServiceListItem( 26, PSS_MESSAGE ); + InsertServiceListItem( 27, PSS_URL ); + InsertServiceListItem( 28, PSS_SETAPPARENTMODE ); + InsertServiceListItem( 29, PS_SETSTATUS ); + InsertServiceListItem( 30, PSS_GETAWAYMSG ); + InsertServiceListItem( 31, PSR_AWAYMSG ); + InsertServiceListItem( 32, PSS_AWAYMSG ); + InsertServiceListItem( 33, PS_SETAWAYMSG ); + InsertServiceListItem( 34, PSS_USERISTYPING ); + InsertServiceListItem( 35, PS_GETNAME ); + InsertServiceListItem( 36, PS_GETSTATUS ); + +#ifdef _UNICODE + InsertServiceListItem( 100, PS_SETAWAYMSGW ); + InsertServiceListItem( 102, PSS_FILEW ); + InsertServiceListItem( 103, PSS_FILEALLOWW ); + InsertServiceListItem( 104, PSS_FILEDENYW ); + InsertServiceListItem( 105, PS_FILERESUMEW ); + InsertServiceListItem( 106, PSS_AUTHREQUESTW ); + InsertServiceListItem( 107, PS_AUTHDENYW ); + InsertServiceListItem( 108, PS_BASICSEARCHW ); + InsertServiceListItem( 109, PS_SEARCHBYNAMEW ); + InsertServiceListItem( 110, PS_SEARCHBYEMAILW ); +#endif + + hAckEvent = CreateHookableEvent(ME_PROTO_ACK); + hTypeEvent = CreateHookableEvent(ME_PROTO_CONTACTISTYPING); + hAccListChanged = CreateHookableEvent(ME_PROTO_ACCLISTCHANGED); + + CreateServiceFunction( MS_PROTO_BROADCASTACK, Proto_BroadcastAck ); + CreateServiceFunction( MS_PROTO_ISPROTOCOLLOADED, srvProto_IsLoaded ); + CreateServiceFunction( MS_PROTO_ENUMPROTOS, Proto_EnumProtocols ); + CreateServiceFunction( MS_PROTO_REGISTERMODULE, Proto_RegisterModule ); + CreateServiceFunction( MS_PROTO_SELFISTYPING, Proto_SelfIsTyping ); + CreateServiceFunction( MS_PROTO_CONTACTISTYPING, Proto_ContactIsTyping ); + + CreateServiceFunction( MS_PROTO_RECVFILE, Proto_RecvFile ); + CreateServiceFunction( MS_PROTO_RECVFILET, Proto_RecvFileT ); + CreateServiceFunction( MS_PROTO_RECVMSG, Proto_RecvMessage ); + + CreateServiceFunction( "Proto/EnumProtocols", Proto_EnumAccounts ); + CreateServiceFunction( MS_PROTO_ENUMACCOUNTS, Proto_EnumAccounts ); + CreateServiceFunction( MS_PROTO_GETACCOUNT, srvProto_GetAccount ); + + CreateServiceFunction( MS_PROTO_ISACCOUNTENABLED, srvProto_IsAccountEnabled ); + CreateServiceFunction( MS_PROTO_ISACCOUNTLOCKED, srvProto_IsAccountLocked ); + + return LoadProtoOptions(); +} + +void UnloadProtocolsModule() +{ + int i; + + if ( !bModuleInitialized ) return; + + if ( hAckEvent ) { + DestroyHookableEvent(hAckEvent); + hAckEvent = NULL; + } + if ( hAccListChanged ) { + DestroyHookableEvent(hAccListChanged); + hAccListChanged = NULL; + } + + if ( protos.getCount() ) { + for( i=0; i < protos.getCount(); i++ ) { + mir_free( protos[i]->szName); + mir_free( protos[i] ); + } + protos.destroy(); + } + + for ( i=0; i < serviceItems.getCount(); i++ ) + mir_free( serviceItems[i] ); + serviceItems.destroy(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +pfnUninitProto GetProtocolDestructor( char* szProto ) +{ + int idx; + PROTOCOLDESCRIPTOR temp; + temp.szName = szProto; + if (( idx = protos.getIndex( &temp )) != -1 ) + return protos[idx]->fnUninit; + + return NULL; +} diff --git a/src/modules/protocols/protodir.cpp b/src/modules/protocols/protodir.cpp new file mode 100644 index 0000000000..7a110268ef --- /dev/null +++ b/src/modules/protocols/protodir.cpp @@ -0,0 +1,248 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2005 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +#if 0 + +extern HANDLE hCacheHeap; + +/* + + the id cache has id/proto against hContact to lookup ID's fast to resolve to hContact, + the protoBaseCache has hContact's sorted, so can lookup hContact->proto fast, these two caches + share the same data, they're indexes, each entry might not have an "id" set. + + There is a small cache which maintains a protocol list + +*/ + +/* + + The information we need to cache is not readily available, data has to be gathered at startup + when a new contact is created/deleted, when a new proto registers and so on. + + The information we get at startup includes walking the contact chain and reading Protocol/p which + will give us the protocol the contact is on, all this info is stored in contactEntry's within + protoCache ONLY - contactCache is EMPTY at this point. + + We can not fetch the id of the contact because this information is only in SOME protocol plugins, + this is a problem but we'll hook all proto registrations and ask each proto if it supports the + returning this ID name, if not - it won't even use our id <-> contact look so no biggie! + +*/ + +typedef struct { + char * proto; // within proto cache + char * id; // optional + HANDLE hContact; +} contactEntry; + +typedef struct { + CRITICAL_SECTION csLock; + SortedList contactCache; // index for id/proto -> hContact + SortedList protoCache; // index for hContact -> proto/id + SortedList protoNameCache; // index of protocol names +} contactDir; + +static contactDir condir; + +// compare's id/proto and return's hContact's +int contactCacheCompare(void * a, void * b) +{ + contactEntry * x = (contactEntry *) a; + contactEntry * y = (contactEntry *) b; + int rc=0; + // same protocol? + rc = strcmp(x->proto, y->proto); + if ( rc == 0 ) { + // same id? id's might be missing + if ( x->id && y->id ) rc = strcmp(x->id, y->id); + } + return rc; +} + +// compares hContact's and returns associated data +int protoCacheCompare(void * a, void * b) +{ + contactEntry * x = (contactEntry *) a; + contactEntry * y = (contactEntry *) b; + if ( x->hContact < y->hContact ) return -1; + if ( x->hContact > y->hContact ) return 1; + return 0; +} + +// keeps a list of protocol names +int protoNameCacheCompare(void * a, void * b) +{ + return strcmp( (char *)a, (char*)b ); +} + +// cache the protocol string so that its not allocated per contact but shared +char * contactDir_Proto_Add(contactDir * cd, char * proto) +{ + int index = 0 ; + char * szCache = 0; + EnterCriticalSection(&cd->csLock); + if ( List_GetIndex(&cd->protoNameCache, proto, &index) ) szCache = cd->protoNameCache.items[index]; + else { + szCache = HeapAlloc(hCacheHeap, HEAP_NO_SERIALIZE, strlen(proto)+1); + strcpy(szCache, proto); + List_Insert(&cd->protoNameCache, szCache, index); + } + LeaveCriticalSection(&cd->csLock); + return szCache; +} + +// thread safe +char * contactDir_Proto_Get(contactDir * cd, HANDLE hContact) +{ + char * szCache = 0; + int index = 0; + contactEntry e; + e.hContact=hContact; + e.proto=""; + e.id=""; + EnterCriticalSection(&cd->csLock); + if ( List_GetIndex(&cd->protoCache, &e, &index) ) { + contactEntry * p = cd->protoCache.items[index]; + szCache = p->proto; + } + LeaveCriticalSection(&cd->csLock); + return szCache; +} + +// thread tolerant, if updating id dont pass proto, if updating proto dont pass id +void contactDir_Contact_Add(contactDir * cd, HANDLE hContact, char * proto, char * id) +{ + // if a contact is gonna exist anywhere it's going to be in the ->protoCache which has a key of hContact + // if id is not null then the contact should be indexed via the ->contactCache instead + if ( id == NULL ) { + int index = 0; + contactEntry e; + e.hContact=hContact; + e.proto = proto; + e.id = ""; + EnterCriticalSection(&cd->csLock); + if ( List_GetIndex(&cd->protoCache, &e, &index) ) { + contactEntry * p = cd->protoCache.items[index]; + // this hContact is in the cache, protcol changing? + p->proto = contactDir_Proto_Add(cd, proto); // just replace the old pointer + } else { + contactEntry * p = 0; + // this hContact isn't in the cache, add it + p = HeapAlloc(hCacheHeap, HEAP_NO_SERIALIZE, sizeof(contactEntry)); + p->proto = contactDir_Proto_Add(cd, proto); + p->id = 0; + p->hContact = hContact; + // add it + List_Insert(&cd->protoCache, p, index); + } + LeaveCriticalSection(&cd->csLock); + } else { + // this contact HAS to be in ->protoCache since it was added during startup + // need to find the contactEntry* that should already exist for it + } //if +} + +// only expected to be called at startup. +void contactDir_Proto_Walk(contactDir * cd) +{ + HANDLE hContact; + char buf[128]; + DBCONTACTGETSETTING gsProto; + DBVARIANT dbvProto; + // setup the read structure + gsProto.szModule="Protocol"; + gsProto.szSetting="p"; + gsProto.pValue = &dbvProto; + // this might not work + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while ( hContact ) { + // and how we'll get the reset + dbvProto.type=DBVT_ASCIIZ; + dbvProto.pszVal = (char *) &buf; + dbvProto.cchVal = SIZEOF(buf); + // figure out what hContact/Protocol/p is + if ( CallService(MS_DB_CONTACT_GETSETTINGSTATIC,(WPARAM)hContact, (LPARAM)&gsProto) == 0 ) { + contactDir_Contact_Add(cd, hContact, buf, NULL); + } + // find next + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } +} + +// ctor/dtor + +void contactDir_Init(contactDir * cd) +{ + InitializeCriticalSection(&cd->csLock); + cd->contactCache.increment=50; + cd->contactCache.sortFunc=contactCacheCompare; + cd->protoCache.increment=50; + cd->protoCache.sortFunc=protoCacheCompare; + cd->protoNameCache.increment=5; + cd->protoNameCache.sortFunc=protoNameCacheCompare; + // build a list of all hContact's and what proto's they are on + contactDir_Proto_Walk(cd); +} + +void contactDir_Deinit(contactDir * cd) +{ + List_Destroy(&cd->contactCache); + List_Destroy(&cd->protoCache); + List_Destroy(&cd->protoNameCache); + DeleteCriticalSection(&cd->csLock); +} + +static int contactDirGetProto(WPARAM wParam, LPARAM lParam) +{ + return (int) contactDir_Proto_Get(&condir,(HANDLE)wParam); +} + +#endif + +void InitContactDir(void) +{ + return; + //contactDir_Init(&condir); + //CreateServiceFunction(MS_PROTODIR_PROTOFROMCONTACT, contactDirGetProto); +} + + +void UninitContactDir(void) +{ + return; +#if 0 + { + int j; + for ( j = 0; j< condir.protoCache.realCount; j++) { + char buf[128]; + contactEntry * p = condir.protoCache.items[j]; + mir_snprintf(buf,SIZEOF(buf)," [%s] %s @ %x \n", p->proto, p->id ? p->id : "", p->hContact); + OutputDebugString(buf); + } + } + contactDir_Deinit(&condir); +#endif +} diff --git a/src/modules/protocols/protoint.cpp b/src/modules/protocols/protoint.cpp new file mode 100644 index 0000000000..dec59cce98 --- /dev/null +++ b/src/modules/protocols/protoint.cpp @@ -0,0 +1,271 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2009 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +char** __fastcall Proto_FilesMatrixA( TCHAR **files ); +void FreeFilesMatrix( TCHAR ***files ); + +INT_PTR __fastcall MyCallProtoService( const char *szModule, const char *szService, WPARAM wParam, LPARAM lParam ) +{ + char str[MAXMODULELABELLENGTH]; + mir_snprintf( str, sizeof(str), "%s%s", szModule, szService ); + return CallService(str,wParam,lParam); +} + +struct DEFAULT_PROTO_INTERFACE : public PROTO_INTERFACE +{ + HANDLE __cdecl AddToList( int flags, PROTOSEARCHRESULT* psr ) + { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_ADDTOLIST, flags, (LPARAM)psr ); + } + + HANDLE __cdecl AddToListByEvent( int flags, int iContact, HANDLE hDbEvent ) + { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_ADDTOLISTBYEVENT, MAKELONG(flags, iContact), (LPARAM)hDbEvent ); + } + + int __cdecl Authorize( HANDLE hContact ) + { return ( int )MyCallProtoService( m_szModuleName, PS_AUTHALLOW, (WPARAM)hContact, 0 ); + } + + int __cdecl AuthDeny( HANDLE hContact, const TCHAR* szReason ) + { return ( int )MyCallProtoService( m_szModuleName, PS_AUTHDENY, (WPARAM)hContact, (LPARAM)StrConvA(szReason)); + } + + int __cdecl AuthRecv( HANDLE hContact, PROTORECVEVENT* evt ) + { CCSDATA ccs = { hContact, PSR_AUTH, 0, (LPARAM)evt }; + return ( int )MyCallProtoService( m_szModuleName, PSR_AUTH, 0, (LPARAM)&ccs ); + } + + int __cdecl AuthRequest( HANDLE hContact, const TCHAR* szMessage ) + { CCSDATA ccs = { hContact, PSS_AUTHREQUEST, 0, (LPARAM)szMessage }; + ccs.lParam = ( LPARAM )mir_t2a( szMessage ); + int res = ( int )MyCallProtoService( m_szModuleName, PSS_AUTHREQUEST, 0, (LPARAM)&ccs ); + mir_free(( char* )ccs.lParam ); + return res; + } + + HANDLE __cdecl ChangeInfo( int iInfoType, void* pInfoData ) + { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_CHANGEINFO, iInfoType, ( LPARAM )pInfoData ); + } + + HANDLE __cdecl FileAllow( HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath ) + { CCSDATA ccs = { hContact, PSS_FILEALLOW, (WPARAM)hTransfer, (LPARAM)szPath }; +#ifdef _UNICODE + ccs.lParam = ( LPARAM )mir_t2a( szPath ); + HANDLE res = ( HANDLE )MyCallProtoService( m_szModuleName, PSS_FILEALLOW, 0, (LPARAM)&ccs ); + mir_free(( char* )ccs.lParam ); + return res; +#else + return ( HANDLE )MyCallProtoService( m_szModuleName, PSS_FILEALLOW, 0, (LPARAM)&ccs ); +#endif + } + + int __cdecl FileCancel( HANDLE hContact, HANDLE hTransfer ) + { CCSDATA ccs = { hContact, PSS_FILECANCEL, (WPARAM)hTransfer, 0 }; + return ( int )MyCallProtoService( m_szModuleName, PSS_FILECANCEL, 0, (LPARAM)&ccs ); + } + + int __cdecl FileDeny( HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason ) + { CCSDATA ccs = { hContact, PSS_FILEDENY, (WPARAM)hTransfer, (LPARAM)szReason }; +#ifdef _UNICODE + ccs.lParam = ( LPARAM )mir_t2a( szReason ); + int res = ( int )MyCallProtoService( m_szModuleName, PSS_FILEDENY, 0, (LPARAM)&ccs ); + mir_free(( char* )ccs.lParam ); + return res; +#else + return ( int )MyCallProtoService( m_szModuleName, PSS_FILEDENY, 0, (LPARAM)&ccs ); +#endif + } + + int __cdecl FileResume( HANDLE hTransfer, int* action, const PROTOCHAR** szFilename ) + { PROTOFILERESUME pfr = { *action, *szFilename }; +#ifdef _UNICODE + pfr.szFilename = ( PROTOCHAR* )mir_t2a( pfr.szFilename ); + int res = ( int )MyCallProtoService( m_szModuleName, PS_FILERESUME, ( WPARAM )hTransfer, ( LPARAM )&pfr); + mir_free(( PROTOCHAR* )*szFilename ); + *action = pfr.action; *szFilename = (PROTOCHAR*)pfr.szFilename; +#else + int res = ( int )MyCallProtoService( m_szModuleName, PS_FILERESUME, ( WPARAM )hTransfer, ( LPARAM )&pfr ); + *action = pfr.action; *szFilename = (PROTOCHAR*)pfr.szFilename; +#endif + return res; + } + + DWORD_PTR __cdecl GetCaps( int type, HANDLE hContact ) + { return ( DWORD_PTR )MyCallProtoService( m_szModuleName, PS_GETCAPS, type, (LPARAM)hContact ); + } + + HICON __cdecl GetIcon( int iconIndex ) + { return ( HICON )MyCallProtoService( m_szModuleName, PS_LOADICON, iconIndex, 0 ); + } + + int __cdecl GetInfo( HANDLE hContact, int flags ) + { CCSDATA ccs = { hContact, PSS_GETINFO, flags, 0 }; + return MyCallProtoService( m_szModuleName, PSS_GETINFO, 0, (LPARAM)&ccs ); + } + + HANDLE __cdecl SearchBasic( const PROTOCHAR* id ) + { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_BASICSEARCH, 0, ( LPARAM )StrConvA( id )); + } + + HANDLE __cdecl SearchByEmail( const PROTOCHAR* email ) + { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_SEARCHBYEMAIL, 0, ( LPARAM )StrConvA( email )); + } + + HANDLE __cdecl SearchByName( const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName ) + { PROTOSEARCHBYNAME psn; +#ifdef _UNICODE + psn.pszNick = ( PROTOCHAR* )mir_t2a( nick ); + psn.pszFirstName = ( PROTOCHAR* )mir_t2a( firstName ); + psn.pszLastName = ( PROTOCHAR* )mir_t2a( lastName ); + HANDLE res = ( HANDLE )MyCallProtoService( m_szModuleName, PS_SEARCHBYNAME, 0, ( LPARAM )&psn ); + mir_free( psn.pszNick ); + mir_free( psn.pszFirstName ); + mir_free( psn.pszLastName ); + return res; +#else + psn.pszNick = ( char* )nick; + psn.pszFirstName = ( char* )firstName; + psn.pszLastName = ( char* )lastName; + return ( HANDLE )MyCallProtoService( m_szModuleName, PS_SEARCHBYNAME, 0, ( LPARAM )&psn ); +#endif + } + + HWND __cdecl SearchAdvanced( HWND owner ) + { return ( HWND )MyCallProtoService( m_szModuleName, PS_SEARCHBYADVANCED, 0, ( LPARAM )owner ); + } + + HWND __cdecl CreateExtendedSearchUI( HWND owner ) + { return ( HWND )MyCallProtoService( m_szModuleName, PS_CREATEADVSEARCHUI, 0, ( LPARAM )owner ); + } + + int __cdecl RecvContacts( HANDLE hContact, PROTORECVEVENT* evt ) + { CCSDATA ccs = { hContact, PSR_CONTACTS, 0, (LPARAM)evt }; + return ( int )MyCallProtoService( m_szModuleName, PSR_CONTACTS, 0, (LPARAM)&ccs ); + } + + int __cdecl RecvFile( HANDLE hContact, PROTOFILEEVENT* evt ) + { CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)evt }; + return MyCallProtoService( m_szModuleName, PSR_FILE, 0, (LPARAM)&ccs ); + } + + int __cdecl RecvMsg( HANDLE hContact, PROTORECVEVENT* evt ) + { CCSDATA ccs = { hContact, PSR_MESSAGE, 0, (LPARAM)evt }; + return ( int )MyCallProtoService( m_szModuleName, PSR_MESSAGE, 0, (LPARAM)&ccs ); + } + + int __cdecl RecvUrl( HANDLE hContact, PROTORECVEVENT* evt ) + { CCSDATA ccs = { hContact, PSR_URL, 0, (LPARAM)evt }; + return ( int )MyCallProtoService( m_szModuleName, PSR_URL, 0, (LPARAM)&ccs ); + } + + int __cdecl SendContacts( HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList ) + { CCSDATA ccs = { hContact, PSS_CONTACTS, MAKEWPARAM(flags,nContacts), (LPARAM)hContactsList }; + return ( int )MyCallProtoService( m_szModuleName, PSS_CONTACTS, 0, (LPARAM)&ccs ); + } + + HANDLE __cdecl SendFile( HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles ) + { CCSDATA ccs = { hContact, PSS_FILE, (WPARAM)szDescription, (LPARAM)ppszFiles }; +#ifdef _UNICODE + ccs.wParam = ( WPARAM )mir_t2a( szDescription ); + ccs.lParam = ( LPARAM )Proto_FilesMatrixA( ppszFiles ); + HANDLE res = ( HANDLE )MyCallProtoService( m_szModuleName, PSS_FILE, 0, ( LPARAM )&ccs ); + if ( res == 0 ) FreeFilesMatrix(( TCHAR*** )&ccs.lParam ); + mir_free(( char* )ccs.wParam ); + return res; +#else + return ( HANDLE )MyCallProtoService( m_szModuleName, PSS_FILE, 0, (LPARAM)&ccs ); +#endif + } + + int __cdecl SendMsg( HANDLE hContact, int flags, const char* msg ) + { CCSDATA ccs = { hContact, PSS_MESSAGE, flags, (LPARAM)msg }; + return ( int )MyCallProtoService( m_szModuleName, PSS_MESSAGE, 0, (LPARAM)&ccs ); + } + + int __cdecl SendUrl( HANDLE hContact, int flags, const char* url ) + { CCSDATA ccs = { hContact, PSS_URL, flags, (LPARAM)url }; + return ( int )MyCallProtoService( m_szModuleName, PSS_URL, 0, (LPARAM)&ccs ); + } + + int __cdecl SetApparentMode( HANDLE hContact, int mode ) + { CCSDATA ccs = { hContact, PSS_SETAPPARENTMODE, mode, 0 }; + return ( int )MyCallProtoService( m_szModuleName, PSS_SETAPPARENTMODE, 0, (LPARAM)&ccs ); + } + + int __cdecl SetStatus( int iNewStatus ) + { return ( int )MyCallProtoService( m_szModuleName, PS_SETSTATUS, iNewStatus, 0 ); + } + + HANDLE __cdecl GetAwayMsg( HANDLE hContact ) + { CCSDATA ccs = { hContact, PSS_GETAWAYMSG, 0, 0 }; + return ( HANDLE )MyCallProtoService( m_szModuleName, PSS_GETAWAYMSG, 0, (LPARAM)&ccs ); + } + + int __cdecl RecvAwayMsg( HANDLE hContact, int statusMode, PROTORECVEVENT* evt ) + { CCSDATA ccs = { hContact, PSR_AWAYMSG, statusMode, (LPARAM)evt }; + return ( int )MyCallProtoService( m_szModuleName, PSR_AWAYMSG, 0, (LPARAM)&ccs ); + } + + int __cdecl SendAwayMsg( HANDLE hContact, HANDLE hProcess, const char* msg ) + { CCSDATA ccs = { hContact, PSS_AWAYMSG, (WPARAM)hProcess, (LPARAM)msg }; + return ( int )MyCallProtoService( m_szModuleName, PSS_AWAYMSG, 0, (LPARAM)&ccs ); + } + + int __cdecl SetAwayMsg( int iStatus, const TCHAR* msg ) + { return ( int )MyCallProtoService( m_szModuleName, PS_SETAWAYMSG, iStatus, (LPARAM)StrConvA(msg)); + } + + int __cdecl UserIsTyping( HANDLE hContact, int type ) + { CCSDATA ccs = { hContact, PSS_USERISTYPING, (WPARAM)hContact, type }; + return MyCallProtoService( m_szModuleName, PSS_USERISTYPING, 0, (LPARAM)&ccs ); + } + + int __cdecl OnEvent( PROTOEVENTTYPE, WPARAM, LPARAM ) + { + return 0; + } +}; + +// creates the default protocol container for compatibility with the old plugins + +PROTO_INTERFACE* AddDefaultAccount( const char* szProtoName ) +{ + PROTO_INTERFACE* ppi = new DEFAULT_PROTO_INTERFACE; + if ( ppi != NULL ) { + ppi->m_iVersion = 1; + ppi->m_szModuleName = mir_strdup( szProtoName ); + ppi->m_szProtoName = mir_strdup( szProtoName ); + ppi->m_tszUserName = mir_a2t( szProtoName ); + } + return ppi; +} + +int FreeDefaultAccount( PROTO_INTERFACE* ppi ) +{ + mir_free( ppi->m_szModuleName ); + mir_free( ppi->m_szProtoName ); + mir_free( ppi->m_tszUserName ); + delete ppi; + return 0; +} diff --git a/src/modules/protocols/protoopts.cpp b/src/modules/protocols/protoopts.cpp new file mode 100644 index 0000000000..3d9c0a2b36 --- /dev/null +++ b/src/modules/protocols/protoopts.cpp @@ -0,0 +1,1084 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2009 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +#define LBN_MY_CHECK 0x1000 +#define LBN_MY_RENAME 0x1001 + +#define WM_MY_REFRESH (WM_USER+0x1000) +#define WM_MY_RENAME (WM_USER+0x1001) + +INT_PTR Proto_EnumProtocols( WPARAM, LPARAM ); +bool CheckProtocolOrder(void); + +#define errMsg \ +"WARNING! The account is going to be deleted. It means that all its \ +settings, contacts and histories will be also erased.\n\n\ +Are you absolutely sure?" + +#define upgradeMsg \ +"Your account was successfully upgraded. \ +To activate it, restart of Miranda is needed.\n\n\ +If you want to restart Miranda now, press Yes, if you want to upgrade another account, press No" + +#define legacyMsg \ +"This account uses legacy protocol plugin. \ +Use Miranda IM options dialogs to change it's preferences." + +#define welcomeMsg \ +"Welcome to Miranda IM's account manager!\n \ +Here you can set up your IM accounts.\n\n \ +Select an account from the list on the left to see the available options. \ +Alternatively, just click on the Plus sign underneath the list to set up a new IM account." + +static HWND hAccMgr = NULL; + +extern HANDLE hAccListChanged; + +int UnloadPlugin( TCHAR* buf, int bufLen ); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Account edit form +// Gets PROTOACCOUNT* as a parameter, or NULL to edit a new one + +typedef struct +{ + int action; + PROTOACCOUNT* pa; +} + AccFormDlgParam; + +static INT_PTR CALLBACK AccFormDlgProc(HWND hwndDlg,UINT message, WPARAM wParam, LPARAM lParam) +{ + switch( message ) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + { + PROTOCOLDESCRIPTOR** proto; + int protoCount, i, cnt = 0; + Proto_EnumProtocols(( WPARAM )&protoCount, ( LPARAM )&proto ); + for ( i=0; i < protoCount; i++ ) { + PROTOCOLDESCRIPTOR* pd = proto[i]; + if ( pd->type == PROTOTYPE_PROTOCOL && pd->cbSize == sizeof( *pd )) { + SendDlgItemMessageA( hwndDlg, IDC_PROTOTYPECOMBO, CB_ADDSTRING, 0, (LPARAM)proto[i]->szName ); + ++cnt; + } + } + SendDlgItemMessage( hwndDlg, IDC_PROTOTYPECOMBO, CB_SETCURSEL, 0, 0 ); + EnableWindow( GetDlgItem( hwndDlg, IDOK ), cnt != 0 ); + + SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam ); + AccFormDlgParam* param = ( AccFormDlgParam* )lParam; + + if ( param->action == PRAC_ADDED ) // new account + SetWindowText( hwndDlg, TranslateT( "Create new account" )); + else { + TCHAR str[200]; + if ( param->action == PRAC_CHANGED ) { // update + EnableWindow( GetDlgItem( hwndDlg, IDC_PROTOTYPECOMBO ), FALSE ); + mir_sntprintf( str, SIZEOF(str), _T("%s: %s"), TranslateT( "Editing account" ), param->pa->tszAccountName ); + } + else mir_sntprintf( str, SIZEOF(str), _T("%s: %s"), TranslateT( "Upgrading account" ), param->pa->tszAccountName ); + + SetWindowText( hwndDlg, str ); + SetDlgItemText( hwndDlg, IDC_ACCNAME, param->pa->tszAccountName ); + SetDlgItemTextA( hwndDlg, IDC_ACCINTERNALNAME, param->pa->szModuleName ); + SendDlgItemMessageA( hwndDlg, IDC_PROTOTYPECOMBO, CB_SELECTSTRING, -1, (LPARAM)param->pa->szProtoName ); + + EnableWindow( GetDlgItem( hwndDlg, IDC_ACCINTERNALNAME ), FALSE ); + } + SendDlgItemMessage( hwndDlg, IDC_ACCINTERNALNAME, EM_LIMITTEXT, 40, 0 ); + } + return TRUE; + + case WM_COMMAND: + switch( LOWORD(wParam)) { + case IDOK: + { + AccFormDlgParam* param = ( AccFormDlgParam* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA ); + PROTOACCOUNT* pa = param->pa; + + if ( param->action == PRAC_ADDED ) { + char buf[200]; + GetDlgItemTextA( hwndDlg, IDC_ACCINTERNALNAME, buf, SIZEOF( buf )); + rtrim( buf ); + if ( buf[0] ) { + for (int i = 0; i < accounts.getCount(); ++i) + if (_stricmp(buf, accounts[i]->szModuleName) == 0) + return FALSE; + } } + + switch( param->action ) { + case PRAC_UPGRADED: + { + int idx; + BOOL oldProto = pa->bOldProto; + TCHAR szPlugin[MAX_PATH]; + mir_sntprintf(szPlugin, SIZEOF(szPlugin), _T("%s.dll"), StrConvT(pa->szProtoName)); + idx = accounts.getIndex(pa); + UnloadAccount(pa, false, false); + accounts.remove(idx); + if (oldProto && UnloadPlugin(szPlugin, SIZEOF(szPlugin))) + { + TCHAR szNewName[MAX_PATH]; + mir_sntprintf(szNewName, SIZEOF(szNewName), _T("%s~"), szPlugin); + MoveFile(szPlugin, szNewName); + } + } + // fall through + + case PRAC_ADDED: + pa = (PROTOACCOUNT*)mir_calloc( sizeof( PROTOACCOUNT )); + pa->cbSize = sizeof( PROTOACCOUNT ); + pa->bIsEnabled = TRUE; + pa->bIsVisible = TRUE; + + pa->iOrder = accounts.getCount(); + pa->type = PROTOTYPE_PROTOCOL; + break; + } + { + TCHAR buf[256]; + GetDlgItemText( hwndDlg, IDC_ACCNAME, buf, SIZEOF( buf )); + mir_free(pa->tszAccountName); + pa->tszAccountName = mir_tstrdup( buf ); + } + if ( param->action == PRAC_ADDED || param->action == PRAC_UPGRADED ) + { + char buf[200]; + GetDlgItemTextA( hwndDlg, IDC_PROTOTYPECOMBO, buf, SIZEOF( buf )); + pa->szProtoName = mir_strdup( buf ); + GetDlgItemTextA( hwndDlg, IDC_ACCINTERNALNAME, buf, SIZEOF( buf )); + rtrim( buf ); + if ( buf[0] == 0 ) { + int count = 1; + for( ;; ) { + DBVARIANT dbv; + mir_snprintf( buf, SIZEOF(buf), "%s_%d", pa->szProtoName, count++ ); + if ( DBGetContactSettingString( NULL, buf, "AM_BaseProto", &dbv )) + break; + DBFreeVariant( &dbv ); + } } + pa->szModuleName = mir_strdup( buf ); + + if ( !pa->tszAccountName[0] ) { + mir_free(pa->tszAccountName); + pa->tszAccountName = mir_a2t(buf); + } + + DBWriteContactSettingString( NULL, pa->szModuleName, "AM_BaseProto", pa->szProtoName ); + accounts.insert( pa ); + + if ( ActivateAccount( pa )) { + pa->ppro->OnEvent( EV_PROTO_ONLOAD, 0, 0 ); + if (!DBGetContactSettingByte(NULL, "CList", "MoveProtoMenus", FALSE)) + pa->ppro->OnEvent( EV_PROTO_ONMENU, 0, 0 ); + } + else pa->type = PROTOTYPE_DISPROTO; + } + + WriteDbAccounts(); + NotifyEventHooks( hAccListChanged, param->action, ( LPARAM )pa ); + + SendMessage( GetParent(hwndDlg), WM_MY_REFRESH, 0, 0 ); + } + + EndDialog( hwndDlg, TRUE ); + break; + + case IDCANCEL: + EndDialog( hwndDlg, FALSE ); + break; + } + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Accounts manager + +struct TAccMgrData +{ + HFONT hfntTitle, hfntText; + int titleHeight, textHeight; + int selectedHeight, normalHeight; + int iSelected; +}; + +struct TAccListData +{ + WNDPROC oldWndProc; + int iItem; + RECT rcCheck; + + HWND hwndEdit; + WNDPROC oldEditProc; +}; + +static void sttClickButton(HWND hwndDlg, int idcButton) +{ + if (IsWindowEnabled(GetDlgItem(hwndDlg, idcButton))) + PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(idcButton, BN_CLICKED), (LPARAM)GetDlgItem(hwndDlg, idcButton)); +} + +static LRESULT CALLBACK sttEditSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_KEYDOWN: + switch (wParam) { + case VK_RETURN: + DestroyWindow(hwnd); + return 0; + + case VK_ESCAPE: + SetWindowLongPtr(hwnd, GWLP_WNDPROC, GetWindowLongPtr(hwnd, GWLP_USERDATA)); + DestroyWindow(hwnd); + return 0; + } + break; + + case WM_GETDLGCODE: + if (wParam == VK_RETURN || wParam == VK_ESCAPE) + return DLGC_WANTMESSAGE; + break; + + case WM_KILLFOCUS: + { + int length = GetWindowTextLength(hwnd) + 1; + TCHAR *str = ( TCHAR* )mir_alloc(sizeof(TCHAR) * length); + GetWindowText(hwnd, str, length); + SendMessage(GetParent(GetParent(hwnd)), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(GetParent(hwnd), GWL_ID), LBN_MY_RENAME), (LPARAM)str); + } + DestroyWindow(hwnd); + return 0; + } + return CallWindowProc((WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA), hwnd, msg, wParam, lParam); +} + +static LRESULT CALLBACK AccListWndProc(HWND hwnd,UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct TAccListData *dat = (struct TAccListData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if ( !dat ) + return DefWindowProc(hwnd, msg, wParam, lParam); + + switch (msg) { + case WM_LBUTTONDOWN: + { + POINT pt = {LOWORD(lParam), HIWORD(lParam)}; + int iItem = LOWORD(SendMessage(hwnd, LB_ITEMFROMPOINT, 0, lParam)); + ListBox_GetItemRect(hwnd, iItem, &dat->rcCheck); + + dat->rcCheck.right = dat->rcCheck.left + GetSystemMetrics(SM_CXSMICON) + 4; + dat->rcCheck.bottom = dat->rcCheck.top + GetSystemMetrics(SM_CYSMICON) + 4; + if (PtInRect(&dat->rcCheck, pt)) + dat->iItem = iItem; + else + dat->iItem = -1; + } + break; + + case WM_LBUTTONUP: + { + POINT pt = {LOWORD(lParam), HIWORD(lParam)}; + if ((dat->iItem >= 0) && PtInRect(&dat->rcCheck, pt)) + PostMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(hwnd, GWL_ID), LBN_MY_CHECK), (LPARAM)dat->iItem); + dat->iItem = -1; + } + break; + + case WM_CHAR: + if (wParam == ' ') { + int iItem = ListBox_GetCurSel(hwnd); + if (iItem >= 0) + PostMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(hwnd, GWL_ID), LBN_MY_CHECK), (LPARAM)iItem); + return 0; + } + + if (wParam == 10 /* enter */) + return 0; + + break; + + case WM_GETDLGCODE: + if (wParam == VK_RETURN) + return DLGC_WANTMESSAGE; + break; + + case WM_MY_RENAME: + { + RECT rc; + struct TAccMgrData *parentDat = (struct TAccMgrData *)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); + PROTOACCOUNT *pa = (PROTOACCOUNT *)ListBox_GetItemData(hwnd, ListBox_GetCurSel(hwnd)); + if (!pa || pa->bOldProto || pa->bDynDisabled) + return 0; + + ListBox_GetItemRect(hwnd, ListBox_GetCurSel(hwnd), &rc); + rc.left += 2*GetSystemMetrics(SM_CXSMICON) + 4; + rc.bottom = rc.top + max(GetSystemMetrics(SM_CXSMICON), parentDat->titleHeight) + 4 - 1; + ++rc.top; --rc.right; + + dat->hwndEdit = CreateWindow(_T("EDIT"), pa->tszAccountName, WS_CHILD|WS_BORDER|ES_AUTOHSCROLL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, hwnd, NULL, hMirandaInst, NULL); + SetWindowLongPtr(dat->hwndEdit, GWLP_USERDATA, SetWindowLongPtr(dat->hwndEdit, GWLP_WNDPROC, (LONG_PTR)sttEditSubclassProc)); + SendMessage(dat->hwndEdit, WM_SETFONT, (WPARAM)parentDat->hfntTitle, 0); + SendMessage(dat->hwndEdit, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN|EC_USEFONTINFO, 0); + SendMessage(dat->hwndEdit, EM_SETSEL, 0, (LPARAM) (-1)); + ShowWindow(dat->hwndEdit, SW_SHOW); + } + SetFocus(dat->hwndEdit); + break; + + case WM_KEYDOWN: + switch (wParam) { + case VK_F2: + PostMessage(hwnd, WM_MY_RENAME, 0, 0); + return 0; + + case VK_INSERT: + sttClickButton(GetParent(hwnd), IDC_ADD); + return 0; + + case VK_DELETE: + sttClickButton(GetParent(hwnd), IDC_REMOVE); + return 0; + + case VK_RETURN: + if (GetAsyncKeyState(VK_CONTROL)) + sttClickButton(GetParent(hwnd), IDC_EDIT); + else + sttClickButton(GetParent(hwnd), IDOK); + return 0; + } + break; + } + + return CallWindowProc(dat->oldWndProc, hwnd, msg, wParam, lParam); +} + +static void sttSubclassAccList(HWND hwnd, BOOL subclass) +{ + if (subclass) { + struct TAccListData *dat = (struct TAccListData *)mir_alloc(sizeof(struct TAccListData)); + dat->iItem = -1; + dat->oldWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)dat); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)AccListWndProc); + } + else { + struct TAccListData *dat = (struct TAccListData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)dat->oldWndProc); + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + mir_free(dat); +} } + +static void sttSelectItem(struct TAccMgrData *dat, HWND hwndList, int iItem) +{ + if ((dat->iSelected != iItem) && (dat->iSelected >= 0)) + ListBox_SetItemHeight(hwndList, dat->iSelected, dat->normalHeight); + + dat->iSelected = iItem; + ListBox_SetItemHeight(hwndList, dat->iSelected, dat->selectedHeight); + RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE); +} + +static void sttUpdateAccountInfo(HWND hwndDlg, struct TAccMgrData *dat) +{ + HWND hwndList = GetDlgItem(hwndDlg, IDC_ACCLIST); + int curSel = ListBox_GetCurSel( hwndList ); + if ( curSel != LB_ERR ) { + HWND hwnd; + char svc[MAXMODULELABELLENGTH]; + + PROTOACCOUNT *pa = (PROTOACCOUNT *)ListBox_GetItemData(hwndList, curSel); + if ( pa ) { + EnableWindow( GetDlgItem( hwndDlg, IDC_UPGRADE ), pa->bOldProto || pa->bDynDisabled ); + EnableWindow( GetDlgItem( hwndDlg, IDC_EDIT ), !pa->bOldProto && !pa->bDynDisabled ); + EnableWindow( GetDlgItem( hwndDlg, IDC_REMOVE ), TRUE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_OPTIONS ), pa->ppro != 0 ); + + if ( dat->iSelected >= 0 ) { + PROTOACCOUNT *pa_old = (PROTOACCOUNT *)ListBox_GetItemData(hwndList, dat->iSelected); + if (pa_old && pa_old != pa && pa_old->hwndAccMgrUI) + ShowWindow(pa_old->hwndAccMgrUI, SW_HIDE); + } + + if ( pa->hwndAccMgrUI ) { + ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_HIDE); + ShowWindow(pa->hwndAccMgrUI, SW_SHOW); + } + else if ( !pa->ppro ) { + ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_SHOW); + SetWindowText(GetDlgItem(hwndDlg, IDC_TXT_INFO), TranslateT("Account is disabled. Please activate it to access options.")); + } + else { + mir_snprintf(svc, SIZEOF(svc), "%s%s", pa->szModuleName, PS_CREATEACCMGRUI); + hwnd = (HWND)CallService(svc, 0, (LPARAM)hwndDlg); + if (hwnd && (hwnd != (HWND)CALLSERVICE_NOTFOUND)) { + RECT rc; + + ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_HIDE); + + GetWindowRect(GetDlgItem(hwndDlg, IDC_TXT_INFO), &rc); + MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rc, 2); + SetWindowPos(hwnd, hwndList, rc.left, rc.top, 0, 0, SWP_NOSIZE|SWP_SHOWWINDOW); + + pa->hwndAccMgrUI = hwnd; + } + else { + ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_SHOW); + SetWindowText(GetDlgItem(hwndDlg, IDC_TXT_INFO), TranslateT(legacyMsg)); + } } + return; + } } + + EnableWindow( GetDlgItem( hwndDlg, IDC_UPGRADE ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_EDIT ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_REMOVE ), FALSE ); + EnableWindow( GetDlgItem( hwndDlg, IDC_OPTIONS ), FALSE ); + + ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_SHOW); + SetWindowText(GetDlgItem(hwndDlg, IDC_TXT_INFO), TranslateT(welcomeMsg)); +} + +INT_PTR CALLBACK AccMgrDlgProc(HWND hwndDlg,UINT message, WPARAM wParam, LPARAM lParam) +{ + struct TAccMgrData *dat = (struct TAccMgrData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch(message) { + case WM_INITDIALOG: + { + TAccMgrData *dat = (TAccMgrData *)mir_alloc(sizeof(TAccMgrData)); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + + TranslateDialogDefault(hwndDlg); + Window_SetIcon_IcoLib( hwndDlg, SKINICON_OTHER_ACCMGR ); + + Button_SetIcon_IcoLib(hwndDlg, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("New account")); + Button_SetIcon_IcoLib(hwndDlg, IDC_EDIT, SKINICON_OTHER_RENAME, LPGEN("Edit")); + Button_SetIcon_IcoLib(hwndDlg, IDC_REMOVE, SKINICON_OTHER_DELETE, LPGEN("Remove account")); + Button_SetIcon_IcoLib(hwndDlg, IDC_OPTIONS, SKINICON_OTHER_OPTIONS, LPGEN( "Configure...")); + Button_SetIcon_IcoLib(hwndDlg, IDC_UPGRADE, SKINICON_OTHER_ACCMGR, LPGEN("Upgrade account")); + + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_OPTIONS), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_UPGRADE), FALSE); + + { + LOGFONT lf; + HDC hdc; + HFONT hfnt; + TEXTMETRIC tm; + + GetObject((HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0), sizeof(lf), &lf); + dat->hfntText = CreateFontIndirect(&lf); + + GetObject((HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0), sizeof(lf), &lf); + lf.lfWeight = FW_BOLD; + dat->hfntTitle = CreateFontIndirect(&lf); + + hdc = GetDC(hwndDlg); + hfnt = ( HFONT )SelectObject(hdc, dat->hfntTitle); + GetTextMetrics(hdc, &tm); + dat->titleHeight = tm.tmHeight; + SelectObject(hdc, dat->hfntText); + GetTextMetrics(hdc, &tm); + dat->textHeight = tm.tmHeight; + SelectObject(hdc, hfnt); + ReleaseDC(hwndDlg, hdc); + + dat->normalHeight = 4 + max(dat->titleHeight, GetSystemMetrics(SM_CYSMICON)); + dat->selectedHeight = dat->normalHeight + 4 + 2 * dat->textHeight; + + SendDlgItemMessage(hwndDlg, IDC_NAME, WM_SETFONT, (WPARAM)dat->hfntTitle, 0); + SendDlgItemMessage(hwndDlg, IDC_TXT_ACCOUNT, WM_SETFONT, (WPARAM)dat->hfntTitle, 0); + SendDlgItemMessage(hwndDlg, IDC_TXT_ADDITIONAL, WM_SETFONT, (WPARAM)dat->hfntTitle, 0); + } + + dat->iSelected = -1; + sttSubclassAccList(GetDlgItem(hwndDlg, IDC_ACCLIST), TRUE); + SendMessage( hwndDlg, WM_MY_REFRESH, 0, 0 ); + + Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, "AccMgr", ""); + } + return TRUE; + + case WM_CTLCOLORSTATIC: + switch ( GetDlgCtrlID(( HWND )lParam )) { + case IDC_WHITERECT: + case IDC_NAME: + SetBkColor(( HDC )wParam, GetSysColor( COLOR_WINDOW )); + return ( INT_PTR )GetSysColorBrush( COLOR_WINDOW ); + } + break; + + case WM_MEASUREITEM: + { + LPMEASUREITEMSTRUCT lps = (LPMEASUREITEMSTRUCT)lParam; + PROTOACCOUNT *acc = (PROTOACCOUNT *)lps->itemData; + + if ((lps->CtlID != IDC_ACCLIST) || !acc) + break; + + lps->itemWidth = 10; + lps->itemHeight = dat->normalHeight; + } + return TRUE; + + case WM_DRAWITEM: + { + int tmp, size, length; + TCHAR *text; + HICON hIcon; + HBRUSH hbrBack; + SIZE sz; + + int cxIcon = GetSystemMetrics(SM_CXSMICON); + int cyIcon = GetSystemMetrics(SM_CYSMICON); + + LPDRAWITEMSTRUCT lps = (LPDRAWITEMSTRUCT)lParam; + PROTOACCOUNT *acc = (PROTOACCOUNT *)lps->itemData; + + if ((lps->CtlID != IDC_ACCLIST) || (lps->itemID == -1) || !acc) + break; + + SetBkMode(lps->hDC, TRANSPARENT); + if (lps->itemState & ODS_SELECTED) { + hbrBack = GetSysColorBrush(COLOR_HIGHLIGHT); + SetTextColor(lps->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); + } + else { + hbrBack = GetSysColorBrush(COLOR_WINDOW); + SetTextColor(lps->hDC, GetSysColor(COLOR_WINDOWTEXT)); + } + FillRect(lps->hDC, &lps->rcItem, hbrBack); + + lps->rcItem.left += 2; + lps->rcItem.top += 2; + lps->rcItem.bottom -= 2; + + if ( acc->bOldProto ) + tmp = SKINICON_OTHER_ON; + else if ( acc->bDynDisabled ) + tmp = SKINICON_OTHER_OFF; + else + tmp = acc->bIsEnabled ? SKINICON_OTHER_TICK : SKINICON_OTHER_NOTICK; + + hIcon = LoadSkinnedIcon(tmp); + DrawIconEx(lps->hDC, lps->rcItem.left, lps->rcItem.top, hIcon, cxIcon, cyIcon, 0, hbrBack, DI_NORMAL); + IconLib_ReleaseIcon(hIcon, 0); + + lps->rcItem.left += cxIcon + 2; + + if (acc->ppro) { + hIcon = acc->ppro->GetIcon( PLI_PROTOCOL | PLIF_SMALL ); + DrawIconEx(lps->hDC, lps->rcItem.left, lps->rcItem.top, hIcon, cxIcon, cyIcon, 0, hbrBack, DI_NORMAL); + DestroyIcon(hIcon); + } + lps->rcItem.left += cxIcon + 2; + + length = SendDlgItemMessage(hwndDlg, IDC_ACCLIST, LB_GETTEXTLEN, lps->itemID, 0); + size = max(length+1, 256); + text = (TCHAR *)_alloca(sizeof(TCHAR) * size); + SendDlgItemMessage(hwndDlg, IDC_ACCLIST, LB_GETTEXT, lps->itemID, (LPARAM)text); + + SelectObject(lps->hDC, dat->hfntTitle); + tmp = lps->rcItem.bottom; + lps->rcItem.bottom = lps->rcItem.top + max(cyIcon, dat->titleHeight); + DrawText(lps->hDC, text, -1, &lps->rcItem, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS|DT_VCENTER); + lps->rcItem.bottom = tmp; + GetTextExtentPoint32(lps->hDC, text, length, &sz); + lps->rcItem.top += max(cxIcon, sz.cy) + 2; + + if (lps->itemID == (unsigned)dat->iSelected) { + SelectObject(lps->hDC, dat->hfntText); + mir_sntprintf(text, size, _T("%s: ") _T(TCHAR_STR_PARAM), TranslateT("Protocol"), acc->szProtoName); + length = lstrlen(text); + DrawText(lps->hDC, text, -1, &lps->rcItem, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS); + GetTextExtentPoint32(lps->hDC, text, length, &sz); + lps->rcItem.top += sz.cy + 2; + + if (acc->ppro && Proto_IsProtocolLoaded(acc->szProtoName)) { + char *szIdName; + TCHAR *tszIdName; + CONTACTINFO ci = { 0 }; + + szIdName = (char *)acc->ppro->GetCaps( PFLAG_UNIQUEIDTEXT, 0 ); + tszIdName = szIdName ? mir_a2t(szIdName) : mir_tstrdup(TranslateT("Account ID")); + + ci.cbSize = sizeof(ci); + ci.hContact = NULL; + ci.szProto = acc->szModuleName; + ci.dwFlag = CNF_UNIQUEID | CNF_TCHAR; + if ( !CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) { + switch (ci.type) { + case CNFT_ASCIIZ: + mir_sntprintf( text, size, _T("%s: %s"), tszIdName, ci.pszVal ); + mir_free(ci.pszVal); + break; + case CNFT_DWORD: + mir_sntprintf( text, size, _T("%s: %d"), tszIdName, ci.dVal ); + break; + } + } + else mir_sntprintf(text, size, _T("%s: %s"), tszIdName, TranslateT("")); + mir_free(tszIdName); + } + else mir_sntprintf(text, size, TranslateT("Protocol is not loaded.")); + + length = lstrlen(text); + DrawText(lps->hDC, text, -1, &lps->rcItem, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS); + GetTextExtentPoint32(lps->hDC, text, length, &sz); + lps->rcItem.top += sz.cy + 2; + } } + return TRUE; + + case WM_MY_REFRESH: + { + HWND hList = GetDlgItem(hwndDlg, IDC_ACCLIST); + int i = ListBox_GetCurSel(hList); + PROTOACCOUNT *acc = (i == LB_ERR) ? NULL : (PROTOACCOUNT *)ListBox_GetItemData(hList, i); + + dat->iSelected = -1; + SendMessage( hList, LB_RESETCONTENT, 0, 0 ); + for (i = 0; i < accounts.getCount(); ++i) { + int iItem = SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)accounts[i]->tszAccountName); + SendMessage(hList, LB_SETITEMDATA, iItem, (LPARAM)accounts[i]); + + if (accounts[i] == acc) + ListBox_SetCurSel(hList, iItem); + } + + dat->iSelected = ListBox_GetCurSel(hList); // -1 if error => nothing selected in our case + if (dat->iSelected >= 0) + sttSelectItem(dat, hList, dat->iSelected); + else if (acc && acc->hwndAccMgrUI) + ShowWindow(acc->hwndAccMgrUI, SW_HIDE); + + sttUpdateAccountInfo(hwndDlg, dat); + } + break; + + case WM_CONTEXTMENU: + if ( GetWindowLongPtr(( HWND )wParam, GWL_ID ) == IDC_ACCLIST ) { + HWND hwndList = GetDlgItem( hwndDlg, IDC_ACCLIST ); + POINT pt = { (signed short)LOWORD( lParam ), (signed short)HIWORD( lParam ) }; + int iItem = ListBox_GetCurSel( hwndList ); + + if (( pt.x == -1 ) && ( pt.y == -1 )) { + if (iItem != LB_ERR) { + RECT rc; + ListBox_GetItemRect( hwndList, iItem, &rc ); + pt.x = rc.left + GetSystemMetrics(SM_CXSMICON) + 4; + pt.y = rc.top + 4 + max(GetSystemMetrics(SM_CXSMICON), dat->titleHeight); + ClientToScreen( hwndList, &pt ); + } + } + else { + // menu was activated with mouse => find item under cursor & set focus to our control. + POINT ptItem = pt; + ScreenToClient( hwndList, &ptItem ); + iItem = (short)LOWORD(SendMessage(hwndList, LB_ITEMFROMPOINT, 0, MAKELPARAM(ptItem.x, ptItem.y))); + if (iItem != LB_ERR) + { + ListBox_SetCurSel(hwndList, iItem); + sttUpdateAccountInfo(hwndDlg, dat); + sttSelectItem(dat, hwndList, iItem); + SetFocus(hwndList); + } + } + + if ( iItem != LB_ERR ) { + PROTOACCOUNT* pa = (PROTOACCOUNT*)ListBox_GetItemData(hwndList, iItem); + HMENU hMenu = CreatePopupMenu(); + if ( !pa->bOldProto && !pa->bDynDisabled ) + AppendMenu(hMenu, MF_STRING, 1, TranslateT("Rename")); + + AppendMenu(hMenu, MF_STRING, 3, TranslateT("Delete")); + + if ( Proto_IsAccountEnabled( pa )) + AppendMenu(hMenu, MF_STRING, 4, TranslateT("Configure")); + + if ( pa->bOldProto || pa->bDynDisabled ) + AppendMenu(hMenu, MF_STRING, 5, TranslateT("Upgrade")); + + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendMenu(hMenu, MF_STRING, 0, TranslateT("Cancel")); + switch (TrackPopupMenu( hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL )) { + case 1: + PostMessage(hwndList, WM_MY_RENAME, 0, 0); + break; + + case 2: + sttClickButton(hwndDlg, IDC_EDIT); + break; + + case 3: + sttClickButton(hwndDlg, IDC_REMOVE); + break; + + case 4: + sttClickButton(hwndDlg, IDC_OPTIONS); + break; + + case 5: + sttClickButton(hwndDlg, IDC_UPGRADE); + break; + } + DestroyMenu( hMenu ); + } + } + break; + + case WM_COMMAND: + switch( LOWORD(wParam)) { + case IDC_ACCLIST: + { + HWND hwndList = GetDlgItem(hwndDlg, IDC_ACCLIST); + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + sttUpdateAccountInfo(hwndDlg, dat); + sttSelectItem(dat, hwndList, ListBox_GetCurSel(hwndList)); + SetFocus(hwndList); + break; + + case LBN_DBLCLK: + PostMessage(hwndList, WM_MY_RENAME, 0, 0); + break; + + case LBN_MY_CHECK: + { + PROTOACCOUNT *pa = (PROTOACCOUNT *)ListBox_GetItemData(hwndList, lParam); + if ( pa ) { + if ( pa->bOldProto || pa->bDynDisabled) + break; + + pa->bIsEnabled = !pa->bIsEnabled; + if ( pa->bIsEnabled ) { + if ( ActivateAccount( pa )) { + pa->ppro->OnEvent( EV_PROTO_ONLOAD, 0, 0 ); + if (!DBGetContactSettingByte(NULL, "CList", "MoveProtoMenus", FALSE)) + pa->ppro->OnEvent( EV_PROTO_ONMENU, 0, 0 ); + } + else pa->type = PROTOTYPE_DISPROTO; + } + else { + DWORD dwStatus = CallProtoService(pa->szModuleName, PS_GETSTATUS, 0, 0); + if (dwStatus >= ID_STATUS_ONLINE) { + if (IDCANCEL == ::MessageBox(hwndDlg, + TranslateT("Account is online. Disable account?"), + TranslateT("Accounts"), MB_OKCANCEL)) { + pa->bIsEnabled = 1; //stay enabled + } + } + + if ( !pa->bIsEnabled ) + DeactivateAccount( pa, true, false ); + } + + WriteDbAccounts(); + NotifyEventHooks( hAccListChanged, PRAC_CHECKED, ( LPARAM )pa ); + sttUpdateAccountInfo(hwndDlg, dat); + RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE); + } } + break; + + case LBN_MY_RENAME: + { + int iItem = ListBox_GetCurSel(hwndList); + PROTOACCOUNT *pa = (PROTOACCOUNT *)ListBox_GetItemData(hwndList, iItem); + if ( pa ) { + mir_free(pa->tszAccountName); + pa->tszAccountName = (TCHAR*)lParam; + WriteDbAccounts(); + NotifyEventHooks(hAccListChanged, PRAC_CHANGED, (LPARAM)pa); + + ListBox_DeleteString(hwndList, iItem); + iItem = ListBox_AddString(hwndList, pa->tszAccountName); + ListBox_SetItemData(hwndList, iItem, (LPARAM)pa); + ListBox_SetCurSel(hwndList, iItem); + + sttSelectItem(dat, hwndList, iItem); + + RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE); + } + else mir_free((TCHAR*)lParam); + } + break; + } } + break; + + case IDC_ADD: + { + AccFormDlgParam param = { PRAC_ADDED, NULL }; + if ( IDOK == DialogBoxParam( hMirandaInst, MAKEINTRESOURCE(IDD_ACCFORM), hwndDlg, AccFormDlgProc, (LPARAM)¶m )) + SendMessage( hwndDlg, WM_MY_REFRESH, 0, 0 ); + } + break; + + case IDC_EDIT: + { + HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST ); + int idx = ListBox_GetCurSel( hList ); + if ( idx != -1 ) + PostMessage(hList, WM_MY_RENAME, 0, 0); + } + break; + + case IDC_REMOVE: + { + HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST ); + int idx = ListBox_GetCurSel( hList ); + if ( idx != -1 ) { + PROTOACCOUNT* pa = ( PROTOACCOUNT* )ListBox_GetItemData( hList, idx ); + TCHAR buf[ 200 ]; + mir_sntprintf( buf, SIZEOF(buf), TranslateT( "Account %s is being deleted" ), pa->tszAccountName ); + if (pa->bOldProto) { + MessageBox( NULL, TranslateT( "You need to disable plugin to delete this account" ), buf, + MB_ICONERROR | MB_OK ); + break; + } + if ( IDYES == MessageBox( NULL, TranslateT( errMsg ), buf, MB_ICONSTOP | MB_DEFBUTTON2 | MB_YESNO )) { + // lock controls to avoid changes during remove process + ListBox_SetCurSel( hList, -1 ); + sttUpdateAccountInfo( hwndDlg, dat ); + EnableWindow( hList, FALSE ); + EnableWindow( GetDlgItem(hwndDlg, IDC_ADD), FALSE ); + + ListBox_SetItemData( hList, idx, 0 ); + + accounts.remove( pa ); + + CheckProtocolOrder(); + + WriteDbAccounts(); + NotifyEventHooks( hAccListChanged, PRAC_REMOVED, ( LPARAM )pa ); + + UnloadAccount( pa, true, true ); + SendMessage( hwndDlg, WM_MY_REFRESH, 0, 0 ); + + EnableWindow( hList, TRUE ); + EnableWindow( GetDlgItem(hwndDlg, IDC_ADD), TRUE ); + } } } + break; + + case IDC_OPTIONS: + { + HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST ); + int idx = ListBox_GetCurSel( hList ); + if ( idx != -1 ) { + PROTOACCOUNT* pa = ( PROTOACCOUNT* )ListBox_GetItemData( hList, idx ); + if ( pa->bOldProto ) { + OPENOPTIONSDIALOG ood; + ood.cbSize = sizeof(ood); + ood.pszGroup = "Network"; + ood.pszPage = pa->szModuleName; + ood.pszTab = NULL; + CallService( MS_OPT_OPENOPTIONS, 0, (LPARAM)&ood ); + } + else OpenAccountOptions( pa ); + } } + break; + + case IDC_UPGRADE: + { + HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST ); + int idx = ListBox_GetCurSel( hList ); + if ( idx != -1 ) { + AccFormDlgParam param = { PRAC_UPGRADED, ( PROTOACCOUNT* )ListBox_GetItemData( hList, idx ) }; + DialogBoxParam( hMirandaInst, MAKEINTRESOURCE(IDD_ACCFORM), hwndDlg, AccFormDlgProc, (LPARAM)¶m ); + } } + break; + + case IDC_LNK_NETWORK: + { + PSHNOTIFY pshn = {0}; + pshn.hdr.code = PSN_APPLY; + pshn.hdr.hwndFrom = hwndDlg; + SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&pshn); + + OPENOPTIONSDIALOG ood = {0}; + ood.cbSize = sizeof(ood); + ood.pszPage = "Network"; + CallService( MS_OPT_OPENOPTIONS, 0, (LPARAM)&ood ); + break; + } + + case IDC_LNK_ADDONS: + CallService(MS_UTILS_OPENURL, TRUE, (LPARAM)"http://addons.miranda-im.org/"); + break; + + case IDOK: + { + PSHNOTIFY pshn = {0}; + pshn.hdr.code = PSN_APPLY; + pshn.hdr.hwndFrom = hwndDlg; + SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&pshn); + DestroyWindow(hwndDlg); + break; + } + + case IDCANCEL: + { + PSHNOTIFY pshn = {0}; + pshn.hdr.code = PSN_RESET; + pshn.hdr.hwndFrom = hwndDlg; + SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&pshn); + DestroyWindow(hwndDlg); + break; + } + } + case PSM_CHANGED: + { + HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST ); + int idx = ListBox_GetCurSel( hList ); + if ( idx != -1 ) { + PROTOACCOUNT *acc = (PROTOACCOUNT *)ListBox_GetItemData(hList, idx); + if (acc) + { + acc->bAccMgrUIChanged = TRUE; + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + break; + } + case WM_NOTIFY: + switch(((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_APPLY: + { + int i; + PSHNOTIFY pshn = {0}; + pshn.hdr.code = PSN_APPLY; + for (i = 0; i < accounts.getCount(); ++i) { + if ( accounts[i]->hwndAccMgrUI && accounts[i]->bAccMgrUIChanged ) { + pshn.hdr.hwndFrom = accounts[i]->hwndAccMgrUI; + SendMessage(accounts[i]->hwndAccMgrUI, WM_NOTIFY, 0, (LPARAM)&pshn); + accounts[i]->bAccMgrUIChanged = FALSE; + } } + return TRUE; + } + case PSN_RESET: + { + int i; + PSHNOTIFY pshn = {0}; + pshn.hdr.code = PSN_RESET; + for (i = 0; i < accounts.getCount(); ++i) { + if ( accounts[i]->hwndAccMgrUI && accounts[i]->bAccMgrUIChanged ) { + pshn.hdr.hwndFrom = accounts[i]->hwndAccMgrUI; + SendMessage(accounts[i]->hwndAccMgrUI, WM_NOTIFY, 0, (LPARAM)&pshn); + accounts[i]->bAccMgrUIChanged = FALSE; + } } + return TRUE; + } + } + break; + } + break; + case WM_DESTROY: + { + for (int i = 0; i < accounts.getCount(); ++i) { + accounts[i]->bAccMgrUIChanged = FALSE; + if (accounts[i]->hwndAccMgrUI) { + DestroyWindow(accounts[i]->hwndAccMgrUI); + accounts[i]->hwndAccMgrUI = NULL; + } } } + + Window_FreeIcon_IcoLib( hwndDlg ); + Button_FreeIcon_IcoLib( hwndDlg, IDC_ADD ); + Button_FreeIcon_IcoLib( hwndDlg, IDC_EDIT ); + Button_FreeIcon_IcoLib( hwndDlg, IDC_REMOVE ); + Button_FreeIcon_IcoLib( hwndDlg, IDC_OPTIONS ); + Button_FreeIcon_IcoLib( hwndDlg, IDC_UPGRADE ); + Utils_SaveWindowPosition( hwndDlg, NULL, "AccMgr", ""); + sttSubclassAccList(GetDlgItem(hwndDlg, IDC_ACCLIST), FALSE); + DeleteObject(dat->hfntTitle); + DeleteObject(dat->hfntText); + mir_free(dat); + hAccMgr = NULL; + break; + } + + return FALSE; +} + +static INT_PTR OptProtosShow(WPARAM, LPARAM) +{ + if ( !hAccMgr ) + hAccMgr = CreateDialogParam( hMirandaInst, MAKEINTRESOURCE(IDD_ACCMGR), NULL, AccMgrDlgProc, 0 ); + + ShowWindow( hAccMgr, SW_RESTORE ); + SetForegroundWindow( hAccMgr ); + SetActiveWindow( hAccMgr ); + return 0; +} + +int OptProtosLoaded(WPARAM, LPARAM) +{ + CLISTMENUITEM mi = { 0 }; + mi.cbSize = sizeof(mi); + mi.flags = CMIF_ICONFROMICOLIB; + mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_ACCMGR ); + mi.position = 1900000000; + mi.pszName = LPGEN("&Accounts..."); + mi.pszService = MS_PROTO_SHOWACCMGR; + CallService( MS_CLIST_ADDMAINMENUITEM, 0, ( LPARAM )&mi ); + return 0; +} + +static int OnAccListChanged( WPARAM eventCode, LPARAM lParam ) +{ + PROTOACCOUNT* pa = (PROTOACCOUNT*)lParam; + + switch( eventCode ) { + case PRAC_CHANGED: + if ( pa->ppro ) { + mir_free( pa->ppro->m_tszUserName ); + pa->ppro->m_tszUserName = mir_tstrdup( pa->tszAccountName ); + pa->ppro->OnEvent( EV_PROTO_ONRENAME, 0, lParam ); + } + } + + return 0; +} + +static int ShutdownAccMgr(WPARAM, LPARAM) +{ + if ( IsWindow( hAccMgr )) + DestroyWindow( hAccMgr ); + hAccMgr = NULL; + return 0; +} + +int LoadProtoOptions( void ) +{ + CreateServiceFunction( MS_PROTO_SHOWACCMGR, OptProtosShow ); + + HookEvent( ME_SYSTEM_MODULESLOADED, OptProtosLoaded ); + HookEvent( ME_PROTO_ACCLISTCHANGED, OnAccListChanged ); + HookEvent( ME_SYSTEM_PRESHUTDOWN, ShutdownAccMgr ); + return 0; +} -- cgit v1.2.3