CONVREPT.CPP
// --convrept.cpp------------------------------------------------ 
// 
// CIPMConvert source code for IPM reports. 
// 
// Copyright (C) Microsoft Corp., 1986-1996.  All rights reserved. 
// 
// -------------------------------------------------------------- 
 
#include "edk.h" 
#include "ipmconv.h" 
#include "msgemit.h" 
#include "convcls.h" 
#include "tagnames.h" 
#include "convrept.chk" 
 
//$--CIPMConvert::HrReportTo822Format--------------------------- 
// 
// DESCRIPTION: Handle convertion of IPM reports to 822-style header 
// 
// INPUT:   none 
// 
// RETURNS: HRESULT --  NOERROR if successful, 
//                      E_INVALIDARG if bad input, 
//                      E_OUTOFMEMORY if memory problems 
//                      E_NOTIMPL if unknown report type is received 
//                      E_FAIL otherwise 
// 
// ------------------------------------------------------------- 
HRESULT CIPMConvert::HrReportTo822Format()  // RETURNS: HRESULT 
{ 
    HRESULT hr  =   NOERROR; 
 
    DEBUGPRIVATE("CIPMConvert::HrReportTo822Format()\n"); 
 
    // switch off of report's type 
    switch ( m_MsgType ) 
    { 
        case mtNDR: 
            // non-delivery report 
 
            // Handle the report's envelope. 
            hr = HrConvertReport(); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Handle non-recipient table. 
            hr = HrConvertNonRecipients(); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            break; 
 
        case mtDR: 
            // delivery report 
 
            // Handle the report's envelope. 
            hr = HrConvertReport(); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Handle recipient table 
            hr = HrConvertRecipients(); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            break; 
 
        case mtNRN: 
        case mtRN: 
            // non-read notification & 
            // read notification 
 
            // Handle notification envelope 
            // (appears to be essentially the same 
            // as a regular IPM envelope. 
            hr = HrConvertEnvelope(); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Handle read/non-read notification content. 
            hr = HrConvertNotifyContent(); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            break; 
 
        default: 
            // shouldn't happen 
            hr = HR_LOG(E_FAIL); 
 
            goto cleanup; 
 
    }   // end switch 
 
    // we are done 
 
cleanup: 
 
    RETURN(hr); 
 
} 
 
