SMHOOF.C
/* 
 *  S M H O O F . C 
 * 
 *  Sample mail handling hook 
 *  Out of office management 
 * 
 *  Copyright 1992-95 Microsoft Corporation.  All Rights Reserved. 
 */ 
 
#include "_pch.h" 
#include <mapiutil.h> 
#include <cindex.h> 
#include <limits.h> 
 
#ifdef _WIN32 
#define szPlatform "32" 
#else 
#define szPlatform 
#endif 
 
enum { ipOofRId, ipOofREid, cpOofMax }; 
enum { icrgFrom, icrgSubmit, icrgTo, icrgCC, icrgSubj, icrgImp, icrgSen, ccrgMax }; 
 
enum 
{ 
    ipRespSen, 
    ipRespConvKey, 
    ipRespConvIdx, 
    ipRespConvTopic, 
    ipRespReportTag, 
    ipRespOrigAuthEid, 
    ipRespOrigAuthorName, 
    ipRespOrigAuthorSKey, 
    ipRespOrigSubmitTime, 
    ipRespPriority, 
    ipRespImportance, 
    ipRespSubject, 
    ipRespSubjectPrefix, 
    ipRespDelAfterSub, 
    ipRespMessageClass, 
    ipRespMessageFlags, 
    cpTargetResponseMax 
}; 
 
enum 
{ 
    ipRespRecipName, 
    ipRespRecipAdrType, 
    ipRespRecipEmail, 
    ipRespRecipType, 
    ipRespRecipEid, 
    ipRespRecipSKey, 
    cpTargetRecipMax 
}; 
 
enum 
{ 
    ipMsgClass, 
    ipMsgFlags, 
    ipRecipMe, 
    ipNSubj, 
    ipSndrEid, 
    ipSndrNm, 
    ipSndrType, 
    ipSndrEmail, 
    ipSndrSKey, 
    ipConvIndex, 
    ipConvTopic, 
    ipConvKey, 
    ipOrigPriority, 
    ipReportTag, 
    ipOrigAuthorEid, 
    ipOrigAuthorName, 
    ipOrigAuthorSKey, 
    ipOrigSubmitTime, 
    ipSentRepName, 
    ipSentRepType, 
    ipSentRepEmail, 
    ipSentRepSKey, 
    ipStoreSupport, 
    ipSubmitTime, 
    ipDisplayTo, 
    ipDisplayCc, 
    ipOofSubj, 
    ipImportance, 
    ipSensitivity, 
    cpResponseMax 
}; 
 
const static SizedSPropTagArray (cpResponseMax, sptResponse) = 
{ 
    cpResponseMax, 
    { 
        PR_MESSAGE_CLASS, 
        PR_MESSAGE_FLAGS, 
        PR_MESSAGE_RECIP_ME, 
        PR_NORMALIZED_SUBJECT, 
        PR_SENDER_ENTRYID, 
        PR_SENDER_NAME, 
        PR_SENDER_ADDRTYPE, 
        PR_SENDER_EMAIL_ADDRESS, 
        PR_SENDER_SEARCH_KEY, 
        PR_CONVERSATION_INDEX, 
        PR_CONVERSATION_TOPIC, 
        PR_CONVERSATION_KEY, 
        PR_PRIORITY, 
        PR_REPORT_TAG, 
        PR_ORIGINAL_AUTHOR_ENTRYID, 
        PR_ORIGINAL_AUTHOR_NAME, 
        PR_ORIGINAL_AUTHOR_SEARCH_KEY, 
        PR_ORIGINAL_SUBMIT_TIME, 
        PR_SENT_REPRESENTING_NAME, 
        PR_SENT_REPRESENTING_ADDRTYPE, 
        PR_SENT_REPRESENTING_EMAIL_ADDRESS, 
        PR_SENT_REPRESENTING_SEARCH_KEY, 
        PR_STORE_SUPPORT_MASK, 
        PR_CLIENT_SUBMIT_TIME, 
        PR_DISPLAY_TO,                   
        PR_DISPLAY_CC, 
        PR_SUBJECT, 
        PR_IMPORTANCE, 
        PR_SENSITIVITY, 
    } 
}; 
 
enum { ipDispNm, ipAdrTyp, ipEmail, ipSKey, cpUserMax }; 
const static SizedSPropTagArray (cpUserMax, sptUser) = 
{ 
    cpUserMax, 
    { 
        PR_DISPLAY_NAME, 
        PR_ADDRTYPE, 
        PR_EMAIL_ADDRESS, 
        PR_SEARCH_KEY, 
    } 
}; 
 
enum { ropOof, ropForward, ropReply }; 
static const LPTSTR rgszSubjPrfx[] = 
{ 
    "OOF: ", 
    "FW: ", 
    "RE: " 
}; 
 
enum 
{ 
    ivHdrSndrName, 
    ivHdrSndrType, 
    ivHdrSndrEmail, 
    ivHdrSentRepName, 
    ivHdrSentRepType, 
    ivHdrSentRepEmail, 
    ivHdrSubmitTime, 
    ivDisplayTo, 
    ivDisplayCc, 
    ivSubject, 
    ivImportance, 
    ivSensitivity, 
    cvHeader 
}; 
 
static const SizedSPropTagArray (cvHeader, sptHeader) =  
{ 
    cvHeader, 
    { 
        PR_SENDER_NAME, 
        PR_SENDER_ADDRTYPE, 
        PR_SENDER_EMAIL_ADDRESS, 
        PR_SENT_REPRESENTING_NAME, 
        PR_SENT_REPRESENTING_ADDRTYPE, 
        PR_SENT_REPRESENTING_EMAIL_ADDRESS, 
        PR_CLIENT_SUBMIT_TIME, 
        PR_DISPLAY_TO,                   
        PR_DISPLAY_CC, 
        PR_SUBJECT, 
        PR_IMPORTANCE, 
        PR_SENSITIVITY 
    } 
}; 
 
static const SizedSPropTagArray (1, sptForward) = 
{ 
    1, 
    { 
        PR_MESSAGE_ATTACHMENTS, 
    } 
}; 
 
enum { ipAttPos, ipAttNum, ipAttMeth, ipAttName, cpTaggingMax }; 
static const SizedSPropTagArray (cpTaggingMax, sptTagging) = 
{ 
    cpTaggingMax, 
    { 
        PR_RENDERING_POSITION, 
        PR_ATTACH_NUM, 
        PR_ATTACH_METHOD, 
        PR_ATTACH_FILENAME 
    } 
}; 
 
