SENDRTF.CPP

//////////////////////////////////////////////////////////////////////////// 
// SENDRTF.CPP
//
// Command line mail program to send rtf files in a message body.
//
// Copyright 1986-1996, Microsoft Corporation. All Rights Reserved.
////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <mapiutil.h>
#include <mapix.h>
#include <mapidbg.h>
#include <cindex.h>

#ifdef DEBUG
#define TraceFnResult(f, hr) \
{ (hr) ? \
printf(#f " returns 0x%08lX %s\n", GetScode(hr), SzDecodeScode(GetScode(hr))) : 0;\
}
#else
#define TraceFnResult(f, hr)
#endif //DEBUG

// Globals
LPSTR szProfile = NULL; //profile name
LPSTR szPassword = NULL; //profile password
LPSTR szSubject = NULL; //message subject
LPSTR szRTFFile = NULL; //rtf file name
LPSTR szToRecips = NULL; //recipients

BOOL fMAPIInited = FALSE;

LPMAPISESSION pses = NULL; //MAPI session
LPADRBOOK pabAddrB = NULL; //Address Book
LPMDB pmdb = NULL; //default message store
LPMAPIFOLDER pfldOutBox = NULL; // Out folder
LPSPropValue pvalSentMailEID = NULL; //Entry ID of the "Sent Mail" folder


//Function Prototypes
BOOL fParseCommandLine(int argc, char * argv[]);
HRESULT InitMAPI(void);
void DeInitMAPI(void);
HRESULT HrOpenDefaultStore(LPMDB * ppmdb);
HRESULT HrOpenAddressBook(LPADRBOOK * ppAdrBook);
HRESULT HrOpenOutFolder(LPMAPIFOLDER FAR * lppF);
HRESULT HrCreateOutMessage(LPMESSAGE FAR * ppmM);
HRESULT HrInitMsg(LPMESSAGE pmsg);
HRESULT HrCreateAndSend(void);
HRESULT HrCreateAddrList(LPADRLIST * ppal);
HRESULT HrSetRTF(LPMESSAGE pmsg);



int main(int argc, char * argv[])
{
HRESULT hr;

if(!fParseCommandLine(argc, argv))
return 1;

hr = InitMAPI();
if(HR_FAILED(hr))
return 1;

hr = HrCreateAndSend();

DeInitMAPI();

return 0;
}


//
// Init MAPI. Open address book, default message store, outbox
//
HRESULT InitMAPI(void)
{

HRESULT hr;
ULONG ulLogonFlag = 0;

hr = MAPIInitialize(NULL);
if(hr)
{
return hr;
}

fMAPIInited = TRUE;

ulLogonFlag = szProfile ? 0 : MAPI_USE_DEFAULT;

hr = MAPILogonEx(NULL, szProfile, szPassword,
MAPI_EXTENDED | MAPI_NEW_SESSION | ulLogonFlag,
&pses);
if(hr)
{
goto err;
}

hr = HrOpenDefaultStore(&pmdb);
if(HR_FAILED(hr))
goto err;

hr = HrOpenAddressBook(&pabAddrB);
if(HR_FAILED(hr))
goto err;

hr = HrOpenOutFolder(&pfldOutBox);
if(HR_FAILED(hr))
goto err;

/* retrieve the EntryID of the sentmail folder and change the property tag
so that it is ready to use on a message*/
hr = HrGetOneProp((LPMAPIPROP)pmdb, PR_IPM_SENTMAIL_ENTRYID, &pvalSentMailEID);
if(hr)
{
goto err;
}
pvalSentMailEID->ulPropTag = PR_SENTMAIL_ENTRYID;

return hrSuccess;

err:
DeInitMAPI();

DebugTraceResult(InitMAPI, hr);

return hr;
}

//
// Release MAPI interfaces and logoff
//
void DeInitMAPI(void)
{

UlRelease(pfldOutBox);
pfldOutBox = NULL;

if(pmdb)
{
//get our message out of the outbox
ULONG ulFlags = LOGOFF_PURGE;
HRESULT hr;

hr = pmdb->StoreLogoff(&ulFlags);

TraceFnResult(StoreLogoff, hr);
#ifdef DEBUG
printf("StoreLogoff output falgs: %lx\n", ulFlags);
#endif
UlRelease(pmdb);
pmdb = NULL;
}

UlRelease(pabAddrB);
pabAddrB = NULL;

MAPIFreeBuffer(pvalSentMailEID);
pvalSentMailEID = NULL;

if(pses)
{
pses->Logoff(0, 0, 0);
UlRelease(pses);
pses = NULL;
}

if(fMAPIInited)
{
MAPIUninitialize();
fMAPIInited = FALSE;
}

}

//
// Open the default message store. (The one that has PR_DEFAULT_STORE set to
// TRUE in the message store table.
//
HRESULT HrOpenDefaultStore(LPMDB * ppmdb)
{
HRESULT hr;
LPMDB lpmdb = NULL;
LPMAPITABLE ptable = NULL;
LPSRowSet prows = NULL;
LPSPropValue pvalProp = NULL;
static SizedSPropTagArray(2, columns) =
{ 2, { PR_DEFAULT_STORE, PR_ENTRYID} };
SPropValue valDefStore;
SRestriction restDefStore;


valDefStore.ulPropTag = PR_DEFAULT_STORE;
valDefStore.dwAlignPad = 0;
valDefStore.Value.b = TRUE;

restDefStore.rt = RES_PROPERTY;
restDefStore.res.resProperty.relop = RELOP_EQ;
restDefStore.res.resProperty.ulPropTag = PR_DEFAULT_STORE;
restDefStore.res.resProperty.lpProp = &valDefStore;

Assert(pses);

hr = pses->GetMsgStoresTable(0, &ptable);
if (HR_FAILED(hr))
{
TraceFnResult(GetMsgStoresTable, hr);
goto ret;
}


hr = HrQueryAllRows(ptable, (LPSPropTagArray) &columns, &restDefStore, NULL, 0, &prows);
if (HR_FAILED(hr))
{
TraceFnResult(HrQyeryAllRows, hr);
goto ret;
}

if (prows == NULL || prows->cRows == 0
|| prows->aRow[0].lpProps[1].ulPropTag != PR_ENTRYID)
{
printf("No default store\n");
goto ret;
}

Assert(prows->cRows == 1);

hr = pses->OpenMsgStore(0,
prows->aRow[0].lpProps[1].Value.bin.cb,
(LPENTRYID)prows->aRow[0].lpProps[1].Value.bin.lpb,
NULL, MDB_WRITE | MAPI_DEFERRED_ERRORS, &lpmdb);
if (HR_FAILED(hr))
{
if (GetScode(hr) != MAPI_E_USER_CANCEL)
TraceFnResult(OpenMsgStore, hr);
goto ret;
}

#if 0
if(hr) /*if we have a warning, display it and succeed */
{
LPMAPIERROR perr = NULL;

pses->lpVtbl->GetLastError(pses, hr, 0, &perr);
MakeMessageBox(hWnd, GetScode(hr), IDS_OPENSTOREWARN, perr, MBS_ERROR);
MAPIFreeBuffer(perr);
}

#endif

Assert(lpmdb != NULL);

*ppmdb = lpmdb;



ret:
FreeProws(prows);
UlRelease(ptable);

DebugTraceResult(OpenDefaultStore, hr);
return hr;
}

//
// Open MAPI address book
//
HRESULT HrOpenAddressBook(LPADRBOOK * ppAddrBook)
{
HRESULT hr;
LPADRBOOK pabAddrBook = NULL;

Assert(pses);

hr = pses->OpenAddressBook(0, NULL, 0, &pabAddrBook);
if(HR_FAILED(hr))
{
TraceFnResult(OpenAddressBook, hr);
return hr;
}
#if 0
if(hr) /*if we have a warning*/
{
LPMAPIERROR perr = NULL;

pses->lpVtbl->GetLastError(pses, hr, 0, &perr);
MakeMessageBox(hwnd, GetScode(hr), IDS_OPENABWARN, perr, MBS_ERROR);
MAPIFreeBuffer(perr);
}
#endif

*ppAddrBook = pabAddrBook;

DebugTraceResult(HrOpenAddressBook, hr);
return hrSuccess;
}

//
// Open the outbox of the default store.
// Assumes the default message store has been opened.
//
HRESULT HrOpenOutFolder(LPMAPIFOLDER FAR * lppF)
{
LPMAPIFOLDER lpfOutF = NULL;
HRESULT hr;
LPSPropValue lpspvFEID = NULL;
ULONG ulObjType = 0;

Assert(pmdb);

*lppF = NULL;
hr = HrGetOneProp((LPMAPIPROP) pmdb, PR_IPM_OUTBOX_ENTRYID, &lpspvFEID);
if(hr)
{
TraceFnResult(HrGetOneProp, hr);
goto err;
}

Assert(lpspvFEID->ulPropTag == PR_IPM_OUTBOX_ENTRYID);

hr = pmdb->OpenEntry(lpspvFEID->Value.bin.cb,
(LPENTRYID)lpspvFEID->Value.bin.lpb, NULL,
MAPI_MODIFY | MAPI_DEFERRED_ERRORS,
&ulObjType, (LPUNKNOWN FAR *) &lpfOutF);
if(HR_FAILED(hr))
{
TraceFnResult(OpenEntry, hr);
goto err;
}

*lppF = lpfOutF;


err:
MAPIFreeBuffer(lpspvFEID);

DebugTraceResult(HrOpenOutFolder, hr);
return hr;

}


//
// Create an outbound message, put data in it and submit.
//
HRESULT HrCreateAndSend()
{
HRESULT hr;
LPMESSAGE pmsg = NULL;

hr = HrCreateOutMessage(&pmsg);
if(hr)
return hr;

hr = HrInitMsg(pmsg);
if(HR_FAILED(hr))
goto err;

hr = pmsg->SubmitMessage(0);
TraceFnResult(SubmitMessage, hr);

err:

UlRelease(pmsg);

DebugTraceResult(HrCreateAndSend, hr);
return hr;

}

//
// Create a message in the outbox
//
HRESULT HrCreateOutMessage(LPMESSAGE FAR * ppmM)
{

LPMESSAGE lpmResM = NULL;
HRESULT hr;

Assert(pfldOutBox);

hr = pfldOutBox->CreateMessage(NULL, MAPI_DEFERRED_ERRORS, &lpmResM);
if(HR_FAILED(hr))
{
TraceFnResult(CreateMessage, hr);
return hr;
}

*ppmM = lpmResM;

DebugTraceResult(HrCreateOutMessage, hr);
return hrSuccess;
}

//
// Put the data from globals in the message
//
HRESULT HrInitMsg(LPMESSAGE pmsg)
{
HRESULT hr;
LPADRLIST pal = NULL;
enum {iSubj, iSentMail, iConvTopic, iConvIdx, iMsgClass, cProps};
// PR_SUBJECT, PR_SENTMAIL_ENTRYID,
// PR_CONVERSATION_TOPIC, PR_COVERSATION_INDEX

SPropValue props[cProps];
ULONG cbConvIdx = 0;
LPBYTE lpbConvIdx = NULL;

//recipients
hr = HrCreateAddrList(&pal);
if(HR_FAILED(hr))
goto err;

Assert(pal);

hr = pmsg->ModifyRecipients(0, pal);
if(HR_FAILED(hr))
{
goto err;
}

//subject and conversation topic
if(szSubject)
{
props[iSubj].ulPropTag = PR_SUBJECT;
props[iSubj].Value.LPSZ = szSubject;

props[iConvTopic].ulPropTag = PR_CONVERSATION_TOPIC;
props[iConvTopic].Value.LPSZ = szSubject;
}
else
{
props[iSubj].ulPropTag = PR_NULL;
props[iConvTopic].ulPropTag = PR_NULL;
}

// conversaton index
if(!ScAddConversationIndex(0, NULL, &cbConvIdx, &lpbConvIdx))
{
props[iConvIdx].ulPropTag = PR_CONVERSATION_INDEX;
props[iConvIdx].Value.bin.lpb = lpbConvIdx;
props[iConvIdx].Value.bin.cb = cbConvIdx;
}
else
{
props[iConvIdx].ulPropTag = PR_NULL;
}

// sent mail entry id (we want to leave a copy in the "Sent Items" folder)
props[iSentMail] = *pvalSentMailEID;

props[iMsgClass].ulPropTag = PR_MESSAGE_CLASS;
props[iMsgClass].Value.lpszA = "IPM.Note";

hr = pmsg->SetProps(cProps, (LPSPropValue)&props, NULL);
if(HR_FAILED(hr))
{
TraceFnResult(SetProps, hr);
goto err;
}

//body (PR_RTF_COMPRESSED)

hr = HrSetRTF(pmsg);
if(HR_FAILED(hr))
goto err;

err:
FreePadrlist(pal);
MAPIFreeBuffer(lpbConvIdx);

DebugTraceResult(HrInitMsg, hr);
return hr;

}

//
// Put the context of the input file in PR_RTF_COMPRESSED
//
HRESULT HrSetRTF(LPMESSAGE pmsg)
{
if(!szRTFFile)
return hrSuccess;

Assert(pmsg);

HRESULT hr;
LPSTREAM lpstrRTFComp = NULL;
LPSTREAM lpstrRTF = NULL;
LPSTREAM lpstrRTFFile = NULL;
STATSTG statstg;
BOOL fUpdated;
LPSPropValue pvalSupMask = NULL;

hr = OpenStreamOnFile(MAPIAllocateBuffer, MAPIFreeBuffer,
STGM_READ | STGM_SHARE_DENY_WRITE,
szRTFFile, NULL, &lpstrRTFFile);
if(HR_FAILED(hr))
{
TraceFnResult(OpenStreamOnFile, hr);
printf("Can't open file %s \n", szRTFFile);
goto err;
}

hr = pmsg->OpenProperty (PR_RTF_COMPRESSED, &IID_IStream,
0, MAPI_CREATE | MAPI_MODIFY | MAPI_DEFERRED_ERRORS,
(LPUNKNOWN FAR *)&lpstrRTFComp);
if(HR_FAILED(hr))
{
TraceFnResult(OpenProperty, hr);
goto err;
}

hr = HrGetOneProp(pmsg, PR_STORE_SUPPORT_MASK, &pvalSupMask);
if(HR_FAILED(hr))
{
TraceFnResult(HrGetOneProp, hr);
goto err;
}

hr = WrapCompressedRTFStream(lpstrRTFComp,
MAPI_MODIFY | (pvalSupMask->Value.l & STORE_UNCOMPRESSED_RTF),
&lpstrRTF);
if(HR_FAILED(hr))
{
TraceFnResult(WrapCompressedRTFStream, hr);
goto err;
}

hr = lpstrRTFFile->Stat(&statstg, STATFLAG_NONAME);
if(HR_FAILED(hr))
{
TraceFnResult(Stat, hr);
goto err;
}

hr = lpstrRTFFile->CopyTo(lpstrRTF, statstg.cbSize, NULL, NULL);
if(HR_FAILED(hr))
{
TraceFnResult(CopyTo, hr);
goto err;
}

hr = lpstrRTF->Commit(STGC_OVERWRITE);
if(HR_FAILED(hr))
{
TraceFnResult(Commit, hr);
goto err;
}

if( !(pvalSupMask->Value.l & STORE_RTF_OK))
{
#ifdef DEBUG
printf("RTFSync will be called\n");
#endif //DEBUG

hr = RTFSync(pmsg, RTF_SYNC_RTF_CHANGED, &fUpdated);
if(HR_FAILED(hr))
{
TraceFnResult(RTFSync, hr);
goto err;
}
}

err:

UlRelease(lpstrRTF);
UlRelease(lpstrRTFComp);
UlRelease(lpstrRTFFile);

MAPIFreeBuffer(pvalSupMask);

DebugTraceResult(HrSetRTF, hr);

return hr;
}

//
// Create an adrlist with resolved recipients
//
HRESULT HrCreateAddrList(LPADRLIST * ppal)
{

HRESULT hr;
LPADRLIST pal = NULL;
int ind;
#define chDELIMITER ';'


int nToRecips = 1;
LPSTR strToken = szToRecips;
while (strToken = strchr(strToken, chDELIMITER))
{
++strToken;
++nToRecips;
}

int cb = CbNewADRLIST(nToRecips);

hr = MAPIAllocateBuffer(cb, (LPVOID FAR *) &pal);
if(hr)
{
TraceFnResult(MAPIAllocateBuffer, hr);
goto err;
}
ZeroMemory(pal, cb);

hr = MAPIAllocateBuffer(2 * sizeof(SPropValue),
(LPVOID FAR *)&pal->aEntries[0].rgPropVals);
if(hr)
{
TraceFnResult(MAPIAllocateBuffer, hr);
goto err;
}

pal->aEntries[0].rgPropVals[0].ulPropTag = PR_DISPLAY_NAME;
pal->aEntries[0].rgPropVals[0].Value.LPSZ = szToRecips;
pal->aEntries[0].rgPropVals[1].ulPropTag = PR_RECIPIENT_TYPE;
pal->aEntries[0].rgPropVals[1].Value.l= MAPI_TO;
pal->aEntries[0].cValues = 2;

strToken = szToRecips;

for(ind = 1; ind < nToRecips; ++ind)
{
LPADRENTRY pae = &pal->aEntries[ind];

hr = MAPIAllocateBuffer(2 * sizeof(SPropValue),
(LPVOID FAR *)&pae->rgPropVals);
if(hr)
{
TraceFnResult(MAPIAllocateBuffer, hr);
goto err;
}

strToken = strchr(strToken, chDELIMITER);
Assert(strToken);

*strToken++ = '\0';

pae->rgPropVals[0].ulPropTag = PR_DISPLAY_NAME;
pae->rgPropVals[0].Value.LPSZ = strToken;
pae->rgPropVals[1].ulPropTag = PR_RECIPIENT_TYPE;
pae->rgPropVals[1].Value.l = MAPI_TO;
pae->cValues = 2;
}

pal->cEntries = nToRecips;
hr = pabAddrB->ResolveName(0, 0, NULL, pal);
if(HR_FAILED(hr))
{
TraceFnResult(ResolveName, hr);
goto err;
}

*ppal = pal;

return hrSuccess;

err:

FreePadrlist(pal);

DebugTraceResult(HrCreateAddrList, hr);
return hr;
}


//======================================================================
// ProcessCommandLineArguments - show usage to the user
//======================================================================

BOOL fParseCommandLine(int argc,char * argv[])
{
BOOL fResult;
int iCount;

fResult = FALSE;
_try
{
if (argc < 2) // Must be at least 1 argument
{
return fResult; // Show Usage to user.
}

for (iCount = 1; iCount < argc; iCount++)
{
if ((*argv[iCount] == '-') || (*argv[iCount] == '/'))
{
switch (tolower(*(argv[iCount]+1))) // Process switches
{
case 'u':
szProfile = argv[++iCount];
break;

case 'p':
szPassword = argv[++iCount];
break;

case 't':
szToRecips = argv[++iCount];
break;

case 's':
szSubject = argv[++iCount];
break;

case 'f':
szRTFFile = argv[++iCount];
break;

case 'h':
case '?': // User asked for help '-?'
default:
return fResult;
} // Switch
} // If
} // For Argument Count


// Check for required command line arguments

if (szToRecips)
{
fResult = TRUE;
}
else
{
printf("ERROR: Recipient(s) required.\n");

}
return fResult;

}
_finally
{
if (!fResult)
{
printf( "\nSend MAPI messages from Win32 command line.\n" );
printf( "Usage: sendrtf [-u] [-p] -t [-s] [-f][-v][-?]\n" );
printf( "Where...\n\n" );
printf( "-u profile name \n" );
printf( "-p profile password \n" );
printf( "-t recipient(s) (multiples must be separated by ';' and "
"enclosed in double\n");
printf( " quotes. recipients must not be ambiguous in default "
"address book.)\n" );
//printf( "-c specifies mail copy list (cc: list) \n" );
printf( "-s subject line \n" );
printf( "-f file (in rtf format) to be used as a message body\n" );
//printf( "-v generates verbose output\n" );
printf( "-? prints this message\n" );
}
} // Finally
} // End of Function