359 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			359 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//-----------------------------------------------------------------------------
 | 
						|
//
 | 
						|
//
 | 
						|
//  File: 
 | 
						|
//      smtpconn.cpp
 | 
						|
//  Description:
 | 
						|
//      Implementation of CSMTPConn
 | 
						|
//  Author: Mike Swafford (MikeSwa)
 | 
						|
//
 | 
						|
//  History:
 | 
						|
//
 | 
						|
//  Copyright (C) 1998 Microsoft Corporation
 | 
						|
//
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
#include "aqprecmp.h"
 | 
						|
#include "SMTPConn.h"
 | 
						|
#include "connmgr.h"
 | 
						|
#include "domcfg.h"
 | 
						|
 | 
						|
CPool CSMTPConn::s_SMTPConnPool;
 | 
						|
 | 
						|
//---[ CSMTPConn::CSMTPConn() ]------------------------------------------------
 | 
						|
//
 | 
						|
//
 | 
						|
//  Description: 
 | 
						|
//      CSMTPConn constructor
 | 
						|
//  Parameters:
 | 
						|
//      IN  pConnMgr                    Ptr to instance connection manager
 | 
						|
//      IN  plmq                        Ptr to link for this connection
 | 
						|
//      IN  cMaxMessagesPerConnection   Max messages to send per connection
 | 
						|
//                                      0 implies unlimited
 | 
						|
//  Returns:
 | 
						|
//      -
 | 
						|
//
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
CSMTPConn::CSMTPConn(CConnMgr *pConnMgr, CLinkMsgQueue *plmq, 
 | 
						|
                     DWORD cMaxMessagesPerConnection)
 | 
						|
{
 | 
						|
    _ASSERT(pConnMgr);
 | 
						|
    _ASSERT(plmq);
 | 
						|
 | 
						|
    m_dwSignature = SMTP_CONNECTION_SIG;
 | 
						|
    m_pConnMgr = pConnMgr;
 | 
						|
    m_pIntDomainInfo = NULL;
 | 
						|
    m_plmq = plmq;
 | 
						|
    m_cFailedMsgs = 0;
 | 
						|
    m_cTriedMsgs = 0;
 | 
						|
    m_cMaxMessagesPerConnection = cMaxMessagesPerConnection;
 | 
						|
    m_dwConnectionStatus = CONNECTION_STATUS_OK;
 | 
						|
    m_szDomainName = NULL;
 | 
						|
    m_cbDomainName = 0;
 | 
						|
    m_liConnections.Flink = NULL;
 | 
						|
    m_liConnections.Blink = NULL;
 | 
						|
    m_cAcks = 0;
 | 
						|
    m_dwTickCountOfLastAck = 0;
 | 
						|
 | 
						|
    if (plmq)
 | 
						|
    {
 | 
						|
        plmq->AddRef();
 | 
						|
    }
 | 
						|
}
 | 
						|
                     
 | 
						|
//---[ CSMTPConn::~CSMTPConn() ]-----------------------------------------------
 | 
						|
//
 | 
						|
//
 | 
						|
//  Description: 
 | 
						|
//      CSMTPConn default destructor
 | 
						|
//  Parameters:
 | 
						|
//      -
 | 
						|
//  Returns:
 | 
						|
//      -
 | 
						|