static const LPTSTR rgszHeaderField[] = 
{ 
    "Sent:", 
    "To:", 
    "Cc:", 
    "Subject:", 
    "Importance:", 
    "Sensitivity:" 
}; 
 
static const LPTSTR rgszImportance[] =  
{ 
    "Low", 
    "Normal", 
    "High" 
}; 
 
static const LPTSTR rgszSensitivity[] = 
{ 
    "Normal", 
    "Personal", 
    "Private", 
    "Confidential" 
}; 
 
static const TCHAR * rgszDay[] = 
{ 
    TEXT ("Sunday"), 
    TEXT ("Monday"), 
    TEXT ("Tuesday"), 
    TEXT ("Wednesday"), 
    TEXT ("Thursday"), 
    TEXT ("Friday"), 
    TEXT ("Saturday") 
}; 
extern TCHAR FAR * rgtstrMonthFull[]; 
 
 
LONG 
CchInsertSz (HWND hwnd, LPTSTR lpsz) 
{ 
    SendMessage (hwnd, EM_REPLACESEL, 0, (LPARAM) lpsz); 
    return lstrlen (lpsz); 
} 
 
 
VOID 
FileTimeToDateTimeSz (FILETIME FAR * lpft, LPTSTR rgch, UINT cb) 
{ 
    SYSTEMTIME st; 
 
    if (FileTimeToSystemTime (lpft, &st)) 
    { 
        wsprintf (rgch, 
            "%s, %s %02d, %4d %d:%02d %s", 
            rgszDay[st.wDayOfWeek], 
            rgtstrMonthFull[st.wMonth - 1], 
            st.wDay, 
            st.wYear, 
            st.wHour & 12, 
            st.wMinute, 
            (st.wHour > 11) ? "PM" : "AM"); 
    } 
    else 
        lstrcpy (rgch, "Unavailable"); 
} 
 
