/* 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" DWORD GetModuleNameOfs(const char *szName); DBCachedContactValueList* AddToCachedContactList(HANDLE hContact, int index); static int mirCp = CP_ACP; HANDLE hCacheHeap = NULL; SortedList lContacts = {0}; HANDLE hLastCachedContact = NULL; static DBCachedContactValueList *LastVL = NULL; static SortedList lSettings={0}, lGlobalSettings={0}, lResidentSettings={0}; static HANDLE hSettingChangeEvent = NULL; static DWORD GetSettingsGroupOfsByModuleNameOfs(struct DBContact *dbc,DWORD ofsModuleName) { struct DBContactSettings *dbcs; DWORD ofsThis; ofsThis=dbc->ofsFirstSettings; while(ofsThis) { dbcs=(struct DBContactSettings*)DBRead(ofsThis,sizeof(struct DBContactSettings),NULL); if(dbcs->signature!=DBCONTACTSETTINGS_SIGNATURE) DatabaseCorruption(NULL); if(dbcs->ofsModuleName==ofsModuleName) return ofsThis; ofsThis=dbcs->ofsNext; } return 0; } static DWORD __inline GetSettingValueLength(PBYTE pSetting) { if(pSetting[0]&DBVTF_VARIABLELENGTH) return 2+*(PWORD)(pSetting+1); return pSetting[0]; } static char* InsertCachedSetting( const char* szName, size_t cbNameLen, int index ) { char* newValue = (char*)HeapAlloc( hCacheHeap, 0, cbNameLen ); *newValue = 0; strcpy(newValue+1,szName+1); li.List_Insert(&lSettings,newValue,index); return newValue; } static char* GetCachedSetting(const char *szModuleName,const char *szSettingName, int moduleNameLen, int settingNameLen) { static char *lastsetting = NULL; int index; char szFullName[512]; strcpy(szFullName+1,szModuleName); szFullName[moduleNameLen+1]='/'; strcpy(szFullName+moduleNameLen+2,szSettingName); if (lastsetting && strcmp(szFullName+1,lastsetting) == 0) return lastsetting; if (li.List_GetIndex(&lSettings,szFullName,&index)) lastsetting = (char*)lSettings.items[index]+1; else lastsetting = InsertCachedSetting( szFullName, settingNameLen+moduleNameLen+3, index )+1; return lastsetting; } static void SetCachedVariant( DBVARIANT* s /* new */, DBVARIANT* d /* cached */ ) { char* szSave = ( d->type == DBVT_UTF8 || d->type == DBVT_ASCIIZ ) ? d->pszVal : NULL; memcpy( d, s, sizeof( DBVARIANT )); if (( s->type == DBVT_UTF8 || s->type == DBVT_ASCIIZ ) && s->pszVal != NULL ) { if ( szSave != NULL ) d->pszVal = (char*)HeapReAlloc(hCacheHeap,0,szSave,strlen(s->pszVal)+1); else d->pszVal = (char*)HeapAlloc(hCacheHeap,0,strlen(s->pszVal)+1); strcpy(d->pszVal,s->pszVal); } else if ( szSave != NULL ) HeapFree(hCacheHeap,0,szSave); #ifdef DBLOGGING switch( d->type ) { case DBVT_BYTE: log1( "set cached byte: %d", d->bVal ); break; case DBVT_WORD: log1( "set cached word: %d", d->wVal ); break; case DBVT_DWORD: log1( "set cached dword: %d", d->dVal ); break; case DBVT_UTF8: case DBVT_ASCIIZ: log1( "set cached string: '%s'", d->pszVal ); break; default: log1( "set cached crap: %d", d->type ); break; } #endif } void FreeCachedVariant( DBVARIANT* V ) { if (( V->type == DBVT_ASCIIZ || V->type == DBVT_UTF8 ) && V->pszVal != NULL ) HeapFree(hCacheHeap,0,V->pszVal); } static DBVARIANT* GetCachedValuePtr( HANDLE hContact, char* szSetting, int bAllocate ) { int index; if ( hContact == 0 ) { DBCachedGlobalValue Vtemp, *V; Vtemp.name = szSetting; if ( li.List_GetIndex(&lGlobalSettings,&Vtemp,&index)) { V = (DBCachedGlobalValue*)lGlobalSettings.items[index]; if ( bAllocate == -1 ) { FreeCachedVariant( &V->value ); li.List_Remove(&lGlobalSettings,index); HeapFree(hCacheHeap,0,V); return NULL; } } else { if ( bAllocate != 1 ) return NULL; V = (DBCachedGlobalValue*)HeapAlloc(hCacheHeap,HEAP_ZERO_MEMORY,sizeof(DBCachedGlobalValue)); V->name = szSetting; li.List_Insert(&lGlobalSettings,V,index); } return &V->value; } else { DBCachedContactValue *V, *V1; DBCachedContactValueList VLtemp,*VL; if (hLastCachedContact==hContact && LastVL) { VL = LastVL; } else { VLtemp.hContact=hContact; if ( !li.List_GetIndex(&lContacts,&VLtemp,&index)) { if ( bAllocate != 1 ) return NULL; VL = AddToCachedContactList(hContact,index); } else VL = (DBCachedContactValueList*)lContacts.items[index]; LastVL = VL; hLastCachedContact = hContact; } for ( V = VL->first; V != NULL; V = V->next) if (V->name == szSetting) break; if ( V == NULL ) { if ( bAllocate != 1 ) return NULL; V = HeapAlloc(hCacheHeap,HEAP_ZERO_MEMORY,sizeof(DBCachedContactValue)); if (VL->last) VL->last->next = V; else VL->first = V; VL->last = V; V->name = szSetting; } else if ( bAllocate == -1 ) { LastVL = NULL; FreeCachedVariant(&V->value); if ( VL->first == V ) { VL->first = V->next; if (VL->last == V) VL->last = V->next; // NULL } else for ( V1 = VL->first; V1 != NULL; V1 = V1->next ) if ( V1->next == V ) { V1->next = V->next; if (VL->last == V) VL->last = V1; break; } HeapFree(hCacheHeap,0,V); return NULL; } return &V->value; } } #define NeedBytes(n) if(bytesRemaining<(n)) pBlob=(PBYTE)DBRead(ofsBlobPtr,(n),&bytesRemaining) #define MoveAlong(n) {int x=n; pBlob+=(x); ofsBlobPtr+=(x); bytesRemaining-=(x);} #define VLT(n) ((n==DBVT_UTF8)?DBVT_ASCIIZ:n) static __inline int GetContactSettingWorker(HANDLE hContact,DBCONTACTGETSETTING *dbcgs,int isStatic) { struct DBContact *dbc; DWORD ofsModuleName,ofsContact,ofsSettingsGroup,ofsBlobPtr; int settingNameLen,moduleNameLen; int bytesRemaining; PBYTE pBlob; char* szCachedSettingName; if ((!dbcgs->szSetting) || (!dbcgs->szModule)) return 1; // the db format can't tolerate more than 255 bytes of space (incl. null) for settings+module name settingNameLen=(int)strlen(dbcgs->szSetting); moduleNameLen=(int)strlen(dbcgs->szModule); if ( settingNameLen > 0xFE ) { #ifdef _DEBUG OutputDebugStringA("GetContactSettingWorker() got a > 255 setting name length. \n"); #endif return 1; } if ( moduleNameLen > 0xFE ) { #ifdef _DEBUG OutputDebugStringA("GetContactSettingWorker() got a > 255 module name length. \n"); #endif return 1; } EnterCriticalSection(&csDbAccess); log3("get [%08p] %s/%s",hContact,dbcgs->szModule,dbcgs->szSetting); szCachedSettingName = GetCachedSetting(dbcgs->szModule,dbcgs->szSetting,moduleNameLen,settingNameLen); { DBVARIANT* pCachedValue = GetCachedValuePtr( hContact, szCachedSettingName, 0 ); if ( pCachedValue != NULL ) { if ( pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8 ) { int cbOrigLen = dbcgs->pValue->cchVal; char* cbOrigPtr = dbcgs->pValue->pszVal; memcpy( dbcgs->pValue, pCachedValue, sizeof( DBVARIANT )); if ( isStatic ) { int cbLen = 0; if ( pCachedValue->pszVal != NULL ) cbLen = (int)strlen( pCachedValue->pszVal ); cbOrigLen--; dbcgs->pValue->pszVal = cbOrigPtr; if(cbLenpValue->pszVal,pCachedValue->pszVal,cbOrigLen); dbcgs->pValue->pszVal[cbOrigLen]=0; dbcgs->pValue->cchVal=cbLen; } else { dbcgs->pValue->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal)+1); strcpy(dbcgs->pValue->pszVal,pCachedValue->pszVal); } } else memcpy( dbcgs->pValue, pCachedValue, sizeof( DBVARIANT )); switch( dbcgs->pValue->type ) { case DBVT_BYTE: log1( "get cached byte: %d", dbcgs->pValue->bVal ); break; case DBVT_WORD: log1( "get cached word: %d", dbcgs->pValue->wVal ); break; case DBVT_DWORD: log1( "get cached dword: %d", dbcgs->pValue->dVal ); break; case DBVT_UTF8: case DBVT_ASCIIZ: log1( "get cached string: '%s'", dbcgs->pValue->pszVal); break; default: log1( "get cached crap: %d", dbcgs->pValue->type ); break; } LeaveCriticalSection(&csDbAccess); return ( pCachedValue->type == DBVT_DELETED ) ? 1 : 0; } } ofsModuleName=GetModuleNameOfs(dbcgs->szModule); if(hContact==NULL) ofsContact=dbHeader.ofsUser; else ofsContact=(DWORD)hContact; dbc=(struct DBContact*)DBRead(ofsContact,sizeof(struct DBContact),NULL); if(dbc->signature!=DBCONTACT_SIGNATURE) { LeaveCriticalSection(&csDbAccess); return 1; } ofsSettingsGroup=GetSettingsGroupOfsByModuleNameOfs(dbc,ofsModuleName); if(ofsSettingsGroup) { ofsBlobPtr=ofsSettingsGroup+offsetof(struct DBContactSettings,blob); pBlob = DBRead(ofsBlobPtr,sizeof(struct DBContactSettings),&bytesRemaining); while(pBlob[0]) { NeedBytes(1+settingNameLen); if(pBlob[0]==settingNameLen && !memcmp(pBlob+1,dbcgs->szSetting,settingNameLen)) { MoveAlong(1+settingNameLen); NeedBytes(5); if(isStatic && pBlob[0]&DBVTF_VARIABLELENGTH && VLT(dbcgs->pValue->type) != VLT(pBlob[0])) { LeaveCriticalSection(&csDbAccess); return 1; } dbcgs->pValue->type=pBlob[0]; switch(pBlob[0]) { case DBVT_DELETED: { /* this setting is deleted */ dbcgs->pValue->type=DBVT_DELETED; LeaveCriticalSection(&csDbAccess); return 2; } case DBVT_BYTE: dbcgs->pValue->bVal=pBlob[1]; break; case DBVT_WORD: dbcgs->pValue->wVal=*(PWORD)(pBlob+1); break; case DBVT_DWORD: dbcgs->pValue->dVal=*(PDWORD)(pBlob+1); break; case DBVT_UTF8: case DBVT_ASCIIZ: NeedBytes(3+*(PWORD)(pBlob+1)); if(isStatic) { dbcgs->pValue->cchVal--; if(*(PWORD)(pBlob+1)pValue->cchVal) dbcgs->pValue->cchVal=*(PWORD)(pBlob+1); CopyMemory(dbcgs->pValue->pszVal,pBlob+3,dbcgs->pValue->cchVal); dbcgs->pValue->pszVal[dbcgs->pValue->cchVal]=0; dbcgs->pValue->cchVal=*(PWORD)(pBlob+1); } else { dbcgs->pValue->pszVal=(char*)mir_alloc(1+*(PWORD)(pBlob+1)); CopyMemory(dbcgs->pValue->pszVal,pBlob+3,*(PWORD)(pBlob+1)); dbcgs->pValue->pszVal[*(PWORD)(pBlob+1)]=0; } break; case DBVT_BLOB: NeedBytes(3+*(PWORD)(pBlob+1)); if(isStatic) { if(*(PWORD)(pBlob+1)pValue->cpbVal) dbcgs->pValue->cpbVal=*(PWORD)(pBlob+1); CopyMemory(dbcgs->pValue->pbVal,pBlob+3,dbcgs->pValue->cpbVal); } else { dbcgs->pValue->pbVal=(char*)mir_alloc(*(PWORD)(pBlob+1)); CopyMemory(dbcgs->pValue->pbVal,pBlob+3,*(PWORD)(pBlob+1)); } dbcgs->pValue->cpbVal=*(PWORD)(pBlob+1); break; } /**** add to cache **********************/ if ( dbcgs->pValue->type != DBVT_BLOB ) { DBVARIANT* pCachedValue = GetCachedValuePtr( hContact, szCachedSettingName, 1 ); if ( pCachedValue != NULL ) SetCachedVariant(dbcgs->pValue,pCachedValue); } LeaveCriticalSection(&csDbAccess); logg(); return 0; } NeedBytes(1); MoveAlong(pBlob[0]+1); NeedBytes(3); MoveAlong(1+GetSettingValueLength(pBlob)); NeedBytes(1); } } /**** add missing setting to cache **********************/ if ( dbcgs->pValue->type != DBVT_BLOB ) { DBVARIANT* pCachedValue = GetCachedValuePtr( hContact, szCachedSettingName, 1 ); if ( pCachedValue != NULL ) pCachedValue->type = DBVT_DELETED; } LeaveCriticalSection(&csDbAccess); logg(); return 1; } static INT_PTR GetContactSetting(WPARAM wParam,LPARAM lParam) { DBCONTACTGETSETTING* dgs = ( DBCONTACTGETSETTING* )lParam; dgs->pValue->type = 0; if ( GetContactSettingWorker(( HANDLE )wParam, dgs, 0 )) return 1; if ( dgs->pValue->type == DBVT_UTF8 ) { WCHAR* tmp = NULL; char* p = NEWSTR_ALLOCA(dgs->pValue->pszVal); if ( mir_utf8decode( p, &tmp ) != NULL ) { BOOL bUsed = FALSE; int result = WideCharToMultiByte( mirCp, WC_NO_BEST_FIT_CHARS, tmp, -1, NULL, 0, NULL, &bUsed ); mir_free( dgs->pValue->pszVal ); if ( bUsed || result == 0 ) { dgs->pValue->type = DBVT_WCHAR; dgs->pValue->pwszVal = tmp; } else { dgs->pValue->type = DBVT_ASCIIZ; dgs->pValue->pszVal = mir_alloc( result ); WideCharToMultiByte( mirCp, WC_NO_BEST_FIT_CHARS, tmp, -1, dgs->pValue->pszVal, result, NULL, NULL ); mir_free( tmp ); } } else { dgs->pValue->type = DBVT_ASCIIZ; mir_free( tmp ); } } return 0; } static INT_PTR GetContactSettingStr(WPARAM wParam,LPARAM lParam) { DBCONTACTGETSETTING* dgs = (DBCONTACTGETSETTING*)lParam; int iSaveType = dgs->pValue->type; if ( GetContactSettingWorker(( HANDLE )wParam, dgs, 0 )) return 1; if ( iSaveType == 0 || iSaveType == dgs->pValue->type ) return 0; if ( dgs->pValue->type != DBVT_ASCIIZ && dgs->pValue->type != DBVT_UTF8 ) return 1; if ( iSaveType == DBVT_WCHAR ) { if ( dgs->pValue->type != DBVT_UTF8 ) { int len = MultiByteToWideChar( CP_ACP, 0, dgs->pValue->pszVal, -1, NULL, 0 ); wchar_t* wszResult = ( wchar_t* )mir_alloc(( len+1 )*sizeof( wchar_t )); if ( wszResult == NULL ) return 1; MultiByteToWideChar( CP_ACP, 0, dgs->pValue->pszVal, -1, wszResult, len ); wszResult[ len ] = 0; mir_free( dgs->pValue->pszVal ); dgs->pValue->pwszVal = wszResult; } else { char* savePtr = NEWSTR_ALLOCA(dgs->pValue->pszVal); mir_free( dgs->pValue->pszVal ); if ( !mir_utf8decode( savePtr, &dgs->pValue->pwszVal )) return 1; } } else if ( iSaveType == DBVT_UTF8 ) { char* tmpBuf = mir_utf8encode( dgs->pValue->pszVal ); if ( tmpBuf == NULL ) return 1; mir_free( dgs->pValue->pszVal ); dgs->pValue->pszVal = tmpBuf; } else if ( iSaveType == DBVT_ASCIIZ ) mir_utf8decode( dgs->pValue->pszVal, NULL ); dgs->pValue->type = iSaveType; return 0; } INT_PTR GetContactSettingStatic(WPARAM wParam,LPARAM lParam) { DBCONTACTGETSETTING* dgs = (DBCONTACTGETSETTING*)lParam; if ( GetContactSettingWorker(( HANDLE )wParam, dgs, 1 )) return 1; if ( dgs->pValue->type == DBVT_UTF8 ) { mir_utf8decode( dgs->pValue->pszVal, NULL ); dgs->pValue->type = DBVT_ASCIIZ; } return 0; } static INT_PTR FreeVariant(WPARAM wParam,LPARAM lParam) { DBVARIANT *dbv=(DBVARIANT*)lParam; if ( dbv == 0 ) return 1; switch ( dbv->type ) { case DBVT_ASCIIZ: case DBVT_UTF8: case DBVT_WCHAR: { if ( dbv->pszVal ) mir_free(dbv->pszVal); dbv->pszVal=0; break; } case DBVT_BLOB: { if ( dbv->pbVal ) mir_free(dbv->pbVal); dbv->pbVal=0; break; } } dbv->type=0; return 0; } static INT_PTR SetSettingResident(WPARAM wParam,LPARAM lParam) { size_t cbSettingNameLen = strlen(( char* )lParam) + 2; if (cbSettingNameLen < 512) { char* szSetting; int idx; char szTemp[512]; strcpy( szTemp+1, ( char* )lParam ); EnterCriticalSection(&csDbAccess); if ( !li.List_GetIndex( &lSettings, szTemp, &idx )) szSetting = InsertCachedSetting( szTemp, cbSettingNameLen, idx ); else szSetting = lSettings.items[ idx ]; *szSetting = (char)wParam; if ( !li.List_GetIndex( &lResidentSettings, szSetting+1, &idx )) { if (wParam) li.List_Insert(&lResidentSettings,szSetting+1,idx); } else if (!wParam) li.List_Remove(&lResidentSettings,idx); LeaveCriticalSection(&csDbAccess); } return 0; } static INT_PTR WriteContactSetting(WPARAM wParam,LPARAM lParam) { DBCONTACTWRITESETTING *dbcws=(DBCONTACTWRITESETTING*)lParam; DBCONTACTWRITESETTING tmp; struct DBContact dbc; DWORD ofsModuleName; struct DBContactSettings dbcs; PBYTE pBlob; int settingNameLen=0; int moduleNameLen=0; int settingDataLen=0; int bytesRequired,bytesRemaining; DWORD ofsContact,ofsSettingsGroup,ofsBlobPtr; if (dbcws == NULL || dbcws->szSetting==NULL || dbcws->szModule==NULL ) return 1; // the db format can't tolerate more than 255 bytes of space (incl. null) for settings+module name settingNameLen=(int)strlen(dbcws->szSetting); moduleNameLen=(int)strlen(dbcws->szModule); if ( settingNameLen > 0xFE ) { #ifdef _DEBUG OutputDebugStringA("WriteContactSetting() got a > 255 setting name length. \n"); #endif return 1; } if ( moduleNameLen > 0xFE ) { #ifdef _DEBUG OutputDebugStringA("WriteContactSetting() got a > 255 module name length. \n"); #endif return 1; } tmp = *dbcws; if (tmp.value.type == DBVT_WCHAR) { if (tmp.value.pszVal != NULL) { char* val = mir_utf8encodeW(tmp.value.pwszVal); if ( val == NULL ) return 1; tmp.value.pszVal = ( char* )alloca( strlen( val )+1 ); strcpy( tmp.value.pszVal, val ); mir_free(val); tmp.value.type = DBVT_UTF8; } else return 1; } if(tmp.value.type!=DBVT_BYTE && tmp.value.type!=DBVT_WORD && tmp.value.type!=DBVT_DWORD && tmp.value.type!=DBVT_ASCIIZ && tmp.value.type!=DBVT_UTF8 && tmp.value.type!=DBVT_BLOB) return 1; if ((!tmp.szModule) || (!tmp.szSetting) || ((tmp.value.type == DBVT_ASCIIZ || tmp.value.type == DBVT_UTF8 )&& tmp.value.pszVal == NULL) || (tmp.value.type == DBVT_BLOB && tmp.value.pbVal == NULL) ) return 1; // the db can not tolerate strings/blobs longer than 0xFFFF since the format writes 2 lengths switch( tmp.value.type ) { case DBVT_ASCIIZ: case DBVT_BLOB: case DBVT_UTF8: { size_t len = ( tmp.value.type != DBVT_BLOB ) ? strlen(tmp.value.pszVal) : tmp.value.cpbVal; if ( len >= 0xFFFF ) { #ifdef _DEBUG OutputDebugStringA("WriteContactSetting() writing huge string/blob, rejecting ( >= 0xFFFF ) \n"); #endif return 1; } } } EnterCriticalSection(&csDbAccess); { char* szCachedSettingName = GetCachedSetting(tmp.szModule, tmp.szSetting, moduleNameLen, settingNameLen); if ( tmp.value.type != DBVT_BLOB ) { DBVARIANT* pCachedValue = GetCachedValuePtr((HANDLE)wParam, szCachedSettingName, 1); if ( pCachedValue != NULL ) { BOOL bIsIdentical = FALSE; if ( pCachedValue->type == tmp.value.type ) { switch(tmp.value.type) { case DBVT_BYTE: bIsIdentical = pCachedValue->bVal == tmp.value.bVal; break; case DBVT_WORD: bIsIdentical = pCachedValue->wVal == tmp.value.wVal; break; case DBVT_DWORD: bIsIdentical = pCachedValue->dVal == tmp.value.dVal; break; case DBVT_UTF8: case DBVT_ASCIIZ: bIsIdentical = strcmp( pCachedValue->pszVal, tmp.value.pszVal ) == 0; break; } if ( bIsIdentical ) { LeaveCriticalSection(&csDbAccess); return 0; } } SetCachedVariant(&tmp.value, pCachedValue); } if ( szCachedSettingName[-1] != 0 ) { LeaveCriticalSection(&csDbAccess); NotifyEventHooks(hSettingChangeEvent,wParam,(LPARAM)&tmp); return 0; } } else GetCachedValuePtr((HANDLE)wParam, szCachedSettingName, -1); } ofsModuleName=GetModuleNameOfs(tmp.szModule); if(wParam==0) ofsContact=dbHeader.ofsUser; else ofsContact=wParam; dbc=*(struct DBContact*)DBRead(ofsContact,sizeof(struct DBContact),NULL); if(dbc.signature!=DBCONTACT_SIGNATURE) { LeaveCriticalSection(&csDbAccess); return 1; } log0("write setting"); //make sure the module group exists ofsSettingsGroup=GetSettingsGroupOfsByModuleNameOfs(&dbc,ofsModuleName); if(ofsSettingsGroup==0) { //module group didn't exist - make it if(tmp.value.type&DBVTF_VARIABLELENGTH) { if(tmp.value.type==DBVT_ASCIIZ || tmp.value.type==DBVT_UTF8) bytesRequired=(int)strlen(tmp.value.pszVal)+2; else if(tmp.value.type==DBVT_BLOB) bytesRequired=tmp.value.cpbVal+2; } else bytesRequired=tmp.value.type; bytesRequired+=2+settingNameLen; bytesRequired+=(DB_SETTINGS_RESIZE_GRANULARITY-(bytesRequired%DB_SETTINGS_RESIZE_GRANULARITY))%DB_SETTINGS_RESIZE_GRANULARITY; ofsSettingsGroup=CreateNewSpace(bytesRequired+offsetof(struct DBContactSettings,blob)); dbcs.signature=DBCONTACTSETTINGS_SIGNATURE; dbcs.ofsNext=dbc.ofsFirstSettings; dbcs.ofsModuleName=ofsModuleName; dbcs.cbBlob=bytesRequired; dbcs.blob[0]=0; dbc.ofsFirstSettings=ofsSettingsGroup; DBWrite(ofsContact,&dbc,sizeof(struct DBContact)); DBWrite(ofsSettingsGroup,&dbcs,sizeof(struct DBContactSettings)); ofsBlobPtr=ofsSettingsGroup+offsetof(struct DBContactSettings,blob); pBlob=(PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); } else { dbcs=*(struct DBContactSettings*)DBRead(ofsSettingsGroup,sizeof(struct DBContactSettings),&bytesRemaining); //find if the setting exists ofsBlobPtr=ofsSettingsGroup+offsetof(struct DBContactSettings,blob); pBlob=(PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); while(pBlob[0]) { NeedBytes(settingNameLen+1); if(pBlob[0]==settingNameLen && !memcmp(pBlob+1,tmp.szSetting,settingNameLen)) break; NeedBytes(1); MoveAlong(pBlob[0]+1); NeedBytes(3); MoveAlong(1+GetSettingValueLength(pBlob)); NeedBytes(1); } if(pBlob[0]) { //setting already existed, and up to end of name is in cache MoveAlong(1+settingNameLen); //if different type or variable length and length is different NeedBytes(3); if(pBlob[0]!=tmp.value.type || ((pBlob[0]==DBVT_ASCIIZ || pBlob[0]==DBVT_UTF8) && *(PWORD)(pBlob+1)!=strlen(tmp.value.pszVal)) || (pBlob[0]==DBVT_BLOB && *(PWORD)(pBlob+1)!=tmp.value.cpbVal)) { //bin it int nameLen,valLen; DWORD ofsSettingToCut; NeedBytes(3); nameLen=1+settingNameLen; valLen=1+GetSettingValueLength(pBlob); ofsSettingToCut=ofsBlobPtr-nameLen; MoveAlong(valLen); NeedBytes(1); while(pBlob[0]) { MoveAlong(pBlob[0]+1); NeedBytes(3); MoveAlong(1+GetSettingValueLength(pBlob)); NeedBytes(1); } DBMoveChunk(ofsSettingToCut,ofsSettingToCut+nameLen+valLen,ofsBlobPtr+1-ofsSettingToCut); ofsBlobPtr-=nameLen+valLen; pBlob=(PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); } else { //replace existing setting at pBlob MoveAlong(1); //skip data type switch(tmp.value.type) { case DBVT_BYTE: DBWrite(ofsBlobPtr,&tmp.value.bVal,1); break; case DBVT_WORD: DBWrite(ofsBlobPtr,&tmp.value.wVal,2); break; case DBVT_DWORD: DBWrite(ofsBlobPtr,&tmp.value.dVal,4); break; case DBVT_UTF8: case DBVT_ASCIIZ: DBWrite(ofsBlobPtr+2,tmp.value.pszVal,(int)strlen(tmp.value.pszVal)); break; case DBVT_BLOB: DBWrite(ofsBlobPtr+2,tmp.value.pbVal,tmp.value.cpbVal); break; } //quit DBFlush(1); LeaveCriticalSection(&csDbAccess); //notify NotifyEventHooks(hSettingChangeEvent,wParam,(LPARAM)&tmp); return 0; } } } //cannot do a simple replace, add setting to end of list //pBlob already points to end of list //see if it fits if(tmp.value.type&DBVTF_VARIABLELENGTH) { if(tmp.value.type==DBVT_ASCIIZ || tmp.value.type==DBVT_UTF8) bytesRequired=(int)strlen(tmp.value.pszVal)+2; else if(tmp.value.type==DBVT_BLOB) bytesRequired=tmp.value.cpbVal+2; } else bytesRequired=tmp.value.type; bytesRequired+=2+settingNameLen; bytesRequired+=ofsBlobPtr+1-(ofsSettingsGroup+offsetof(struct DBContactSettings,blob)); if((DWORD)bytesRequired>dbcs.cbBlob) { //doesn't fit: move entire group struct DBContactSettings *dbcsPrev; DWORD ofsDbcsPrev,ofsNew; bytesRequired+=(DB_SETTINGS_RESIZE_GRANULARITY-(bytesRequired%DB_SETTINGS_RESIZE_GRANULARITY))%DB_SETTINGS_RESIZE_GRANULARITY; //find previous group to change its offset ofsDbcsPrev=dbc.ofsFirstSettings; if(ofsDbcsPrev==ofsSettingsGroup) ofsDbcsPrev=0; else { dbcsPrev=(struct DBContactSettings*)DBRead(ofsDbcsPrev,sizeof(struct DBContactSettings),NULL); while(dbcsPrev->ofsNext!=ofsSettingsGroup) { if(dbcsPrev->ofsNext==0) DatabaseCorruption(NULL); ofsDbcsPrev=dbcsPrev->ofsNext; dbcsPrev=(struct DBContactSettings*)DBRead(ofsDbcsPrev,sizeof(struct DBContactSettings),NULL); } } //create the new one ofsNew=ReallocSpace(ofsSettingsGroup, dbcs.cbBlob+offsetof(struct DBContactSettings,blob), bytesRequired+offsetof(struct DBContactSettings,blob)); dbcs.cbBlob=bytesRequired; DBWrite(ofsNew,&dbcs,offsetof(struct DBContactSettings,blob)); if(ofsDbcsPrev==0) { dbc.ofsFirstSettings=ofsNew; DBWrite(ofsContact,&dbc,sizeof(struct DBContact)); } else { dbcsPrev=(struct DBContactSettings*)DBRead(ofsDbcsPrev,sizeof(struct DBContactSettings),NULL); dbcsPrev->ofsNext=ofsNew; DBWrite(ofsDbcsPrev,dbcsPrev,offsetof(struct DBContactSettings,blob)); } ofsBlobPtr+=ofsNew-ofsSettingsGroup; ofsSettingsGroup=ofsNew; pBlob=(PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); } //we now have a place to put it and enough space: make it DBWrite(ofsBlobPtr,&settingNameLen,1); DBWrite(ofsBlobPtr+1,(PVOID)tmp.szSetting,settingNameLen); MoveAlong(1+settingNameLen); DBWrite(ofsBlobPtr,&tmp.value.type,1); MoveAlong(1); switch(tmp.value.type) { case DBVT_BYTE: DBWrite(ofsBlobPtr,&tmp.value.bVal,1); MoveAlong(1); break; case DBVT_WORD: DBWrite(ofsBlobPtr,&tmp.value.wVal,2); MoveAlong(2); break; case DBVT_DWORD: DBWrite(ofsBlobPtr,&tmp.value.dVal,4); MoveAlong(4); break; case DBVT_UTF8: case DBVT_ASCIIZ: { int len=(int)strlen(tmp.value.pszVal); DBWrite(ofsBlobPtr,&len,2); DBWrite(ofsBlobPtr+2,tmp.value.pszVal,len); MoveAlong(2+len); } break; case DBVT_BLOB: DBWrite(ofsBlobPtr,&tmp.value.cpbVal,2) ; DBWrite(ofsBlobPtr+2,tmp.value.pbVal,tmp.value.cpbVal); MoveAlong(2+tmp.value.cpbVal); break; } { BYTE zero=0; DBWrite(ofsBlobPtr,&zero,1); } //quit DBFlush(1); LeaveCriticalSection(&csDbAccess); //notify NotifyEventHooks(hSettingChangeEvent, wParam, (LPARAM)&tmp ); return 0; } static INT_PTR DeleteContactSetting(WPARAM wParam,LPARAM lParam) { DBCONTACTGETSETTING *dbcgs=(DBCONTACTGETSETTING*)lParam; struct DBContact *dbc; DWORD ofsModuleName,ofsSettingsGroup,ofsBlobPtr; PBYTE pBlob; int settingNameLen,moduleNameLen,bytesRemaining; char* szCachedSettingName; WPARAM saveWparam = wParam; if ((!dbcgs->szModule) || (!dbcgs->szSetting)) return 1; // the db format can't tolerate more than 255 bytes of space (incl. null) for settings+module name settingNameLen=(int)strlen(dbcgs->szSetting); moduleNameLen=(int)strlen(dbcgs->szModule); if ( settingNameLen > 0xFE ) { #ifdef _DEBUG OutputDebugStringA("DeleteContactSetting() got a > 255 setting name length. \n"); #endif return 1; } if ( moduleNameLen > 0xFE ) { #ifdef _DEBUG OutputDebugStringA("DeleteContactSetting() got a > 255 module name length. \n"); #endif return 1; } EnterCriticalSection(&csDbAccess); ofsModuleName=GetModuleNameOfs(dbcgs->szModule); if(wParam==0) wParam=dbHeader.ofsUser; dbc=(struct DBContact*)DBRead(wParam,sizeof(struct DBContact),NULL); if(dbc->signature!=DBCONTACT_SIGNATURE) { LeaveCriticalSection(&csDbAccess); return 1; } //make sure the module group exists ofsSettingsGroup=GetSettingsGroupOfsByModuleNameOfs(dbc,ofsModuleName); if(ofsSettingsGroup==0) { LeaveCriticalSection(&csDbAccess); return 1; } //find if the setting exists ofsBlobPtr=ofsSettingsGroup+offsetof(struct DBContactSettings,blob); pBlob=(PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); while(pBlob[0]) { NeedBytes(settingNameLen+1); if(pBlob[0]==settingNameLen && !memcmp(pBlob+1,dbcgs->szSetting,settingNameLen)) break; NeedBytes(1); MoveAlong(pBlob[0]+1); NeedBytes(3); MoveAlong(1+GetSettingValueLength(pBlob)); NeedBytes(1); } if(!pBlob[0]) { //setting didn't exist LeaveCriticalSection(&csDbAccess); return 1; } { //bin it int nameLen,valLen; DWORD ofsSettingToCut; MoveAlong(1+settingNameLen); NeedBytes(3); nameLen=1+settingNameLen; valLen=1+GetSettingValueLength(pBlob); ofsSettingToCut=ofsBlobPtr-nameLen; MoveAlong(valLen); NeedBytes(1); while(pBlob[0]) { MoveAlong(pBlob[0]+1); NeedBytes(3); MoveAlong(1+GetSettingValueLength(pBlob)); NeedBytes(1); } DBMoveChunk(ofsSettingToCut,ofsSettingToCut+nameLen+valLen,ofsBlobPtr+1-ofsSettingToCut); } szCachedSettingName = GetCachedSetting(dbcgs->szModule,dbcgs->szSetting,moduleNameLen,settingNameLen); GetCachedValuePtr((HANDLE)saveWparam, szCachedSettingName, -1 ); //quit DBFlush(1); LeaveCriticalSection(&csDbAccess); { //notify DBCONTACTWRITESETTING dbcws={0}; dbcws.szModule=dbcgs->szModule; dbcws.szSetting=dbcgs->szSetting; dbcws.value.type=DBVT_DELETED; NotifyEventHooks(hSettingChangeEvent,saveWparam,(LPARAM)&dbcws); } return 0; } static INT_PTR EnumContactSettings(WPARAM wParam,LPARAM lParam) { DBCONTACTENUMSETTINGS *dbces=(DBCONTACTENUMSETTINGS*)lParam; struct DBContact *dbc; DWORD ofsModuleName,ofsContact,ofsBlobPtr; int bytesRemaining, result; PBYTE pBlob; char szSetting[256]; if (!dbces->szModule) return -1; EnterCriticalSection(&csDbAccess); ofsModuleName=GetModuleNameOfs(dbces->szModule); if(wParam==0) ofsContact=dbHeader.ofsUser; else ofsContact=wParam; dbc=(struct DBContact*)DBRead(ofsContact,sizeof(struct DBContact),NULL); if(dbc->signature!=DBCONTACT_SIGNATURE) { LeaveCriticalSection(&csDbAccess); return -1; } dbces->ofsSettings=GetSettingsGroupOfsByModuleNameOfs(dbc,ofsModuleName); if(!dbces->ofsSettings) { LeaveCriticalSection(&csDbAccess); return -1; } ofsBlobPtr=dbces->ofsSettings+offsetof(struct DBContactSettings,blob); pBlob=(PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); if(pBlob[0]==0) { LeaveCriticalSection(&csDbAccess); return -1; } result = 0; while(pBlob[0]) { NeedBytes(1); NeedBytes(1+pBlob[0]); CopyMemory(szSetting,pBlob+1,pBlob[0]); szSetting[pBlob[0]]=0; result = (dbces->pfnEnumProc)(szSetting,dbces->lParam); MoveAlong(1+pBlob[0]); NeedBytes(3); MoveAlong(1+GetSettingValueLength(pBlob)); NeedBytes(1); } LeaveCriticalSection(&csDbAccess); return result; } static INT_PTR EnumResidentSettings(WPARAM wParam,LPARAM lParam) { int i; int ret; for(i = 0; i < lResidentSettings.realCount; i++) { ret=((DBMODULEENUMPROC)lParam)(lResidentSettings.items[i],0,wParam); if(ret) return ret; } return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // // Module initialization procedure static int stringCompare( DBCachedSettingName* p1, DBCachedSettingName* p2 ) { return strcmp( p1->name, p2->name ); } static int stringCompare2( char* p1, char* p2 ) { return strcmp( p1, p2); } int InitSettings(void) { CreateServiceFunction(MS_DB_CONTACT_GETSETTING,GetContactSetting); CreateServiceFunction(MS_DB_CONTACT_GETSETTING_STR,GetContactSettingStr); CreateServiceFunction(MS_DB_CONTACT_GETSETTINGSTATIC,GetContactSettingStatic); CreateServiceFunction(MS_DB_CONTACT_FREEVARIANT,FreeVariant); CreateServiceFunction(MS_DB_CONTACT_WRITESETTING,WriteContactSetting); CreateServiceFunction(MS_DB_CONTACT_DELETESETTING,DeleteContactSetting); CreateServiceFunction(MS_DB_CONTACT_ENUMSETTINGS,EnumContactSettings); CreateServiceFunction(MS_DB_SETSETTINGRESIDENT,SetSettingResident); CreateServiceFunction("DB/ResidentSettings/Enum",EnumResidentSettings); hSettingChangeEvent=CreateHookableEvent(ME_DB_CONTACT_SETTINGCHANGED); hCacheHeap=HeapCreate(0,0,0); lSettings.sortFunc=stringCompare; lSettings.increment=100; lContacts.sortFunc=HandleKeySort; lContacts.increment=50; lGlobalSettings.sortFunc=HandleKeySort; lGlobalSettings.increment=50; lResidentSettings.sortFunc=stringCompare2; lResidentSettings.increment=50; mirCp = CallService( MS_LANGPACK_GETCODEPAGE, 0, 0 ); return 0; } void UninitSettings(void) { HeapDestroy(hCacheHeap); li.List_Destroy(&lContacts); li.List_Destroy(&lSettings); li.List_Destroy(&lGlobalSettings); li.List_Destroy(&lResidentSettings); }