//$--CIPMConvert::HrConvertReport-------------------------------- 
// 
// DESCRIPTION: Handle conversion of REPORT.[N]DR delivery/non-delivery 
//              report envelope 
// 
// INPUT:   none 
// 
// RETURNS: HRESULT --  NOERROR if successful, 
//                      E_INVALIDARG if bad input, 
//                      E_OUTOFMEMORY if memory problems, 
//                      E_NOTIMPL if don't support report type 
//                      E_FAIL otherwise. 
// 
//---------------------------------------------------------------- 
HRESULT CIPMConvert::HrConvertReport()   // RETURNS: HRESULT 
{ 
    HRESULT         hr              =   NOERROR; 
    LPTSTR          lpTempString    =   NULL;       // temporary string pointer 
    BOOL            fReportXID      =   TRUE;       // FALSE if report does not have XID property 
    LPTRACEINFO     lpTraceInfo     =   NULL;       // trace information structure pointer 
    ULONG           iLoop           =   0;          // loop index 
    LPTRACEENTRY    lpTraceEntry    =   NULL;       // trace entry pointer 
    BOOL            fTraceInfo      =   TRUE;       // FALSE if no trace information 
    ULONG           ulObjType       =   0;          // MAPI object type 
    LPMAILUSER      lpReportToUser  =   NULL;       // report to recipient pointer 
    LPSPropValue    lpRecipProps    =   NULL;       // recipient property value array pointer 
 
    PINTTRACEINFO   lpIntTraceInfo  =   NULL;       // trace information structure pointer 
    PINTTRACEENTRY  lpIntTraceEntry =   NULL;       // trace entry pointer 
    BOOL            fIntTraceInfo   =   TRUE;       // FALSE if no trace information 
 
    // Report property indices 
    const UINT  iClass       =    0;  // index of message class value     
    const UINT  iFromAddr    =    1;  // index of sender address 
    const UINT  iXID         =    2;  // index of X-message identifier 
    const UINT  iTrace       =    3;  // index of trace information 
    const UINT  iSentTime    =    4;  // index of sent time value 
    const UINT  iImportance  =    5;  // index of importance value 
    const UINT  iPriority    =    6;  // index of priority value 
    const UINT  iSubject     =    7;  // index of the subject value 
    const UINT  iReportToEID =    8;  // index of report destination entry ID 
    const UINT  iSubjectXID  =    9;  // index of report subject X-message ID value 
    const UINT  iIntTrace    =   10;  // index of trace information 
 
    // number of report properties 
    const ULONG     nProps          =   11; 
 
    // Properties to retrieve for the report envelope 
    SizedSPropTagArray(nProps, sPropTagArray) = 
    { 
        nProps,  // number of properties 
        { 
            PR_MESSAGE_CLASS,   // report class property ID 
            PR_SENDER_EMAIL_ADDRESS,    // report from address 
            PR_MESSAGE_SUBMISSION_ID,   // report X-identifier 
            PR_TRACE_INFO,      // report trace information 
            PR_CREATION_TIME,   // report creation time 
            PR_IMPORTANCE,      // report importance 
            PR_PRIORITY,        // report priority 
            PR_SUBJECT,         // report subject 
            PR_REPORT_DESTINATION_ENTRYID,  // report destination entry identifier 
            PR_MTS_SUBJECT_ID,  // report's subject X-identifier 
            PR_INTERNAL_TRACE_INFO,  // report internal trace information 
        } 
    }; 
 
    // indices into the recipient property array. 
    const UINT  iEmailAddr  =   0;      // recipient's email address 
     
    const ULONG nRecipProps =   1;      // # of recipient properties 
 
    // Array of recipient properties to retrieve 
    SizedSPropTagArray(nRecipProps, sRecipProps) = 
    { 
        nRecipProps,      // # recipient properties to retrieve 
        { 
            PR_EMAIL_ADDRESS    // recipient's email address 
        } 
    }; 
 
    DEBUGPRIVATE("CIPMConvert::HrConvertReport()\n"); 
 
    // consistency checks 
    ASSERT_IUNKNOWN_PTR(m_lpAB, "Bad m_lpAB"); 
 
    // Retieve the properties needed on the report envelope. 
    hr = HrRetrieveProps(m_lpEnvelope, 
                          (LPSPropTagArray) &sPropTagArray, 
                          &m_lpEnvProps); 
 
    // Delivery reports may not have the PR_MESSAGE_SUBMISSION_ID 
    // nor the PR_MTS_SUBJECT_ID property.  This is a bug.  For the time 
    // being, however, we shall not fail because of this. 
    if ( hr == EDK_E_NOT_FOUND ) 
    { 
        UINT    iPropIndex  =   0; 
        for ( iPropIndex = 0; iPropIndex < sPropTagArray.cValues; 
              iPropIndex++ ) 
        { 
            if ( PROP_TYPE(m_lpEnvProps[iPropIndex].ulPropTag) ==  
                 PT_ERROR ) 
            { 
                if ( iPropIndex == iXID ) 
                { 
                    fReportXID = FALSE; 
 
                    hr = HR_LOG(NOERROR);   // not treated as an error 
                } 
 
                // It's also O.K. for there to be no trace 
                // information. 
                else if ( iPropIndex == iTrace ) 
                { 
                    // no trace information 
                    fTraceInfo = FALSE;  
 
                    hr = HR_LOG(NOERROR); 
 
                }   // end if no PR_TRACE_INFO 
 
                else if ( iPropIndex == iIntTrace ) 
                { 
                    // no internal trace information 
                    fIntTraceInfo = FALSE;  
 
                    hr = HR_LOG(NOERROR); 
 
                }   // end if no PR_INTERNAL_TRACE_INFO 
 
                else 
                { 
                    hr = HR_LOG(E_FAIL);    // another property isn't set 
 
                    goto cleanup; 
                } 
            } 
        }   // end for 
    } 
 
    // Test for other errors 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Build and emit the "X-Tnef-Attach" tag and data line. 
    // The TNEF data file name is always "MAPIMAIL.DAT" if 
    // we are doing TNEF encoding and null otherwise. 
    hr = HrEmitTagDataLine( 
        lpszTagTnefAttach,      // tag 
        (LPTSTR) (m_fTNEFEncode ? lpszTagTnefHdr : lpszNullData),  // data 
        m_lpStream);            // stream pointer 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // build and emit the "MAIL FROM" line.   
    // from the sender's address string. 
    hr = HrEmitTagDataLine( 
            lpszTagMailFrom,        // tag 
            m_lpEnvProps[iFromAddr].Value.LPSZ,  // sender's Email address 
            m_lpStream);              // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Emit the "RCPT TO" tag and data line. 
    // First, we must open the address book entry associated with the  
    // destination entry identifier and retrieve the entry's email 
    // address. 
    hr = m_lpAB->OpenEntry( 
        m_lpEnvProps[iReportToEID].Value.bin.cb,    // # bytes in entry ID 
        (LPENTRYID) m_lpEnvProps[iReportToEID].Value.bin.lpb,   // entry identifier 
        NULL,                                       // interface identifer 
        MAPI_DEFERRED_ERRORS,                       // reduces RPCs 
        &ulObjType,                                 // MAPI object type 
        (LPUNKNOWN *) &lpReportToUser);             // report to recipient pointer 
 
    if ( FAILED(hr) ) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    ASSERTERROR(ulObjType == MAPI_MAILUSER, "Bad ulObjType"); 
    ASSERT_IUNKNOWN_PTR(lpReportToUser, "Bad lpReportToUser."); 
 
    // Retrieve the email address of the report recipient. 
    hr = HrRetrieveProps( 
        lpReportToUser,     // MAPI recipient pointer 
        (LPSPropTagArray) &sRecipProps,   // property tag array 
        &lpRecipProps);     // property value array pointer 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the recipient's address for the "RCPT_TO" tag 
    hr = HrEmitTagDataLine( 
            lpszTagRcptTo,          // tag 
            lpRecipProps[iEmailAddr].Value.LPSZ, // report destination 
            m_lpStream);          // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Emit the "DATA" marker 
    hr = m_lpStream->Write( 
            lpszTagData,        // data 
            lstrlen(lpszTagData) * sizeof(TCHAR),   // length (no terminator) 
            NULL); 
 
    if ( FAILED(hr) ) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    // Print out the "X-Message-Class:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
            lpszTagMsgClass,        // tag 
            m_lpEnvProps[iClass].Value.LPSZ,  // report class                               
            m_lpStream);          // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Build the date and time string 
    hr = HrCreateDateTimeString( 
                  &(m_lpEnvProps[iSentTime].Value.ft),  // PR_CLIENT_SUBMIT_TIME property value 
                  &lpTempString); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "Date:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
                lpszTagDate,  
                lpTempString,  
                m_lpStream); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    MAPIFREEBUFFER(lpTempString); 
 
    // Print out the "X-Message-ID:" tag and data line to the stream 
    // The X-Message ID string is null terminated. 
    if ( fReportXID ) 
    { 
        hr = HrEmitTagDataLine( 
            lpszTagMsgID, 
            (LPTSTR) m_lpEnvProps[iXID].Value.bin.lpb,   // really a null-terminated string 
            m_lpStream); 
    } 
 
    else 
    { 
        // Delivery reports may not have a PR_MESSAGE_SUBMISSION_ID 
        // nor a PR_MTS_SUBJECT_ID.   
        // We will print out a "null" XID in this case. 
        hr = HrEmitTagDataLine( 
            lpszTagMsgID,            // tag 
            (LPTSTR) lpszNullData,   // blank data 
            m_lpStream); 
    } 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "X-Message-Subject-ID:" tag and data line to the stream 
    // The X-Message ID string is already null terminated. 
    hr = HrEmitTagDataLine( 
        lpszTagSubjectID,  
        (LPTSTR) m_lpEnvProps[iSubjectXID].Value.bin.lpb,    // really a null-terminated string 
        m_lpStream); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Build the trace information string, if any 
    if ( fTraceInfo == TRUE ) 
    { 
        // have trace information 
        lpTraceInfo = (LPTRACEINFO) (m_lpEnvProps[iTrace].Value.bin.lpb); 
 
        // Print out trace entry data 
        for ( iLoop = 0; iLoop < lpTraceInfo->cEntries; iLoop++ ) 
        { 
            // retrieve trace entry. 
            lpTraceEntry = &(lpTraceInfo->rgtraceentry[iLoop]); 
 
            // Create "External-Received-By" string 
            hr = HrCreateExternalTraceString( 
                lpTraceEntry->lAction,          // trace action 
                lpTraceEntry->rgchCountryName,  // country name 
                lpTraceEntry->rgchADMDName,     // ADMD name 
                lpTraceEntry->rgchPRMDId,       // PRMD identifier 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out the "External-Received-By:" tag and data line to the stream. 
            hr = HrEmitTagDataLine( 
                lpszTagExternalRcvdBy,  
                lpTempString,  
                m_lpStream); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Build "External-Received-At" date and time string 
            hr = HrCreateDateTimeString( 
                &(lpTraceEntry->ftArrivalTime), // trace arrival time 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out External-Received-At date and time string 
            hr = HrEmitTagDataLine( 
                lpszTagExternalRcvdAt,  // tag 
                lpTempString,       // data 
                m_lpStream);        // stream 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Create "External-Attempted-By" string 
            hr = HrCreateExternalTraceString( 
                lpTraceEntry->lAction,              // trace action 
                lpTraceEntry->rgchAttCountryName,   // country name 
                lpTraceEntry->rgchAttADMDName,      // ADMD name 
                lpTraceEntry->rgchAttPRMDId,        // PRMD identifier 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out the "External-Attempted-By:" tag and data line to the stream. 
            hr = HrEmitTagDataLine( 
                lpszTagExternalAttmBy,  
                lpTempString,  
                m_lpStream); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Build "External-Deferred-At" date and time string 
            hr = HrCreateDateTimeString( 
                &(lpTraceEntry->ftDeferredTime), // trace deferal time 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out External-Deferred-At data and time string 
            hr = HrEmitTagDataLine( 
                lpszTagExternalDefdAt,  // tag 
                lpTempString,       // data 
                m_lpStream);        // stream 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
        }   // end for each trace entry 
    }   // end if any trace information 
 
    // Build the internal trace information string, if any 
    if ( fIntTraceInfo == TRUE ) 
    { 
        // have trace information 
        lpIntTraceInfo = (PINTTRACEINFO) (m_lpEnvProps[iIntTrace].Value.bin.lpb); 
 
        // Print out trace entry data 
        for ( iLoop = 0; iLoop < lpIntTraceInfo->cEntries; iLoop++ ) 
        { 
            // retrieve trace entry. 
            lpIntTraceEntry = &(lpIntTraceInfo->rgIntTraceEntry[iLoop]); 
 
            // Create "Internal-Received-By" string 
            hr = HrCreateInternalTraceString( 
                lpIntTraceEntry->lAction,          // trace action 
                lpIntTraceEntry->rgchCountryName,  // country name 
                lpIntTraceEntry->rgchADMDName,     // ADMD name 
                lpIntTraceEntry->rgchPRMDId,       // PRMD identifier 
                lpIntTraceEntry->rgchMTAName,      // ADMD name 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out the "Internal-Received-By:" tag and data line to the stream. 
            hr = HrEmitTagDataLine( 
                lpszTagInternalRcvdBy,  
                lpTempString,  
                m_lpStream); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Build "Internal-Received-At" date and time string 
            hr = HrCreateDateTimeString( 
                &(lpIntTraceEntry->ftArrivalTime), // trace arrival time 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out Internal-Received-At date and time string 
            hr = HrEmitTagDataLine( 
                lpszTagInternalRcvdAt,  // tag 
                lpTempString,       // data 
                m_lpStream);        // stream 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Create "Internal-Attempted-By" string 
            hr = HrCreateInternalTraceString( 
                lpIntTraceEntry->lAction,              // trace action 
                lpIntTraceEntry->rgchAttCountryName,   // country name 
                lpIntTraceEntry->rgchAttADMDName,      // ADMD name 
                lpIntTraceEntry->rgchAttPRMDId,        // PRMD identifier 
                lpIntTraceEntry->rgchAttMTAName,       // MTA name 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out the "Internal-Attempted-By:" tag and data line to the stream. 
            hr = HrEmitTagDataLine( 
                lpszTagInternalAttmBy,  
                lpTempString,  
                m_lpStream); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Build "Internal-Deferred-At" date and time string 
            hr = HrCreateDateTimeString( 
                &(lpIntTraceEntry->ftDeferredTime), // trace deferal time 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out Internal-Deferred-At data and time string 
            hr = HrEmitTagDataLine( 
                lpszTagInternalDefdAt,  // tag 
                lpTempString,       // data 
                m_lpStream);        // stream 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
        }   // end for each internal trace entry 
    }   // end if any internal trace information 
 
    // Print out the "From:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
            lpszTagFrom,     // tag 
            m_lpEnvProps[iFromAddr].Value.LPSZ,    // report creator 
            m_lpStream);                // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "TO:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
            lpszTagTo,     // tag 
            lpRecipProps[iEmailAddr].Value.LPSZ,    // report destination 
            m_lpStream);                // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out a blank line 
    hr = m_lpStream->Write( 
            lpszNewLine,            // data 
            lstrlen(lpszNewLine) * sizeof(TCHAR),   // length (no terminator) 
            NULL); 
 
    if ( FAILED(hr) ) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    // Print out the "Subject:" tag and data line to the stream 
    hr = HrEmitTagDataLine( 
            lpszTagSubject,                // tag 
            m_lpEnvProps[iSubject].Value.LPSZ,  // PR_SUBJECT property value 
            m_lpStream);                    // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Build priority string. 
    hr = HrCreatePriorityString( 
                m_lpEnvProps[iPriority].Value.ul,  // PR_PRIORITY property value 
                &lpTempString); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "Priority:" tag and data line to the stream 
    hr = HrEmitTagDataLine( 
            lpszTagPriority,  
            lpTempString,  
            m_lpStream); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    MAPIFREEBUFFER(lpTempString); 
 
    // Build the importance string 
    hr = HrCreateImportanceString( 
                m_lpEnvProps[iImportance].Value.ul,    // PR_IMPORTANCE property value 
                &lpTempString);                             // buffer pointer 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "Importance:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
                lpszTagImportance,  
                lpTempString,  
                m_lpStream); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
    
    MAPIFREEBUFFER(lpTempString); 
 
cleanup: 
 
    // Release MAPI objects 
    ULRELEASE(lpReportToUser); 
 
    // Release MAPI buffers 
    MAPIFREEBUFFER(lpTempString); 
    MAPIFREEBUFFER(lpRecipProps); 
 
    RETURN(hr); 
 
} 
 
//$--CIPMConvert::HrConvertNonRecipients-------------------------- 
// 
// DESCRIPTION: Handle NDR report's non-recipient table 
// 
// INPUT:   none 
// 
// RETURNS: HRESULT --  NOERROR if successful, 
//                      E_FAIL otherwise. 
// 
// ------------------------------------------------------------ 
HRESULT CIPMConvert::HrConvertNonRecipients()    // RETURNS: HRESULT 
{ 
    HRESULT     hr                  =   NOERROR; 
    LPSRowSet   lpRows              =   NULL;       // recipient table rows pointer 
    ULONG       iRowCount           =   0;          // row counter 
 
    // temporary string buffer 
    TCHAR       szTempBuffer[ulMaxOutStringLen + 1]    =   TEXT(""); 
 
    // Columns needed from the report's recipient table 
    SizedSPropTagArray(4, sRecipProps)  =    
    { 
        4,      // number of properties (columns) desired 
        { 
            PR_EMAIL_ADDRESS,   // non-recipient Email address 
            PR_REPORT_TEXT,     // report text 
            PR_NDR_DIAG_CODE,   // diagnostic failure code 
            PR_NDR_REASON_CODE  // reason failure code 
        } 
    }; 
 
    // indices into the property values array 
    const UINT  iRecipient      =   0;  // non-recipient email address 
    const UINT  iText           =   1;  // report text 
    const UINT  iDiagCode       =   2;  // diagnostic reason code 
    const UINT  iNDRCode        =   3;  // non-delivery reason code 
 
    DEBUGPRIVATE("CIPMConvert::HrConvertNonRecipients()\n"); 
 
    // Retrieve the rows from the NDR's recipient table 
    hr = HrGetRecipientList( 
                m_lpEnvelope,   // report pointer 
                (LPSPropTagArray) &sRecipProps, // columns to retrieve 
                &lpRows);       // pointer to rows array pointer 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Make sure that we have at least one non-recipient 
    if ( lpRows->cRows == 0 ) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    // Process each non-recipient 
    for ( iRowCount = 0; iRowCount < lpRows->cRows;  
          iRowCount++ ) 
    { 
        // Emit the "Your message was not delivered to" tag and  
        // data line. 
        hr = HrEmitTagDataLine( 
                lpszTagNotSentTo,   // tag 
                lpRows->aRow[iRowCount].lpProps[iRecipient].Value.LPSZ, // data 
                m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Emit the "for the following reason" tag and data line. 
        hr = HrEmitTagDataLine( 
                lpszTagReason,      // tag 
                lpRows->aRow[iRowCount].lpProps[iText].Value.LPSZ, // data 
                m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Format the diagnostic code 
        wsprintf( 
            szTempBuffer,  
            TEXT("%ld"),  
            lpRows->aRow[iRowCount].lpProps[iDiagCode].Value.ul); 
 
        // Emit the "Diagnostic code" tag and data line. 
        hr = HrEmitTagDataLine( 
            lpszTagDiagCode,        // tag 
            szTempBuffer,           // data 
            m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Format the NDR reason code 
        wsprintf( 
            szTempBuffer,  
            TEXT("%ld"),  
            lpRows->aRow[iRowCount].lpProps[iNDRCode].Value.ul); 
 
        // Emit the "Diagnostic reason" tag and data line. 
        hr = HrEmitTagDataLine( 
            lpszTagNDRCode,         // tag 
            szTempBuffer,           // data 
            m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // done with this recipient 
 
    }   // end for 
 
cleanup: 
 
    // Free MAPI buffers 
    FREEPROWS(lpRows); 
 
    RETURN(hr); 
 
} 
 
//$--CIPMConvert::HrConvertRecipients-------------------------- 
// 
// DESCRIPTION: Handle DR report's recipient table 
// 
// INPUT:   none 
// 
// RETURNS: HRESULT --  NOERROR if successful, 
//                      E_FAIL otherwise. 
// 
// ------------------------------------------------------------ 
HRESULT CIPMConvert::HrConvertRecipients()    // RETURNS: HRESULT 
{ 
    HRESULT     hr                  =   NOERROR; 
    LPSRowSet   lpRows              =   NULL;       // recipient table rows pointer 
    ULONG       iRowCount           =   0;          // row counter 
    LPTSTR      lpTempString        =   NULL;       // temporary string pointer 
 
    // Columns needed from the report's recipient table 
    SizedSPropTagArray(2, sRecipProps)  =    
    { 
        2,      // number of properties (columns) desired 
        { 
            PR_EMAIL_ADDRESS,   // non-recipient Email address 
            PR_DELIVER_TIME     // delivery time 
        } 
    }; 
 
    // indices into the property values array 
    const UINT  iRecipient      =   0;  // recipient email address 
    const UINT  iTime           =   1;  // delivery time 
 
    DEBUGPRIVATE("CIPMConvert::HrConvertRecipients()\n"); 
 
    // Retrieve the rows from the DR's recipient table 
    hr = HrGetRecipientList( 
                m_lpEnvelope,   // report pointer 
                (LPSPropTagArray) &sRecipProps, // columns to retrieve 
                &lpRows);       // pointer to rows array pointer 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Make sure that we have at least one recipient 
    if ( lpRows->cRows == 0 ) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
} 
 
    for ( iRowCount = 0; iRowCount < lpRows->cRows;  
          iRowCount++ ) 
    { 
        // Emit the "Your message was successfully delivered to" 
        // tag and data line. 
        hr = HrEmitTagDataLine( 
                    lpszTagSentTo,      // tag 
                    lpRows->aRow[iRowCount].lpProps[iRecipient].Value.LPSZ, // data 
                    m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Create the delivery time string 
        hr = HrCreateDateTimeString( 
                    &(lpRows->aRow[iRowCount].lpProps[iTime].Value.ft),   // delivery time 
                    &lpTempString); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Emit the "at" time tag and data line. 
        hr = HrEmitTagDataLine( 
                    lpszTagAtTime,      // tag 
                    lpTempString,       // data 
                    m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Free temporary buffer 
        MAPIFREEBUFFER(lpTempString); 
 
        // We are done with this recipient 
 
    }   // end for 
 
cleanup: 
 
    // Free MAPI buffers 
    FREEPROWS(lpRows); 
     
    MAPIFREEBUFFER(lpTempString); 
 
    RETURN(hr); 
 
} 
 
//$--CIPMConvert::HrConvertNotifyContent-------------------------- 
// 
// DESCRIPTION: Handles read and non-read notification report's content 
// 
// INPUT:   none 
// 
// RETURNS: HRESULT --  NOERROR if successful, 
//                      E_FAIL otherwise. 
// 
// ------------------------------------------------------------ 
HRESULT CIPMConvert::HrConvertNotifyContent()    // RETURNS: HRESULT 
{ 
    HRESULT         hr              =   NOERROR; 
    LPTSTR          lpTempString    =   NULL;   // temporary string pointer 
    LPSRowSet       lpRecipRows     =   NULL;   // recipient table rows pointer 
    LPTRACEINFO     lpTraceInfo     =   NULL;   // trace information structure pointer 
    ULONG           iLoop           =   0;      // loop index 
    LPTRACEENTRY    lpTraceEntry    =   NULL;   // trace entry pointer 
 
    PINTTRACEINFO   lpIntTraceInfo  =   NULL;   // trace information structure pointer 
    PINTTRACEENTRY  lpIntTraceEntry =   NULL;   // trace entry pointer 
 
    // temporary string buffer 
    TCHAR           szTempBuf[ulMaxOutStringLen + 1]   =   TEXT(""); 
 
    // common notification property indices 
    const UINT  iClass       =    0;  // index of message class value     
    const UINT  iFromAddr    =    1;  // index of sender address 
    const UINT  iXID         =    2;  // index of X-message identifier 
    const UINT  iSentTime    =    3;  // index of sent time value 
    const UINT  iImportance  =    4;  // index of importance value 
    const UINT  iPriority    =    5;  // index of priority value 
    const UINT  iSubject     =    6;  // index of the subject value 
    const UINT  iSubjectXID  =    7;  // index of X-message subject ID 
    const UINT  iReportTime  =    8;  // index of report time 
 
    // Number of read-notification properties 
    const ULONG nRNProps     =    9;   
 
    // properties unique to non read notifications 
    const UINT  iDiscardCode =    9;  // PR_DISCARD_REASON 
    const UINT  iNonRcptCode =    10; // PR_NON_RECEIPT_REASON 
 
    // number of non-read notification content properties 
    const ULONG     nNRNProps    =   11; 
 
    // Properties needed from the NRN report's content 
    SizedSPropTagArray(nNRNProps, sPropsNotify)  =    
    { 
        nNRNProps,   // number of properties (columns) desired 
        { 
            PR_MESSAGE_CLASS,       // report class property ID 
            PR_SENDER_EMAIL_ADDRESS,// content's sender's email address, index iFromAddr 
            PR_SEARCH_KEY,          // dummy--acutally use PR_MESSAGE_SUBMISSION_ID on from 
                                    // the envelope 
            PR_CLIENT_SUBMIT_TIME,  // content's sent time, index iSentTime 
            PR_IMPORTANCE,          // report importance 
            PR_PRIORITY,            // report priority 
            PR_SUBJECT,             // report subject 
            // Notifications use PR_PARENT_KEY instread of 
            // PR_MTS_SUBJECT_ID for the X-message subject identifier 
            PR_PARENT_KEY,          // X-message-subject identifier 
            PR_REPORT_TIME,         // report time 
            PR_DISCARD_REASON,      // reason why message was discarded 
            PR_NON_RECEIPT_REASON   // reson why message wansn't read 
        } 
    }; 
 
    // indices used into envelope property array 
    const UINT  iTrace       =    3;  // PR_TRACE_INFO 
    const UINT  iIntTrace    =    4;  // PR_INTERNAL_TRACE_INFO 
 
    // Columns needed from the report content's recipient table 
    SizedSPropTagArray(1, sRecipProps)  =    
    { 
        1,      // number of properties (columns) desired 
        { 
            PR_EMAIL_ADDRESS   // notification "TO" Email address 
        } 
    }; 
 
    // indices into the recipient table property values 
    const UINT  iRecipient  =   0;  // PR_EMAIL_ADDRESS 
 
    DEBUGPRIVATE("CIPMConvert::HrConvertNotifyContent()\n"); 
 
    // consistency checking 
    ASSERTERROR((m_MsgType == mtNRN || m_MsgType == mtRN), 
                "Bad m_MsgType"); 
 
    // Open the envelope's content. 
    hr = HrOpenContent(); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    if ( m_MsgType == mtRN ) 
    { 
        // Adjust number of properties to retrieve for read notifications. 
        sPropsNotify.cValues = nRNProps; 
    } 
 
    // Retrieve the desired properties from the NRN or RN's 
    // content 
    hr = HrRetrieveProps( 
                m_lpContent,    // report pointer 
                (LPSPropTagArray) &sPropsNotify,  // Properties to retrieve 
                &m_lpCntProps); // pointer to property array pointer 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "X-Message-Class:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
            lpszTagMsgClass,        // tag 
            m_lpCntProps[iClass].Value.LPSZ,  // report class                               
            m_lpStream);          // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Build the date and time string 
    hr = HrCreateDateTimeString( 
                  &(m_lpCntProps[iSentTime].Value.ft),  // PR_CLIENT_SUBMIT_TIME property value 
                  &lpTempString); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "Date:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
                lpszTagDate,  
                lpTempString,  
                m_lpStream); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    MAPIFREEBUFFER(lpTempString); 
 
    // Print out the "X-Message-ID:" tag and data line to the stream 
    // The X-Message ID string is null terminated. 
    // Note:  The PR_MESSAGE_SUBMISSION_ID is really on the 
    // envelope. 
    hr = HrEmitTagDataLine( 
            lpszTagMsgID, 
            (LPTSTR) m_lpEnvProps[iXID].Value.bin.lpb,   // really a null-terminated string 
            m_lpStream); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "X-Message-Subject-ID:" tag and data line to the stream 
    // The X-Message ID string is already null terminated. 
    // This is the binary PR_PARENT_KEY property for notifications, 
    // so we shall need to convert it to ASCII. 
 
    // Check that we have enough room in our output buffer. 
    ASSERTERROR((m_lpCntProps[iSubjectXID].Value.bin.cb * 2 <= ulMaxOutStringLen), 
                "Bad cbParentKey"); 
    HexFromBin( 
            m_lpCntProps[iSubjectXID].Value.bin.lpb,    // binary data 
            m_lpCntProps[iSubjectXID].Value.bin.cb,     // # bytes of binary data 
            szTempBuf);                                 // string buffer 
 
    ASSERTERROR(!IsBadStringPtr(szTempBuf, INFINITE), "Bad szTempBuf"); 
 
    hr = HrEmitTagDataLine( 
            lpszTagSubjectID,       // tag 
            szTempBuf,              // data             
            m_lpStream); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the trace information, if any. 
    if ( PROP_TYPE(m_lpEnvProps[iTrace].ulPropTag) != PT_ERROR ) 
    { 
        // We have valid trace information 
        lpTraceInfo = (LPTRACEINFO) (m_lpEnvProps[iTrace].Value.bin.lpb); 
 
        // Print out trace entries 
        for ( iLoop = 0; iLoop < lpTraceInfo->cEntries; iLoop++ ) 
        { 
            // retrieve trace entry. 
            lpTraceEntry = &(lpTraceInfo->rgtraceentry[iLoop]); 
 
            // Create "External-Received-By" string 
            hr = HrCreateExternalTraceString( 
                lpTraceEntry->lAction,          // trace action 
                lpTraceEntry->rgchCountryName,  // country name 
                lpTraceEntry->rgchADMDName,     // ADMD name 
                lpTraceEntry->rgchPRMDId,       // PRMD identifier 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out the "External-Received-By:" tag and data line to the stream. 
            hr = HrEmitTagDataLine( 
                lpszTagExternalRcvdBy,  
                lpTempString,  
                m_lpStream); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Build "External-Received-At" date and time string 
            hr = HrCreateDateTimeString( 
                &(lpTraceEntry->ftArrivalTime), // trace arrival time 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out External-Received-At data and time string 
            hr = HrEmitTagDataLine( 
                lpszTagExternalRcvdAt,  // tag 
                lpTempString,       // data 
                m_lpStream);        // stream 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Create "External-Attempted-By" string 
            hr = HrCreateExternalTraceString( 
                lpTraceEntry->lAction,              // trace action 
                lpTraceEntry->rgchAttCountryName,   // country name 
                lpTraceEntry->rgchAttADMDName,      // ADMD name 
                lpTraceEntry->rgchAttPRMDId,        // PRMD identifier 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out the "External-Attempted-By:" tag and data line to the stream. 
            hr = HrEmitTagDataLine( 
                lpszTagExternalAttmBy,  
                lpTempString,  
                m_lpStream); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Build "External-Deferred-At" date and time string 
            hr = HrCreateDateTimeString( 
                &(lpTraceEntry->ftDeferredTime), // trace deferal time 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out External-Deferred-At data and time string 
            hr = HrEmitTagDataLine( 
                lpszTagExternalDefdAt,  // tag 
                lpTempString,       // data 
                m_lpStream);        // stream 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
        }   // end for each trace entry 
    }   // end if trace information 
 
    // Print out the internal trace information, if any. 
    if ( PROP_TYPE(m_lpEnvProps[iIntTrace].ulPropTag) != PT_ERROR ) 
    { 
        // We have valid trace information 
        lpIntTraceInfo = (PINTTRACEINFO) (m_lpEnvProps[iIntTrace].Value.bin.lpb); 
 
        // Print out trace entries 
        for ( iLoop = 0; iLoop < lpIntTraceInfo->cEntries; iLoop++ ) 
        { 
            // retrieve trace entry. 
            lpIntTraceEntry = &(lpIntTraceInfo->rgIntTraceEntry[iLoop]); 
 
            // Create "Internal-Received-By" string 
            hr = HrCreateInternalTraceString( 
                lpIntTraceEntry->lAction,          // trace action 
                lpIntTraceEntry->rgchCountryName,  // country name 
                lpIntTraceEntry->rgchADMDName,     // ADMD name 
                lpIntTraceEntry->rgchPRMDId,       // PRMD identifier 
                lpIntTraceEntry->rgchMTAName,      // MTA name 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out the "Internal-Received-By:" tag and data line to the stream. 
            hr = HrEmitTagDataLine( 
                lpszTagInternalRcvdBy,  
                lpTempString,  
                m_lpStream); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Build "Internal-Received-At" date and time string 
            hr = HrCreateDateTimeString( 
                &(lpIntTraceEntry->ftArrivalTime), // trace arrival time 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out Internal-Received-At data and time string 
            hr = HrEmitTagDataLine( 
                lpszTagInternalRcvdAt,  // tag 
                lpTempString,       // data 
                m_lpStream);        // stream 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Create "Internal-Attempted-By" string 
            hr = HrCreateInternalTraceString( 
                lpIntTraceEntry->lAction,              // trace action 
                lpIntTraceEntry->rgchAttCountryName,   // country name 
                lpIntTraceEntry->rgchAttADMDName,      // ADMD name 
                lpIntTraceEntry->rgchAttPRMDId,        // PRMD identifier 
                lpIntTraceEntry->rgchAttMTAName,       // MTA name 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out the "Internal-Attempted-By:" tag and data line to the stream. 
            hr = HrEmitTagDataLine( 
                lpszTagInternalAttmBy,  
                lpTempString,  
                m_lpStream); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
            // Build "Internal-Deferred-At" date and time string 
            hr = HrCreateDateTimeString( 
                &(lpIntTraceEntry->ftDeferredTime), // trace deferal time 
                &lpTempString); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            // Print out Internal-Deferred-At data and time string 
            hr = HrEmitTagDataLine( 
                lpszTagInternalDefdAt,  // tag 
                lpTempString,       // data 
                m_lpStream);        // stream 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
            MAPIFREEBUFFER(lpTempString); 
 
        }   // end for each internal trace entry 
    }   // end if internal trace information 
 
    // Print out the "From:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
            lpszTagFrom,     // tag 
            m_lpCntProps[iFromAddr].Value.LPSZ,    // report creator 
            m_lpStream);                // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Build the "TO:" tag and data line from the 
    // content's recipient table. 
    hr = HrGetRecipientList( 
            m_lpContent,        // MAPI message content pointer 
            (LPSPropTagArray) &sRecipProps,        // property columns desired array 
            &lpRecipRows);      // Rows retrieved from recipient table 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // There should only be one "TO" for a read or non-read 
    // notification, since an IPM.NOTE message can have only one  
    // sender. 
    if ( lpRecipRows->cRows != 1 ) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    // Print out the "TO:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
            lpszTagTo,     // tag 
            lpRecipRows->aRow[0].lpProps[iRecipient].Value.LPSZ,    // report destination 
            m_lpStream);                // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out a blank line 
    hr = m_lpStream->Write( 
            lpszNewLine,            // data 
            lstrlen(lpszNewLine) * sizeof(TCHAR),   // length (no terminator) 
            NULL); 
 
    if ( FAILED(hr) ) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    // Print out the "Subject:" tag and data line to the stream 
    hr = HrEmitTagDataLine( 
            lpszTagSubject,                // tag 
            m_lpCntProps[iSubject].Value.LPSZ,  // PR_SUBJECT property value 
            m_lpStream);                    // stream 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Build priority string. 
    hr = HrCreatePriorityString( 
                m_lpCntProps[iPriority].Value.ul,  // PR_PRIORITY property value 
                &lpTempString); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "Priority:" tag and data line to the stream 
    hr = HrEmitTagDataLine( 
            lpszTagPriority,  
            lpTempString,  
            m_lpStream); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    MAPIFREEBUFFER(lpTempString); 
 
    // Build the importance string 
    hr = HrCreateImportanceString( 
                m_lpCntProps[iImportance].Value.ul,    // PR_IMPORTANCE property value 
                &lpTempString);                             // buffer pointer 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
    // Print out the "Importance:" tag and data line to the stream. 
    hr = HrEmitTagDataLine( 
                lpszTagImportance,  
                lpTempString,  
                m_lpStream); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
    
    MAPIFREEBUFFER(lpTempString); 
 
    // The output for the two notifications diverges 
    // here. 
    if ( m_MsgType == mtNRN )    // non-read notification 
    { 
        // Emit the "Your message was not read by" tag 
        // and data line 
        hr = HrEmitTagDataLine( 
                lpszTagNotReadBy,   // tag 
                m_lpCntProps[iFromAddr].Value.LPSZ,    // data 
                m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Create the report time string. 
        hr = HrCreateDateTimeString( 
            &(m_lpCntProps[iReportTime].Value.ft),    // time 
            &lpTempString); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Emit the "at" tag and data line 
        hr = HrEmitTagDataLine( 
                lpszTagAtTime,      // tag 
                lpTempString,       // data 
                m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // free temporary string buffer 
        MAPIFREEBUFFER(lpTempString); 
 
        // Format the discard code data 
        wsprintf( 
                szTempBuf,      // buffer 
                TEXT("%ld"),    // format string 
                m_lpCntProps[iDiscardCode].Value.ul);   // discard code 
 
        // Emit the "Discard code" tag and data line. 
        hr = HrEmitTagDataLine( 
                lpszTagDiscCode,    // tag 
                szTempBuf,          // data 
                m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Format the non-receipt code data 
        wsprintf( 
                szTempBuf,      // buffer 
                TEXT("%ld"),    // format string 
                m_lpCntProps[iNonRcptCode].Value.ul);   // code 
 
        // Emit the "Non-receipt code" tag and data line 
        hr = HrEmitTagDataLine( 
                lpszTagNRNCode,     // tag 
                szTempBuf,          // buffer 
                m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
    }   // end if NRN 
 
    else if ( m_MsgType == mtRN )    // read notification 
    { 
        // Emit the "Your message was read by" tag and 
        // data line. 
        hr = HrEmitTagDataLine( 
                lpszTagReadBy,          // tag 
                m_lpCntProps[iFromAddr].Value.LPSZ,    // data 
                m_lpStream); 
 
        // Create the receipt time string. 
        hr = HrCreateDateTimeString( 
            &(m_lpCntProps[iReportTime].Value.ft),    // time 
            &lpTempString); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Emit the "at" tag and data line 
        hr = HrEmitTagDataLine( 
                lpszTagAtTime,      // tag 
                lpTempString,       // data 
                m_lpStream); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // free temporary string buffer 
        MAPIFREEBUFFER(lpTempString); 
    } 
 
    else 
    { 
        // shouldn't happen 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    // we are done. 
 
cleanup: 
 
    // Free MAPI buffers 
    FREEPROWS(lpRecipRows); 
 
    MAPIFREEBUFFER(lpTempString); 
 
    RETURN(hr); 
 
}