//
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
CSMTPConn::~CSMTPConn()
 | 
						|
{
 | 
						|
    HRESULT hrConnectionStatus = S_OK;
 | 
						|
    BOOL    fHardErrorForceNDR = FALSE;
 | 
						|
    _ASSERT(m_cAcks == m_cTriedMsgs);
 | 
						|
    if (m_plmq != NULL)
 | 
						|
    {
 | 
						|
        _ASSERT(m_pConnMgr);
 | 
						|
        m_pConnMgr->ReleaseConnection(this, &fHardErrorForceNDR);
 | 
						|
 | 
						|
        switch(m_dwConnectionStatus)
 | 
						|
        {
 | 
						|
            case CONNECTION_STATUS_OK:
 | 
						|
                hrConnectionStatus = S_OK;
 | 
						|
                break;
 | 
						|
            case CONNECTION_STATUS_FAILED:
 | 
						|
                hrConnectionStatus = AQUEUE_E_HOST_NOT_RESPONDING;
 | 
						|
                break;
 | 
						|
            case CONNECTION_STATUS_DROPPED:
 | 
						|
                hrConnectionStatus = AQUEUE_E_CONNECTION_DROPPED;
 | 
						|
                break;
 | 
						|
            case CONNECTION_STATUS_FAILED_LOOPBACK:
 | 
						|
                hrConnectionStatus = AQUEUE_E_LOOPBACK_DETECTED;
 | 
						|
                break;
 | 
						|
            case CONNECTION_STATUS_FAILED_NDR_UNDELIVERED:
 | 
						|
                hrConnectionStatus = AQUEUE_E_NDR_ALL;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                _ASSERT(0 && "Undefined Connection Status");
 | 
						|
                hrConnectionStatus = S_OK;
 | 
						|
        }
 | 
						|
 | 
						|
        m_plmq->SetLastConnectionFailure(hrConnectionStatus);
 | 
						|
        m_plmq->RemoveConnection(this, fHardErrorForceNDR);
 | 
						|
 | 
						|
        m_plmq->Release();
 | 
						|
 | 
						|
        //We should kick the connection manager, because if we were generating
 | 
						|
        //DSNs, no connection could be made
 | 
						|
        m_pConnMgr->KickConnections();
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_pIntDomainInfo)
 | 
						|
        m_pIntDomainInfo->Release();
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//---[ CSMTPConn::GetNextMessage ]---------------------------------------------
 | 
						|
//
 | 
						|
//
 | 
						|
//  Description: 
 | 
						|
//      Implementation of ISMTPConnection::GetNextMsg.
 | 
						|
//      Gets the next message queued for this connection and determines which 
 | 
						|
//      recipients should be delivered for this connection.
 | 
						|
//  Parameters:
 | 
						|
//      OUT ppimsg          New IMsg top be delivered
 | 
						|
//      OUT pdwMsgContext   A 32-bit Context that needs to be provided in the 
 | 
						|
//                          message ack.
 | 
						|
//      OUT pcIndexes       The number of index in prgdwRecipIndex
 | 
						|
//      OUT prgdwRecipIndex Recipient indexes that the caller is responsible 
 | 
						|
//                          for attempting delivery to.
 | 
						|
//  Returns:
 | 
						|
//
 | 
						|
//
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
STDMETHODIMP CSMTPConn::GetNextMessage(
 | 
						|
        OUT IMailMsgProperties  **ppIMailMsgProperties, 
 | 
						|
        OUT DWORD ** ppvMsgContext, 
 | 
						|
        OUT DWORD *  pcIndexes, 
 | 
						|
        OUT DWORD ** prgdwRecipIndex)
 | 
						|
{
 | 
						|
    TraceFunctEnterEx((LPARAM) this, "CSMTPConn::GetNextMessage");
 | 
						|
    HRESULT hr = S_OK;
 | 
						|
 | 
						|
   //We get the next message only if we are under the batch limit
 | 
						|
 | 
						|
    if(m_cMaxMessagesPerConnection && 
 | 
						|
       (m_cTriedMsgs >= m_cMaxMessagesPerConnection) &&
 | 
						|
       (!m_pIntDomainInfo || 
 | 
						|
        !((DOMAIN_INFO_TURN_ONLY | DOMAIN_INFO_ETRN_ONLY) &
 | 
						|
          m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags)))
 | 
						|
    {
 | 
						|
        //SMTP does not check - but we may need a specific error for this case
 | 
						|
        hr = AQUEUE_E_QUEUE_EMPTY;
 | 
						|
        goto Exit;
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_pConnMgr && m_pConnMgr->fConnectionsStoppedByAdmin())
 | 
						|
    {
 | 
						|
        //Admin has requested that all outbound connections stop
 | 
						|
        hr = AQUEUE_E_QUEUE_EMPTY;
 | 
						|
        goto Exit;
 | 
						|
    }
 | 
						|
 | 
						|
    hr = m_plmq->HrGetNextMsg(&m_dcntxtCurrentDeliveryContext, ppIMailMsgProperties, 
 | 
						|
                              pcIndexes, prgdwRecipIndex);
 | 
						|
    if (FAILED(hr))
 | 
						|
        goto Exit;  
 | 
						|
    //this will automagically catch the queue empty case...
 | 
						|
    //If the Link has no more messages it will return AQUEUE_E_QUEUE_EMPTY, which
 | 
						|
    //should cause the caller to Release() and query GetNextConnection again.
 | 
						|
 | 
						|
    *ppvMsgContext = (DWORD *) &m_dcntxtCurrentDeliveryContext;
 | 
						|
 | 
						|
    //increment the messages served
 | 
						|
    InterlockedIncrement((PLONG)&m_cTriedMsgs);
 | 
						|
 | 
						|
  Exit:
 | 
						|
    if (!m_cTriedMsgs)
 | 
						|
        DebugTrace((LPARAM) this, "GetNextMessage called, but no messages tried for this connection");
 | 
						|
 | 
						|
    //rewrite error for SMTPSVC
 | 
						|
    if (AQUEUE_E_QUEUE_EMPTY == hr)
 | 
						|
        hr = HRESULT_FROM_WIN32(ERROR_EMPTY);
 | 
						|
 | 
						|
    TraceFunctLeave();
 | 
						|
    return hr;
 | 
						|
}
 | 
						|
 | 
						|
//---[ CSMTPConn::AckMessage ]-------------------------------------------------
 | 
						|
//
 | 
						|
//
 | 
						|
//  Description: 
 | 
						|
//      Acknowledges the delivery of a message (success/error codes are put in
 | 
						|
//      the envelope by the transport).
 | 
						|
//
 | 
						|
//      Implements ISMTPConnection::AckMessage();
 | 
						|
//  Parameters:
 | 
						|
//      IN pIMsg        IMsg to acknowledge
 | 
						|
//      IN dwMsgContext Context that was returned by GetNextMessage
 | 
						|
//      IN eMsgStatus   Status of message
 | 
						|
//  Returns:
 | 
						|
//      S_OK on success
 | 
						|
//      E_INVALIDARG if dwMsgContext is invalid
 | 
						|
//
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
STDMETHODIMP CSMTPConn::AckMessage(/*[in]*/ MessageAck *pMsgAck)
 | 
						|
{
 | 
						|
    HRESULT hr = S_OK;
 | 
						|
    DWORD   dwTickCount = GetTickCount();
 | 
						|
    _ASSERT(m_plmq);
 | 
						|
    _ASSERT(pMsgAck);
 | 
						|
 | 
						|
    if (!(pMsgAck->dwMsgStatus & MESSAGE_STATUS_ALL_DELIVERED))
 | 
						|
    {
 | 
						|
        m_cFailedMsgs++;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    InterlockedIncrement((PLONG)&m_cAcks);
 | 
						|
    _ASSERT(m_cAcks == m_cTriedMsgs);
 | 
						|
    hr = m_plmq->HrAckMsg(pMsgAck);
 | 
						|
 | 
						|
    m_dwTickCountOfLastAck = dwTickCount; //Set after assert so we can compare
 | 
						|
 | 
						|
    return hr;
 | 
						|
}
 | 
						|
 | 
						|
//---[ CSMTPConn::GetSMTPDomain ]----------------------------------------------
 | 
						|
//
 | 
						|
//
 | 
						|
//  Description: 
 | 
						|
//      Returns the SMTPDomain of the link associated with this connections.
 | 
						|
//
 | 
						|
//      $$REVIEW:
 | 
						|
//      This method does not allocate new memory for this string, but instead 
 | 
						|
//      relies on the good intentions of the SMTP stack (or test driver) to 
 | 
						|
//      not overwrite this memory. If we ever expose this interface externally,
 | 
						|
//      then we should revert to allocating memory and doing a buffer copy
 | 
						|
//
 | 
						|
//      Implements ISMTPConnection::GetSMTPDomain
 | 
						|
//  Parameters:
 | 
						|
//      IN OUT  pDomainInfo     Ptr to DomainInfo struct supplied by caller
 | 
						|
//                              and filled in here
 | 
						|
//  Returns:
 | 
						|
//      S_OK on success
 | 
						|
//
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
STDMETHODIMP CSMTPConn::GetDomainInfo(IN OUT DomainInfo *pDomainInfo)
 | 
						|
{
 | 
						|
    HRESULT hr = S_OK;
 | 
						|
 | 
						|
    _ASSERT(pDomainInfo->cbVersion >= sizeof(DomainInfo));
 | 
						|
    _ASSERT(pDomainInfo);
 | 
						|
 | 
						|
    if (NULL == m_plmq)
 | 
						|
    {
 | 
						|
        hr = AQUEUE_E_LINK_INVALID;
 | 
						|
        goto Exit;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!m_pIntDomainInfo)
 | 
						|
    {
 | 
						|
        //Try to get domain info
 | 
						|
        hr = m_plmq->HrGetDomainInfo(&m_cbDomainName, &m_szDomainName,
 | 
						|
                            &m_pIntDomainInfo);
 | 
						|
        if (FAILED(hr))
 | 
						|
        {
 | 
						|
            m_pIntDomainInfo = NULL;
 | 
						|
            _ASSERT(AQUEUE_E_INVALID_DOMAIN != hr);
 | 
						|
            goto Exit;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    _ASSERT(m_pIntDomainInfo);
 | 
						|
    _ASSERT(m_cbDomainName);
 | 
						|
    _ASSERT(m_szDomainName);
 | 
						|
 | 
						|
    // Is it OK to send client side commands on this connection
 | 
						|
    // If not, we reset those domain info flags so SMTp cannot see them
 | 
						|
    if(!m_plmq->fCanSendCmd())
 | 
						|
    {
 | 
						|
         m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &= ~(DOMAIN_INFO_SEND_TURN | DOMAIN_INFO_SEND_ETRN);
 | 
						|
    }
 | 
						|
 | 
						|
    // If SMTP doesn't have the DOMAIN_INFO_TURN_ON_EMPTY then it is the older,
 | 
						|
    // broken SMTP and we shouldn't allow TURN on empty to work.
 | 
						|
    if ((m_plmq->cGetTotalMsgCount() == 0) && 
 | 
						|
        !(m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags & 
 | 
						|
          DOMAIN_INFO_TURN_ON_EMPTY))
 | 
						|
    {
 | 
						|
         m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &= ~DOMAIN_INFO_SEND_TURN;
 | 
						|
    }
 | 
						|
 | 
						|
    //copy everything but size
 | 
						|
    memcpy(&(pDomainInfo->dwDomainInfoFlags), 
 | 
						|
            &(m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags), 
 | 
						|
            sizeof(DomainInfo) - sizeof(DWORD));
 | 
						|
 | 
						|
    //make sure our assumptions about the struct of DomainInfo are valid
 | 
						|
    _ASSERT(1 == ((DWORD *) &(pDomainInfo->dwDomainInfoFlags)) - ((DWORD *) pDomainInfo));
 | 
						|
 | 
						|
    //we've filled pDomainInfo with the info for our Domain
 | 
						|
    if (pDomainInfo->szDomainName[0] == '*')
 | 
						|
    {
 | 
						|
        //we matched a wildcard domain... substitute our domain name
 | 
						|
        pDomainInfo->cbDomainNameLength = m_cbDomainName;
 | 
						|
        pDomainInfo->szDomainName = m_szDomainName;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        //if it wasn't a wildcard match... strings should match!
 | 
						|
        _ASSERT(0 == _stricmp(m_szDomainName, pDomainInfo->szDomainName));
 | 
						|
    }
 | 
						|
 | 
						|
  Exit:
 | 
						|
    return hr;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//---[ CSMTPConn::SetDiagnosticInfo ]------------------------------------------
 | 
						|
//
 | 
						|
//
 | 
						|
//  Description: 
 | 
						|
//      Sets the extra diagnostic information for this connection.
 | 
						|
//  Parameters:
 | 
						|
//      IN      hrDiagnosticError       Error code... if SUCCESS we thow away
 | 
						|
//                                      the rest of the information
 | 
						|
//      IN      szDiagnosticVerb        String pointing to the protocol
 | 
						|
//                                      verb that caused the failure.
 | 
						|
//      IN      szDiagnosticResponse    String that contains the remote
 | 
						|
//                                      servers response.
 | 
						|
//  Returns:
 | 
						|
//      S_OK always
 | 
						|
//  History:
 | 
						|
//      2/18/99 - MikeSwa Created 
 | 
						|
//
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
STDMETHODIMP CSMTPConn::SetDiagnosticInfo(
 | 
						|
                    IN  HRESULT hrDiagnosticError,
 | 
						|
                    IN  LPCSTR szDiagnosticVerb,
 | 
						|
                    IN  LPCSTR szDiagnosticResponse)
 | 
						|
{
 | 
						|
 | 
						|
    TraceFunctEnterEx((LPARAM) this, "CSMTPConn::SetDiagnosticInfo");
 | 
						|
 | 
						|
    if (m_plmq && FAILED(hrDiagnosticError))
 | 
						|
    {
 | 
						|
        m_plmq->SetDiagnosticInfo(hrDiagnosticError, szDiagnosticVerb,
 | 
						|
                                  szDiagnosticResponse);
 | 
						|
    }
 | 
						|
    TraceFunctLeave();
 | 
						|
    return S_OK; //always return S_OK
 | 
						|
}
 |