/*++ Copyright (c) 1996 Microsoft Corporation Module Name: mapi.c Abstract: This file implements wrappers for all mapi apis. The wrappers are necessary because mapi does not implement unicode and this code must be non-unicode. Author: Wesley Witt (wesw) 13-Sept-1996 Revision History: --*/ #undef UNICODE #undef _UNICODE #include #include #include #include #include #include "profinfo.h" #include "faxutil.h" typedef ULONG (STDAPIVCALLTYPE*ULRELEASE)(LPVOID); typedef VOID (STDAPIVCALLTYPE*FREEPADRLIST)(LPADRLIST); typedef ULONG (STDAPIVCALLTYPE*HRQUERYALLROWS)(LPMAPITABLE,LPSPropTagArray,LPSRestriction,LPSSortOrderSet,LONG,LPSRowSet*); typedef SCODE (STDAPIVCALLTYPE*SCDUPPROPSET)(int, LPSPropValue,LPALLOCATEBUFFER, LPSPropValue*); static LPMAPIINITIALIZE MapiInitialize; static LPMAPIUNINITIALIZE MapiUnInitialize; static LPMAPILOGONEX MapiLogonEx; static LPMAPIFREEBUFFER MapiFreeBuffer; static LPMAPIALLOCATEBUFFER MapiAllocateBuffer; static LPMAPIADMINPROFILES MapiAdminProfiles; static ULRELEASE pUlRelease; static FREEPADRLIST pFreePadrlist; static HRQUERYALLROWS pHrQueryAllRows; static SCDUPPROPSET pScDupPropset; static MAPIINIT_0 MapiInit; extern "C" BOOL MapiIsInitialized = FALSE; extern "C" DWORD ServiceDebug; extern "C" LPSTR UnicodeStringToAnsiString( LPWSTR UnicodeString ); extern "C" VOID FreeString( LPVOID String ); extern "C" BOOL InitializeMapi( VOID ) /*++ Routine Description: Initializes MAPI. Arguments: NONE Return Value: TRUE if successful, FALSE if not --*/ { HMODULE MapiMod = NULL; HRESULT Result; // // load the mapi dll // MapiMod = LoadLibrary( "mapi32.dll" ); if (!MapiMod) { return FALSE; } // // get the addresses of the mapi functions that we need // MapiInitialize = (LPMAPIINITIALIZE) GetProcAddress( MapiMod, "MAPIInitialize" ); MapiUnInitialize = (LPMAPIUNINITIALIZE) GetProcAddress( MapiMod, "MAPIUninitialize" ); MapiLogonEx = (LPMAPILOGONEX) GetProcAddress( MapiMod, "MAPILogonEx" ); MapiFreeBuffer = (LPMAPIFREEBUFFER) GetProcAddress( MapiMod, "MAPIFreeBuffer" ); MapiAllocateBuffer = (LPMAPIALLOCATEBUFFER) GetProcAddress( MapiMod, "MAPIAllocateBuffer" ); MapiAdminProfiles = (LPMAPIADMINPROFILES) GetProcAddress( MapiMod, "MAPIAdminProfiles" ); pUlRelease = (ULRELEASE) GetProcAddress( MapiMod, "UlRelease@4" ); pFreePadrlist = (FREEPADRLIST) GetProcAddress( MapiMod, "FreePadrlist@4" ); pHrQueryAllRows = (HRQUERYALLROWS) GetProcAddress( MapiMod, "HrQueryAllRows@24" ); pScDupPropset = (SCDUPPROPSET) GetProcAddress( MapiMod, "ScDupPropset@16" ); if ((!MapiInitialize) || (!MapiUnInitialize) || (!MapiLogonEx) || (!MapiAllocateBuffer) || (!MapiFreeBuffer) || (!MapiAdminProfiles) || (!pUlRelease) || (!pFreePadrlist) || (!pHrQueryAllRows) || (!pScDupPropset)) { return FALSE; } MapiInit.ulFlags = MAPI_MULTITHREAD_NOTIFICATIONS; if (!ServiceDebug) { MapiInit.ulFlags |= MAPI_NT_SERVICE; } Result = MapiInitialize(&MapiInit); if (Result != S_OK) { return FALSE; } return MapiIsInitialized = TRUE; } VOID FreeProws( LPSRowSet prows ) /*++ Routine Description: Destroy SRowSet structure. Copied from MAPI. Arguments: hFile - Pointer to SRowSet Return value: NONE --*/ { ULONG irow; if (!prows) { return; } for (irow = 0; irow < prows->cRows; ++irow) { MapiFreeBuffer(prows->aRow[irow].lpProps); } MapiFreeBuffer( prows ); } HRESULT HrMAPIFindInbox( IN LPMDB lpMdb, OUT ULONG *lpcbeid, OUT LPENTRYID *lppeid ) /*++ Routine Description: Find IPM inbox folder. Copied from Exchange SDK. Arguments: lpMdb - pointer to message store lpcbeid - count of bytes in entry ID lppeid - entry ID of IPM inbox Return value: HRESULT (see MAPI docs) --*/ { HRESULT hr = NOERROR; SCODE sc = 0; *lpcbeid = 0; *lppeid = NULL; // // Get the entry ID of the Inbox from the message store // hr = lpMdb->GetReceiveFolder( NULL, 0, lpcbeid, lppeid, NULL ); return hr; } HRESULT HrMAPIFindOutbox( IN LPMDB lpMdb, OUT ULONG *lpcbeid, OUT LPENTRYID *lppeid ) /*++ Routine Description: Find IPM outbox folder. Copied from Exchange SDK. Arguments: lpMdb - pointer to message store lpcbeid - count of bytes in entry ID lppeid - entry ID of IPM inbox Return value: HRESULT (see MAPI docs) --*/ { HRESULT hr = NOERROR; SCODE sc = 0; ULONG cValues = 0; LPSPropValue lpPropValue = NULL; ULONG cbeid = 0; SPropTagArray rgPropTag = { 1, { PR_IPM_OUTBOX_ENTRYID } }; *lpcbeid = 0; *lppeid = NULL; // // Get the outbox entry ID property. // hr = lpMdb->GetProps( &rgPropTag, 0, &cValues, &lpPropValue ); if (hr == MAPI_W_ERRORS_RETURNED) { goto cleanup; } if (FAILED(hr)) { lpPropValue = NULL; goto cleanup; } // // Check to make sure we got the right property. // if (lpPropValue->ulPropTag != PR_IPM_OUTBOX_ENTRYID) { goto cleanup; } cbeid = lpPropValue->Value.bin.cb; sc = MapiAllocateBuffer( cbeid, (void **)lppeid ); if(FAILED(sc)) { goto cleanup; } // // Copy outbox Entry ID // CopyMemory( *lppeid, lpPropValue->Value.bin.lpb, cbeid ); *lpcbeid = cbeid; cleanup: MapiFreeBuffer( lpPropValue ); return hr; } HRESULT HrMAPIFindDefaultMsgStore( IN LPMAPISESSION lplhSession, OUT ULONG *lpcbeid, OUT LPENTRYID *lppeid ) /*++ Routine Description: Get the entry ID of the default message store. Copied from Exchange SDK. Arguments: lplhSession - session pointer lpcbeid - count of bytes in entry ID lppeid - entry ID default store Return value: HRESULT (see MAPI docs) --*/ { HRESULT hr = NOERROR; SCODE sc = 0; LPMAPITABLE lpTable = NULL; LPSRowSet lpRows = NULL; LPENTRYID lpeid = NULL; ULONG cbeid = 0; ULONG cRows = 0; ULONG i = 0; SizedSPropTagArray(2, rgPropTagArray) = { 2, { PR_DEFAULT_STORE, PR_ENTRYID } }; // // Get the list of available message stores from MAPI // hr = lplhSession->GetMsgStoresTable( 0, &lpTable ); if (FAILED(hr)) { goto cleanup; } // // Get the row count for the message recipient table // hr = lpTable->GetRowCount( 0, &cRows ); if (FAILED(hr)) { goto cleanup; } // // Set the columns to return // hr = lpTable->SetColumns( (LPSPropTagArray)&rgPropTagArray, 0 ); if (FAILED(hr)) { goto cleanup; } // // Go to the beginning of the recipient table for the envelope // hr = lpTable->SeekRow( BOOKMARK_BEGINNING, 0, NULL ); if (FAILED(hr)) { goto cleanup; } // // Read all the rows of the table // hr = lpTable->QueryRows( cRows, 0, &lpRows ); if (SUCCEEDED(hr) && (lpRows != NULL) && (lpRows->cRows == 0)) { FreeProws( lpRows ); hr = MAPI_E_NOT_FOUND; } if (FAILED(hr) || (lpRows == NULL)) { goto cleanup; } for (i = 0; i < cRows; i++) { if(lpRows->aRow[i].lpProps[0].Value.b == TRUE) { cbeid = lpRows->aRow[i].lpProps[1].Value.bin.cb; sc = MapiAllocateBuffer( cbeid, (void **)&lpeid ); if(FAILED(sc)) { cbeid = 0; lpeid = NULL; goto cleanup; } // // Copy entry ID of message store // CopyMemory( lpeid, lpRows->aRow[i].lpProps[1].Value.bin.lpb, cbeid ); break; } } cleanup: if(!lpRows) { FreeProws( lpRows ); } lpTable->Release(); *lpcbeid = cbeid; *lppeid = lpeid; return hr; } HRESULT HrMAPIWriteFileToStream( IN HANDLE hFile, OUT LPSTREAM lpStream ) /*++ Routine Description: Write file to a stream given a stream pointer. Copied from Exchange SDK. Arguments: hFile - Handle to file lpStream - Pointer to stream Return value: HRESULT (see MAPI docs) --*/ { HRESULT hr = NOERROR; DWORD cBytesRead = 0; ULONG cBytesWritten = 0; BYTE byteBuffer[128] = {0}; BOOL fReadOk = FALSE; for(;;) { fReadOk = ReadFile( hFile, byteBuffer, sizeof(byteBuffer), &cBytesRead, NULL ); if (!fReadOk) { break; } if (!cBytesRead) { hr = NOERROR; break; } hr = lpStream->Write( byteBuffer, cBytesRead, &cBytesWritten ); if (FAILED(hr)) { break; } if(cBytesWritten != cBytesRead) { break; } } return hr; } extern "C" VOID DoMapiLogon( PPROFILE_INFO ProfileInfo ) { HRESULT HResult = 0; FLAGS MAPILogonFlags = MAPI_NEW_SESSION | MAPI_EXTENDED | MAPI_NO_MAIL; LPSTR ProfileName; LPMAPISESSION Session = NULL; if (!MapiIsInitialized) { ProfileInfo->Session = NULL; SetEvent( ProfileInfo->EventHandle ); return; } if (!ServiceDebug) { MAPILogonFlags |= MAPI_NT_SERVICE; } if (ProfileInfo->UseMail) { MAPILogonFlags &= ~MAPI_NO_MAIL; } if (ProfileInfo->ProfileName[0] == 0) { MAPILogonFlags |= MAPI_USE_DEFAULT; } ProfileName = UnicodeStringToAnsiString( ProfileInfo->ProfileName ); __try { HResult = MapiLogonEx( 0, ProfileName, NULL, MAPILogonFlags, &Session ); } __except (EXCEPTION_EXECUTE_HANDLER) { HResult = GetExceptionCode(); } if (HR_FAILED(HResult)) { SetLastError( HResult ); ProfileInfo->Session = NULL; } else { InitializeCriticalSection( &ProfileInfo->CsSession ); ProfileInfo->Session = Session; } FreeString( ProfileName ); SetEvent( ProfileInfo->EventHandle ); } extern "C" BOOL DoMapiLogoff( LPMAPISESSION Session ) { HRESULT HResult = Session->Logoff( 0, 0, 0 ); if (HR_FAILED(HResult)) { return FALSE; } return TRUE; } extern "C" BOOL StoreMapiMessage( LPMAPISESSION Session, LPWSTR MsgSenderNameW, LPWSTR MsgSubjectW, LPWSTR MsgBodyW, LPWSTR MsgAttachmentFileNameW, LPWSTR MsgAttachmentTitleW, DWORD MsgImportance, LPFILETIME MsgTime, PULONG ResultCode ) /*++ Routine Description: Mails a TIFF file to the inbox in the specified profile. Arguments: TiffFileName - Name of TIFF file to mail ProfileName - Profile name to use ResultCode - The result of the failed API call Return Value: TRUE for success, FALSE on error --*/ { LPATTACH Attach = NULL; ULONG AttachmentNum; CHAR FileExt[_MAX_EXT]; CHAR FileName[MAX_PATH]; HRESULT HResult = 0; LPMAPIFOLDER Inbox = NULL; LPMESSAGE Message = NULL; LPSTR MsgAttachmentFileName = NULL; LPSTR MsgAttachmentTitle = NULL; LPSTR MsgBody = NULL; LPSTR MsgSenderName = NULL; LPSTR MsgSubject = NULL; DWORD RenderingPosition = 0; LPMDB Store = NULL; LPSTREAM Stream = NULL; ULONG cbInEntryID = 0; HANDLE hFile = INVALID_HANDLE_VALUE; LPENTRYID lpInEntryID = NULL; LPSPropProblemArray lppProblems; ULONG lpulObjType; SPropValue spvAttachProps[5] = { 0 }; SPropValue spvMsgProps[9] = { 0 }; FILETIME CurrentTime; _try { // // get the time if the caller wants us to // if (!MsgTime) { MsgTime = &CurrentTime; GetSystemTimeAsFileTime( MsgTime ); } // // find the default message store // HResult = HrMAPIFindDefaultMsgStore( Session, &cbInEntryID, &lpInEntryID ); if(HR_FAILED(HResult)) { _leave; } // // open the message store // HResult = Session->OpenMsgStore( 0, cbInEntryID, lpInEntryID, NULL, MDB_NO_DIALOG | MDB_WRITE, &Store ); if (HR_FAILED(HResult)) { _leave; } MapiFreeBuffer( lpInEntryID ); // // find the inbox // HResult= HrMAPIFindInbox( Store, &cbInEntryID, &lpInEntryID ); if(HR_FAILED(HResult)) { _leave; } // // open the inbox // HResult = Session->OpenEntry( cbInEntryID, lpInEntryID, NULL, MAPI_MODIFY, &lpulObjType, (LPUNKNOWN *) &Inbox ); if (HR_FAILED(HResult)) { _leave; } // // Create a message // HResult = Inbox->CreateMessage( NULL, 0, &Message ); if (HR_FAILED(HResult)) { _leave; } // // convert all of the strings to ansi strings // MsgSenderName = UnicodeStringToAnsiString( MsgSenderNameW ); MsgSubject = UnicodeStringToAnsiString( MsgSubjectW ); MsgBody = UnicodeStringToAnsiString( MsgBodyW ); MsgAttachmentFileName = UnicodeStringToAnsiString( MsgAttachmentFileNameW ); MsgAttachmentTitle = UnicodeStringToAnsiString( MsgAttachmentTitleW ); // // Fill in message properties and set them // spvMsgProps[0].ulPropTag = PR_SENDER_NAME; spvMsgProps[1].ulPropTag = PR_SENT_REPRESENTING_NAME; spvMsgProps[2].ulPropTag = PR_SUBJECT; spvMsgProps[3].ulPropTag = PR_MESSAGE_CLASS; spvMsgProps[4].ulPropTag = PR_BODY; spvMsgProps[5].ulPropTag = PR_MESSAGE_DELIVERY_TIME; spvMsgProps[6].ulPropTag = PR_CLIENT_SUBMIT_TIME; spvMsgProps[7].ulPropTag = PR_MESSAGE_FLAGS; spvMsgProps[8].ulPropTag = PR_IMPORTANCE; spvMsgProps[0].Value.lpszA = MsgSenderName; spvMsgProps[1].Value.lpszA = MsgSenderName; spvMsgProps[2].Value.lpszA = MsgSubject; spvMsgProps[3].Value.lpszA = "IPM.Note"; spvMsgProps[4].Value.lpszA = MsgBody; spvMsgProps[5].Value.ft = *MsgTime; spvMsgProps[6].Value.ft = *MsgTime; spvMsgProps[7].Value.ul = 0; spvMsgProps[8].Value.ul = MsgImportance; HResult = Message->SetProps( sizeof(spvMsgProps)/sizeof(SPropValue), (LPSPropValue) spvMsgProps, &lppProblems ); if (HR_FAILED(HResult)) { _leave; } MapiFreeBuffer( lppProblems ); if (MsgAttachmentFileName) { // // Create an attachment // HResult = Message->CreateAttach( NULL, 0, &AttachmentNum, &Attach ); if (HR_FAILED(HResult)) { _leave; } _splitpath( MsgAttachmentFileName, NULL, NULL, FileName, FileExt ); strcat( FileName, FileExt ); // // Fill in attachment properties and set them // if (!MsgAttachmentTitle) { MsgAttachmentTitle = FileName; } RenderingPosition = strlen(MsgBody); spvAttachProps[0].ulPropTag = PR_RENDERING_POSITION; spvAttachProps[1].ulPropTag = PR_ATTACH_METHOD; spvAttachProps[2].ulPropTag = PR_ATTACH_LONG_FILENAME; spvAttachProps[3].ulPropTag = PR_DISPLAY_NAME; spvAttachProps[4].ulPropTag = PR_ATTACH_EXTENSION; spvAttachProps[0].Value.ul = RenderingPosition; spvAttachProps[1].Value.ul = ATTACH_BY_VALUE; spvAttachProps[2].Value.lpszA = MsgAttachmentTitle; spvAttachProps[3].Value.lpszA = MsgAttachmentTitle; spvAttachProps[4].Value.lpszA = FileExt; HResult = Attach->SetProps( sizeof(spvAttachProps)/sizeof(SPropValue), (LPSPropValue) spvAttachProps, &lppProblems ); if (HR_FAILED(HResult)) { _leave; } MapiFreeBuffer( lppProblems ); // // Attach a data property to the attachment // HResult = Attach->OpenProperty( PR_ATTACH_DATA_BIN, &IID_IStream, 0, MAPI_CREATE | MAPI_MODIFY, (LPUNKNOWN *) &Stream ); if (HR_FAILED(HResult)) { _leave; } // // open the message attachment file // hFile = CreateFile( MsgAttachmentFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (hFile == INVALID_HANDLE_VALUE) { _leave; } // // Write the file to the data property // HResult = HrMAPIWriteFileToStream( hFile, Stream ); if (HR_FAILED(HResult)) { _leave; } } // // Save the changes and logoff // HResult = Attach->SaveChanges( FORCE_SAVE ); if (HR_FAILED(HResult)) { _leave; } HResult = Message->SaveChanges( FORCE_SAVE ); if (HR_FAILED(HResult)) { _leave; } } _finally { MapiFreeBuffer( lpInEntryID ); if (Store) { Store->Release(); } if (Inbox) { Inbox->Release(); } if (Message) { Message->Release(); } if (Attach) { Attach->Release(); } if (Stream) { Stream->Release(); } FreeString( MsgSenderName ); FreeString( MsgSubject ); FreeString( MsgBody ); FreeString( MsgAttachmentFileName ); if (MsgAttachmentTitleW && MsgAttachmentTitle) { FreeString( MsgAttachmentTitle ); } CloseHandle( hFile ); } *ResultCode = HResult; return HResult == 0; } extern "C" LONG GetMapiProfiles( LPWSTR *OutBuffer, LPDWORD OutBufferSize ) { HMODULE MapiMod = NULL; LPMAPITABLE pmt = NULL; LPSRowSet prws = NULL; LPSPropValue pval; LPPROFADMIN lpProfAdmin; DWORD i; HRESULT hr; DWORD Count; LPWSTR Buffer; DWORD BytesNeeded; DWORD Offset = 0; if (!MapiIsInitialized) { return MAPI_E_NO_SUPPORT; } if (hr = MapiAdminProfiles( 0, &lpProfAdmin )) { return hr; } // // get the mapi table object // if (hr = lpProfAdmin->GetProfileTable( 0, &pmt )) { goto exit; } // // get the actual profile data, FINALLY // if (hr = pmt->QueryRows( 4000, 0, &prws )) { goto exit; } // // enumerate the profiles and put the name // of each profile in the combo box // BytesNeeded = 0; for (i=0; icRows; i++) { pval = prws->aRow[i].lpProps; Count = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pval[0].Value.lpszA, -1, NULL, 0 ); if (Count == 0) { hr = GetLastError(); goto exit; } else { BytesNeeded += Count * sizeof(WCHAR); } } BytesNeeded += sizeof(UNICODE_NULL); Buffer = (LPWSTR) MemAlloc( BytesNeeded ); if (Buffer == NULL) { hr = ERROR_INSUFFICIENT_BUFFER; goto exit; } for (i=0; icRows; i++) { pval = prws->aRow[i].lpProps; Count = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pval[0].Value.lpszA, -1, &Buffer[Offset], BytesNeeded - (Offset * sizeof(WCHAR)) ); if (Count == 0) { hr = GetLastError(); goto exit; } else { Offset += Count; } } Buffer[Offset] = 0; *OutBuffer = Buffer; *OutBufferSize = BytesNeeded; hr = ERROR_SUCCESS; exit: FreeProws( prws ); if (pmt) { pmt->Release(); } if (lpProfAdmin) { lpProfAdmin->Release(); } return hr; } extern "C" BOOL GetDefaultMapiProfile( LPWSTR ProfileName ) { BOOL rVal = FALSE; LPMAPITABLE pmt = NULL; LPSRowSet prws = NULL; LPSPropValue pval; LPPROFADMIN lpProfAdmin; DWORD i; DWORD j; if (!MapiIsInitialized) { goto exit; } if (MapiAdminProfiles( 0, &lpProfAdmin )) { goto exit; } // // get the mapi profile table object // if (lpProfAdmin->GetProfileTable( 0, &pmt )) { goto exit; } // // get the actual profile data, FINALLY // if (pmt->QueryRows( 4000, 0, &prws )) { goto exit; } // // enumerate the profiles looking for the default profile // for (i=0; icRows; i++) { pval = prws->aRow[i].lpProps; for (j = 0; j < 2; j++) { if (pval[j].ulPropTag == PR_DEFAULT_PROFILE && pval[j].Value.b) { // // this is the default profile // MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pval[0].Value.lpszA, -1, ProfileName, (cchProfileNameMax + 1) * sizeof(WCHAR) ); rVal = TRUE; break; } } } exit: FreeProws( prws ); if (pmt) { pmt->Release(); } return rVal; } #define IADDRTYPE 0 #define IEMAILADDR 1 #define IMAPIRECIP 2 #define IPROXYADDR 3 #define PR_EMS_AB_CONTAINERID PROP_TAG(PT_LONG, 0xFFFD) #define PR_EMS_AB_PROXY_ADDRESSES_A PROP_TAG(PT_MV_STRING8, 0x800F) #define MUIDEMSAB {0xDC, 0xA7, 0x40, 0xC8, 0xC0, 0x42, 0x10, 0x1A, 0xB4, 0xB9, 0x08, 0x00, 0x2B, 0x2F, 0xE1, 0x82} #define CbNewFlagList(_cflag) (offsetof(FlagList,ulFlag) + (_cflag)*sizeof(ULONG)) HRESULT HrMAPICreateSizedAddressList( // RETURNS: return code IN ULONG cEntries, // count of entries in address list OUT LPADRLIST *lppAdrList // pointer to address list pointer ) { HRESULT hr = NOERROR; SCODE sc = 0; ULONG cBytes = 0; *lppAdrList = NULL; cBytes = CbNewADRLIST(cEntries); sc = MapiAllocateBuffer(cBytes, (PVOID*) lppAdrList); if(FAILED(sc)) { hr = E_OUTOFMEMORY; goto cleanup; } // Initialize ADRLIST structure ZeroMemory(*lppAdrList, cBytes); (*lppAdrList)->cEntries = cEntries; cleanup: return hr; } HRESULT HrMAPISetAddressList( // RETURNS: return code IN ULONG iEntry, // index of address list entry IN ULONG cProps, // count of values in address list entry IN LPSPropValue lpPropValues, // pointer to address list entry IN OUT LPADRLIST lpAdrList // pointer to address list pointer ) { HRESULT hr = NOERROR; SCODE sc = 0; LPSPropValue lpNewPropValues = NULL; ULONG cBytes = 0; if(iEntry >= lpAdrList->cEntries) { hr = E_FAIL; goto cleanup; } sc = pScDupPropset( cProps, lpPropValues, MapiAllocateBuffer, &lpNewPropValues ); if(FAILED(sc)) { hr = E_FAIL; goto cleanup; } if(lpAdrList->aEntries[iEntry].rgPropVals != NULL) { MapiFreeBuffer(lpAdrList->aEntries[iEntry].rgPropVals); } lpAdrList->aEntries[iEntry].cValues = cProps; lpAdrList->aEntries[iEntry].rgPropVals = lpNewPropValues; cleanup: return hr; } HRESULT HrCheckForTypeA( // RETURNS: return code IN LPCSTR lpszAddrType, // pointer to address type IN LPCSTR lpszProxy, // pointer to proxy address OUT LPSTR * lppszAddress // pointer to address pointer ) { HRESULT hr = E_FAIL; LPCSTR lpszProxyAddr = NULL; ULONG cbAddress = 0; SCODE sc = 0; ULONG cchProxy = 0; ULONG cchProxyType = 0; // Initialize output parameter *lppszAddress = NULL; // find the ':' separator. cchProxy = lstrlenA(lpszProxy); cchProxyType = strcspn(lpszProxy, ":"); if((cchProxyType == 0) || (cchProxyType >= cchProxy)) { hr = E_FAIL; goto cleanup; } hr = MAPI_E_NOT_FOUND; // does the address type match? if((cchProxyType == (ULONG)lstrlenA(lpszAddrType)) && (_strnicmp(lpszProxy, lpszAddrType, cchProxyType) == 0)) { // specified address type found lpszProxyAddr = lpszProxy + cchProxyType + 1; cbAddress = strlen(lpszProxyAddr); // make a buffer to hold it. sc = MapiAllocateBuffer(cbAddress, (void **)lppszAddress); if(FAILED(sc)) { hr = E_OUTOFMEMORY; } else { CopyMemory(*lppszAddress, lpszProxyAddr, cbAddress); hr = NOERROR; } } cleanup: return hr; } HRESULT HrFindExchangeGlobalAddressList( IN LPADRBOOK lpAdrBook, OUT ULONG *lpcbeid, OUT LPENTRYID *lppeid ) { HRESULT hr = NOERROR; ULONG ulObjType = 0; ULONG i = 0; LPMAPIPROP lpRootContainer = NULL; LPMAPIPROP lpContainer = NULL; LPMAPITABLE lpContainerTable = NULL; LPSRowSet lpRows = NULL; ULONG cbContainerEntryId = 0; LPENTRYID lpContainerEntryId = NULL; LPSPropValue lpCurrProp = NULL; SRestriction SRestrictAnd[2] = {0}; SRestriction SRestrictGAL = {0}; SPropValue SPropID = {0}; SPropValue SPropProvider = {0}; BYTE muid[] = MUIDEMSAB; SizedSPropTagArray(1, rgPropTags) = { 1, { PR_ENTRYID, } }; *lpcbeid = 0; *lppeid = NULL; // Open the root container of the address book hr = lpAdrBook->OpenEntry( 0, NULL, NULL, MAPI_DEFERRED_ERRORS, &ulObjType, (LPUNKNOWN FAR *)&lpRootContainer ); if(FAILED(hr)) { goto cleanup; } if(ulObjType != MAPI_ABCONT) { hr = E_FAIL; goto cleanup; } // Get the hierarchy table of the root container hr = ((LPABCONT)lpRootContainer)->GetHierarchyTable( MAPI_DEFERRED_ERRORS|CONVENIENT_DEPTH, &lpContainerTable ); if(FAILED(hr)) { goto cleanup; } // Restrict the table to the global address list (GAL) // --------------------------------------------------- // Initialize provider restriction to only Exchange providers SRestrictAnd[0].rt = RES_PROPERTY; SRestrictAnd[0].res.resProperty.relop = RELOP_EQ; SRestrictAnd[0].res.resProperty.ulPropTag = PR_AB_PROVIDER_ID; SPropProvider.ulPropTag = PR_AB_PROVIDER_ID; SPropProvider.Value.bin.cb = 16; SPropProvider.Value.bin.lpb = (LPBYTE)muid; SRestrictAnd[0].res.resProperty.lpProp = &SPropProvider; // Initialize container ID restriction to only GAL container SRestrictAnd[1].rt = RES_PROPERTY; SRestrictAnd[1].res.resProperty.relop = RELOP_EQ; SRestrictAnd[1].res.resProperty.ulPropTag = PR_EMS_AB_CONTAINERID; SPropID.ulPropTag = PR_EMS_AB_CONTAINERID; SPropID.Value.l = 0; SRestrictAnd[1].res.resProperty.lpProp = &SPropID; // Initialize AND restriction SRestrictGAL.rt = RES_AND; SRestrictGAL.res.resAnd.cRes = 2; SRestrictGAL.res.resAnd.lpRes = &SRestrictAnd[0]; // Restrict the table to the GAL - only a single row should remain // Get the row corresponding to the GAL // // Query all the rows // hr = pHrQueryAllRows( lpContainerTable, (LPSPropTagArray)&rgPropTags, &SRestrictGAL, NULL, 0, &lpRows ); if(FAILED(hr) || (lpRows == NULL) || (lpRows->cRows != 1)) { hr = E_FAIL; goto cleanup; } // Get the entry ID for the GAL lpCurrProp = &(lpRows->aRow[0].lpProps[0]); if(lpCurrProp->ulPropTag == PR_ENTRYID) { cbContainerEntryId = lpCurrProp->Value.bin.cb; lpContainerEntryId = (LPENTRYID)lpCurrProp->Value.bin.lpb; } else { hr = E_FAIL; goto cleanup; } hr = MapiAllocateBuffer( cbContainerEntryId, (LPVOID *)lppeid ); if(FAILED(hr)) { *lpcbeid = 0; *lppeid = NULL; } else { CopyMemory( *lppeid, lpContainerEntryId, cbContainerEntryId); *lpcbeid = cbContainerEntryId; } cleanup: pUlRelease(lpRootContainer); pUlRelease(lpContainerTable); pUlRelease(lpContainer); FreeProws( lpRows ); if(FAILED(hr)) { MapiFreeBuffer( *lppeid ); *lpcbeid = 0; *lppeid = NULL; } return hr; } HRESULT HrGWResolveProxy( IN LPADRBOOK lpAdrBook, // pointer to address book IN ULONG cbeid, // count of bytes in the entry ID IN LPENTRYID lpeid, // pointer to the entry ID IN LPCSTR lpszAddrType, // pointer to the address type OUT BOOL *lpfMapiRecip, // MAPI recipient OUT LPSTR *lppszAddress // pointer to the address pointer ) { HRESULT hr = E_FAIL; HRESULT hrT = 0; SCODE sc = 0; ULONG i = 0; ULONG cbAddress = 0; ULONG cProxy = 0; LPSPropValue lpProps = NULL; LPADRLIST lpAdrList = NULL; SPropValue prop[2] = {0}; SizedSPropTagArray(4, rgPropTags) = { 4, { PR_ADDRTYPE_A, PR_EMAIL_ADDRESS_A, PR_SEND_RICH_INFO, PR_EMS_AB_PROXY_ADDRESSES_A } }; // Initialize output parameters *lpfMapiRecip = FALSE; *lppszAddress = NULL; hr = HrMAPICreateSizedAddressList(1, &lpAdrList); if(FAILED(hr)) { goto cleanup; } prop[0].ulPropTag = PR_ENTRYID; prop[0].Value.bin.cb = cbeid; prop[0].Value.bin.lpb = (LPBYTE)lpeid; prop[1].ulPropTag = PR_RECIPIENT_TYPE; prop[1].Value.ul = MAPI_TO; hr = HrMAPISetAddressList( 0, 2, prop, lpAdrList ); if(FAILED(hr)) { goto cleanup; } hrT = lpAdrBook->PrepareRecips( 0, (LPSPropTagArray)&rgPropTags, lpAdrList ); if(FAILED(hrT)) { goto cleanup; } lpProps = lpAdrList->aEntries[0].rgPropVals; // // Hack: detect the case where prepare recips doesn't work correctly. // This happens when trying to look up a recipient that is in // a replicated directory but not in the local directory. // if (lpAdrList->aEntries[0].cValues == 3) { hr = E_FAIL; goto cleanup; } // If the given address type matches the PR_ADDRTYPE value, // return the PR_EMAIL_ADDRESS value if((PROP_TYPE(lpProps[IADDRTYPE].ulPropTag) != PT_ERROR) && (PROP_TYPE(lpProps[IEMAILADDR].ulPropTag) != PT_ERROR) && (_strcmpi(lpProps[IADDRTYPE].Value.lpszA, lpszAddrType) == 0)) { cbAddress = strlen(lpProps[IEMAILADDR].Value.lpszA); sc = MapiAllocateBuffer(cbAddress, (void **)lppszAddress); if(FAILED(sc)) { hr = E_OUTOFMEMORY; } else { CopyMemory(*lppszAddress, lpProps[IEMAILADDR].Value.lpszW, cbAddress); hr = NOERROR; } goto cleanup; } // Search for a PR_EMS_AB_PROXY_ADDRESSES of the given type if present. else if(PROP_TYPE(lpProps[IPROXYADDR].ulPropTag) != PT_ERROR) { // count of proxy addresses cProxy = lpAdrList->aEntries[0].rgPropVals[IPROXYADDR].Value.MVszA.cValues; for(i = 0; i < cProxy; i++) { hr = HrCheckForTypeA( lpszAddrType, lpProps[IPROXYADDR].Value.MVszA.lppszA[i], lppszAddress ); if(hr == MAPI_E_NOT_FOUND) { continue; } else if(FAILED(hr)) { goto cleanup; } else { // // Found a matching proxy address. // goto cleanup; } } } else { hr = E_FAIL; goto cleanup; } cleanup: if(SUCCEEDED(hr)) { *lpfMapiRecip = lpAdrList->aEntries[0].rgPropVals[IMAPIRECIP].Value.b; } pFreePadrlist(lpAdrList); return hr; } HRESULT HrGWResolveAddress( IN LPABCONT lpGalABCont, // pointer to GAL container IN LPCSTR lpszAddress, // pointer to proxy address OUT BOOL *lpfMapiRecip, // MAPI recipient OUT ULONG *lpcbEntryID, // count of bytes in entry ID OUT LPENTRYID *lppEntryID, // pointer to entry ID OUT LPADRLIST *lpAdrList // address list ) { HRESULT hr = NOERROR; HRESULT hrT = 0; SCODE sc = 0; LPFlagList lpFlagList = NULL; SPropValue prop[2] = {0}; ULONG cbEntryID = 0; LPENTRYID lpEntryID = NULL; static const SizedSPropTagArray(2, rgPropTags) = { 2, { PR_ENTRYID, PR_SEND_RICH_INFO } }; *lpfMapiRecip = FALSE; *lpcbEntryID = 0; *lppEntryID = NULL; *lpAdrList = NULL; sc = MapiAllocateBuffer( CbNewFlagList(1), (LPVOID*)&lpFlagList); if(FAILED(sc)) { hr = E_OUTOFMEMORY; goto cleanup; } lpFlagList->cFlags = 1; lpFlagList->ulFlag[0] = MAPI_UNRESOLVED; hr = HrMAPICreateSizedAddressList( 1, lpAdrList ); if(FAILED(hr)) { goto cleanup; } prop[0].ulPropTag = PR_DISPLAY_NAME_A; prop[0].Value.lpszA = (LPSTR)lpszAddress; prop[1].ulPropTag = PR_RECIPIENT_TYPE; prop[1].Value.ul = MAPI_TO; hr = HrMAPISetAddressList( 0, 2, prop, *lpAdrList ); if(FAILED(hr)) { goto cleanup; } hrT = lpGalABCont->ResolveNames( (LPSPropTagArray)&rgPropTags, 0, *lpAdrList, lpFlagList ); if(lpFlagList->ulFlag[0] != MAPI_RESOLVED) { if(lpFlagList->ulFlag[0] == MAPI_AMBIGUOUS) { hrT = MAPI_E_AMBIGUOUS_RECIP; } else { hrT = MAPI_E_NOT_FOUND; } } if(FAILED(hrT)) { if(hrT == MAPI_E_NOT_FOUND) { hr = MAPI_E_NOT_FOUND; } else { hr = E_FAIL; } goto cleanup; } cbEntryID = (*lpAdrList)->aEntries[0].rgPropVals[0].Value.bin.cb; lpEntryID = (LPENTRYID)(*lpAdrList)->aEntries[0].rgPropVals[0].Value.bin.lpb; sc = MapiAllocateBuffer( cbEntryID, (LPVOID*)lppEntryID); if(FAILED(sc)) { hr = E_OUTOFMEMORY; goto cleanup; } CopyMemory(*lppEntryID, lpEntryID, cbEntryID); *lpcbEntryID = cbEntryID; *lpfMapiRecip = (*lpAdrList)->aEntries[0].rgPropVals[1].Value.b; cleanup: MapiFreeBuffer(lpFlagList); return hr; } extern "C" BOOL MailMapiMessage( LPMAPISESSION Session, LPWSTR RecipientNameW, LPWSTR MsgSubjectW, LPWSTR MsgBodyW, LPWSTR MsgAttachmentFileNameW, LPWSTR MsgAttachmentTitleW, DWORD MsgImportance, PULONG ResultCode ) /*++ Routine Description: Mails a TIFF file to the addressbook recipient in the specified profile. Arguments: TiffFileName - Name of TIFF file to mail ProfileName - Profile name to use ResultCode - The result of the failed API call Return Value: TRUE for success, FALSE on error --*/ { ULONG cbInEntryID = 0; LPENTRYID lpInEntryID = NULL; LPMDB Store = NULL; ULONG lpulObjType; LPMAPIFOLDER Inbox = NULL; LPMAPIFOLDER Outbox = NULL; LPMESSAGE Message = NULL; LPATTACH Attach = NULL; LPSTREAM Stream = NULL; ULONG AttachmentNum; HRESULT HResult = 0; LPSTR MsgAttachmentFileName = NULL; LPSTR MsgAttachmentTitle = NULL; LPSTR MsgBody = NULL; LPSTR MsgSubject = NULL; LPSTR BodyStrA = NULL; LPSTR SubjectStrA = NULL; LPSTR SenderStrA = NULL; LPSTR LongFileNameA = NULL; LPSTR RecipientName = NULL; DWORD RenderingPosition = 0; LPADRBOOK AddrBook; LPADRLIST lpAddrList = NULL; ULONG ulFlags = LOGOFF_PURGE; LPSPropProblemArray lppProblems; ULONG cbGalEid = 0; LPENTRYID lpGalEid = NULL; LPSTR lpszProxyAddr = NULL; BOOL fMapiRecip = FALSE; ULONG ulObjType = 0; LPABCONT lpGalABCont = NULL; ULONG cbEntryID = 0; LPENTRYID lpEntryID = NULL; SPropValue spvAttachProps[5] = { 0 }; SPropValue spvMsgProps[5] = { 0 }; CHAR FileExt[_MAX_EXT]; CHAR FileName[MAX_PATH]; HANDLE hFile = INVALID_HANDLE_VALUE; _try { // // convert all of the strings to ansi strings // RecipientName = UnicodeStringToAnsiString( RecipientNameW ); MsgSubject = UnicodeStringToAnsiString( MsgSubjectW ); MsgBody = UnicodeStringToAnsiString( MsgBodyW ); MsgAttachmentFileName = UnicodeStringToAnsiString( MsgAttachmentFileNameW ); MsgAttachmentTitle = UnicodeStringToAnsiString( MsgAttachmentTitleW ); HResult = Session->OpenAddressBook( 0, NULL, AB_NO_DIALOG, &AddrBook ); if(HR_FAILED(HResult)) { _leave; } HResult = HrFindExchangeGlobalAddressList( AddrBook, &cbGalEid, &lpGalEid ); if(HR_FAILED(HResult)) { _leave; } HResult = AddrBook->OpenEntry( cbGalEid, lpGalEid, NULL, MAPI_DEFERRED_ERRORS, &ulObjType, (LPUNKNOWN FAR *)&lpGalABCont ); if(HR_FAILED(HResult)) { _leave; } HResult = HrGWResolveAddress( lpGalABCont, RecipientName, &fMapiRecip, &cbEntryID, &lpEntryID, &lpAddrList ); if(HR_FAILED(HResult)) { _leave; } // // Find the default message store // HResult = HrMAPIFindDefaultMsgStore( Session, &cbInEntryID, &lpInEntryID ); if(HR_FAILED(HResult)) { _leave; } // // Open it // HResult = Session->OpenMsgStore( (ULONG)0, cbInEntryID, lpInEntryID, NULL, MDB_NO_DIALOG | MDB_WRITE, &Store ); if(HR_FAILED(HResult)) { _leave; } MapiFreeBuffer(lpInEntryID); // // Find the outbox // HResult= HrMAPIFindOutbox( Store, &cbInEntryID, &lpInEntryID ); if(HR_FAILED(HResult)) { _leave; } // // Open it // HResult = Store->OpenEntry( cbInEntryID, lpInEntryID, NULL, MAPI_MODIFY | MAPI_DEFERRED_ERRORS, &lpulObjType, (LPUNKNOWN *) &Outbox ); if(HR_FAILED(HResult)) { _leave; } // // Create a message // HResult = Outbox->CreateMessage( NULL, 0, &Message ); if(HR_FAILED(HResult)) { _leave; } HResult = Message->ModifyRecipients( 0, lpAddrList ); if(HR_FAILED(HResult)) { _leave; } // // Fill in message properties and set them // spvMsgProps[0].ulPropTag = PR_SUBJECT; spvMsgProps[1].ulPropTag = PR_MESSAGE_CLASS; spvMsgProps[2].ulPropTag = PR_BODY; spvMsgProps[3].ulPropTag = PR_IMPORTANCE; spvMsgProps[4].ulPropTag = PR_DELETE_AFTER_SUBMIT; spvMsgProps[0].Value.lpszA = MsgSubject; spvMsgProps[1].Value.lpszA = "IPM.Note"; spvMsgProps[2].Value.lpszA = MsgBody; spvMsgProps[3].Value.ul = MsgImportance; spvMsgProps[4].Value.ul = TRUE; HResult = Message->SetProps( sizeof(spvMsgProps)/sizeof(SPropValue), (LPSPropValue) spvMsgProps, &lppProblems ); if (HR_FAILED(HResult)) { _leave; } MapiFreeBuffer( lppProblems ); if (MsgAttachmentFileName) { // // Create an attachment // HResult = Message->CreateAttach( NULL, 0, &AttachmentNum, &Attach ); if (HR_FAILED(HResult)) { _leave; } _splitpath( MsgAttachmentFileName, NULL, NULL, FileName, FileExt ); strcat( FileName, FileExt ); // // Fill in attachment properties and set them // if (!MsgAttachmentTitle) { MsgAttachmentTitle = FileName; } RenderingPosition = strlen(MsgBody); spvAttachProps[0].ulPropTag = PR_RENDERING_POSITION; spvAttachProps[1].ulPropTag = PR_ATTACH_METHOD; spvAttachProps[2].ulPropTag = PR_ATTACH_LONG_FILENAME; spvAttachProps[3].ulPropTag = PR_DISPLAY_NAME; spvAttachProps[4].ulPropTag = PR_ATTACH_EXTENSION; spvAttachProps[0].Value.ul = RenderingPosition; spvAttachProps[1].Value.ul = ATTACH_BY_VALUE; spvAttachProps[2].Value.lpszA = MsgAttachmentTitle; spvAttachProps[3].Value.lpszA = MsgAttachmentTitle; spvAttachProps[4].Value.lpszA = FileExt; HResult = Attach->SetProps( sizeof(spvAttachProps)/sizeof(SPropValue), (LPSPropValue) spvAttachProps, &lppProblems ); if (HR_FAILED(HResult)) { _leave; } MapiFreeBuffer( lppProblems ); // // Attach a data property to the attachment // HResult = Attach->OpenProperty( PR_ATTACH_DATA_BIN, &IID_IStream, 0, MAPI_CREATE | MAPI_MODIFY, (LPUNKNOWN *) &Stream ); if (HR_FAILED(HResult)) { _leave; } // // open the message attachment file // hFile = CreateFile( MsgAttachmentFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (hFile == INVALID_HANDLE_VALUE) { _leave; } // // Write the file to the data property // HResult = HrMAPIWriteFileToStream( hFile, Stream ); if (HR_FAILED(HResult)) { _leave; } HResult = Attach->SaveChanges( FORCE_SAVE ); if (HR_FAILED(HResult)) { _leave; } } // // mail the message // HResult = Message->SubmitMessage( 0 ); if(HR_FAILED(HResult)) { _leave; } HResult = Store->StoreLogoff( &ulFlags ); if(HR_FAILED(HResult)) { _leave; } } _finally { if (Store) { Store->Release(); } if (Inbox) { Inbox->Release(); } if (Message) { Message->Release(); } if (Attach) { Attach->Release(); } if (Stream) { Stream->Release(); } if (AddrBook) { AddrBook->Release(); } if (lpAddrList) { pFreePadrlist( lpAddrList ); } if (lpEntryID) { MapiFreeBuffer( lpEntryID ); } if (lpszProxyAddr) { MapiFreeBuffer( lpszProxyAddr ); } if (lpInEntryID) { MapiFreeBuffer( lpInEntryID ); } FreeString( MsgSubject ); FreeString( MsgBody ); FreeString( MsgAttachmentFileName ); if (MsgAttachmentTitleW && MsgAttachmentTitle) { FreeString( MsgAttachmentTitle ); } CloseHandle( hFile ); goto exit; } exit: *ResultCode = HResult; return HResult == 0; }