STDMETHODIMP_(HANDLE) CDb3Mmap::FindLastEvent(MCONTACT contactID) { DBCachedContact *cc; DWORD ofsContact = GetContactOffset(contactID, &cc); mir_cslock lck(m_csDbAccess); DBContact *dbc = (DBContact*)DBRead(ofsContact, sizeof(DBContact), NULL); if (dbc->signature != DBCONTACT_SIGNATURE) return NULL; if (!cc || !cc->IsSub()) return HANDLE(dbc->ofsLastEvent); if ((cc = m_cache->GetCachedContact(cc->parentID)) == NULL) return NULL; dbc = (DBContact*)DBRead(cc->dwDriverData, sizeof(DBContact), NULL); if (dbc->signature != DBCONTACT_SIGNATURE) return NULL; for (DWORD dwOffset = dbc->ofsLastEvent; dwOffset != 0;) { DBEvent *dbe = (DBEvent*)DBRead(dwOffset, sizeof(DBEvent), NULL); if (dbe->signature != DBEVENT_SIGNATURE) return NULL; if (dbe->contactID == contactID) return HANDLE(dwOffset); dwOffset = dbe->ofsPrev; } return NULL; }
STDMETHODIMP_(MEVENT) CDb3Mmap::FindLastEvent(MCONTACT contactID) { DBCachedContact *cc; DWORD ofsContact = GetContactOffset(contactID, &cc); mir_cslock lck(m_csDbAccess); DBContact *dbc = (DBContact*)DBRead(ofsContact, nullptr); if (dbc->signature != DBCONTACT_SIGNATURE) return 0; if (!cc || !cc->IsSub()) return MEVENT(dbc->ofsLastEvent); if ((cc = m_cache->GetCachedContact(cc->parentID)) == nullptr) return 0; dbc = (DBContact*)DBRead(cc->dwOfsContact, nullptr); if (dbc->signature != DBCONTACT_SIGNATURE) return 0; for (DWORD dwOffset = dbc->ofsLastEvent; dwOffset != 0;) { DBEvent *dbe = AdaptEvent(dwOffset, contactID); if (dbe->signature != DBEVENT_SIGNATURE) return 0; if (dbe->contactID == contactID) return MEVENT(dwOffset); dwOffset = dbe->ofsPrev; } return 0; }
STDMETHODIMP_(BOOL) CDb3Mmap::EnumContactSettings(MCONTACT contactID, DBCONTACTENUMSETTINGS* dbces) { if (!dbces->szModule) return -1; mir_cslock lck(m_csDbAccess); DWORD ofsContact = GetContactOffset(contactID); if (ofsContact == 0) return -1; DBContact *dbc = (DBContact*)DBRead(ofsContact, NULL); if (dbc->signature != DBCONTACT_SIGNATURE) return -1; DWORD ofsModuleName = GetModuleNameOfs(dbces->szModule); dbces->ofsSettings = GetSettingsGroupOfsByModuleNameOfs(dbc, ofsModuleName); if (!dbces->ofsSettings) return -1; DWORD ofsBlobPtr = dbces->ofsSettings + offsetof(DBContactSettings, blob); int bytesRemaining; PBYTE pBlob = (PBYTE)DBRead(ofsBlobPtr, &bytesRemaining); if (pBlob[0] == 0) return -1; int result = 0; while (pBlob[0]) { NeedBytes(1); NeedBytes(1 + pBlob[0]); char szSetting[256]; memcpy(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); } return result; }
STDMETHODIMP_(BOOL) CDb3Mmap::DeleteContactSetting(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting) { if (!szModule || !szSetting) return 1; // the db format can't tolerate more than 255 bytes of space (incl. null) for settings+module name int settingNameLen = (int)mir_strlen(szSetting); int moduleNameLen = (int)mir_strlen(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; } MCONTACT saveContact = contactID; { mir_cslock lck(m_csDbAccess); char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen); if (szCachedSettingName[-1] == 0) { // it's not a resident variable DWORD ofsModuleName = GetModuleNameOfs(szModule); DWORD ofsContact = GetContactOffset(contactID); DBContact *dbc = (DBContact*)DBRead(ofsContact, NULL); if (dbc->signature != DBCONTACT_SIGNATURE) return 1; // make sure the module group exists DWORD ofsSettingsGroup = GetSettingsGroupOfsByModuleNameOfs(dbc, ofsModuleName); if (ofsSettingsGroup == 0) return 1; // find if the setting exists DWORD ofsBlobPtr = ofsSettingsGroup + offsetof(DBContactSettings, blob); int bytesRemaining; PBYTE pBlob = (PBYTE)DBRead(ofsBlobPtr, &bytesRemaining); while (pBlob[0]) { NeedBytes(settingNameLen + 1); if (pBlob[0] == settingNameLen && !memcmp(pBlob + 1, szSetting, settingNameLen)) break; NeedBytes(1); MoveAlong(pBlob[0] + 1); NeedBytes(3); MoveAlong(1 + GetSettingValueLength(pBlob)); NeedBytes(1); } if (!pBlob[0]) //setting didn't exist return 1; // bin it MoveAlong(1 + settingNameLen); NeedBytes(3); int nameLen = 1 + settingNameLen; int valLen = 1 + GetSettingValueLength(pBlob); DWORD 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); DBFlush(1); } m_cache->GetCachedValuePtr(saveContact, szCachedSettingName, -1); } // notify DBCONTACTWRITESETTING dbcws = { 0 }; dbcws.szModule = szModule; dbcws.szSetting = szSetting; dbcws.value.type = DBVT_DELETED; NotifyEventHooks(hSettingChangeEvent, saveContact, (LPARAM)&dbcws); return 0; }
int CDb3Mmap::GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic) { if (szSetting == NULL || szModule == NULL) return 1; // the db format can't tolerate more than 255 bytes of space (incl. null) for settings+module name int settingNameLen = (int)mir_strlen(szSetting); int moduleNameLen = (int)mir_strlen(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; } mir_cslock lck(m_csDbAccess); LBL_Seek: char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen); log3("get [%08p] %s (%p)", hContact, szCachedSettingName, szCachedSettingName); DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 0); if (pCachedValue != NULL) { if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) { int cbOrigLen = dbv->cchVal; char *cbOrigPtr = dbv->pszVal; memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); if (isStatic) { int cbLen = 0; if (pCachedValue->pszVal != NULL) cbLen = (int)mir_strlen(pCachedValue->pszVal); cbOrigLen--; dbv->pszVal = cbOrigPtr; if (cbLen < cbOrigLen) cbOrigLen = cbLen; memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen); dbv->pszVal[cbOrigLen] = 0; dbv->cchVal = cbLen; } else { dbv->pszVal = (char*)mir_alloc(mir_strlen(pCachedValue->pszVal) + 1); mir_strcpy(dbv->pszVal, pCachedValue->pszVal); } } else memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); log2("get cached %s (%p)", printVariant(dbv), pCachedValue); return (pCachedValue->type == DBVT_DELETED) ? 1 : 0; } // never look db for the resident variable if (szCachedSettingName[-1] != 0) return 1; DBCachedContact *cc; DWORD ofsContact = GetContactOffset(contactID, &cc); DWORD ofsModuleName = GetModuleNameOfs(szModule); DBContact dbc = *(DBContact*)DBRead(ofsContact, NULL); if (dbc.signature != DBCONTACT_SIGNATURE) return 1; DWORD ofsSettingsGroup = GetSettingsGroupOfsByModuleNameOfs(&dbc, ofsModuleName); if (ofsSettingsGroup) { int bytesRemaining; unsigned varLen; DWORD ofsBlobPtr = ofsSettingsGroup + offsetof(DBContactSettings, blob); PBYTE pBlob = DBRead(ofsBlobPtr, &bytesRemaining); while (pBlob[0]) { NeedBytes(1 + settingNameLen); if (pBlob[0] == settingNameLen && !memcmp(pBlob + 1, szSetting, settingNameLen)) { MoveAlong(1 + settingNameLen); NeedBytes(5); if (isStatic && (pBlob[0] & DBVTF_VARIABLELENGTH) && VLT(dbv->type) != VLT(pBlob[0])) return 1; BYTE iType = dbv->type = pBlob[0]; switch (iType) { case DBVT_DELETED: /* this setting is deleted */ dbv->type = DBVT_DELETED; return 2; case DBVT_BYTE: dbv->bVal = pBlob[1]; break; case DBVT_WORD: memmove(&(dbv->wVal), (PWORD)(pBlob + 1), 2); break; case DBVT_DWORD: memmove(&(dbv->dVal), (PDWORD)(pBlob + 1), 4); break; case DBVT_UTF8: case DBVT_ASCIIZ: varLen = *(PWORD)(pBlob + 1); NeedBytes(int(3 + varLen)); if (isStatic) { dbv->cchVal--; if (varLen < dbv->cchVal) dbv->cchVal = varLen; memmove(dbv->pszVal, pBlob + 3, dbv->cchVal); // decode dbv->pszVal[dbv->cchVal] = 0; dbv->cchVal = varLen; } else { dbv->pszVal = (char*)mir_alloc(1 + varLen); memmove(dbv->pszVal, pBlob + 3, varLen); dbv->pszVal[varLen] = 0; } break; case DBVT_BLOB: varLen = *(PWORD)(pBlob + 1); NeedBytes(int(3 + varLen)); if (isStatic) { if (varLen < dbv->cpbVal) dbv->cpbVal = varLen; memmove(dbv->pbVal, pBlob + 3, dbv->cpbVal); } else { dbv->pbVal = (BYTE *)mir_alloc(varLen); memmove(dbv->pbVal, pBlob + 3, varLen); } dbv->cpbVal = varLen; break; case DBVT_ENCRYPTED: if (m_crypto == NULL) return 1; else { varLen = *(PWORD)(pBlob + 1); NeedBytes(int(3 + varLen)); size_t realLen; ptrA decoded(m_crypto->decodeString(pBlob + 3, varLen, &realLen)); if (decoded == NULL) return 1; varLen = (WORD)realLen; dbv->type = DBVT_UTF8; if (isStatic) { dbv->cchVal--; if (varLen < dbv->cchVal) dbv->cchVal = varLen; memmove(dbv->pszVal, decoded, dbv->cchVal); dbv->pszVal[dbv->cchVal] = 0; dbv->cchVal = varLen; } else { dbv->pszVal = (char*)mir_alloc(1 + varLen); memmove(dbv->pszVal, decoded, varLen); dbv->pszVal[varLen] = 0; } } break; } /**** add to cache **********************/ if (iType != DBVT_BLOB && iType != DBVT_ENCRYPTED) { DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 1); if (pCachedValue != NULL) { m_cache->SetCachedVariant(dbv, pCachedValue); log3("set cached [%08p] %s (%p)", hContact, szCachedSettingName, pCachedValue); } } return 0; } NeedBytes(1); MoveAlong(pBlob[0] + 1); NeedBytes(3); MoveAlong(1 + GetSettingValueLength(pBlob)); NeedBytes(1); } } // try to get the missing mc setting from the active sub if (cc && cc->IsMeta() && ValidLookupName(szModule, szSetting)) { if (contactID = db_mc_getDefault(contactID)) { if (szModule = GetContactProto(contactID)) { moduleNameLen = (int)mir_strlen(szModule); goto LBL_Seek; } } } logg(); return 1; }
STDMETHODIMP_(BOOL) CDb3Mmap::WriteContactSetting(MCONTACT contactID, DBCONTACTWRITESETTING *dbcws) { if (dbcws == NULL || dbcws->szSetting == NULL || dbcws->szModule == NULL || m_bReadOnly) return 1; // the db format can't tolerate more than 255 bytes of space (incl. null) for settings+module name int settingNameLen = (int)mir_strlen(dbcws->szSetting); int moduleNameLen = (int)mir_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; } // used for notifications DBCONTACTWRITESETTING dbcwNotif = *dbcws; if (dbcwNotif.value.type == DBVT_WCHAR) { if (dbcwNotif.value.pszVal != NULL) { char* val = mir_utf8encodeW(dbcwNotif.value.pwszVal); if (val == NULL) return 1; dbcwNotif.value.pszVal = (char*)alloca(mir_strlen(val) + 1); mir_strcpy(dbcwNotif.value.pszVal, val); mir_free(val); dbcwNotif.value.type = DBVT_UTF8; } else return 1; } if (dbcwNotif.szModule == NULL || dbcwNotif.szSetting == NULL) return 1; DBCONTACTWRITESETTING dbcwWork = dbcwNotif; mir_ptr<BYTE> pEncoded(NULL); bool bIsEncrypted = false; switch (dbcwWork.value.type) { case DBVT_BYTE: case DBVT_WORD: case DBVT_DWORD: break; case DBVT_ASCIIZ: case DBVT_UTF8: bIsEncrypted = m_bEncrypted || IsSettingEncrypted(dbcws->szModule, dbcws->szSetting); LBL_WriteString: if (dbcwWork.value.pszVal == NULL) return 1; dbcwWork.value.cchVal = (WORD)mir_strlen(dbcwWork.value.pszVal); if (bIsEncrypted) { size_t len; BYTE *pResult = m_crypto->encodeString(dbcwWork.value.pszVal, &len); if (pResult != NULL) { pEncoded = dbcwWork.value.pbVal = pResult; dbcwWork.value.cpbVal = (WORD)len; dbcwWork.value.type = DBVT_ENCRYPTED; } } break; case DBVT_UNENCRYPTED: dbcwNotif.value.type = dbcwWork.value.type = DBVT_UTF8; goto LBL_WriteString; case DBVT_BLOB: case DBVT_ENCRYPTED: if (dbcwWork.value.pbVal == NULL) return 1; break; default: return 1; } mir_cslockfull lck(m_csDbAccess); DWORD ofsBlobPtr, ofsContact = GetContactOffset(contactID); if (ofsContact == 0) { _ASSERT(false); // contact doesn't exist? return 2; } char *szCachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, moduleNameLen, settingNameLen); log3("set [%08p] %s (%p)", hContact, szCachedSettingName, szCachedSettingName); // we don't cache blobs and passwords if (dbcwWork.value.type != DBVT_BLOB && dbcwWork.value.type != DBVT_ENCRYPTED && !bIsEncrypted) { DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 1); if (pCachedValue != NULL) { bool bIsIdentical = false; if (pCachedValue->type == dbcwWork.value.type) { switch (dbcwWork.value.type) { case DBVT_BYTE: bIsIdentical = pCachedValue->bVal == dbcwWork.value.bVal; break; case DBVT_WORD: bIsIdentical = pCachedValue->wVal == dbcwWork.value.wVal; break; case DBVT_DWORD: bIsIdentical = pCachedValue->dVal == dbcwWork.value.dVal; break; case DBVT_UTF8: case DBVT_ASCIIZ: bIsIdentical = mir_strcmp(pCachedValue->pszVal, dbcwWork.value.pszVal) == 0; break; } if (bIsIdentical) return 0; } m_cache->SetCachedVariant(&dbcwWork.value, pCachedValue); } if (szCachedSettingName[-1] != 0) { lck.unlock(); log2(" set resident as %s (%p)", printVariant(&dbcwWork.value), pCachedValue); NotifyEventHooks(hSettingChangeEvent, contactID, (LPARAM)&dbcwWork); return 0; } } else m_cache->GetCachedValuePtr(contactID, szCachedSettingName, -1); log1(" write database as %s", printVariant(&dbcwWork.value)); DWORD ofsModuleName = GetModuleNameOfs(dbcwWork.szModule); DBContact dbc = *(DBContact*)DBRead(ofsContact, NULL); if (dbc.signature != DBCONTACT_SIGNATURE) return 1; // make sure the module group exists PBYTE pBlob; int bytesRequired, bytesRemaining; DBContactSettings dbcs; DWORD ofsSettingsGroup = GetSettingsGroupOfsByModuleNameOfs(&dbc, ofsModuleName); if (ofsSettingsGroup == 0) { //module group didn't exist - make it switch (dbcwWork.value.type) { case DBVT_ASCIIZ: case DBVT_UTF8: bytesRequired = dbcwWork.value.cchVal + 2; break; case DBVT_BLOB: case DBVT_ENCRYPTED: bytesRequired = dbcwWork.value.cpbVal + 2; break; default: bytesRequired = dbcwWork.value.type; } bytesRequired += 2 + settingNameLen; bytesRequired += (DB_SETTINGS_RESIZE_GRANULARITY - (bytesRequired % DB_SETTINGS_RESIZE_GRANULARITY)) % DB_SETTINGS_RESIZE_GRANULARITY; ofsSettingsGroup = CreateNewSpace(bytesRequired + offsetof(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(DBContact)); DBWrite(ofsSettingsGroup, &dbcs, sizeof(DBContactSettings)); ofsBlobPtr = ofsSettingsGroup + offsetof(DBContactSettings, blob); pBlob = (PBYTE)DBRead(ofsBlobPtr, &bytesRemaining); } else { dbcs = *(DBContactSettings*)DBRead(ofsSettingsGroup, &bytesRemaining); // find if the setting exists ofsBlobPtr = ofsSettingsGroup + offsetof(DBContactSettings, blob); pBlob = (PBYTE)DBRead(ofsBlobPtr, &bytesRemaining); while (pBlob[0]) { NeedBytes(settingNameLen + 1); if (pBlob[0] == settingNameLen && !memcmp(pBlob + 1, dbcwWork.szSetting, settingNameLen)) break; NeedBytes(1); MoveAlong(pBlob[0] + 1); NeedBytes(3); MoveAlong(1 + GetSettingValueLength(pBlob)); NeedBytes(1); } // setting already existed, and up to end of name is in cache if (pBlob[0]) { MoveAlong(1 + settingNameLen); // if different type or variable length and length is different NeedBytes(3); if (pBlob[0] != dbcwWork.value.type || ((pBlob[0] == DBVT_ASCIIZ || pBlob[0] == DBVT_UTF8) && *(PWORD)(pBlob + 1) != dbcwWork.value.cchVal) || ((pBlob[0] == DBVT_BLOB || pBlob[0] == DBVT_ENCRYPTED) && *(PWORD)(pBlob + 1) != dbcwWork.value.cpbVal)) { // bin it NeedBytes(3); int nameLen = 1 + settingNameLen; int valLen = 1 + GetSettingValueLength(pBlob); DWORD 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, &bytesRemaining); } else { // replace existing setting at pBlob MoveAlong(1); // skip data type switch (dbcwWork.value.type) { case DBVT_BYTE: DBWrite(ofsBlobPtr, &dbcwWork.value.bVal, 1); break; case DBVT_WORD: DBWrite(ofsBlobPtr, &dbcwWork.value.wVal, 2); break; case DBVT_DWORD: DBWrite(ofsBlobPtr, &dbcwWork.value.dVal, 4); break; case DBVT_BLOB: DBWrite(ofsBlobPtr + 2, dbcwWork.value.pbVal, dbcwWork.value.cpbVal); break; case DBVT_ENCRYPTED: DBWrite(ofsBlobPtr + 2, dbcwWork.value.pbVal, dbcwWork.value.cpbVal); break; case DBVT_UTF8: case DBVT_ASCIIZ: DBWrite(ofsBlobPtr + 2, dbcwWork.value.pszVal, dbcwWork.value.cchVal); break; } // quit DBFlush(1); lck.unlock(); // notify NotifyEventHooks(hSettingChangeEvent, contactID, (LPARAM)&dbcwNotif); return 0; } } } // cannot do a simple replace, add setting to end of list // pBlob already points to end of list // see if it fits switch (dbcwWork.value.type) { case DBVT_ASCIIZ: case DBVT_UTF8: bytesRequired = dbcwWork.value.cchVal + 2; break; case DBVT_BLOB: case DBVT_ENCRYPTED: bytesRequired = dbcwWork.value.cpbVal + 2; break; default: bytesRequired = dbcwWork.value.type; } bytesRequired += 2 + settingNameLen; bytesRequired += ofsBlobPtr + 1 - (ofsSettingsGroup + offsetof(DBContactSettings, blob)); if ((DWORD)bytesRequired > dbcs.cbBlob) { // doesn't fit: move entire group DBContactSettings *dbcsPrev; DWORD ofsDbcsPrev, ofsNew; InvalidateSettingsGroupOfsCacheEntry(ofsSettingsGroup); 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 = (DBContactSettings*)DBRead(ofsDbcsPrev, NULL); while (dbcsPrev->ofsNext != ofsSettingsGroup) { if (dbcsPrev->ofsNext == 0) DatabaseCorruption(NULL); ofsDbcsPrev = dbcsPrev->ofsNext; dbcsPrev = (DBContactSettings*)DBRead(ofsDbcsPrev, NULL); } } // create the new one ofsNew = ReallocSpace(ofsSettingsGroup, dbcs.cbBlob + offsetof(DBContactSettings, blob), bytesRequired + offsetof(DBContactSettings, blob)); dbcs.cbBlob = bytesRequired; DBWrite(ofsNew, &dbcs, offsetof(DBContactSettings, blob)); if (ofsDbcsPrev == 0) { dbc.ofsFirstSettings = ofsNew; DBWrite(ofsContact, &dbc, sizeof(DBContact)); } else { dbcsPrev = (DBContactSettings*)DBRead(ofsDbcsPrev, NULL); dbcsPrev->ofsNext = ofsNew; DBWrite(ofsDbcsPrev, dbcsPrev, offsetof(DBContactSettings, blob)); } ofsBlobPtr += ofsNew - ofsSettingsGroup; ofsSettingsGroup = ofsNew; pBlob = (PBYTE)DBRead(ofsBlobPtr, &bytesRemaining); } // we now have a place to put it and enough space: make it DBWrite(ofsBlobPtr, &settingNameLen, 1); DBWrite(ofsBlobPtr + 1, (PVOID)dbcwWork.szSetting, settingNameLen); MoveAlong(1 + settingNameLen); DBWrite(ofsBlobPtr, &dbcwWork.value.type, 1); MoveAlong(1); switch (dbcwWork.value.type) { case DBVT_BYTE: DBWrite(ofsBlobPtr, &dbcwWork.value.bVal, 1); MoveAlong(1); break; case DBVT_WORD: DBWrite(ofsBlobPtr, &dbcwWork.value.wVal, 2); MoveAlong(2); break; case DBVT_DWORD: DBWrite(ofsBlobPtr, &dbcwWork.value.dVal, 4); MoveAlong(4); break; case DBVT_BLOB: DBWrite(ofsBlobPtr, &dbcwWork.value.cpbVal, 2); DBWrite(ofsBlobPtr + 2, dbcwWork.value.pbVal, dbcwWork.value.cpbVal); MoveAlong(2 + dbcwWork.value.cpbVal); break; case DBVT_ENCRYPTED: DBWrite(ofsBlobPtr, &dbcwWork.value.cpbVal, 2); DBWrite(ofsBlobPtr + 2, dbcwWork.value.pbVal, dbcwWork.value.cpbVal); MoveAlong(2 + dbcwWork.value.cpbVal); break; case DBVT_UTF8: case DBVT_ASCIIZ: DBWrite(ofsBlobPtr, &dbcwWork.value.cchVal, 2); DBWrite(ofsBlobPtr + 2, dbcwWork.value.pszVal, dbcwWork.value.cchVal); MoveAlong(2 + dbcwWork.value.cchVal); break; } BYTE zero = 0; DBWrite(ofsBlobPtr, &zero, 1); // quit DBFlush(1); lck.unlock(); // notify NotifyEventHooks(hSettingChangeEvent, contactID, (LPARAM)&dbcwNotif); return 0; }
STDMETHODIMP_(HANDLE) CDb3Mmap::AddEvent(MCONTACT contactID, DBEVENTINFO *dbei) { if (dbei == NULL || dbei->cbSize != sizeof(DBEVENTINFO)) return 0; if (dbei->timestamp == 0) return 0; DBEvent dbe; dbe.signature = DBEVENT_SIGNATURE; dbe.contactID = contactID; // store native or subcontact's id dbe.timestamp = dbei->timestamp; dbe.flags = dbei->flags; dbe.wEventType = dbei->eventType; dbe.cbBlob = dbei->cbBlob; BYTE *pBlob = dbei->pBlob; MCONTACT contactNotifyID = contactID; DBCachedContact *ccSub = NULL; if (contactID != 0) { DBCachedContact *cc = m_cache->GetCachedContact(contactID); if (cc == NULL) return NULL; if (cc->IsSub()) { ccSub = cc; // set default sub to the event's source db_mc_setDefault(cc->parentID, contactID, false); contactID = cc->parentID; // and add an event to a metahistory if (db_mc_isEnabled()) contactNotifyID = contactID; } } if (NotifyEventHooks(hEventFilterAddedEvent, contactNotifyID, (LPARAM)dbei)) return NULL; mir_ptr<BYTE> pCryptBlob; if (m_bEncrypted) { size_t len; BYTE *pResult = m_crypto->encodeBuffer(pBlob, dbe.cbBlob, &len); if (pResult != NULL) { pCryptBlob = pBlob = pResult; dbe.cbBlob = (DWORD)len; dbe.flags |= DBEF_ENCRYPTED; } } bool neednotify; mir_cslockfull lck(m_csDbAccess); DWORD ofsContact = GetContactOffset(contactID); DBContact dbc = *(DBContact*)DBRead(ofsContact, sizeof(DBContact), NULL); if (dbc.signature != DBCONTACT_SIGNATURE) return NULL; DWORD ofsNew = CreateNewSpace(offsetof(DBEvent, blob) + dbe.cbBlob); dbe.ofsModuleName = GetModuleNameOfs(dbei->szModule); // find where to put it - sort by timestamp if (dbc.eventCount == 0) { dbe.ofsPrev = dbe.ofsNext = 0; dbc.ofsFirstEvent = dbc.ofsLastEvent = ofsNew; } else { DBEvent *dbeTest = (DBEvent*)DBRead(dbc.ofsFirstEvent, sizeof(DBEvent), NULL); // Should new event be placed before first event in chain? if (dbe.timestamp < dbeTest->timestamp) { dbe.ofsPrev = 0; dbe.ofsNext = dbc.ofsFirstEvent; dbc.ofsFirstEvent = ofsNew; dbeTest = (DBEvent*)DBRead(dbe.ofsNext, sizeof(DBEvent), NULL); dbeTest->ofsPrev = ofsNew; DBWrite(dbe.ofsNext, dbeTest, sizeof(DBEvent)); } else { // Loop through the chain, starting at the end DWORD ofsThis = dbc.ofsLastEvent; dbeTest = (DBEvent*)DBRead(ofsThis, sizeof(DBEvent), NULL); for (;;) { // If the new event's timesstamp is equal to or greater than the // current dbevent, it will be inserted after. If not, continue // with the previous dbevent in chain. if (dbe.timestamp >= dbeTest->timestamp) { dbe.ofsPrev = ofsThis; dbe.ofsNext = dbeTest->ofsNext; dbeTest->ofsNext = ofsNew; DBWrite(ofsThis, dbeTest, sizeof(DBEvent)); if (dbe.ofsNext == 0) dbc.ofsLastEvent = ofsNew; else { dbeTest = (DBEvent*)DBRead(dbe.ofsNext, sizeof(DBEvent), NULL); dbeTest->ofsPrev = ofsNew; DBWrite(dbe.ofsNext, dbeTest, sizeof(DBEvent)); } break; } ofsThis = dbeTest->ofsPrev; dbeTest = (DBEvent*)DBRead(ofsThis, sizeof(DBEvent), NULL); } } } dbc.eventCount++; if (!(dbe.flags & (DBEF_READ | DBEF_SENT))) { if (dbe.timestamp < dbc.tsFirstUnread || dbc.tsFirstUnread == 0) { dbc.tsFirstUnread = dbe.timestamp; dbc.ofsFirstUnread = ofsNew; } neednotify = true; } else neednotify = m_safetyMode; if (ccSub != NULL) { DBContact *pSub = (DBContact*)DBRead(ccSub->dwDriverData, sizeof(DBContact), NULL); pSub->eventCount++; } DBWrite(ofsContact, &dbc, sizeof(DBContact)); DBWrite(ofsNew, &dbe, offsetof(DBEvent, blob)); DBWrite(ofsNew + offsetof(DBEvent, blob), pBlob, dbe.cbBlob); DBFlush(0); lck.unlock(); log1("add event @ %08x", ofsNew); // Notify only in safe mode or on really new events if (neednotify) NotifyEventHooks(hEventAddedEvent, contactNotifyID, (LPARAM)ofsNew); return (HANDLE)ofsNew; }
STDMETHODIMP_(LONG) CDb3Mmap::GetEventCount(MCONTACT contactID) { mir_cslock lck(m_csDbAccess); DBContact *dbc = (DBContact*)DBRead(GetContactOffset(contactID), sizeof(DBContact), NULL); return (dbc->signature != DBCONTACT_SIGNATURE) ? -1 : dbc->eventCount; }
STDMETHODIMP_(BOOL) CDb3Mmap::DeleteEvent(MCONTACT contactID, HANDLE hDbEvent) { DBCachedContact *cc; if (contactID) { if ((cc = m_cache->GetCachedContact(contactID)) == NULL) return 2; if (cc->IsSub()) if ((cc = m_cache->GetCachedContact(cc->parentID)) == NULL) return 3; } else cc = NULL; mir_cslockfull lck(m_csDbAccess); DWORD ofsContact = (cc) ? cc->dwDriverData : m_dbHeader.ofsUser; DBContact dbc = *(DBContact*)DBRead(ofsContact, sizeof(DBContact), NULL); DBEvent dbe = *(DBEvent*)DBRead((DWORD)hDbEvent, sizeof(DBEvent), NULL); if (dbc.signature != DBCONTACT_SIGNATURE || dbe.signature != DBEVENT_SIGNATURE) return 1; lck.unlock(); log1("delete event @ %08x", hContact); // call notifier while outside mutex NotifyEventHooks(hEventDeletedEvent, contactID, (LPARAM)hDbEvent); // get back in lck.lock(); dbc = *(DBContact*)DBRead(ofsContact, sizeof(DBContact), NULL); dbe = *(DBEvent*)DBRead((DWORD)hDbEvent, sizeof(DBEvent), NULL); // check if this was the first unread, if so, recalc the first unread if (dbc.ofsFirstUnread == (DWORD)hDbEvent) { for (DBEvent *dbeNext = &dbe;;) { if (dbeNext->ofsNext == 0) { dbc.ofsFirstUnread = 0; dbc.tsFirstUnread = 0; break; } DWORD ofsThis = dbeNext->ofsNext; dbeNext = (DBEvent*)DBRead(ofsThis, sizeof(DBEvent), NULL); if (!dbeNext->markedRead()) { dbc.ofsFirstUnread = ofsThis; dbc.tsFirstUnread = dbeNext->timestamp; break; } } } // get previous and next events in chain and change offsets if (dbe.ofsPrev == 0) { if (dbe.ofsNext == 0) dbc.ofsFirstEvent = dbc.ofsLastEvent = 0; else { DBEvent *dbeNext = (DBEvent*)DBRead(dbe.ofsNext, sizeof(DBEvent), NULL); dbeNext->ofsPrev = 0; DBWrite(dbe.ofsNext, dbeNext, sizeof(DBEvent)); dbc.ofsFirstEvent = dbe.ofsNext; } } else { if (dbe.ofsNext == 0) { DBEvent *dbePrev = (DBEvent*)DBRead(dbe.ofsPrev, sizeof(DBEvent), NULL); dbePrev->ofsNext = 0; DBWrite(dbe.ofsPrev, dbePrev, sizeof(DBEvent)); dbc.ofsLastEvent = dbe.ofsPrev; } else { DBEvent *dbePrev = (DBEvent*)DBRead(dbe.ofsPrev, sizeof(DBEvent), NULL); dbePrev->ofsNext = dbe.ofsNext; DBWrite(dbe.ofsPrev, dbePrev, sizeof(DBEvent)); DBEvent *dbeNext = (DBEvent*)DBRead(dbe.ofsNext, sizeof(DBEvent), NULL); dbeNext->ofsPrev = dbe.ofsPrev; DBWrite(dbe.ofsNext, dbeNext, sizeof(DBEvent)); } } // decrement event count dbc.eventCount--; DBWrite(ofsContact, &dbc, sizeof(DBContact)); // delete event DeleteSpace((DWORD)hDbEvent, offsetof(DBEvent, blob) + dbe.cbBlob); // also update a sub if (cc && dbe.contactID != cc->contactID) { DBContact *pSub = (DBContact*)DBRead(GetContactOffset(dbe.contactID), sizeof(DBContact), NULL); if (pSub->eventCount > 0) pSub->eventCount--; } DBFlush(0); return 0; }
STDMETHODIMP_(LONG) CDb3Mmap::DeleteContact(MCONTACT contactID) { if (contactID == 0) // global contact cannot be removed return 1; mir_cslockfull lck(m_csDbAccess); DWORD ofsContact = GetContactOffset(contactID); DBContact *dbc = (DBContact*)DBRead(ofsContact, NULL); if (dbc->signature != DBCONTACT_SIGNATURE) return 1; if (ofsContact == m_dbHeader.ofsUser) { log0("FATAL: del of user chain attempted."); return 1; } lck.unlock(); log0("del contact"); // call notifier while outside mutex NotifyEventHooks(hContactDeletedEvent, contactID, 0); // get back in lck.lock(); // delete settings chain DWORD ofsThis = dbc->ofsFirstSettings; DWORD ofsFirstEvent = dbc->ofsFirstEvent; while (ofsThis) { DBContactSettings *dbcs = (DBContactSettings*)DBRead(ofsThis, NULL); DWORD ofsNext = dbcs->ofsNext; DeleteSpace(ofsThis, offsetof(DBContactSettings, blob) + dbcs->cbBlob); ofsThis = ofsNext; } // delete event chain ofsThis = ofsFirstEvent; while (ofsThis) { DBEvent *dbe = (DBEvent*)DBRead(ofsThis, NULL); DWORD ofsNext = dbe->ofsNext; DeleteSpace(ofsThis, offsetof(DBEvent, blob) + dbe->cbBlob); ofsThis = ofsNext; } // find previous contact in chain and change ofsNext if (m_dbHeader.ofsFirstContact == ofsContact) { m_dbHeader.ofsFirstContact = dbc->ofsNext; DBWrite(0, &m_dbHeader, sizeof(m_dbHeader)); } else { DWORD ofsNext = dbc->ofsNext; ofsThis = m_dbHeader.ofsFirstContact; DBContact *dbcPrev = (DBContact*)DBRead(ofsThis, NULL); while (dbcPrev->ofsNext != ofsContact) { if (dbcPrev->ofsNext == 0) DatabaseCorruption(NULL); ofsThis = dbcPrev->ofsNext; dbcPrev = (DBContact*)DBRead(ofsThis, NULL); } dbcPrev->ofsNext = ofsNext; DBWrite(ofsThis, dbcPrev, sizeof(DBContact)); } // delete contact DeleteSpace(ofsContact, sizeof(DBContact)); // decrement contact count m_dbHeader.contactCount--; DBWrite(0, &m_dbHeader, sizeof(m_dbHeader)); DBFlush(0); // free cache item m_cache->FreeCachedContact(contactID); if (contactID == m_hLastCachedContact) m_hLastCachedContact = NULL; return 0; }
void CDb3Mmap::ToggleEventsEncryption(MCONTACT contactID) { DWORD ofsContact = GetContactOffset(contactID); if (ofsContact == 0) return; DBContact contact = *(DBContact*)DBRead(ofsContact, NULL); if (contact.ofsFirstEvent == 0 || contact.signature != DBCONTACT_SIGNATURE) return; // fast cycle through all events for (DWORD offset = contact.ofsFirstEvent; offset != 0;) { DBEvent evt = *(DBEvent*)DBRead(offset, NULL); if (evt.signature != DBEVENT_SIGNATURE) return; size_t len; DWORD ofsDest; mir_ptr<BYTE> pBlob; BYTE *pSource = DBRead(offset + offsetof(DBEvent, blob), 0); if (!m_bEncrypted) { // we need more space if ((pBlob = m_crypto->encodeBuffer(pSource, evt.cbBlob, &len)) == NULL) return; ofsDest = ReallocSpace(offset, offsetof(DBEvent, blob) + evt.cbBlob, offsetof(DBEvent, blob) + (DWORD)len); if (evt.ofsNext) { DBEvent *e = (DBEvent*)DBRead(evt.ofsNext, NULL); e->ofsPrev = ofsDest; DBWrite(evt.ofsNext, e, sizeof(DBEvent)); } if (evt.ofsPrev) { DBEvent *e = (DBEvent*)DBRead(evt.ofsPrev, NULL); e->ofsNext = ofsDest; DBWrite(evt.ofsPrev, e, sizeof(DBEvent)); } if (contact.ofsFirstEvent == offset) contact.ofsFirstEvent = ofsDest; if (contact.ofsLastEvent == offset) contact.ofsLastEvent = ofsDest; if (contact.ofsFirstUnread == offset) contact.ofsFirstUnread = ofsDest; evt.flags |= DBEF_ENCRYPTED; } else { if ((pBlob = (BYTE*)m_crypto->decodeBuffer(pSource, evt.cbBlob, &len)) == NULL) return; ofsDest = offset; // reuse the old space evt.flags &= ~DBEF_ENCRYPTED; if (len < evt.cbBlob) DBFill(ofsDest + offsetof(DBEvent, blob) + (DWORD)len, evt.cbBlob - (DWORD)len); } evt.cbBlob = (DWORD)len; DBWrite(ofsDest, &evt, offsetof(DBEvent, blob)); DBWrite(ofsDest + offsetof(DBEvent, blob), pBlob, (DWORD)len); offset = evt.ofsNext; } DBWrite(ofsContact, &contact, sizeof(DBContact)); }
void CDb3Mmap::ToggleSettingsEncryption(MCONTACT contactID) { DWORD ofsContact = GetContactOffset(contactID); if (ofsContact == 0) return; DBContact *contact = (DBContact*)DBRead(ofsContact, NULL); if (contact->ofsFirstSettings == 0) return; // fast cycle through all settings DBContactSettings *setting = (DBContactSettings*)DBRead(contact->ofsFirstSettings, NULL); DWORD offset = contact->ofsFirstSettings; char *szModule = GetModuleNameByOfs(setting->ofsModuleName); if (szModule == NULL) return; while (true) { OBJLIST<VarDescr> arSettings(10); char szSetting[256]; int bytesRemaining, len; DWORD ofsBlobPtr = offset + offsetof(DBContactSettings, blob), ofsNext = setting->ofsNext; PBYTE pBlob = (PBYTE)DBRead(ofsBlobPtr, &bytesRemaining); while (pBlob[0]) { NeedBytes(1); len = pBlob[0]; memcpy(szSetting, pBlob + 1, len); szSetting[len] = 0; NeedBytes(1 + pBlob[0]); MoveAlong(1 + pBlob[0]); NeedBytes(5); switch (pBlob[0]) { case DBVT_ASCIIZ: len = *(PWORD)(pBlob + 1); // we need to convert a string into utf8 and encrypt it if (!m_bEncrypted) { BYTE bSave = pBlob[len + 3]; pBlob[len + 3] = 0; arSettings.insert(new VarDescr(szSetting, mir_utf8encode((LPCSTR)pBlob + 3))); pBlob[len + 3] = bSave; } NeedBytes(3 + len); break; case DBVT_UTF8: len = *(PWORD)(pBlob + 1); // we need to encrypt these strings if (!m_bEncrypted) { BYTE bSave = pBlob[len + 3]; pBlob[len + 3] = 0; arSettings.insert(new VarDescr(szSetting, (LPCSTR)pBlob + 3)); pBlob[len + 3] = bSave; } NeedBytes(3 + len); break; case DBVT_ENCRYPTED: len = *(PWORD)(pBlob + 1); // we need to decrypt these strings if (m_bEncrypted && !IsSettingEncrypted(szModule, szSetting)) arSettings.insert(new VarDescr(szSetting, pBlob + 3, len)); NeedBytes(3 + len); break; case DBVT_BLOB: NeedBytes(3 + *(PWORD)(pBlob + 1)); break; } NeedBytes(3); MoveAlong(1 + GetSettingValueLength(pBlob)); NeedBytes(1); } for (int i = 0; i < arSettings.getCount(); i++) { VarDescr &p = arSettings[i]; if (!m_bEncrypted) { size_t encodedLen; BYTE *pResult = m_crypto->encodeString(p.szValue, &encodedLen); if (pResult != NULL) { DBCONTACTWRITESETTING dbcws = { szModule, p.szVar }; dbcws.value.type = DBVT_ENCRYPTED; dbcws.value.pbVal = pResult; dbcws.value.cpbVal = (WORD)encodedLen; WriteContactSetting(contactID, &dbcws); mir_free(pResult); } } else { size_t realLen; ptrA decoded(m_crypto->decodeString((PBYTE)(char*)p.szValue, p.iLen, &realLen)); if (decoded != NULL) { DBCONTACTWRITESETTING dbcws = { szModule, p.szVar }; dbcws.value.type = DBVT_UNENCRYPTED; dbcws.value.pszVal = decoded; dbcws.value.cchVal = (WORD)realLen; WriteContactSetting(contactID, &dbcws); } } } if (!ofsNext) break; setting = (DBContactSettings*)DBRead(offset = ofsNext, NULL); if ((szModule = GetModuleNameByOfs(setting->ofsModuleName)) == NULL) break; } }