HRESULT 
HrCopyOriginalBody (LPSMH lpsmh, 
    HWND hwnd, 
    LPMESSAGE lpmsg, 
    LONG FAR * lpcch) 
{ 
    HRESULT hr; 
    CHARRANGE chrg = {0}; 
    EDITSTREAM es = {0}; 
    LPSTREAM lpstm = NULL; 
    LPSTREAM lpstmT = NULL; 
 
    *lpcch = 0; 
    hr = lpmsg->lpVtbl->OpenProperty (lpmsg, 
                                PR_RTF_COMPRESSED, 
                                &IID_IStream, 
                                0, 0, 
                                (LPUNKNOWN FAR *)&lpstmT); 
    if (!HR_FAILED (hr)) 
    { 
        hr = WrapCompressedRTFStream (lpstmT, 0, &lpstm); 
        if (!HR_FAILED (hr)) 
        { 
            es.pfnCallback = (EDITSTREAMCALLBACK)lpstm->lpVtbl->Read; 
            es.dwCookie = (DWORD)lpstm; 
             
            /*  Stuff a newline into the edit 
             *  control such that whatever preceeds 
             *  the body will be separated from the 
             *  the text of the original message 
             */ 
            SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg); 
            CchInsertSz (hwnd, "\r\n"); 
 
            /*  Do the body now */ 
             
            SendMessage (hwnd, 
                EM_STREAMIN, 
                SF_RTF | SFF_SELECTION | SFF_PLAINRTF, 
                (LPARAM)&es); 
 
            /*  Calculate the size of the body in characters */ 
             
            Edit_SetSel (hwnd, chrg.cpMin, INT_MAX); 
            SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg); 
            *lpcch = chrg.cpMax - chrg.cpMin; 
 
            /*  Reset the selection to the begining 
             *  of the edit control such that all 
             *  additions occur before the original 
             *  body 
             */ 
            Edit_SetSel (hwnd, 0, 0); 
        } 
    } 
 
    UlRelease (lpstm); 
    UlRelease (lpstmT); 
    DebugTraceResult (HrCopyOriginalBody(), hr); 
    return hr; 
} 
 
 
HRESULT 
HrInsertOriginalHeader (LPSMH lpsmh, 
    HWND hwnd, 
    LPSPropValue lpval, 
    CHARFORMAT FAR * lpcf, 
    LONG FAR * lpcch) 
{ 
    CHAR rgch[MAX_PATH]; 
    CHARRANGE chrg = {0}; 
    CHARRANGE chrgHdr = {0}; 
    CHARRANGE rgchrg[ccrgMax] = {0}; 
    LONG cp; 
    LPTSTR lpsz; 
    UINT icrg = 0; 
    UINT ip; 
     
    /*  Stuff a newline into the edit 
     *  control such that whatever preceeds 
     *  the body will be separated from the 
     *  the text of the original message 
     */ 
    SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrgHdr); 
    CchInsertSz (hwnd, "\r\n"); 
 
    /*  Mark the begining of the header */ 
     
    SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg); 
     
    cp = chrg.cpMin; 
    cp += CchInsertSz (hwnd, "\r\n----------\r\n"); 
 
    /*  Insert the "From: xxxx" line */ 
     
    if ((lpval[ipSndrNm].ulPropTag == PR_SENDER_NAME) && 
        (lpval[ipSndrNm].Value.LPSZ != NULL) && 
        (*lpval[ipSndrNm].Value.LPSZ != 0)) 
    { 
        rgchrg[icrg].cpMin = cp; 
        cp += CchInsertSz (hwnd, "From:"); 
        rgchrg[icrg].cpMax = cp; 
        icrg++; 
         
        cp += CchInsertSz (hwnd, "\t"); 
        cp += CchInsertSz (hwnd, lpval[ipSndrNm].Value.LPSZ); 
 
        /*  If we were representing someone else... */ 
 
        if ((lpval[ipSentRepName].ulPropTag == PR_SENT_REPRESENTING_NAME) && 
            (lpval[ipSentRepName].Value.LPSZ != NULL) && 
            (*lpval[ipSentRepName].Value.LPSZ != 0)) 
        { 
            if ((lpval[ipSndrSKey].ulPropTag != PR_SENDER_SEARCH_KEY) || 
                (lpval[ipSentRepSKey].ulPropTag != PR_SENT_REPRESENTING_SEARCH_KEY) || 
                (lpval[ipSndrSKey].Value.bin.cb != lpval[ipSentRepSKey].Value.bin.cb) || 
                memcmp (lpval[ipSndrSKey].Value.bin.lpb, 
                    lpval[ipSentRepSKey].Value.bin.lpb, 
                    lpval[ipSndrSKey].Value.bin.cb)) 
            { 
                cp += CchInsertSz (hwnd, " on behalf of "); 
                cp += CchInsertSz (hwnd, lpval[ipSentRepName].Value.LPSZ); 
            } 
            cp += CchInsertSz (hwnd, TEXT("\r\n")); 
        } 
 
        /*  Insert the remaining lines */ 
         
        for (ip = ipSubmitTime; ip < cpResponseMax; ip++) 
        { 
            lpsz = NULL; 
            switch (PROP_TYPE (lpval[ip].ulPropTag)) 
            { 
              case PT_TSTRING: 
 
                /*  Strings are strings */ 
                   
                lpsz = lpval[ip].Value.LPSZ; 
                break; 
 
              case PT_SYSTIME: 
 
                /*  Convertt the date to a string */ 
                   
                FileTimeToDateTimeSz (&lpval[ip].Value.ft, rgch, sizeof(rgch)); 
                lpsz = rgch; 
                break; 
 
              case PT_LONG: 
 
                /*  Importance and Sensitivity use a look-up to 
                 *  find the proper string to insert.  If the value 
                 *  equates to the "normal" level of a given message, 
                 *  then no value is displayed. 
                 */ 
                if ((lpval[ip].ulPropTag == PR_IMPORTANCE) && 
                    (lpval[ip].Value.l != IMPORTANCE_NORMAL)) 
                    lpsz = rgszImportance[lpval[ip].Value.l]; 
                else if ((lpval[ip].ulPropTag == PR_SENSITIVITY) && 
                    (lpval[ip].Value.l != SENSITIVITY_NONE)) 
                    lpsz = rgszSensitivity[lpval[ip].Value.l]; 
                break; 
            } 
 
            if (lpsz && *lpsz) 
            { 
                rgchrg[icrg].cpMin = cp; 
                cp += CchInsertSz (hwnd, rgszHeaderField[ip - ipSubmitTime]); 
                rgchrg[icrg].cpMax = cp; 
                icrg++; 
                 
                cp += CchInsertSz (hwnd, "\t"); 
                cp += CchInsertSz (hwnd, lpsz); 
                cp += CchInsertSz (hwnd, TEXT("\r\n")); 
            } 
        } 
        cp += CchInsertSz (hwnd, TEXT("\r\n")); 
 
        /*  Ensure that the text is formated in the 
         *  charformat passed in.  Such that we can 
         *  manipulate the rest with no worries 
         */ 
        chrg.cpMin += 2; 
        chrg.cpMax = cp; 
        SendMessage (hwnd, EM_EXSETSEL, 0, (LPARAM) &chrg); 
        SendMessage (hwnd, EM_SETCHARFORMAT, SCF_SELECTION|SCF_WORD, (LPARAM)lpcf); 
 
        /*  Run through all the field headers and make them bold */ 
         
        lpcf->cbSize = sizeof(CHARFORMAT); 
        lpcf->dwMask = CFM_BOLD; 
        lpcf->dwEffects = CFE_BOLD; 
 
        while (icrg) 
        { 
            chrg = rgchrg[--icrg]; 
            SendMessage (hwnd, EM_EXSETSEL, 0, (LPARAM)&chrg); 
            SendMessage (hwnd, EM_SETCHARFORMAT, SCF_SELECTION|SCF_WORD, (LPARAM)lpcf); 
        } 
 
        /*  Calculate the size of the header */ 
         
        *lpcch = cp - chrgHdr.cpMin; 
        Edit_SetSel (hwnd, 0, 0); 
    } 
     
    DebugTraceResult (HrInsertOriginalHeader(), hrSuccess); 
    return hrSuccess; 
} 
 
 
HRESULT 
HrInsertAnnotation (LPSMH lpsmh, 
    HWND hwnd, 
    UINT rop, 
    LPRULE lprl, 
    LONG FAR * lpcch) 
{ 
    CHARRANGE chrg = {0}; 
    EDITSTREAM es = {0}; 
    LPBYTE lpb; 
    RTFS rtfs = {0}; 
    ULONG cb; 
     
    /*  Setup which annotation to use */ 
     
    if (rop != ropOof) 
    { 
        cb = lprl->cbRTF; 
        lpb = lprl->lpbRTF; 
    } 
    else 
    { 
        cb = lpsmh->oof.cbRTF; 
        lpb = lpsmh->oof.lpbRTF; 
    } 
 
    /*  Stream the bad boy in */ 
     
    rtfs.cb = 0; 
    rtfs.cbMax = cb; 
    rtfs.lpb = lpb; 
    es.pfnCallback = ReadRTFFromBuffer; 
    es.dwCookie = (DWORD)&rtfs; 
    SendMessage (hwnd, 
        EM_STREAMIN, 
        SF_RTF | SFF_SELECTION | SFF_PLAINRTF, 
        (LPARAM)&es); 
 
    /*  Calculate the size of what we just streamed in */ 
     
    SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg); 
    *lpcch = chrg.cpMax; 
 
    DebugTraceResult (HrInsertAnnotation(), hrSuccess); 
    return hrSuccess; 
} 
 
 
HRESULT 
HrTagAttachments (LPSMH lpsmh, 
    HWND hwnd, 
    LONG dch, 
    LPMESSAGE lpmsg) 
{ 
    HRESULT hr; 
    CHAR rgch[MAX_PATH] = {0}; 
    LONG ichPos; 
    LPATTACH lpatt = NULL; 
    LPMAPITABLE lptbl = NULL; 
    LPMESSAGE lpmsgT = NULL; 
    LPSPropValue lpval = NULL; 
    LPSRowSet lprws = NULL; 
    UINT irw; 
 
    hr = lpmsg->lpVtbl->GetAttachmentTable (lpmsg, 0, &lptbl); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    hr = lptbl->lpVtbl->SetColumns (lptbl, (LPSPropTagArray)&sptTagging, 0); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    while (TRUE) 
    { 
        hr = lptbl->lpVtbl->QueryRows (lptbl, 64, 0, &lprws); 
        if (HR_FAILED (hr)) 
            goto ret; 
 
        if (lprws->cRows == 0) 
            break; 
 
        for (irw = 0; irw < lprws->cRows; irw++) 
        { 
            switch (lprws->aRow[irw].lpProps[ipAttMeth].Value.l) 
            { 
              case ATTACH_OLE: 
 
                lstrcpy (rgch, "<<OLE Object: unknown>>"); 
                break; 
 
              case ATTACH_EMBEDDED_MSG: 
 
                hr = lpmsg->lpVtbl->OpenAttach (lpmsg, 
                                        lprws->aRow[irw].lpProps[ipAttNum].Value.l, 
                                        NULL, 
                                        0, 
                                        &lpatt); 
                if (!HR_FAILED (hr)) 
                { 
                    hr = lpatt->lpVtbl->OpenProperty (lpatt, 
                                        PR_ATTACH_DATA_OBJ, 
                                        &IID_IMessage, 
                                        0, 0, 
                                        (LPUNKNOWN FAR *)&lpmsgT); 
                    if (!HR_FAILED (hr)) 
                    { 
                        /*  Get the subject of the embedded message 
                         *  as the tag identifier 
                         */ 
                        hr = HrGetOneProp ((LPMAPIPROP)lpmsgT, PR_SUBJECT, &lpval); 
                    } 
                } 
 
                wsprintf (rgch, "<<Message: %s>>", 
                    HR_FAILED (hr) ? "" : lpval->Value.LPSZ); 
                 
                (*lpsmh->lpfnFree) (lpval); 
                UlRelease (lpmsgT); 
                UlRelease (lpatt); 
                lpmsgT = NULL; 
                lpval = NULL; 
                lpatt = NULL; 
                break; 
 
              default: 
              case ATTACH_BY_VALUE: 
              case ATTACH_BY_REFERENCE: 
 
                /*  Use the filename for the attachment tag */ 
 
                wsprintf (rgch, "<<File: %s>>", 
                    (lprws->aRow[irw].lpProps[ipAttName].ulPropTag == PR_ATTACH_FILENAME) 
                          ? lprws->aRow[irw].lpProps[ipAttName].Value.LPSZ 
                          : ""); 
                break; 
            } 
 
            /*  Setup the selection such that we replace the attachment 
             *  place holder wiht the attachment tag 
             */ 
            ichPos = lprws->aRow[irw].lpProps[ipAttPos].Value.l; 
            if (ichPos == -1) 
                Edit_SetSel (hwnd, INT_MAX, INT_MAX); 
            else 
                Edit_SetSel (hwnd, ichPos + dch - 1, ichPos + dch); 
 
            /*  Insert the tag and adjust the offset 
             *  of the next attachment tag posiiton. 
             */ 
            dch += CchInsertSz (hwnd, rgch) - 1; 
 
            /*  Free the row data */ 
             
            (*lpsmh->lpfnFree) (lprws->aRow[irw].lpProps); 
        } 
         
        hr = hrSuccess; 
        (*lpsmh->lpfnFree) (lprws); 
        lprws = NULL; 
    } 
    (*lpsmh->lpfnFree) (lprws); 
    lprws = NULL; 
 
ret: 
     
    UlRelease (lptbl); 
     
    DebugTraceResult (HrTagAttachments(), hr); 
    return hr; 
} 
 
 
HRESULT 
HrOffsetAttachments (LPSMH lpsmh, 
    LONG dch, 
    LPMESSAGE lpmsg) 
{ 
    HRESULT hr; 
    LPATTACH lpatt = NULL; 
    LPMAPITABLE lptbl = NULL; 
    LPSRowSet lprws = NULL; 
    UINT irw; 
 
    hr = lpmsg->lpVtbl->GetAttachmentTable (lpmsg, 0, &lptbl); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    hr = lptbl->lpVtbl->SetColumns (lptbl, (LPSPropTagArray)&sptTagging, 0); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    while (TRUE) 
    { 
        hr = lptbl->lpVtbl->QueryRows (lptbl, 64, 0, &lprws); 
        if (HR_FAILED (hr)) 
            goto ret; 
 
        if (lprws->cRows == 0) 
            break; 
 
        for (irw = 0; irw < lprws->cRows; irw++) 
        { 
            /*  If the rendering position is not -1, we 
             *  want to adjust the positioning by the value 
             *  passed in dch 
             */ 
            if (lprws->aRow[irw].lpProps[ipAttPos].Value.l != -1) 
            { 
                /*  Adjust the positioning, and set in into the attachment */ 
                 
                lprws->aRow[irw].lpProps[ipAttPos].Value.l += dch; 
 
                hr = lpmsg->lpVtbl->OpenAttach (lpmsg, 
                                            lprws->aRow[irw].lpProps[ipAttNum].Value.l, 
                                            NULL, 
                                            MAPI_MODIFY, 
                                            &lpatt); 
                if (!HR_FAILED (hr)) 
                { 
                    hr = lpatt->lpVtbl->SetProps (lpatt, 
                                            1, 
                                            &lprws->aRow[irw].lpProps[ipAttPos], 
                                            NULL); 
                    if (!HR_FAILED (hr)) 
                    { 
                        /*  Save out the new positioning */ 
                         
                        hr = lpatt->lpVtbl->SaveChanges (lpatt, 0); 
                         
                    } 
                    UlRelease (lpatt); 
                    lpatt = NULL; 
                } 
                hr = hrSuccess; 
            } 
 
            /*  Free the row data */ 
             
            (*lpsmh->lpfnFree) (lprws->aRow[irw].lpProps); 
        } 
         
        (*lpsmh->lpfnFree) (lprws); 
        lprws = NULL; 
    } 
    (*lpsmh->lpfnFree) (lprws); 
    lprws = NULL; 
 
ret: 
     
    UlRelease (lptbl); 
     
    DebugTraceResult (HrOffsetAttachments(), hr); 
    return hr; 
} 
 
 
HRESULT 
HrInsertBody (LPSMH lpsmh, 
    HWND hwnd, 
    LPSPropValue lpval, 
    LPMESSAGE lpmsg) 
{ 
    HRESULT hr = hrSuccess; 
    BOOL fUpdated; 
    EDITSTREAM es = {0}; 
    LPSTREAM lpstm = NULL; 
    LPSTREAM lpstmRTF = NULL; 
    SPropValue val; 
    ULONG ulFlags = 0; 
 
    /*  Do PR_BODY iff the store is not RTF_AWARE */ 
     
    if ((lpval[ipStoreSupport].ulPropTag != PR_STORE_SUPPORT_MASK) || 
        !(lpval[ipStoreSupport].Value.l & STORE_RTF_OK)) 
    { 
        hr = lpmsg->lpVtbl->OpenProperty (lpmsg, 
                                PR_BODY, 
                                &IID_IStream, 
                                0, 
                                MAPI_CREATE | MAPI_MODIFY, 
                                (LPUNKNOWN FAR *)&lpstm); 
        if (HR_FAILED (hr)) 
            goto ret; 
 
        es.dwCookie = (DWORD)lpstm; 
        es.pfnCallback = (EDITSTREAMCALLBACK)lpstm->lpVtbl->Write; 
        SendMessage (hwnd, EM_STREAMOUT, SF_TEXT, (LPARAM)&es); 
        UlRelease (lpstm); 
        lpstm = NULL; 
 
        if (!es.dwError) 
            ulFlags |= RTF_SYNC_BODY_CHANGED; 
    } 
 
    /*  Add in PR_COMPRESSED_RTF */ 
     
    hr = lpmsg->lpVtbl->OpenProperty (lpmsg, 
                                PR_RTF_COMPRESSED, 
                                &IID_IStream, 
                                0, 
                                MAPI_CREATE | MAPI_MODIFY, 
                                (LPUNKNOWN FAR *)&lpstm); 
    if (HR_FAILED (hr)) 
        goto ret; 
     
    hr = WrapCompressedRTFStream (lpstm, 
                MAPI_MODIFY | (lpval[ipStoreSupport].Value.l & STORE_UNCOMPRESSED_RTF), 
                &lpstmRTF); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    es.dwCookie = (DWORD)lpstmRTF; 
    es.pfnCallback = (EDITSTREAMCALLBACK)lpstmRTF->lpVtbl->Write; 
    SendMessage (hwnd, EM_STREAMOUT, SF_RTF | SFF_PLAINRTF, (LPARAM)&es); 
 
    hr = lpstmRTF->lpVtbl->Commit (lpstmRTF, 0); 
    if (HR_FAILED (hr)) 
        goto ret; 
     
    if (!es.dwError) 
        ulFlags |= RTF_SYNC_RTF_CHANGED; 
 
    /*  Sync the RTF and the body iff the store is not RTF aware */ 
 
    if ((lpval[ipStoreSupport].ulPropTag != PR_STORE_SUPPORT_MASK) || 
        !(lpval[ipStoreSupport].Value.l & STORE_RTF_OK)) 
    { 
        /*  We are not aware, so we better do a full sync */ 
         
        hr = RTFSync (lpmsg, ulFlags, &fUpdated); 
    } 
    else 
    { 
        /*  If we are aware, then we want to tell the 
         *  store that we are completely in sync.  Otherwise, 
         *  we could loose our attachment positioning on 
         *  RTF aware stores.  And that would be bad. 
         */ 
        val.ulPropTag = PR_RTF_IN_SYNC; 
        val.Value.b = TRUE; 
        lpmsg->lpVtbl->SetProps (lpmsg, 1, &val, NULL); 
    } 
 
ret: 
 
    UlRelease (lpstm); 
    UlRelease (lpstmRTF); 
 
    DebugTraceResult (HrInsertBody(), hr); 
    return hr; 
} 
 
 
HRESULT 
HrBuildRecipient (LPSMH lpsmh, 
    LPSPropValue lpval, 
    LPMESSAGE lpmsg) 
{ 
    SCODE sc; 
    HRESULT hr; 
    LPADRLIST lpadr = NULL; 
    LPMAPIPROP lpusr = NULL; 
    LPSPropValue lpvalUsr = NULL; 
    LPSPropValue rgval = NULL; 
    UINT cval = 0; 
    ULONG ulT; 
 
    /*  Open the recipient up */ 
     
    hr = lpsmh->lpsess->lpVtbl->OpenEntry (lpsmh->lpsess, 
                            lpval->Value.bin.cb, 
                            (LPENTRYID)lpval->Value.bin.lpb, 
                            NULL, 0, 
                            &ulT, 
                            (LPUNKNOWN FAR *)&lpusr); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    /*  Get the properties we need */ 
     
    hr = lpusr->lpVtbl->GetProps (lpusr, 
                            (LPSPropTagArray)&sptUser, 
                            0, 
                            &ulT, 
                            &lpvalUsr); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    /*  Allocate the adrlist */ 
     
    if (FAILED (sc = (*lpsmh->lpfnAlloc) (CbNewADRLIST (1), &lpadr)) || 
        FAILED (sc = (*lpsmh->lpfnAlloc) (cpTargetRecipMax * sizeof(SPropValue), &rgval))) 
    { 
        hr = ResultFromScode (sc); 
        goto ret; 
    } 
 
    /*  Stuff the properties and add the recipient */ 
     
    rgval[cval].ulPropTag = PR_ENTRYID; 
    rgval[cval].Value = lpval->Value; 
    cval++; 
 
    rgval[cval].ulPropTag = PR_DISPLAY_NAME; 
    rgval[cval].Value.LPSZ = lpvalUsr[ipDispNm].Value.LPSZ; 
    cval++; 
     
    if (lpvalUsr[ipAdrTyp].ulPropTag == PR_ADDRTYPE) 
    { 
        rgval[cval].ulPropTag = PR_ADDRTYPE; 
        rgval[cval].Value.LPSZ = lpvalUsr[ipAdrTyp].Value.LPSZ; 
        cval++; 
    } 
     
    if (lpvalUsr[ipEmail].ulPropTag == PR_EMAIL_ADDRESS) 
    { 
        rgval[cval].ulPropTag = PR_EMAIL_ADDRESS; 
        rgval[cval].Value.LPSZ = lpvalUsr[ipEmail].Value.LPSZ; 
        cval++; 
    } 
 
    if (lpvalUsr[ipSKey].ulPropTag == PR_SEARCH_KEY) 
    { 
        rgval[cval].ulPropTag = PR_SEARCH_KEY; 
        rgval[cval].Value = lpvalUsr[ipSKey].Value; 
        cval++; 
    } 
     
    rgval[cval].ulPropTag = PR_RECIPIENT_TYPE; 
    rgval[cval].Value.l = MAPI_TO; 
    cval++; 
     
    lpadr->cEntries = 1; 
    lpadr->aEntries[0].cValues = cval; 
    lpadr->aEntries[0].rgPropVals = rgval; 
    hr = lpmsg->lpVtbl->ModifyRecipients (lpmsg, MODRECIP_ADD, lpadr); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
ret: 
     
    if (lpadr) 
    { 
        (*lpsmh->lpfnFree) (lpadr->aEntries[0].rgPropVals); 
        (*lpsmh->lpfnFree) (lpadr); 
    } 
    (*lpsmh->lpfnFree) (lpvalUsr); 
    UlRelease (lpusr); 
 
    DebugTraceResult (HrBuildRecipient(), hr); 
    return hr; 
} 
 
 
HRESULT 
HrCreateResponse (LPSMH lpsmh, 
    LPRULE lprl, 
    LPMAPIFOLDER lpfldr, 
    LPMESSAGE lpmsgOrig, 
    LPSPropValue lpval, 
    LPMESSAGE FAR * lppmsg) 
{ 
    SCODE sc; 
    HRESULT hr; 
    CHARFORMAT cf = {0}; 
    HINSTANCE hlib = NULL; 
    HWND hwnd = NULL; 
    LONG cch = 0; 
    LONG cchHdr = 0; 
    LPBYTE lpbConvIndex = NULL; 
    LPMESSAGE lpmsg = NULL; 
    LPREOC lpreoc = NULL; 
    LPSPropValue rgval = NULL; 
    PARAFORMAT pf = {0}; 
    TCHAR rgchClass[MAX_PATH]; 
    TCHAR rgchSubj[MAX_PATH]; 
    UINT rop; 
    ULONG cval = 0; 
 
    *lppmsg = NULL; 
     
    /*  Calculate the response operation based on 
     *  the supplied rule.  If no rule is supplied 
     *  then the response is an out-of-office msg. 
     */ 
    if (lprl) 
    { 
        Assert (lprl->ulFlags & RULE_AUTO_RESPONSE); 
        if (lprl->ulFlags & RULE_AUTO_FORWARD) 
        { 
            Assert (!(lprl->ulFlags & RULE_AUTO_REPLY)); 
            rop = ropForward; 
        } 
        else 
        { 
            Assert (!(lprl->ulFlags & RULE_AUTO_FORWARD)); 
            rop = ropReply; 
        } 
    } 
    else 
        rop = ropOof; 
 
    /*  Create the response message */ 
     
    hr = lpfldr->lpVtbl->CreateMessage (lpfldr, NULL, 0, &lpmsg); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    /*  Create and initialized the RTF edit control */ 
 
    hlib = LoadLibrary (RICHEDIT_LIB); 
    hwnd = CreateWindow (RICHEDIT_CLASS, 
                        "", 
                        WS_BORDER|ES_MULTILINE, 
                        CW_USEDEFAULT, CW_USEDEFAULT, INT_MAX, INT_MAX, 
                        NULL, 
                        NULL, 
                        lpsmh->hinst, 
                        NULL); 
    if (!hwnd) 
    { 
        hr = ResultFromScode (MAPI_E_CALL_FAILED); 
        goto ret; 
    } 
 
    /*  Create the richedit OLE callback.  If this 
     *  fails, it is non-fatal.  It just means that 
     *  any OLE objects in the annotation will not 
     *  be preserved in then response message. 
     */ 
    if (!FAILED (ScNewRicheditCallback (NULL, 
                        lpsmh->lpfnAlloc, 
                        lpsmh->lpfnAllocMore, 
                        lpsmh->lpfnFree, 
                        &lpreoc))) 
    { 
        /*  We have an OLE callback that we need 
         *  to hand off to the richedit control. 
         *  Although, the richedit control should 
         *  be AddRef()ing the object, we will hold 
         *  our reference until we are through with 
         *  the edit control. 
         */ 
        SendMessage (hwnd, EM_SETOLECALLBACK, 0, (LPARAM)lpreoc); 
    } 
 
    /*  Setup the default character format */ 
     
    cf.cbSize = sizeof(CHARFORMAT); 
    cf.dwMask = CFM_FACE | CFM_SIZE | CFM_COLOR | CFM_BOLD | 
                CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | 
                CFM_OFFSET | CFM_CHARSET; 
    cf.dwEffects = CFE_AUTOCOLOR; 
    cf.yHeight = 160; 
    lstrcpy (cf.szFaceName, "MS Sans Serif"); 
    cf.bCharSet = DEFAULT_CHARSET; 
    SendMessage (hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf); 
 
    /*  Copy over the original body if it makes sense to do so */ 
 
    if ((rop != ropOof) && (lprl->ulFlags & RULE_AUTO_APPEND_ORIG)) 
    { 
        hr = HrCopyOriginalBody (lpsmh, hwnd, lpmsgOrig, &cch); 
        if (HR_FAILED (hr) && (rop == ropForward)) 
goto ret; 
         
        hr = hrSuccess; 
    } 
 
    /*  Create and insert the original message header */ 
 
    hr = HrInsertOriginalHeader (lpsmh, hwnd, lpval, &cf, &cchHdr); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    /*  If this response is not a forward, we will want to 
     *  indent the header (and maybe the body) such that they 
     *  are offset from the annotation. 
     */ 
    if (rop != ropForward) 
    { 
        /*  Skip indenting the blankline */ 
         
        Edit_SetSel (hwnd, 2, INT_MAX); 
        pf.cbSize = sizeof(PARAFORMAT); 
        pf.dwMask = PFM_STARTINDENT | PFM_RIGHTINDENT | PFM_ALIGNMENT | 
                    PFM_OFFSET | PFM_TABSTOPS | PFM_NUMBERING; 
        pf.dxOffset = 1440; 
        pf.cTabCount = 1; 
        pf.rgxTabs[0] = 1440; 
        pf.wAlignment = PFA_LEFT; 
        pf.dxStartIndent = 1440 / 4; 
        SendMessage (hwnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf); 
        Edit_SetSel (hwnd, 0, 0); 
    } 
 
    /*  Copy over the annotation */ 
 
    hr = HrInsertAnnotation (lpsmh, hwnd, rop, lprl, &cch); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    /*  Tag the attachments for replies */ 
 
    if (rop == ropReply) 
    { 
        hr = HrTagAttachments (lpsmh, hwnd, cchHdr + cch + 3, lpmsgOrig); 
        if (HR_FAILED (hr)) 
            goto ret; 
    } 
 
    /*  Allocate space for the new message properties */ 
 
    sc = (*lpsmh->lpfnAlloc) (cpTargetResponseMax * sizeof(SPropValue), &rgval); 
    if (FAILED (sc)) 
    { 
        hr = ResultFromScode (sc); 
        goto ret; 
    } 
 
    /*  Build the response subject */ 
 
    lstrcpy (rgchSubj, rgszSubjPrfx[rop]); 
    lstrcat (rgchSubj, lpval[ipNSubj].Value.LPSZ); 
    rgval[cval].ulPropTag = PR_SUBJECT; 
    rgval[cval].Value.LPSZ = rgchSubj; 
    cval++; 
 
    rgval[cval].ulPropTag = PR_SUBJECT_PREFIX; 
    rgval[cval].Value.LPSZ = rgszSubjPrfx[rop]; 
    cval++; 
         
    /*  Build the response message class */ 
 
    if (rop == ropOof) 
        wsprintf (rgchClass, "Report.%s.OOF", lpval[ipMsgClass].Value.LPSZ); 
    else if (rop == ropReply) 
        lstrcpy (rgchClass, "IPM.Note.AutoReply"); 
    else 
        lstrcpy (rgchClass, lpval[ipMsgClass].Value.LPSZ); 
    rgval[cval].ulPropTag = PR_MESSAGE_CLASS; 
    rgval[cval].Value.LPSZ = rgchClass; 
    cval++; 
 
    /*  Compose the set of remaining properties */ 
 
    rgval[cval].ulPropTag = PR_DELETE_AFTER_SUBMIT; 
    rgval[cval].Value.b = TRUE; 
     
#ifdef  DEBUG 
    rgval[cval].Value.b = GetPrivateProfileInt ("SMH", "DeleteResponses", 1, "MAPIDBG.INI"); 
#endif 
 
    cval++; 
 
    /*  Tell the store that the message has not 
     *  been sent.  Otherwise, since the spooler 
     *  is the creater of the message, the store 
     *  will treat the message as if it has been 
     *  delivered into the store by a transport. 
     */ 
    rgval[cval].ulPropTag = PR_MESSAGE_FLAGS; 
    rgval[cval].Value.l = MSGFLAG_UNSENT; 
    cval++; 
     
    /*  Create appropriate conversation topic and indexes */ 
     
    if (lpval[ipConvKey].ulPropTag == PR_CONVERSATION_KEY) 
        rgval[cval++] = lpval[ipConvKey]; 
    else 
    { 
        rgval[cval].ulPropTag = PR_CONVERSATION_TOPIC; 
        rgval[cval].Value.LPSZ = lpval[ipNSubj].Value.LPSZ; 
        cval++; 
    } 
 
    if (lpval[ipConvTopic].ulPropTag == PR_CONVERSATION_TOPIC) 
        rgval[cval++] = lpval[ipConvTopic]; 
    else 
    { 
        rgval[cval].ulPropTag = PR_CONVERSATION_TOPIC; 
        rgval[cval].Value.LPSZ = lpval[ipNSubj].Value.LPSZ; 
        cval++; 
    } 
 
    if (lpval[ipConvIndex].ulPropTag == PR_CONVERSATION_INDEX) 
    { 
        sc = ScAddConversationIndex (lpval[ipConvIndex].Value.bin.cb, 
                            lpval[ipConvIndex].Value.bin.lpb, 
                            &rgval[cval].Value.bin.cb, 
                            &lpbConvIndex); 
    } 
    else 
    { 
        sc = ScAddConversationIndex (0, 
                            NULL, 
                            &rgval[cval].Value.bin.cb, 
                            &lpbConvIndex); 
    } 
    if (!FAILED (sc)) 
    { 
        rgval[cval].ulPropTag = PR_CONVERSATION_INDEX; 
        rgval[cval].Value.bin.lpb = lpbConvIndex; 
        cval++; 
    } 
 
    /*  Report tag */ 
     
    if (lpval[ipReportTag].ulPropTag == PR_REPORT_TAG) 
        rgval[cval++] = lpval[ipReportTag]; 
 
    /*  Sensitivity */ 
 
    if (lpval[ipSensitivity].ulPropTag == PR_SENSITIVITY) 
        rgval[cval++] = lpval[ipSensitivity]; 
 
    /*  Build response specific properties */ 
     
    if (rop == ropForward) 
    { 
        /*  Importance */ 
 
        if (lpval[ipImportance].ulPropTag == PR_IMPORTANCE) 
            rgval[cval++] = lpval[ipImportance]; 
         
        /*  Priority */ 
 
        if (lpval[ipOrigPriority].ulPropTag == PR_PRIORITY) 
            rgval[cval++] = lpval[ipOrigPriority]; 
         
        /*  Original author */ 
 
        if (lpval[ipOrigAuthorName].ulPropTag == PR_ORIGINAL_AUTHOR_NAME) 
            rgval[cval++] = lpval[ipOrigAuthorName]; 
         
        if (lpval[ipOrigAuthorEid].ulPropTag == PR_ORIGINAL_AUTHOR_ENTRYID) 
            rgval[cval++] = lpval[ipOrigAuthorEid]; 
         
        if (lpval[ipOrigAuthorSKey].ulPropTag == PR_ORIGINAL_AUTHOR_SEARCH_KEY) 
            rgval[cval++] = lpval[ipOrigAuthorSKey]; 
         
        /*  Original submit time */ 
 
        if (lpval[ipOrigSubmitTime].ulPropTag == PR_ORIGINAL_SUBMIT_TIME) 
            rgval[cval++] = lpval[ipOrigSubmitTime]; 
    } 
     
    /*  Set the properties */ 
 
    hr = lpmsg->lpVtbl->SetProps (lpmsg, cval, rgval, NULL); 
    if (HR_FAILED (hr)) 
        goto ret; 
     
    /*  Insert the body into the mesage directly */ 
 
    hr = HrInsertBody (lpsmh, hwnd, lpval, lpmsg); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    /*  Copy the attachment across in the case of a forward */ 
     
    if (rop == ropForward) 
    { 
        hr = lpmsgOrig->lpVtbl->CopyProps(lpmsgOrig, 
                        (LPSPropTagArray)&sptForward, 
                        0, 
                        NULL, 
                        (LPIID) &IID_IMessage, 
                        lpmsg, 
                        0, 
                        NULL); 
        if (HR_FAILED (hr)) 
            goto ret; 
 
        hr = HrOffsetAttachments (lpsmh, cchHdr + cch + 2, lpmsg); 
        if (HR_FAILED (hr)) 
            goto ret; 
    } 
 
    /*  Do the recipient */ 
 
    hr = HrBuildRecipient (lpsmh, 
                    (rop == ropForward) 
                        ? lprl->lpvalRecip 
                        : &lpval[ipSndrEid], 
                    lpmsg); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
ret: 
     
    if (hwnd) 
        DestroyWindow (hwnd); 
     
    if (HR_FAILED (hr)) 
    { 
        UlRelease (lpmsg); 
        lpmsg = NULL; 
    } 
     
    if (hlib) 
        FreeLibrary (hlib); 
 
    (*lpsmh->lpfnFree) (lpbConvIndex); 
    (*lpsmh->lpfnFree) (rgval); 
    UlRelease (lpreoc); 
     
    *lppmsg = lpmsg; 
 
    DebugTraceResult (HrCreateResponse(), hr); 
    return hr; 
} 
 
 
BOOL 
FOofRecip (LPOOF lpoof, LPSPropValue lpvalEid) 
{ 
    BOOL fOof = TRUE; 
    LPMAPITABLE lptbl = lpoof->lptbl; 
    SPropValue val; 
    SRestriction res; 
 
    if (lptbl) 
    { 
#ifdef  DEBUG 
        if (!GetPrivateProfileInt ("SMH", "OofAlways", 0, "MAPIDBG.INI")) 
#endif 
        { 
            val.ulPropTag = PR_ENTRYID; 
            val.Value = lpvalEid->Value; 
 
            res.rt = RES_PROPERTY; 
            res.res.resProperty.relop = RELOP_EQ; 
            res.res.resProperty.ulPropTag = PR_ENTRYID; 
            res.res.resProperty.lpProp = &val; 
            fOof = HR_FAILED (lptbl->lpVtbl->FindRow (lptbl, &res, BOOKMARK_BEGINNING, 0L)); 
        } 
    } 
    return fOof; 
} 
 
 
HRESULT 
HrRegOofRecip (LPSMH lpsmh, LPOOF lpoof, LPSPropValue lpvalEid) 
{ 
    HRESULT hr; 
    LPTABLEDATA lptad = lpoof->lptad; 
    SizedSPropTagArray (cpOofMax, sptOofTbl) = {cpOofMax, { PR_ROWID, PR_ENTRYID }}; 
    SPropValue rgval[cpOofMax]; 
    SRow rw; 
 
    if (!lptad) 
    { 
        //  This is the first OOF recip so we need to create the 
        //  table of recips 
        // 
        hr = ResultFromScode (CreateTable ((LPIID)&IID_IMAPITableData, 
                            lpsmh->lpfnAlloc, 
                            lpsmh->lpfnAllocMore, 
                            lpsmh->lpfnFree, 
                            NULL, 
                            TBLTYPE_DYNAMIC, 
                            PR_ROWID, 
                            (LPSPropTagArray)&sptOofTbl, 
                            (LPTABLEDATA FAR *)&lptad)); 
        if (HR_FAILED (hr)) 
            goto ret; 
         
        hr = lptad->lpVtbl->HrGetView (lptad, NULL, NULL, 0, &lpoof->lptbl); 
        if (HR_FAILED (hr)) 
        { 
            UlRelease (lptad); 
            goto ret; 
        } 
        lpoof->lptad = lptad; 
    } 
 
    rgval[ipOofRId].ulPropTag = PR_ROWID; 
    rgval[ipOofRId].Value.l = lpoof->cRecips++; 
    rgval[ipOofREid].ulPropTag = PR_ENTRYID; 
    rgval[ipOofREid].Value = lpvalEid->Value; 
     
    rw.cValues = cpOofMax; 
    rw.lpProps = rgval;  
    hr = lptad->lpVtbl->HrModifyRow (lptad, &rw); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
ret: 
 
    DebugTraceResult (HrRegOofRecip(), hr); 
    return hr; 
} 
 
 
HRESULT 
HrGenerateResponse (LPSMH lpsmh, LPRULE lprl, LPMAPIFOLDER lpfldr, LPMESSAGE lpmsg) 
{ 
    HRESULT hr; 
    LPSPropValue lpval = NULL; 
    LPMESSAGE lpmsgRep = NULL; 
    ULONG cval; 
     
    hr = lpmsg->lpVtbl->SaveChanges (lpmsg, KEEP_OPEN_READONLY); 
    if (HR_FAILED (hr)) 
        goto ret; 
     
    hr = lpmsg->lpVtbl->GetProps (lpmsg,  
                            (LPSPropTagArray)&sptResponse, 
                            0, 
                            &cval, 
                            &lpval); 
    if (HR_FAILED (hr)) 
        goto ret; 
 
    hr = ResultFromScode (MAPI_E_NOT_ME); 
     
    if ((lpval[ipMsgClass].ulPropTag == PR_MESSAGE_CLASS) && 
        (lpval[ipMsgClass].Value.LPSZ != NULL) && 
        FLpszContainsLpsz (lpval[ipMsgClass].Value.LPSZ, "Report.")) 
        goto ret; 
 
    if ((lpval[ipFlags].ulPropTag == PR_MESSAGE_FLAGS) && 
        (lpval[ipFlags].Value.l == MSGFLAG_FROMME)) 
        goto ret; 
 
    /*  If there is no rule, then we must be OOF'ing */ 
    if (!lprl && !FOofRecip (&lpsmh->oof, &lpval[ipSndrEid])) 
        goto ret; 
 
    if ((lpval[ipRecipMe].ulPropTag == PR_MESSAGE_RECIP_ME) && 
        (lpval[ipRecipMe].Value.b)) 
    { 
        DebugTrace ("SMH: generating response message\n"); 
         
        if (!lprl) 
        { 
            /*  Register the OOF recipient */ 
             
            hr = HrRegOofRecip (lpsmh, &lpsmh->oof, &lpval[ipSndrEid]); 
            if (HR_FAILED (hr)) 
                goto ret; 
        } 
 
        /*  Create the response and submit it */ 
         
        hr = HrCreateResponse (lpsmh, 
                        lprl, 
                        lpfldr, 
                        lpmsg, 
                        lpval, 
                        &lpmsgRep); 
        if (!HR_FAILED (hr)) 
            hr = lpmsgRep->lpVtbl->SubmitMessage (lpmsgRep, 0); 
         
        UlRelease (lpmsgRep); 
    } 
 
ret: 
 
    (*lpsmh->lpfnFree) (lpval); 
    DebugTraceResult (HrGenerateResponse(), hr); 
    return hr; 
} 
 
 
HRESULT 
HrInitOof (LPSMH lpsmh, LPSPropValue lpvalAnno, LPSPropValue lpvalRTF) 
{ 
    SCODE sc = MAPI_E_UNCONFIGURED; 
 
    if (lpvalRTF->ulPropTag == PR_SMH_OOF_RTF) 
    { 
        if (!FAILED (sc = (*lpsmh->lpfnAlloc) (lpvalRTF->Value.bin.cb, 
                                    (LPVOID FAR *)&lpsmh->oof.lpbRTF))) 
        { 
            lpsmh->oof.cbRTF = lpvalRTF->Value.bin.cb; 
            memcpy (lpsmh->oof.lpbRTF, 
                lpvalRTF->Value.bin.lpb, 
                (UINT)lpvalRTF->Value.bin.cb); 
        } 
    } 
    if (lpvalAnno->ulPropTag == PR_SMH_OOF_TEXT) 
    { 
        if (!FAILED (sc = (*lpsmh->lpfnAlloc) (lstrlen (lpvalAnno->Value.LPSZ) + sizeof(TCHAR), 
                                    (LPVOID FAR *)&lpsmh->oof.lpszBody))) 
            lstrcpy (lpsmh->oof.lpszBody, lpvalAnno->Value.LPSZ); 
    } 
 
    DebugTraceSc (HrInitOof(), sc); 
    return ResultFromScode (sc); 
}