/* 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 "..\..\core\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(); }