//----------------------------------------------------------------------------- // // // File: mailadmq.cpp // // Description: Implementation for CMailMsgAdminLink // // Author: Gautam Pulla (GPulla) // // History: // 6/24/1999 - GPulla Created // // Copyright (C) 1999 Microsoft Corporation // //----------------------------------------------------------------------------- #include "aqprecmp.h" #include "linkmsgq.h" #include "mailadmq.h" #include "dcontext.h" #include "dsnevent.h" #include "asyncq.inl" #include "asyncadm.inl" //---[ CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueNDR ]----------------------- // // // Description: // Wraps call to NDR MailMsg // Parameters: // *pIUnknown - IUnkown of MailMsg // Returns: // S_OK on success // History: // 12/7/2000 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueNDR( IUnknown *pIUnknownMsg) { TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueNDR"); HRESULT hr = S_OK; IMailMsgProperties *pIMailMsgProperties = NULL; CDSNParams dsnparams; _ASSERT(pIUnknownMsg); _ASSERT(m_paqinst); hr = pIUnknownMsg->QueryInterface(IID_IMailMsgProperties, (void **) &pIMailMsgProperties); _ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!"); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr); goto Exit; } // // Initialize DSN params // SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__); dsnparams.dwStartDomain = 0; dsnparams.dwDSNActions = DSN_ACTION_FAILURE_ALL; dsnparams.pIMailMsgProperties = pIMailMsgProperties; dsnparams.hrStatus = AQUEUE_E_QADMIN_NDR; // // Attempt to NDR message // hr = HrLinkAllDomains(pIMailMsgProperties); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "Unable to link all domains for DSN generation", hr); goto Exit; } // // Fire DSN Generation event // hr = m_paqinst->HrTriggerDSNGenerationEvent(&dsnparams, FALSE); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "Unable to NDR message via QAPI 0x%08X", hr); goto Exit; } // // Now that we have generated an NDR... we need to delete the // Message. // hr = HrDeleteMsgFromQueueSilent(pIUnknownMsg); if (FAILED(hr)) goto Exit; Exit: if (pIMailMsgProperties) pIMailMsgProperties->Release(); TraceFunctLeave(); return hr; } //---[ CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueSilent ]-------------------- // // // Description: // Wrapper function to silently delete a message from a queue // Parameters: // *pIUnknown - IUnkown of MailMsg // Returns: // S_OK on success // History: // 12/7/2000 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueSilent( IUnknown *pIUnknownMsg) { TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueSilent"); HRESULT hr = S_OK; IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL; _ASSERT(pIUnknownMsg); hr = pIUnknownMsg->QueryInterface(IID_IMailMsgQueueMgmt, (void **) &pIMailMsgQueueMgmt); _ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!"); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr); goto Exit; } // // Attempt to delete the message // hr = pIMailMsgQueueMgmt->Delete(NULL); if (FAILED(hr)) ErrorTrace((LPARAM) this, "Unable to delete msg 0x%08X", hr); Exit: if (pIMailMsgQueueMgmt) pIMailMsgQueueMgmt->Release(); TraceFunctLeave(); return hr; } //---[ CAsyncAdminMailMsgQueue::HrFreezeMsg ]----------------------------------- // // // Description: // Wrapper to freeze a pIMailMsgProperties // Parameters: // *pIUnknown - IUnkown of MailMsg // Returns: // S_OK on success // History: // 12/7/2000 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrFreezeMsg(IUnknown *pIUnknownMsg) { TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrFreezeMsg"); HRESULT hr = S_OK; IMailMsgProperties *pIMailMsgProperties = NULL; _ASSERT(pIUnknownMsg); hr = pIUnknownMsg->QueryInterface(IID_IMailMsgProperties, (void **) &pIMailMsgProperties); _ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!"); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr); goto Exit; } // // $$TODO - Attempt to freeze the message -- Not supported for this type of queue // Exit: if (pIMailMsgProperties) pIMailMsgProperties->Release(); TraceFunctLeave(); return hr; } //---[ CAsyncAdminMailMsgQueue::HrThawMsg ]------------------------------------- // // // Description: // Wrapper function to thaw a message // Parameters: // *pIUnknown - IUnkown of MailMsg // Returns: // S_OK on success // History: // 12/7/2000 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrThawMsg(IUnknown *pIUnknownMsg) { TraceFunctEnterEx((LPARAM) this, "AsyncAdminMailMsgQueue::HrThawMsg"); HRESULT hr = S_OK; IMailMsgProperties *pIMailMsgProperties = NULL; _ASSERT(pIUnknownMsg); hr = pIUnknownMsg->QueryInterface(IID_IMailMsgProperties, (void **) &pIMailMsgProperties); _ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!"); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr); goto Exit; } // // $$TODO - Attempt to thaw message -- Not supported for this type of queue // Exit: if (pIMailMsgProperties) pIMailMsgProperties->Release(); TraceFunctLeave(); return hr; } //---[ CAsyncAdminMailMsgQueue::HrGetStatsForMsg ]------------------------------ // // // Description: // Wrapper function to fill in the CAQStats struct for a message // Parameters: // *pIUnknown - IUnkown of MailMsg // *paqstats - Ptr to aqstats struction to fill in. // Returns: // S_OK on success // History: // 12/7/2000 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrGetStatsForMsg( IUnknown *pIUnknownMsg, CAQStats *paqstats) { TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrGetStatsForMsg"); HRESULT hr = S_OK; IMailMsgProperties *pIMailMsgProperties = NULL; _ASSERT(pIUnknownMsg); _ASSERT(paqstats); hr = pIUnknownMsg->QueryInterface(IID_IMailMsgProperties, (void **) &pIMailMsgProperties); _ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!"); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr); goto Exit; } // // $$TODO - GetStats for Msg -- Not supported for this type of queue // Exit: if (pIMailMsgProperties) pIMailMsgProperties->Release(); TraceFunctLeave(); return hr; } //---[ CAsyncAdminMailMsgQueue::HrSendDelayOrNDR ]----------------------------- // // // Description: // Checks the MailMsg to see if it has expired or needs a delay DSN sent // and acts accordingly // Parameters: // IMailMsgProperties - The MailMsg that needs to be checked // Returns: // S_OK : OK, may have sent delay NDR // S_FALSE : OK, MailMsg handled (NDR'd or nothing left to do) // Or returns error from called fnct. // History: // 5/15/2001 - dbraun Created // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrSendDelayOrNDR(IMailMsgProperties *pIMailMsgProperties) { HRESULT hr = S_OK; DWORD cbProp = 0; DWORD dwTimeContext = 0; CDSNParams dsnparams; BOOL fSentDelay = FALSE; BOOL fSentNDR = FALSE; FILETIME ftExpireTimeNDR; FILETIME ftExpireTimeDelay; TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrSendDelayOrNDR"); _ASSERT(m_paqinst); // Try to get the expire time from the message, otherwise calculate it // from the file time hr = pIMailMsgProperties->GetProperty(IMMPID_MP_LOCAL_EXPIRE_NDR, sizeof(FILETIME), &cbProp, (BYTE *) &ftExpireTimeNDR); if (MAILMSG_E_PROPNOTFOUND == hr) { // Prop not set ... calculate it from the file time hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME, sizeof(FILETIME), &cbProp, (BYTE *) &ftExpireTimeNDR); if (FAILED(hr)) { // Message should not make it this far without being stamped _ASSERT(MAILMSG_E_PROPNOTFOUND != hr); // Prop not set or other failure, we cannot expire this message goto Exit; } m_paqinst->CalcExpireTimeNDR(ftExpireTimeNDR, TRUE, &ftExpireTimeNDR); } else if (FAILED(hr)) { goto Exit; } hr = pIMailMsgProperties->GetProperty(IMMPID_MP_LOCAL_EXPIRE_DELAY, sizeof(FILETIME), &cbProp, (BYTE *) &ftExpireTimeDelay); if (MAILMSG_E_PROPNOTFOUND == hr) { // Prop not set ... calculate it from the file time hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME, sizeof(FILETIME), &cbProp, (BYTE *) &ftExpireTimeDelay); if (FAILED(hr)) { // Message should not make it this far without being stamped _ASSERT(MAILMSG_E_PROPNOTFOUND != hr); // Prop not set or other failure, we cannot expire this message goto Exit; } m_paqinst->CalcExpireTimeDelay(ftExpireTimeDelay, TRUE, &ftExpireTimeDelay); } else if (FAILED(hr)) { goto Exit; } // // Initialize DSN params // SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__); dsnparams.dwStartDomain = 0; dsnparams.dwDSNActions = 0; dsnparams.pIMailMsgProperties = pIMailMsgProperties; dsnparams.hrStatus = 0; // Check if we have passed either expire time if (m_paqinst->fInPast(&ftExpireTimeNDR, &dwTimeContext)) { dsnparams.dwDSNActions |= DSN_ACTION_FAILURE_ALL; dsnparams.hrStatus = AQUEUE_E_MSG_EXPIRED; fSentNDR = TRUE; } else if (m_paqinst->fInPast(&ftExpireTimeDelay, &dwTimeContext)) { dsnparams.dwDSNActions |= DSN_ACTION_DELAYED; dsnparams.hrStatus = AQUEUE_E_MSG_EXPIRED; fSentDelay = TRUE; } // If we are going to generate an NDR if (dsnparams.hrStatus) { // // Attempt to NDR message // hr = HrLinkAllDomains(pIMailMsgProperties); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "Unable to link all domains for DSN generation", hr); goto Exit; } // // Fire DSN Generation event // hr = m_paqinst->HrTriggerDSNGenerationEvent(&dsnparams, FALSE); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "Unable to NDR message via QAPI 0x%08X", hr); goto Exit; } // Return based on what we did if (fSentNDR) { // This message has been handled, delete it hr = HrDeleteMsgFromQueueSilent(pIMailMsgProperties); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "Failed to delete message after sending NDR", hr); goto Exit; } // NDR'd and successfully deleted message hr = S_FALSE; } else if (fSentDelay) { hr = S_OK; } } Exit: TraceFunctLeave(); return hr; } //---[ CAsyncAdminMailMsgQueue::fHandleCompletionFailure ]--------------------- // // // Description: // Overrides base class and checks to see if message has expired before // putting it on the retry queue // Parameters: // IMailMsgProperties - The MailMsg that triggered failure // Returns: // - // History: // 5/15/2001 - dbraun Created // //----------------------------------------------------------------------------- BOOL CAsyncAdminMailMsgQueue::fHandleCompletionFailure(IMailMsgProperties *pIMailMsgProperties) { HRESULT hr = S_OK; // Has this message expired? hr = HrSendDelayOrNDR (pIMailMsgProperties); if (hr == S_FALSE) { // This message was NDR'd, we are done return TRUE; } else { return CAsyncAdminQueue::fHandleCompletionFailure(pIMailMsgProperties); } } //---[ CAsyncAdminMailMsgQueue::HrQueueRequest ]------------------------------- // // // Description: // Function that will queue a request to the async queue and close // the handles associated with a message if we are above our simple // "throttle" limit. // Parameters: // pIMailMsgProperties The IMailMsgProperties interface to queue // fRetry TRUE - if this message is being retried // FALSE - otherwise // cMsgsInSystem The total number of messages in the system // Returns: // S_OK on success // Error code from async queue on failure. // History: // 10/7/1999 - MikeSwa Created // 12/7/2000 - MikeSwa Moved to CAsyncAdminMailMsgQueue from asyncq.cpp // 4/6/2001 - MikeSwa Modified to take into account queue length // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrQueueRequest(IMailMsgProperties *pIMailMsgProperties, BOOL fRetry, DWORD cMsgsInSystem) { TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrQueueRequest"); IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL; HRESULT hr = S_OK; DWORD cThresholdUsed = g_cMaxIMsgHandlesThreshold; DWORD cItemsPending = dwGetTotalThreads()+m_cItemsPending; if (m_qhmgr.fShouldCloseHandle(cItemsPending, m_cPendingAsyncCompletions, cMsgsInSystem)) { DebugTrace((LPARAM) this, "INFO: Closing IMsg Content - %d messsages in system", cMsgsInSystem); hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgQueueMgmt, (void **) &pIMailMsgQueueMgmt); if (SUCCEEDED(hr)) { //bounce usage count off of zero pIMailMsgQueueMgmt->ReleaseUsage(); pIMailMsgQueueMgmt->AddUsage(); pIMailMsgQueueMgmt->Release(); } else { ErrorTrace((LPARAM) this, "Unable to QI for IMailMsgQueueMgmt - hr 0x%08X", hr); } } TraceFunctLeave(); return CAsyncAdminQueue::HrQueueRequest(pIMailMsgProperties, fRetry); } //---[ CAsyncAdminMailMsgQueue::HrQueueRequest ]------------------------------- // // // Description: // Since we inherit from AsyncQueue who implmenents this, we should assert // so that a dev adding a new call to this class later on, will use the // version that closes handles. // // In RTL this will force the handles closed and queue the request // Parameters: // pIMailMsgProperties The IMailMsgProperties interface to queue // fRetry TRUE - if this message is being retried // FALSE - otherwise // Returns: // returns return value from proper version of HrQueueRequest // History: // 10/7/1999 - MikeSwa Created // 12/7/2000 - MikeSwa Moved to CAsyncAdminMailMsgQueue from asyncq.cpp // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrQueueRequest(IMailMsgProperties *pIMailMsgProperties, BOOL fRetry) { _ASSERT(0 && "Should use HrQueueRequest with 3 parameters"); return HrQueueRequest(pIMailMsgProperties, fRetry, g_cMaxIMsgHandlesThreshold+1); } //---[ CAsyncAdminMailMsgQueue::HrApplyQueueAdminFunction ]-------------------- // // // Description: // Will call the IQueueAdminMessageFilter::Process message for every // message in this queue. If the message passes the filter, then // HrApplyActionToMessage on this object will be called. // // This is different from other implemenentations in that the // location of the message implies something about the state of the // message. Messages in the retry... or frozen queue are considered // failed or frozen. // // We do this instead of writing a mailmsg property, because of the // *huge* perf hit (we would ruin our async message flow by blocking // to check if a message is frozen). We already have the retry // queue, so it makes sense to use it in a similar manner // // Parameters: // IN pIQueueAdminMessageFilter // Returns: // S_OK on success // History: // 2/23/99 - MikeSwa Created // 12/7/2000 - MikeSwa Modified - Made template base class // 12/13/2000 - MikeSwa Modified from CAsyncAdminQueue // //----------------------------------------------------------------------------- STDMETHODIMP CAsyncAdminMailMsgQueue::HrApplyQueueAdminFunction( IQueueAdminMessageFilter *pIQueueAdminMessageFilter) { HRESULT hr = S_OK; CQueueAdminContext qapictx(m_pAQNotify, m_paqinst); _ASSERT(pIQueueAdminMessageFilter); hr = pIQueueAdminMessageFilter->HrSetQueueAdminAction( (IQueueAdminAction *) this); //This is an internal interface that should not fail _ASSERT(SUCCEEDED(hr) && "HrSetQueueAdminAction"); if (FAILED(hr)) goto Exit; hr = pIQueueAdminMessageFilter->HrSetCurrentUserContext(&qapictx); //This is an internal interface that should not fail _ASSERT(SUCCEEDED(hr) && "HrSetCurrentUserContext"); if (FAILED(hr)) goto Exit; // // Iterate over messages in the base queue // qapictx.SetQueueState(LI_READY); hr = HrMapFnBaseQueue(m_pfnMessageAction, pIQueueAdminMessageFilter); // // Iterate over messages in the retry queue // qapictx.SetQueueState(LI_RETRY); hr = HrMapFnRetryQueue(m_pfnMessageAction, pIQueueAdminMessageFilter); hr = pIQueueAdminMessageFilter->HrSetCurrentUserContext(NULL); //This is an internal interface that should not fail _ASSERT(SUCCEEDED(hr) && "HrSetCurrentUserContext"); if (FAILED(hr)) goto Exit; Exit: return hr; } //----------------------------------------------------------------------------- // Description: // Used to query the admin interfaces for CMailMsgAdminLink // Parameters: // IN REFIID riid GUID for interface // OUT LPVOID *ppvObj Ptr to Interface. // // Returns: // S_OK Interface supported by this class. // E_POINTER NULL parameter. // E_NOINTERFACE No such interface exists. // History: // 6/25/1999 - GPulla Created //----------------------------------------------------------------------------- STDMETHODIMP CMailMsgAdminLink::QueryInterface(REFIID riid, LPVOID *ppvObj) { HRESULT hr = S_OK; if (!ppvObj) { hr = E_POINTER; goto Exit; } if (IID_IUnknown == riid) { *ppvObj = static_cast(this); } else if (IID_IQueueAdminAction == riid) { *ppvObj = static_cast(this); } else if (IID_IQueueAdminLink == riid) { *ppvObj = static_cast(this); } else { *ppvObj = NULL; hr = E_NOINTERFACE; goto Exit; } static_cast(*ppvObj)->AddRef(); Exit: return hr; } //---[ CMailMsgAdminLink::CMailMsgAdminLink]--------------------------------- // // // Description: // Default constructor for CMailMsgAdminLink // Parameters: // IN guidLink GUID to associate with this object // IN szQueueName Name to associate with admin object // IN *pasyncmmq Async MailMsg queue for precat or prerouting // IN dwLinkType Bit-Field identifying this admin object // IN paqinst CAQSvrInst object // // Returns: // - // History: // 6/25/1999 - GPulla Created // //----------------------------------------------------------------------------- CMailMsgAdminLink::CMailMsgAdminLink( GUID guid, LPSTR szQueueName, CAsyncAdminMailMsgQueue *pasyncmmq, DWORD dwLinkType, CAQSvrInst *paqinst ) : m_aqsched(guid, 0) { _ASSERT(pasyncmmq); _ASSERT(szQueueName); _ASSERT(paqinst); m_guid = guid; m_cbQueueName = lstrlen(szQueueName); m_szQueueName = (LPSTR) pvMalloc(m_cbQueueName+1); _ASSERT(m_szQueueName); if(m_szQueueName) lstrcpy(m_szQueueName, szQueueName); m_pasyncmmq = pasyncmmq; m_dwLinkType = dwLinkType; m_dwSignature = MAIL_MSG_ADMIN_QUEUE_VALID_SIGNATURE; if (m_pasyncmmq) m_pasyncmmq->SetAQNotify((IAQNotify *) this); m_paqinst = paqinst; ZeroMemory(&m_ftRetry, sizeof(m_ftRetry)); } //---[CMailMsgAdminLink::~CMailMsgAdminLink]--------------------------------- // Description: // Destructor. // Parameters: // - // Returns: // - // History: // 6/24/1999 - GPulla created //----------------------------------------------------------------------------- CMailMsgAdminLink::~CMailMsgAdminLink() { if (m_szQueueName) FreePv(m_szQueueName); m_dwSignature = MAIL_MSG_ADMIN_QUEUE_INVALID_SIGNATURE; } //---[ CMailMsgAdminLink::HrApplyQueueAdminFunction ]--------------------------- // // // Description: // Wrapper to call into underlying queue's implementation // Parameters: // IN pIQueueAdminMessageFilter // Returns: // S_OK on success // S_FALSE if no contained queue (will assert as well) // History: // 12/11/2000 - MikeSwa Created // //----------------------------------------------------------------------------- STDMETHODIMP CMailMsgAdminLink::HrApplyQueueAdminFunction( IQueueAdminMessageFilter *pIQueueAdminMessageFilter) { HRESULT hr = S_FALSE; _ASSERT(m_pasyncmmq); if (m_pasyncmmq) hr = m_pasyncmmq->HrApplyQueueAdminFunction(pIQueueAdminMessageFilter); return hr; } //---[ CMailMsgAdminLink::HrApplyActionToMessage ]----------------------------- // // // Description: // Wrapper function to pass call on to queue implementation // Parameters: // IN *pIUnknownMsg ptr to message abstraction // IN ma Message action to perform // IN pvContext Context set on IQueueAdminFilter // OUT pfShouldDelete TRUE if the message should be deleted // Returns: // S_OK on success // S_FALSE if no contained queue (will assert as well) // History: // 12/11/2000 - MikeSwa Created // //----------------------------------------------------------------------------- STDMETHODIMP CMailMsgAdminLink::HrApplyActionToMessage( IUnknown *pIUnknownMsg, MESSAGE_ACTION ma, PVOID pvContext, BOOL *pfShouldDelete) { HRESULT hr = S_FALSE; _ASSERT(m_pasyncmmq); if (m_pasyncmmq) { hr = m_pasyncmmq->HrApplyActionToMessage(pIUnknownMsg, ma, pvContext, pfShouldDelete); } return hr; } //---[CMailMsgAdminLink::HrGetLinkInfo]--------------------------------------- // Description: // Gets information about this admin object. Note that the diagnostic error // is not implemented for this object but the parameter is supported purely // to support the IQueueAction interface. // Parameters: // OUT LINK_INFO *pliLinkInfo Struct to fill information into. // OUT HRESULT *phrDiagnosticError Diagnostic error if any, for this link // Returns: // S_OK on success // E_POINTER if argument is NULL // E_OUTOFMEMORY if unable to allocate memory for returning information. // History: // 6/24/1999 - GPulla created //----------------------------------------------------------------------------- STDMETHODIMP CMailMsgAdminLink::HrGetLinkInfo(LINK_INFO *pliLinkInfo, HRESULT *phrDiagnosticError) { TraceFunctEnterEx((LPARAM) this, "CMailMsgAdminLink::HrGetLinkInfo"); HRESULT hr = S_OK; _ASSERT(m_pasyncmmq); _ASSERT(pliLinkInfo); if(!m_pasyncmmq) { hr = S_FALSE; goto Exit; } if(!pliLinkInfo) { hr = E_POINTER; goto Exit; } // // Get the link state from our base queue implementation // pliLinkInfo->fStateFlags = m_pasyncmmq->dwQueueAdminLinkGetLinkState(); // // If we are in retry... try to report the time // if (LI_RETRY & pliLinkInfo->fStateFlags) QueueAdminFileTimeToSystemTime(&m_ftRetry, &(pliLinkInfo->stNextScheduledConnection)); pliLinkInfo->fStateFlags |= GetLinkType(); pliLinkInfo->szLinkName = wszQueueAdminConvertToUnicode(m_szQueueName, m_cbQueueName); if (!pliLinkInfo->szLinkName) { hr = E_OUTOFMEMORY; goto Exit; } //We return 0 since size statistics are not calculated pliLinkInfo->cbLinkVolume.QuadPart = 0; // // Include the items queued for retry in the total count // pliLinkInfo->cMessages = m_pasyncmmq->cQueueAdminGetNumItems(); pliLinkInfo->dwSupportedLinkActions = LA_KICK | LA_THAW | LA_FREEZE; //Write diagnostic *phrDiagnosticError = S_OK; Exit: TraceFunctLeave(); return hr; } //---[CMailMsgAdminLink::HrGetNumQueues]------------------------------------- // Description: // Used to query number of queues in object. Since this class does not // expose the one queue it contains, 0 is returned, // Parameters: // OUT DWORD *pcQueues # of queues (0) written to this. // Returns: // S_OK unless... // E_POINTER parameter is not allocated // History: // 6/24/1999 - GPulla created // 12/11/2000 - MikeSwa Updated to support sub-queues //----------------------------------------------------------------------------- STDMETHODIMP CMailMsgAdminLink::HrGetNumQueues(DWORD *pcQueues) { _ASSERT (pcQueues); if (!pcQueues) return E_POINTER; *pcQueues = 1; return S_OK; } //---[CMailMsgAdminLink::HrApplyActionToLink]--------------------------------- // Description: // Applies action to the embedded queue. Only kicking the queue is supported. // Parameters: // IN LINK_ACTION la Action to apply. // Returns: // S_OK Action was successfully applied. // S_FALSE Action not supported or severe error. // History: // 6/24/1999 - GPulla created //----------------------------------------------------------------------------- STDMETHODIMP CMailMsgAdminLink::HrApplyActionToLink(LINK_ACTION la) { HRESULT hr = S_OK; _ASSERT(m_pasyncmmq); if (!m_pasyncmmq) { hr = S_FALSE; goto Exit; } if (LA_KICK == la) m_pasyncmmq->StartRetry(); //kick off processing else if (LA_FREEZE == la) m_pasyncmmq->FreezeQueue(); else if (LA_THAW == la) m_pasyncmmq->ThawQueue(); else hr = S_FALSE; Exit: return hr; } //---[CMailMsgAdminLink::HrGetQueueIDs]--------------------------------------- // Description: // Returns an enumeration of embedded queues in this object. Since the one // emmbedded queue is not exposed, zero queues are returned. // Parameters: // OUT DWORD *pcQueues Number of queues (0) // OUT QUEUELINK_ID *rgQueues Array into which queueIDs are returned. // Returns: // S_OK Success // E_POINTER pcQueues is NULL // History: // 6/24/1999 - GPulla created // 12/11/2000 - MikeSwa Modified to expose queues //----------------------------------------------------------------------------- STDMETHODIMP CMailMsgAdminLink::HrGetQueueIDs(DWORD *pcQueues, QUEUELINK_ID *rgQueues) { TraceFunctEnterEx((LPARAM) this, "CMailMsgAdminLink::HrGetQueueIDs"); _ASSERT(pcQueues); _ASSERT(rgQueues); HRESULT hr = S_OK; QUEUELINK_ID* pCurrentQueueID = rgQueues; if(!pcQueues) { hr = E_POINTER; goto Exit; } if (*pcQueues < 1) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); goto Exit; } *pcQueues = 0; _ASSERT(m_pasyncmmq); hr = m_pasyncmmq->HrGetQueueID(pCurrentQueueID); if (FAILED(hr)) goto Exit; *pcQueues = 1; Exit: TraceFunctLeave(); return hr; } //---[CMailMsgAdminLink::fMatchesID]------------------------------------------ // Description: // Checks if this admin object matches a specified ID. // Parameters: // IN QUEUELINK_ID *pQueueLinkID Ptr to ID to be matched against. // Returns: // TRUE on match. // FALSE if did not matched or unrecoverable error (m_szQueueName not alloced) // History: // 6/24/1999 - GPulla created //----------------------------------------------------------------------------- BOOL STDMETHODCALLTYPE CMailMsgAdminLink::fMatchesID(QUEUELINK_ID *pQueueLinkID) { _ASSERT(pQueueLinkID); _ASSERT(pQueueLinkID->szName); _ASSERT(m_szQueueName); if(!m_szQueueName) return FALSE; CAQScheduleID aqsched(pQueueLinkID->uuid, pQueueLinkID->dwId); if (!fIsSameScheduleID(&aqsched)) return FALSE; if (!fBiStrcmpi(m_szQueueName, pQueueLinkID->szName)) return FALSE; //Everything matched! return TRUE; } //---[ CMailMsgAdminLink::QuerySupportedActions ]------------------------------ // // // Description: // Returns the actions and filters that this implementation supports // Parameters: // pdwSupportedActions - QAPI MsgActions that this queue suppprts // pdwSupportedFilterFlags - QAPI filter flags that this queue supports // Returns: // S_OK on success // History: // 12/12/2000 - MikeSwa Created // //----------------------------------------------------------------------------- STDMETHODIMP CMailMsgAdminLink::QuerySupportedActions( DWORD *pdwSupportedActions, DWORD *pdwSupportedFilterFlags) { HRESULT hr = S_OK; _ASSERT(m_pasyncmmq); if (m_pasyncmmq) { hr = m_pasyncmmq->QuerySupportedActions(pdwSupportedActions, pdwSupportedFilterFlags); } return hr; } //---[CMailMsgAdminLink::fIsSameScheduleID]----------------------------------- // Description: // Helper function for fMatchesID() // Parameters: // Returns: // TRUE if schedule IDs are identical // FALSE otherwise. // History: // 6/24/1999 - GPulla created //----------------------------------------------------------------------------- BOOL CMailMsgAdminLink::fIsSameScheduleID(CAQScheduleID *paqsched) { return (m_aqsched.fIsEqual(paqsched)); } //---[CMailMsgAdminLink::HrGetLinkID]----------------------------------------- // Description: // Get the ID for this admin object. // Parameters: // OUT QUEUELINK_ID *pLinkID struct into which to put ID. // Returns: // S_OK Successfully copied out ID. // E_POINTER out struct is NULL. // E_OUTOFMEMORY Cannot allocate memory for output of ID name. // History: // 6/24/1999 - GPulla created //----------------------------------------------------------------------------- HRESULT CMailMsgAdminLink::HrGetLinkID(QUEUELINK_ID *pLinkID) { HRESULT hr = S_OK; _ASSERT(pLinkID); if(!pLinkID) { hr = E_POINTER; goto Exit; } pLinkID->qltType = QLT_LINK; pLinkID->dwId = m_aqsched.dwGetScheduleID(); m_aqsched.GetGUID(&pLinkID->uuid); if (!fRPCCopyName(&pLinkID->szName)) hr = E_OUTOFMEMORY; else hr = S_OK; Exit: return hr; } //---[CMailMsgAdminLink::fRPCCopyName]---------------------------------------- // Description: // Helper function to create a unicode copy of the string identifying // this admin object. The unicode string is de-allocated by RPC. // Parameters: // OUT LPWSTR *pwszLinkName Ptr to wchar string allocated and written // into by this function. // Returns: // TRUE On success. // FALSE if there is no name for this object // FALSE if memory cannot be allocated for unicode string. // History: // 6/24/1999 - GPulla created //----------------------------------------------------------------------------- BOOL CMailMsgAdminLink::fRPCCopyName(OUT LPWSTR *pwszLinkName) { _ASSERT(pwszLinkName); if (!m_cbQueueName || !m_szQueueName) return FALSE; *pwszLinkName = wszQueueAdminConvertToUnicode(m_szQueueName, m_cbQueueName); if (!*pwszLinkName) return FALSE; return TRUE; } //---[ CAsyncAdminMailMsgLink::HrNotify ]-------------------------------------- // // // Description: // Notification for stats purposes // Parameters: // // Returns: // // History: // 1/10/2001 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CMailMsgAdminLink::HrNotify(CAQStats *aqstats, BOOL fAdd) { UpdateCountersForLinkType(m_paqinst, m_dwLinkType); return S_OK; } //---[ CMailMsgAdminLink::SetNextRetry ]--------------------------------------- // // // Description: // Updates internal retry time // Parameters: // pft Filetime to update to // Returns: // - // History: // 1/16/2001 - MikeSwa Created // //----------------------------------------------------------------------------- void CMailMsgAdminLink::SetNextRetry(FILETIME *pft) { if (pft) memcpy(&m_ftRetry, pft, sizeof(FILETIME)); } //--------[ CAsyncAdminMailMsgQueue::fMatchesQueueAdminFilter ]----------------- // // Description: // Checks a message against a queue admin message filter to see if it // is a match // Parameters: // IN pIMailMsgProperties mail msg object to perform check on // IN paqmf Message Filter to check against // Returns: // TRUE if it matches // FALSE if it does not // History: // 8/8/00 - t-toddc created // 12/11/2000 - MikeSwa Merged for checkin // //----------------------------------------------------------------------------- BOOL CAsyncAdminMailMsgQueue::fMatchesQueueAdminFilter( IN IMailMsgProperties* pIMailMsgProperties, IN CAQAdminMessageFilter* paqmf) { TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "CAsyncAdminMailMsgQueue::fMatchesQueueAdminFilter"); BOOL fMatch = TRUE; DWORD dwFilterFlags = 0; DWORD cbMsgSize = 0; DWORD cbProp = 0; FILETIME ftQueueEntry = {0, 0}; LPSTR szSender = NULL; LPSTR szMsgId = NULL; LPSTR szRecip = NULL; BOOL fFoundRecipString = FALSE; HRESULT hr = S_OK; DWORD cOpenHandlesForMsg = 1; //don't close by default _ASSERT(pIMailMsgProperties); _ASSERT(paqmf); dwFilterFlags = paqmf->dwGetMsgFilterFlags(); if (!dwFilterFlags) { fMatch = FALSE; goto Exit; } if (AQ_MSG_FILTER_ALL & dwFilterFlags) { fMatch = TRUE; goto Exit; } // check size. if (AQ_MSG_FILTER_LARGER_THAN & dwFilterFlags) { //Get the size of the message hr = HrQADMGetMsgSize(pIMailMsgProperties, &cbMsgSize); if (FAILED(hr)) { fMatch = FALSE; goto Exit; } fMatch = paqmf->fMatchesSize(cbMsgSize); if (!fMatch) goto Exit; } if (AQ_MSG_FILTER_OLDER_THAN & dwFilterFlags) { //Get time message was queued hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME, sizeof(FILETIME), &cbProp, (BYTE *) &ftQueueEntry); if (FAILED(hr)) { ErrorTrace((LPARAM) pIMailMsgProperties, "Unable to get arrival time 0x%08X", hr); fMatch = FALSE; goto Exit; } fMatch = paqmf->fMatchesTime(&ftQueueEntry); if (!fMatch) goto Exit; } if (AQ_MSG_FILTER_FROZEN & dwFilterFlags) { // obtaining state information about freezing/thawing not supported yet. fMatch = FALSE; if (AQ_MSG_FILTER_INVERTSENSE & dwFilterFlags) fMatch = !fMatch; if (!fMatch) goto Exit; } if (AQ_MSG_FILTER_FAILED & dwFilterFlags) { // fMatch was originally set to TRUE // currently, no information about failures is available // for IMailMsgProperties mail msg objects fMatch = FALSE; if (AQ_MSG_FILTER_INVERTSENSE & dwFilterFlags) fMatch = !fMatch; if (!fMatch) goto Exit; } //If we haven't failed by this point, we may need to AddUsage and read //props from the mailmsg. Double-check to make sure that we need to //add usage. if (!((AQ_MSG_FILTER_MESSAGEID | AQ_MSG_FILTER_SENDER | AQ_MSG_FILTER_RECIPIENT) & dwFilterFlags)) goto Exit; // // Check to see if the message is already open // hr = pIMailMsgProperties->GetDWORD( IMMPID_MPV_MESSAGE_OPEN_HANDLES, &cOpenHandlesForMsg); if (FAILED(hr)) { ErrorTrace((LPARAM) pIMailMsgProperties, "Not running SP1 of W2K"); cOpenHandlesForMsg = 0; hr = S_OK; } if (AQ_MSG_FILTER_MESSAGEID & dwFilterFlags) { hr = HrQueueAdminGetStringProp(pIMailMsgProperties, IMMPID_MP_RFC822_MSG_ID, &szMsgId); if (FAILED(hr)) szMsgId = NULL; fMatch = paqmf->fMatchesId(szMsgId); if (!fMatch) goto Exit; } if (AQ_MSG_FILTER_SENDER & dwFilterFlags) { fMatch = paqmf->fMatchesMailMsgSender(pIMailMsgProperties); if (!fMatch) goto Exit; } if (AQ_MSG_FILTER_RECIPIENT & dwFilterFlags) { fMatch = paqmf->fMatchesMailMsgRecipient(pIMailMsgProperties); if (!fMatch) goto Exit; } Exit: // // If this operation resulted in opening the message, we should close it // The message is not dirty (we did not write anything), so it should not // need to commit // if (!cOpenHandlesForMsg) { HRESULT hrTmp = S_OK; hrTmp = HrReleaseIMailMsgUsageCount(pIMailMsgProperties); if (SUCCEEDED(hrTmp)) HrIncrementIMailMsgUsageCount(pIMailMsgProperties); } if (szMsgId) QueueAdminFree(szMsgId); TraceFunctLeave(); return fMatch; } //---[ CAsyncAdminMailMsgQueue::HrGetQueueAdminMsgInfo ]----------------------- // // Description: // Fills out a queue admin MESSAGE_INFO structure. All allocations are // done with pvQueueAdminAlloc to be freed by the RPC code // Parameters: // IN pIMailMsgProperties mail msg object to get info from // IN OUT pMsgInfo MESSAGE_INFO struct to dump data to // Returns: // S_OK on success // AQUEUE_E_MESSAGE_HANDLED if the underlying message has been deleted // E_OUTOFMEMORY if an allocation failure // History: // 8/8/00 - t-toddc created // 12/11/2000 - MikeSwa Merged for checkin // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrGetQueueAdminMsgInfo( IMailMsgProperties* pIMailMsgProperties, MESSAGE_INFO* pMsgInfo, PVOID pvContext) { TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "CAsyncAdminMailMsgQueue::HrGetQueueAdminMsgInfo"); HRESULT hr = S_OK; DWORD cbProp = 0; DWORD cOpenHandlesForMsg = 0; LPSTR szRecipients = NULL; LPSTR szCCRecipients = NULL; LPSTR szBCCRecipients = NULL; FILETIME ftSubmitted = {0,0}; //Origination time property buffer FILETIME ftQueueEntry = {0,0}; FILETIME ftExpire = {0,0}; DWORD cbMsgSize = 0; CQueueAdminContext *pqapictx = (CQueueAdminContext *) pvContext; _ASSERT(pIMailMsgProperties); _ASSERT(pMsgInfo); _ASSERT(pqapictx); _ASSERT(pqapictx->fIsValid()); // // If we have a context... it better be valid // if (pqapictx && !pqapictx->fIsValid()) { _ASSERT(FALSE && "CQueueAdminContext is not valid"); pqapictx = NULL; //be defensive... don't use it. } // // Check to see if the message is already open // hr = pIMailMsgProperties->GetDWORD( IMMPID_MPV_MESSAGE_OPEN_HANDLES, &cOpenHandlesForMsg); if (FAILED(hr)) { ErrorTrace((LPARAM) pIMailMsgProperties, "Not running SP1 of W2K"); cOpenHandlesForMsg = 0; hr = S_OK; } // // Extract properties that are stored only on mailmsg (this is shared // with all QAPI code). // hr = HrGetMsgInfoFromIMailMsgProperty(pIMailMsgProperties, pMsgInfo); if (FAILED(hr)) goto Exit; //can't report the number of failures from IMailMsgProperties pMsgInfo->cFailures = 0; //Get the size of the message hr = HrQADMGetMsgSize(pIMailMsgProperties, &cbMsgSize); if (FAILED(hr)) goto Exit; pMsgInfo->cbMessageSize = cbMsgSize; //Get time message was queued hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME, sizeof(FILETIME), &cbProp, (BYTE *) &ftQueueEntry); if (FAILED(hr)) { // there is a possibility that the msg will not have entry time // (i.e. presubmission queue) if (MAILMSG_E_PROPNOTFOUND == hr) { ZeroMemory(&ftQueueEntry, sizeof(FILETIME)); hr = S_OK; } else goto Exit; } //Get submission and expiration times QueueAdminFileTimeToSystemTime(&ftQueueEntry, &pMsgInfo->stReceived); //Get the time the message entered the org hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ORIGINAL_ARRIVAL_TIME, sizeof(FILETIME), &cbProp, (BYTE *) &ftSubmitted); if (FAILED(hr)) { //Time was not written... use entry time. hr = S_OK; memcpy(&ftSubmitted, &ftQueueEntry, sizeof(FILETIME)); } QueueAdminFileTimeToSystemTime(&ftSubmitted, &pMsgInfo->stSubmission); // Try to get the expire time from the message, otherwise calculate it // from the file time hr = pIMailMsgProperties->GetProperty(IMMPID_MP_LOCAL_EXPIRE_NDR, sizeof(FILETIME), &cbProp, (BYTE *) &ftExpire); if (MAILMSG_E_PROPNOTFOUND == hr) { if (pqapictx->paqinstGetAQ()) { // Prop not set ... calculate it from the file time pqapictx->paqinstGetAQ()->CalcExpireTimeNDR(ftQueueEntry, TRUE, &ftExpire); // This is OK hr = S_OK; } else { // This shouldn't happen but we don't want to crash in RTL over it _ASSERT(FALSE && "AQInst was not set in context!"); // We can return this field blank ZeroMemory(&ftExpire, sizeof(FILETIME)); hr = S_OK; } } else if (FAILED(hr)) { goto Exit; } QueueAdminFileTimeToSystemTime(&ftExpire, &pMsgInfo->stExpiry); // // Get the state of the message // pMsgInfo->fMsgFlags = MP_NORMAL; if (pqapictx) { if (LI_RETRY == pqapictx->lfGetQueueState()) pMsgInfo->fMsgFlags |= MP_MSG_RETRY; else if (LI_FROZEN == pqapictx->lfGetQueueState()) pMsgInfo->fMsgFlags |= MP_MSG_FROZEN; } Exit: // // If this operation resulted in opening the message, we should close it // The message is not dirty (we did not write anything), so it should not // need to commit // if (!cOpenHandlesForMsg) { HRESULT hrTmp = S_OK; hrTmp = HrReleaseIMailMsgUsageCount(pIMailMsgProperties); if (SUCCEEDED(hrTmp)) HrIncrementIMailMsgUsageCount(pIMailMsgProperties); } TraceFunctLeave(); return hr; } //---[ CAsyncAdminMailMsgQueue::HrInternalQuerySupportedActions ]--------------- // // // Description: // Returns the actions and filters that this implementation supports // Parameters: // pdwSupportedActions - QAPI MsgActions that this queue suppprts // pdwSupportedFilterFlags - QAPI filter flags that this queue supports // Returns: // S_OK on success // History: // 12/12/2000 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncAdminMailMsgQueue::HrInternalQuerySupportedActions( DWORD *pdwSupportedActions, DWORD *pdwSupportedFilterFlags) { TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrInternalQuerySupportedActions"); HRESULT hr = S_OK; hr = QueryDefaultSupportedActions(pdwSupportedActions, pdwSupportedFilterFlags); if (FAILED(hr)) goto Exit; // // This queue implementation does not support all of the default flags. // _ASSERT(pdwSupportedActions); _ASSERT(pdwSupportedFilterFlags); // // We don't support: // - Freeze global - No status to set on a mailmsg // - Thaw global - can't freeze... therefore cannot thaw // *pdwSupportedActions &= ~(MA_FREEZE_GLOBAL | MA_THAW_GLOBAL); // // We don't support // - Checking for frozen messages (we have no status to indicate // that a message is frozen) // *pdwSupportedFilterFlags &= ~(MF_FROZEN); Exit: return hr; }