/////////////////////////////////////////////////////////////////////////////// // // Rule.cpp // /////////////////////////////////////////////////////////////////////////////// #include #include "rule.h" #include "strconst.h" #include "goptions.h" #include "criteria.h" #include "actions.h" #include "ruleutil.h" /////////////////////////////////////////////////////////////////////////////// // // HrCreateRule // // This creates a rule. // // ppIRule - pointer to return the rule // // Returns: S_OK, on success // E_OUTOFMEMORY, if can't create the Rule object // /////////////////////////////////////////////////////////////////////////////// HRESULT HrCreateRule(IOERule ** ppIRule) { COERule * pRule = NULL; HRESULT hr = S_OK; // Check the incoming params if (NULL == ppIRule) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing params *ppIRule = NULL; // Create the rules manager object pRule = new COERule; if (NULL == pRule) { hr = E_OUTOFMEMORY; goto exit; } // Get the rules manager interface hr = pRule->QueryInterface(IID_IOERule, (void **) ppIRule); if (FAILED(hr)) { goto exit; } pRule = NULL; // Set the proper return value hr = S_OK; exit: if (NULL != pRule) { delete pRule; } return hr; } COERule::COERule() { m_cRef = 0; m_dwState = RULE_STATE_NULL; m_pszName = NULL; m_pICrit = NULL; m_pIAct = NULL; m_dwVersion = 0; } COERule::~COERule() { ULONG ulIndex = 0; AssertSz(m_cRef == 0, "Somebody still has a hold of us!!"); SafeMemFree(m_pszName); SafeRelease(m_pICrit); SafeRelease(m_pIAct); } STDMETHODIMP_(ULONG) COERule::AddRef() { return ::InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) COERule::Release() { LONG cRef = 0; cRef = ::InterlockedDecrement(&m_cRef); if (0 == cRef) { delete this; return cRef; } return cRef; } STDMETHODIMP COERule::QueryInterface(REFIID riid, void ** ppvObject) { HRESULT hr = S_OK; // Check the incoming params if (NULL == ppvObject) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing param *ppvObject = NULL; if ((riid == IID_IUnknown) || (riid == IID_IOERule)) { *ppvObject = static_cast(this); } else if ((riid == IID_IPersistStream) || (riid == IID_IPersist)) { *ppvObject = static_cast(this); } else { hr = E_NOINTERFACE; goto exit; } reinterpret_cast(*ppvObject)->AddRef(); hr = S_OK; exit: return hr; } STDMETHODIMP COERule::Reset(void) { HRESULT hr = S_OK; LPSTR pszKeyPath = NULL; LPCSTR pszKeyStart = NULL; // Release the criteria SafeRelease(m_pICrit); // Release the actions SafeRelease(m_pIAct); // Free up the rule name SafeMemFree(m_pszName); // Set the current state m_dwState |= RULE_STATE_INIT; // Clear the dirty bit m_dwState &= ~RULE_STATE_DIRTY; // Set the return value hr = S_OK; return hr; } STDMETHODIMP COERule::GetState(DWORD * pdwState) { HRESULT hr = S_OK; ULONG ulIndex = 0; // Check incoming params if (NULL == pdwState) { hr = E_INVALIDARG; goto exit; } // If we're not enabled if ((0 != (m_dwState & RULE_STATE_DISABLED)) || (0 != (m_dwState & RULE_STATE_INVALID))) { *pdwState = RULE_STATE_NULL; } else { *pdwState = m_dwState; } // Set the proper return value hr = S_OK; exit: return hr; } STDMETHODIMP COERule::Validate(DWORD dwFlags) { HRESULT hr = S_OK; BOOL fValid = FALSE; // If we don't have a criteria or actions object then we fail if ((NULL == m_pICrit) || (NULL == m_pIAct)) { hr = E_FAIL; goto exit; } // Validate the criteria hr = m_pICrit->Validate(dwFlags); if (FAILED(hr)) { goto exit; } fValid = TRUE; if (S_OK != hr) { fValid = FALSE; } // Validate the actions hr = m_pIAct->Validate(dwFlags); if (FAILED(hr)) { goto exit; } if (S_OK != hr) { fValid = FALSE; } // If the rule is invalid, make sure we disable it if (FALSE == fValid) { m_dwState |= RULE_STATE_INVALID; } else { m_dwState &= ~RULE_STATE_INVALID; } // Set the proper return value hr = fValid ? S_OK : S_FALSE; exit: return hr; } STDMETHODIMP COERule::GetProp(RULE_PROP prop, DWORD dwFlags, PROPVARIANT * pvarResult) { HRESULT hr = S_OK; LPSTR pszName = NULL; CRIT_ITEM * pCrit = NULL; ACT_ITEM * pAct = NULL; ULONG cItem = 0; // Check incoming params if (NULL == pvarResult) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing params ZeroMemory(pvarResult, sizeof(*pvarResult)); switch(prop) { case RULE_PROP_NAME: if (NULL == m_pszName) { pszName = PszDupA(""); } else { pszName = PszDupA(m_pszName); } pvarResult->vt = VT_LPSTR; pvarResult->pszVal = pszName; pszName = NULL; break; case RULE_PROP_DISABLED: pvarResult->vt = VT_BOOL; pvarResult->boolVal = !!(m_dwState & RULE_STATE_DISABLED); break; case RULE_PROP_VERSION: pvarResult->vt = VT_UI4; pvarResult->ulVal = m_dwVersion; break; case RULE_PROP_CRITERIA: if (NULL == m_pICrit) { cItem = 0; pCrit = NULL; } else { hr = m_pICrit->GetCriteria(0, &pCrit, &cItem); if (FAILED(hr)) { goto exit; } } pvarResult->vt = VT_BLOB; pvarResult->blob.cbSize = cItem * sizeof(CRIT_ITEM); pvarResult->blob.pBlobData = (BYTE *) pCrit; pCrit = NULL; break; case RULE_PROP_ACTIONS: if (NULL == m_pIAct) { cItem = 0; pAct = NULL; } else { hr = m_pIAct->GetActions(0, &pAct, &cItem); if (FAILED(hr)) { goto exit; } } pvarResult->vt = VT_BLOB; pvarResult->blob.cbSize = cItem * sizeof(ACT_ITEM); pvarResult->blob.pBlobData = (BYTE *) pAct; pAct = NULL; break; default: hr = E_INVALIDARG; goto exit; } // Set the return value hr = S_OK; exit: SafeMemFree(pszName); RuleUtil_HrFreeCriteriaItem(pCrit, cItem); SafeMemFree(pCrit); RuleUtil_HrFreeActionsItem(pAct, cItem); SafeMemFree(pAct); return hr; } STDMETHODIMP COERule::SetProp(RULE_PROP prop, DWORD dwFlags, PROPVARIANT * pvarResult) { HRESULT hr = S_OK; LPSTR pszName = NULL; DWORD dwState = 0; ULONG cItems = 0; // Check incoming params if (NULL == pvarResult) { hr = E_INVALIDARG; goto exit; } switch(prop) { case RULE_PROP_NAME: if ((VT_LPSTR != pvarResult->vt) || (NULL == pvarResult->pszVal)) { hr = E_INVALIDARG; goto exit; } // Create a new copy pszName = PszDupA(pvarResult->pszVal); if (NULL == pszName) { hr = E_OUTOFMEMORY; goto exit; } // Free up any old value SafeMemFree(m_pszName); // Set the new value m_pszName = pszName; pszName = NULL; break; case RULE_PROP_DISABLED: if (VT_BOOL != pvarResult->vt) { hr = E_INVALIDARG; goto exit; } // Set the new value if (FALSE != !!(pvarResult->boolVal)) { m_dwState |= RULE_STATE_DISABLED; } else { Assert(0 == (m_dwState & RULE_STATE_INVALID)); m_dwState &= ~RULE_STATE_DISABLED; } break; case RULE_PROP_VERSION: if (VT_UI4 != pvarResult->vt) { hr = E_INVALIDARG; goto exit; } // Set the new value m_dwVersion = pvarResult->ulVal; break; case RULE_PROP_CRITERIA: if ((VT_BLOB != pvarResult->vt) || (0 == pvarResult->blob.cbSize) || (NULL == pvarResult->blob.pBlobData)) { hr = E_INVALIDARG; goto exit; } if (NULL == m_pICrit) { hr = HrCreateCriteria(&m_pICrit); if (FAILED(hr)) { goto exit; } } cItems = pvarResult->blob.cbSize / sizeof(CRIT_ITEM); Assert(cItems * sizeof(CRIT_ITEM) == pvarResult->blob.cbSize); hr = m_pICrit->SetCriteria(0, (CRIT_ITEM *) pvarResult->blob.pBlobData, cItems); if (FAILED(hr)) { goto exit; } hr = m_pICrit->GetState(&dwState); if (FAILED(hr)) { goto exit; } m_dwState = (m_dwState & ~CRIT_STATE_MASK) | (dwState & CRIT_STATE_MASK); break; case RULE_PROP_ACTIONS: if ((VT_BLOB != pvarResult->vt) || (0 == pvarResult->blob.cbSize) || (NULL == pvarResult->blob.pBlobData)) { hr = E_INVALIDARG; goto exit; } if (NULL == m_pIAct) { hr = HrCreateActions(&m_pIAct); if (FAILED(hr)) { goto exit; } } cItems = pvarResult->blob.cbSize / sizeof(ACT_ITEM); Assert(cItems * sizeof(ACT_ITEM) == pvarResult->blob.cbSize); hr = m_pIAct->SetActions(0, (ACT_ITEM *) pvarResult->blob.pBlobData, cItems); if (FAILED(hr)) { goto exit; } hr = m_pIAct->GetState(&dwState); if (FAILED(hr)) { goto exit; } m_dwState = (m_dwState & ~ACT_STATE_MASK) | (dwState & ACT_STATE_MASK); break; default: hr = E_INVALIDARG; goto exit; } // Mark the rule as dirty m_dwState |= RULE_STATE_DIRTY; // Set the return value hr = S_OK; exit: SafeMemFree(pszName); return hr; } STDMETHODIMP COERule::Evaluate(LPCSTR pszAcct, MESSAGEINFO * pMsgInfo, IMessageFolder * pFolder, IMimePropertySet * pIMPropSet, IMimeMessage * pIMMsg, ULONG cbMsgSize, ACT_ITEM ** ppActions, ULONG * pcActions) { HRESULT hr = S_OK; ACT_ITEM * pAct = NULL; ULONG cAct = 0; // Check incoming variables if (((NULL == pMsgInfo) && (NULL == pIMPropSet)) || (0 == cbMsgSize) || (NULL == ppActions) || (NULL == pcActions)) { hr = E_INVALIDARG; goto exit; } // Set outgoing params to default *ppActions = NULL; *pcActions = 0; // If we don't have a criteria or actions object then we fail if ((NULL == m_pICrit) || (NULL == m_pIAct)) { hr = S_FALSE; goto exit; } // If we ain't valid then we can just bail if (0 != (m_dwState & RULE_STATE_INVALID)) { hr = S_FALSE; goto exit; } // Do we match?? hr = m_pICrit->MatchMessage(pszAcct, pMsgInfo, pFolder, pIMPropSet, pIMMsg, cbMsgSize); if (FAILED(hr)) { goto exit; } // If we didn't match then just return if (S_FALSE == hr) { goto exit; } // Grab the actions and return them to the caller hr = m_pIAct->GetActions(0, &pAct, &cAct); if (FAILED(hr)) { goto exit; } // Set the outgoing parameters *ppActions = pAct; pAct = NULL; *pcActions = cAct; // Set proper return value hr = S_OK; exit: RuleUtil_HrFreeActionsItem(pAct, cAct); SafeMemFree(pAct); return hr; } STDMETHODIMP COERule::LoadReg(LPCSTR pszRegPath) { HRESULT hr = S_OK; LONG lErr = 0; HKEY hkeyRoot = NULL; ULONG cbData = 0; ULONG cbRead = 0; DWORD dwData = 0; LPSTR pszName = NULL; BOOL fDisabled = FALSE; IOECriteria * pICriteria = NULL; IOEActions * pIActions = NULL; LPSTR pszRegPathNew = NULL; ULONG cchRegPath = 0; DWORD dwState = 0; // Check incoming param if (NULL == pszRegPath) { hr = E_INVALIDARG; goto exit; } // Should we fail if we're already loaded? AssertSz(0 == (m_dwState & RULE_STATE_LOADED), "We're already loaded!!!"); // Open the reg key from the path lErr = AthUserOpenKey(pszRegPath, KEY_ALL_ACCESS, &hkeyRoot); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Get the rule name hr = RuleUtil_HrGetRegValue(hkeyRoot, c_szRuleName, NULL, (BYTE **) &pszName, NULL); if (FAILED(hr)) { SafeMemFree(pszName); } // Get the enabled state cbData = sizeof(dwData); lErr = RegQueryValueEx(hkeyRoot, c_szRuleEnabled, 0, NULL, (BYTE *) &dwData, &cbData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } Assert(cbData == sizeof(dwData)); fDisabled = ! (BOOL) dwData; // Get the version of the rule cbData = sizeof(dwData); lErr = RegQueryValueEx(hkeyRoot, c_szRulesVersion, 0, NULL, (BYTE *) &dwData, &cbData); if ((ERROR_SUCCESS != lErr) && (ERROR_FILE_NOT_FOUND != lErr)) { hr = E_FAIL; goto exit; } if (ERROR_FILE_NOT_FOUND == lErr) { dwData = 0; lErr = RegSetValueEx(hkeyRoot, c_szRulesVersion, 0, REG_DWORD, (CONST BYTE *) &dwData, sizeof(dwData)); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } } m_dwVersion = dwData; // Allocate space to hold the new reg path cchRegPath = lstrlen(pszRegPath); Assert(lstrlen(c_szRuleCriteria) >= lstrlen(c_szRuleActions)); DWORD cchSize = (cchRegPath + lstrlen(c_szRuleCriteria) + 2); if (FAILED(HrAlloc((void **) &pszRegPathNew, cchSize))) { goto exit; } // Build reg path to criteria StrCpyN(pszRegPathNew, pszRegPath, cchSize); if ('\\' != pszRegPath[cchRegPath]) { StrCatBuff(pszRegPathNew, g_szBackSlash, cchSize); cchRegPath++; } StrCatBuff(pszRegPathNew, c_szRuleCriteria, cchSize); // Create a new criteria object hr = HrCreateCriteria(&pICriteria); if (FAILED(hr)) { goto exit; } // Get the criteria hr = pICriteria->LoadReg(pszRegPathNew); if (FAILED(hr)) { goto exit; } // Get the state of the criteria hr = pICriteria->GetState(&dwState); if (FAILED(hr)) { goto exit; } m_dwState = (m_dwState & ~CRIT_STATE_MASK) | (dwState & CRIT_STATE_MASK); // Build reg path to actions StrCpyN(pszRegPathNew + cchRegPath, c_szRuleActions, (cchSize - cchRegPath)); // Create a new actions object hr = HrCreateActions(&pIActions); if (FAILED(hr)) { goto exit; } // Get the actions hr = pIActions->LoadReg(pszRegPathNew); if (FAILED(hr)) { goto exit; } // Get the state of the actions hr = pIActions->GetState(&dwState); if (FAILED(hr)) { goto exit; } m_dwState = (m_dwState & ~ACT_STATE_MASK) | (dwState & ACT_STATE_MASK); // Free up the current values SafeMemFree(m_pszName); SafeRelease(m_pICrit); SafeRelease(m_pIAct); // Save the new values m_pszName = pszName; pszName = NULL; if (FALSE == fDisabled) { m_dwState &= ~RULE_STATE_DISABLED; } else { m_dwState |= RULE_STATE_DISABLED; } m_pICrit = pICriteria; pICriteria = NULL; m_pIAct = pIActions; pIActions = NULL; // Make sure we clear the dirty bit m_dwState &= ~RULE_STATE_DIRTY; // Note that we have been loaded m_dwState |= RULE_STATE_LOADED; // Set the return value hr = S_OK; exit: SafeMemFree(pszRegPathNew); SafeRelease(pIActions); SafeRelease(pICriteria); SafeMemFree(pszName); if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } STDMETHODIMP COERule::SaveReg(LPCSTR pszRegPath, BOOL fClearDirty) { HRESULT hr = S_OK; LONG lErr = 0; HKEY hkeyRoot = NULL; DWORD dwDisp = 0; DWORD dwData = 0; LPSTR pszRegPathNew = NULL; ULONG cchRegPath = 0; // Check incoming param if (NULL == pszRegPath) { hr = E_INVALIDARG; goto exit; } // Can't save out a rule if we don't have criteria or actions // or a rule name if ((NULL == m_pICrit) || (NULL == m_pIAct)) { hr = E_FAIL; goto exit; } // Let's make sure we clear out the key first AthUserDeleteKey(pszRegPath); // Create the reg key from the path lErr = AthUserCreateKey(pszRegPath, KEY_ALL_ACCESS, &hkeyRoot, &dwDisp); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } Assert(REG_CREATED_NEW_KEY == dwDisp); // Write out the rule name if (NULL != m_pszName) { lErr = RegSetValueEx(hkeyRoot, c_szRuleName, 0, REG_SZ, (BYTE *) m_pszName, lstrlen(m_pszName) + 1); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } } // Write out the disabled state dwData = !(m_dwState & RULE_STATE_DISABLED); lErr = RegSetValueEx(hkeyRoot, c_szRuleEnabled, 0, REG_DWORD, (BYTE *) &dwData, sizeof(dwData)); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Allocate space to hold the new reg path cchRegPath = lstrlen(pszRegPath); Assert(lstrlen(c_szRuleCriteria) >= lstrlen(c_szRuleActions)); DWORD cchSize = (cchRegPath + lstrlen(c_szRuleCriteria) + 2); if (FAILED(HrAlloc((void **) &pszRegPathNew, cchSize))) { goto exit; } // Build reg path to criteria StrCpyN(pszRegPathNew, pszRegPath, cchSize); if ('\\' != pszRegPath[cchRegPath]) { StrCatBuff(pszRegPathNew, g_szBackSlash, cchSize); cchRegPath++; } StrCatBuff(pszRegPathNew, c_szRuleCriteria, cchSize); // Write out the criteria hr = m_pICrit->SaveReg(pszRegPathNew, fClearDirty); if (FAILED(hr)) { goto exit; } // Build reg path to actions StrCpyN(pszRegPathNew + cchRegPath, c_szRuleActions, (cchSize - cchRegPath)); // Write out the actions hr = m_pIAct->SaveReg(pszRegPathNew, fClearDirty); if (FAILED(hr)) { goto exit; } // Bump up the version if (0 != (m_dwState & RULE_STATE_DIRTY)) { m_dwVersion++; } lErr = RegSetValueEx(hkeyRoot, c_szRulesVersion, 0, REG_DWORD, (BYTE *) &m_dwVersion, sizeof(m_dwVersion)); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Should we clear the dirty bit? if (FALSE != fClearDirty) { m_dwState &= ~RULE_STATE_DIRTY; } // Set the return value hr = S_OK; exit: SafeMemFree(pszRegPathNew); if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } STDMETHODIMP COERule::Clone(IOERule ** ppIRule) { HRESULT hr = S_OK; COERule * pRule = NULL; // Check incoming params if (NULL == ppIRule) { hr = E_INVALIDARG; goto exit; } // Initialize the outgoing params *ppIRule = NULL; // Create a new rule pRule = new COERule; if (NULL == pRule) { hr = E_OUTOFMEMORY; goto exit; } // Copy over the rule name if (NULL != m_pszName) { pRule->m_pszName = PszDupA(m_pszName); if (NULL == pRule->m_pszName) { hr = E_OUTOFMEMORY; goto exit; } } // Copy over the enabled state pRule->m_dwState = m_dwState; // Copy over the version pRule->m_dwVersion = m_dwVersion; // Clone the criteria if (FAILED(m_pICrit->Clone(&(pRule->m_pICrit)))) { goto exit; } // Clone the actions if (FAILED(m_pIAct->Clone(&(pRule->m_pIAct)))) { goto exit; } // Get the rule interface hr = pRule->QueryInterface(IID_IOERule, (void **) ppIRule); if (FAILED(hr)) { goto exit; } pRule = NULL; // Set the proper return value hr = S_OK; exit: if (NULL != pRule) { delete pRule; } return hr; } STDMETHODIMP COERule::GetClassID(CLSID * pclsid) { HRESULT hr = S_OK; if (NULL == pclsid) { hr = E_INVALIDARG; goto exit; } *pclsid = CLSID_OERule; // Set the proper return value hr = S_OK; exit: return hr; } STDMETHODIMP COERule::IsDirty(void) { HRESULT hr = S_OK; hr = (RULE_STATE_DIRTY == (m_dwState & RULE_STATE_DIRTY)) ? S_OK : S_FALSE; return hr; } STDMETHODIMP COERule::Load(IStream * pStm) { HRESULT hr = S_OK; ULONG cbData = 0; ULONG cbRead = 0; DWORD dwData = 0; LPSTR pszName = NULL; BOOL fDisabled = FALSE; IOECriteria * pICriteria = NULL; IPersistStream * pIPStm = NULL; IOEActions * pIActions = NULL; // Check incoming param if (NULL == pStm) { hr = E_INVALIDARG; goto exit; } // Verify we have the correct version hr = pStm->Read(&dwData, sizeof(dwData), &cbRead); if (FAILED(hr)) { goto exit; } if ((cbRead != sizeof(dwData)) || (dwData != RULE_VERSION)) { hr = E_FAIL; goto exit; } // Get the size of the rule name hr = pStm->Read(&cbData, sizeof(cbData), &cbRead); if (FAILED(hr)) { goto exit; } if (cbRead != sizeof(cbData)) { hr = E_FAIL; goto exit; } if (0 != cbData) { // Allocate space to hold the rule name hr = HrAlloc((void **) &pszName, cbData); if (FAILED(hr)) { goto exit; } // Get the rule name hr = pStm->Read(pszName, cbData, &cbRead); if (FAILED(hr)) { goto exit; } if (cbRead != cbData) { hr = E_FAIL; goto exit; } } // Get the enabled state hr = pStm->Read(&dwData, sizeof(dwData), &cbRead); if (FAILED(hr)) { goto exit; } if (cbRead != sizeof(dwData)) { hr = E_FAIL; goto exit; } fDisabled = ! (BOOL) dwData; // Create a new criteria object hr = HrCreateCriteria(&pICriteria); if (FAILED(hr)) { goto exit; } // Get the persistance interface for the criteria hr = pICriteria->QueryInterface(IID_IPersistStream, (void **) &pIPStm); if (FAILED(hr)) { goto exit; } // Get the criteria hr = pIPStm->Load(pStm); if (FAILED(hr)) { goto exit; } // Create a new actions object hr = HrCreateActions(&pIActions); if (FAILED(hr)) { goto exit; } // Get the persistance interface for the actions pIPStm->Release(); pIPStm = NULL; hr = pIActions->QueryInterface(IID_IPersistStream, (void **) &pIPStm); if (FAILED(hr)) { goto exit; } // Get the actions hr = pIPStm->Load(pStm); if (FAILED(hr)) { goto exit; } // Free up the current values SafeMemFree(m_pszName); SafeRelease(m_pICrit); SafeRelease(m_pIAct); // Save the new values m_pszName = pszName; pszName = NULL; if (FALSE == fDisabled) { m_dwState &= ~RULE_STATE_DISABLED; } else { m_dwState |= RULE_STATE_DISABLED; } m_pICrit = pICriteria; pICriteria = NULL; m_pIAct = pIActions; pIActions = NULL; // Make sure we clear the dirty bit m_dwState &= ~RULE_STATE_DIRTY; // Note that we have been loaded m_dwState |= RULE_STATE_LOADED; // Set the return value hr = S_OK; exit: SafeRelease(pIActions); SafeRelease(pICriteria); SafeRelease(pIPStm); SafeMemFree(pszName); return hr; } STDMETHODIMP COERule::Save(IStream * pStm, BOOL fClearDirty) { HRESULT hr = S_OK; ULONG cbData = 0; ULONG cbWritten = 0; DWORD dwData = 0; ULONG ulIndex = 0; IPersistStream * pIPStm = NULL; // Check incoming param if (NULL == pStm) { hr = E_INVALIDARG; goto exit; } // Can't write out a rule if we don't have criteria or actions // or a rule name if ((NULL == m_pICrit) || (NULL == m_pIAct)) { hr = E_FAIL; goto exit; } // Write out the version dwData = RULE_VERSION; hr = pStm->Write(&dwData, sizeof(dwData), &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == sizeof(dwData)); // Write out the size of the rule name if (NULL != m_pszName) { cbData = lstrlen(m_pszName) + 1; } else { cbData = 0; } hr = pStm->Write(&cbData, sizeof(cbData), &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == sizeof(cbData)); if (NULL != m_pszName) { // Write out the rule name hr = pStm->Write(m_pszName, cbData, &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == cbData); } // Write out the enabled state dwData = !(m_dwState & RULE_STATE_DISABLED); hr = pStm->Write(&dwData, sizeof(dwData), &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == sizeof(dwData)); // Get the persistance interface for the criteria hr = m_pICrit->QueryInterface(IID_IPersistStream, (void **) &pIPStm); if (FAILED(hr)) { goto exit; } // Write out the criteria hr = pIPStm->Save(pStm, fClearDirty); if (FAILED(hr)) { goto exit; } // Get the persistance interface for the actions pIPStm->Release(); pIPStm = NULL; hr = m_pIAct->QueryInterface(IID_IPersistStream, (void **) &pIPStm); if (FAILED(hr)) { goto exit; } // Write out the actions hr = pIPStm->Save(pStm, fClearDirty); if (FAILED(hr)) { goto exit; } // Should we clear out the dirty bit if (FALSE != fClearDirty) { m_dwState &= ~RULE_STATE_DIRTY; } // Set the return value hr = S_OK; exit: SafeRelease(pIPStm); return hr; }