/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2003 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" extern HANDLE hCacheHeap; extern SortedList lContacts; extern HANDLE hLastCachedContact; INT_PTR GetContactSettingStatic(WPARAM wParam,LPARAM lParam); void FreeCachedVariant( DBVARIANT* V ); static INT_PTR GetContactCount(WPARAM wParam,LPARAM lParam); static INT_PTR FindFirstContact(WPARAM wParam,LPARAM lParam); static INT_PTR FindNextContact(WPARAM wParam,LPARAM lParam); static INT_PTR DeleteContact(WPARAM wParam,LPARAM lParam); static INT_PTR AddContact(WPARAM wParam,LPARAM lParam); static INT_PTR IsDbContact(WPARAM wParam,LPARAM lParam); static HANDLE hContactDeletedEvent,hContactAddedEvent; int InitContacts(void) { CreateServiceFunction(MS_DB_CONTACT_GETCOUNT,GetContactCount); CreateServiceFunction(MS_DB_CONTACT_FINDFIRST,FindFirstContact); CreateServiceFunction(MS_DB_CONTACT_FINDNEXT,FindNextContact); CreateServiceFunction(MS_DB_CONTACT_DELETE,DeleteContact); CreateServiceFunction(MS_DB_CONTACT_ADD,AddContact); CreateServiceFunction(MS_DB_CONTACT_IS,IsDbContact); hContactDeletedEvent=CreateHookableEvent(ME_DB_CONTACT_DELETED); hContactAddedEvent=CreateHookableEvent(ME_DB_CONTACT_ADDED); return 0; } void UninitContacts(void) { } DBCachedContactValueList* AddToCachedContactList(HANDLE hContact, int index) { DBCachedContactValueList* VL; VL = (DBCachedContactValueList*)HeapAlloc(hCacheHeap,HEAP_ZERO_MEMORY,sizeof(DBCachedContactValueList)); VL->hContact = hContact; if (index == -1) li.List_GetIndex(&lContacts,VL,&index); li.List_Insert(&lContacts,VL,index); return VL; } static INT_PTR GetContactCount(WPARAM wParam,LPARAM lParam) { int ret; EnterCriticalSection(&csDbAccess); ret=dbHeader.contactCount; LeaveCriticalSection(&csDbAccess); return ret; } #define proto_module "Protocol" #define proto_setting "p" static int CheckProto(HANDLE hContact, const char *proto) { static char protobuf[MAX_PATH] = {0}; static DBVARIANT dbv; static DBCONTACTGETSETTING sVal = {proto_module,proto_setting,&dbv}; dbv.type = DBVT_ASCIIZ; dbv.pszVal = protobuf; dbv.cchVal = sizeof(protobuf); if (GetContactSettingStatic((WPARAM)hContact, (LPARAM )&sVal) != 0 || (dbv.type != DBVT_ASCIIZ)) return 0; return !strcmp(protobuf,proto); } static INT_PTR FindFirstContact(WPARAM wParam,LPARAM lParam) { INT_PTR ret = 0; EnterCriticalSection(&csDbAccess); ret = (INT_PTR)dbHeader.ofsFirstContact; if (lParam && !CheckProto((HANDLE)ret,(const char*)lParam)) ret = FindNextContact((WPARAM)ret,lParam); LeaveCriticalSection(&csDbAccess); return ret; } static INT_PTR FindNextContact(WPARAM wParam,LPARAM lParam) { int index; struct DBContact *dbc; DBCachedContactValueList VLtemp, *VL = NULL; VLtemp.hContact = (HANDLE)wParam; EnterCriticalSection(&csDbAccess); while(VLtemp.hContact) { if ( li.List_GetIndex(&lContacts,&VLtemp,&index)) { VL = ( DBCachedContactValueList* )lContacts.items[index]; if (VL->hNext != NULL) { if (!lParam || CheckProto(VL->hNext,(const char*)lParam)) { LeaveCriticalSection(&csDbAccess); return (INT_PTR)VL->hNext; } else { VLtemp.hContact = VL->hNext; continue; } } } dbc=(struct DBContact*)DBRead((DWORD)VLtemp.hContact,sizeof(struct DBContact),NULL); if (dbc->signature!=DBCONTACT_SIGNATURE) break; else { if ( VL == NULL ) VL = AddToCachedContactList(VLtemp.hContact,index); VL->hNext = (HANDLE)dbc->ofsNext; if (VL->hNext != NULL && (!lParam || CheckProto(VL->hNext,(const char*)lParam))) { LeaveCriticalSection(&csDbAccess); return (INT_PTR)VL->hNext; } VLtemp.hContact = VL->hNext; } } LeaveCriticalSection(&csDbAccess); return 0; } static INT_PTR DeleteContact(WPARAM wParam,LPARAM lParam) { struct DBContact *dbc,*dbcPrev; DWORD ofsThis,ofsNext,ofsFirstEvent; struct DBContactSettings *dbcs; struct DBEvent *dbe; int index; if ((HANDLE)wParam==NULL) return 1; EnterCriticalSection(&csDbAccess); dbc=(struct DBContact*)DBRead(wParam,sizeof(struct DBContact),NULL); if(dbc->signature!=DBCONTACT_SIGNATURE) { LeaveCriticalSection(&csDbAccess); return 1; } if ( (HANDLE)wParam == (HANDLE)dbHeader.ofsUser ) { LeaveCriticalSection(&csDbAccess); log0("FATAL: del of user chain attempted."); return 1; } log0("del contact"); LeaveCriticalSection(&csDbAccess); //call notifier while outside mutex NotifyEventHooks(hContactDeletedEvent,wParam,0); //get back in EnterCriticalSection(&csDbAccess); { DBCachedContactValueList VLtemp; VLtemp.hContact = (HANDLE)wParam; if ( li.List_GetIndex(&lContacts,&VLtemp,&index)) { DBCachedContactValueList *VL = ( DBCachedContactValueList* )lContacts.items[index]; DBCachedContactValue* V = VL->first; while ( V != NULL ) { DBCachedContactValue* V1 = V->next; FreeCachedVariant(&V->value); HeapFree( hCacheHeap, 0, V ); V = V1; } HeapFree( hCacheHeap, 0, VL ); if (VLtemp.hContact == hLastCachedContact) hLastCachedContact = NULL; li.List_Remove(&lContacts,index); } } dbc=(struct DBContact*)DBRead(wParam,sizeof(struct DBContact),NULL); //delete settings chain ofsThis=dbc->ofsFirstSettings; ofsFirstEvent=dbc->ofsFirstEvent; while(ofsThis) { dbcs=(struct DBContactSettings*)DBRead(ofsThis,sizeof(struct DBContactSettings),NULL); ofsNext=dbcs->ofsNext; DeleteSpace(ofsThis,offsetof(struct DBContactSettings,blob)+dbcs->cbBlob); ofsThis=ofsNext; } //delete event chain ofsThis=ofsFirstEvent; while(ofsThis) { dbe=(struct DBEvent*)DBRead(ofsThis,sizeof(struct DBEvent),NULL); ofsNext=dbe->ofsNext; DeleteSpace(ofsThis,offsetof(struct DBEvent,blob)+dbe->cbBlob); ofsThis=ofsNext; } //find previous contact in chain and change ofsNext dbc=(struct DBContact*)DBRead(wParam,sizeof(struct DBContact),NULL); if(dbHeader.ofsFirstContact==wParam) { dbHeader.ofsFirstContact=dbc->ofsNext; DBWrite(0,&dbHeader,sizeof(dbHeader)); } else { ofsNext=dbc->ofsNext; ofsThis=dbHeader.ofsFirstContact; dbcPrev=(struct DBContact*)DBRead(ofsThis,sizeof(struct DBContact),NULL); while(dbcPrev->ofsNext!=wParam) { if(dbcPrev->ofsNext==0) DatabaseCorruption(NULL); ofsThis=dbcPrev->ofsNext; dbcPrev=(struct DBContact*)DBRead(ofsThis,sizeof(struct DBContact),NULL); } dbcPrev->ofsNext=ofsNext; DBWrite(ofsThis,dbcPrev,sizeof(struct DBContact)); { DBCachedContactValueList VLtemp; VLtemp.hContact = (HANDLE)ofsThis; if ( li.List_GetIndex(&lContacts,&VLtemp,&index)) { DBCachedContactValueList *VL = ( DBCachedContactValueList* )lContacts.items[index]; VL->hNext = ( HANDLE )ofsNext; } } } //delete contact DeleteSpace(wParam,sizeof(struct DBContact)); //decrement contact count dbHeader.contactCount--; DBWrite(0,&dbHeader,sizeof(dbHeader)); DBFlush(0); //quit LeaveCriticalSection(&csDbAccess); return 0; } static INT_PTR AddContact(WPARAM wParam,LPARAM lParam) { struct DBContact dbc; DWORD ofsNew; log0("add contact"); EnterCriticalSection(&csDbAccess); ofsNew=CreateNewSpace(sizeof(struct DBContact)); dbc.signature=DBCONTACT_SIGNATURE; dbc.eventCount=0; dbc.ofsFirstEvent=dbc.ofsLastEvent=0; dbc.ofsFirstSettings=0; dbc.ofsNext=dbHeader.ofsFirstContact; dbc.ofsFirstUnreadEvent=0; dbc.timestampFirstUnread=0; dbHeader.ofsFirstContact=ofsNew; dbHeader.contactCount++; DBWrite(ofsNew,&dbc,sizeof(struct DBContact)); DBWrite(0,&dbHeader,sizeof(dbHeader)); DBFlush(0); AddToCachedContactList((HANDLE)ofsNew, -1); LeaveCriticalSection(&csDbAccess); NotifyEventHooks(hContactAddedEvent,(WPARAM)ofsNew,0); return (INT_PTR)ofsNew; } static INT_PTR IsDbContact(WPARAM wParam,LPARAM lParam) { struct DBContact *dbc; DWORD ofsContact=(DWORD)wParam; int ret; EnterCriticalSection(&csDbAccess); { int index; DBCachedContactValueList VLtemp; VLtemp.hContact = (HANDLE)wParam; if ( li.List_GetIndex(&lContacts,&VLtemp,&index)) ret = TRUE; else { dbc=(struct DBContact*)DBRead(ofsContact,sizeof(struct DBContact),NULL); ret=dbc->signature==DBCONTACT_SIGNATURE; if (ret) AddToCachedContactList((HANDLE)wParam, index); } } LeaveCriticalSection(&csDbAccess); return ret; }