Outlook 2013 Profile creation code


// CreateProfile.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <MAPIX.h>
#include <MAPIUtil.h>
#include <EdkMdb.h>
#include <iostream>
#include <vector>
#include <unordered_map>

using namespace std;

#define PR_PROFILE_USER_SMTP_EMAIL_ADDRESS PROP_TAG(PT_TSTRING, pidProfileMin+0x41)
#define PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W PROP_TAG(PT_UNICODE, pidProfileMin+0x41)

#define PR_EMSMDB_SECTION_UID PROP_TAG(PT_BINARY, 0x3D15)

#define PR_PROFILE_MAPIHTTP_MAILSTORE_INTERNAL_URL PROP_TAG(PT_UNICODE, pidProfileMin+0x52)
#define PR_PROFILE_MAPIHTTP_MAILSTORE_EXTERNAL_URL PROP_TAG(PT_UNICODE, pidProfileMin+0x53)
#define PR_PROFILE_MAPIHTTP_ADDRESSBOOK_INTERNAL_URL PROP_TAG(PT_UNICODE, pidProfileMin+0x54)
#define PR_PROFILE_MAPIHTTP_ADDRESSBOOK_EXTERNAL_URL PROP_TAG(PT_UNICODE, pidProfileMin+0x55)

#define MAPI_FORCE_ACCESS 0x80000

STDMETHODIMP GetStoreProvidersSection(LPSERVICEADMIN lpSvcAdmin, LPMAPIUID lpMapiUid, LPPROFSECT * lppProfSect);

LONG lConfigFlags = CONFIG_PROMPT_FOR_CREDENTIALS | CONFIG_SHOW_CONNECT_UI;
LONG lAuthPackage = RPC_C_AUTHN_GSS_NEGOTIATE;

// Simulate Data retrieved from an Autodiscover response.
unordered_map<ULONG, LPVOID> PropValueMap
{
{
PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W,
L"SMTP:John.Smith@contoso.com"
},
{
PR_PROFILE_USER,
"/o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=23F1A9B90B9F4BC8B1E10CC7CA666266-john"
},
{
PR_PROFILE_CONFIG_FLAGS,
&lConfigFlags
},
// I put these in here with no values to illustrate the scenario of these properties being blank or not present in the Autodiscover response
// If they are blank or not present in the Autodiscover response just don't add them
{
PR_PROFILE_MAPIHTTP_MAILSTORE_INTERNAL_URL,
nullptr
},
{
PR_PROFILE_MAPIHTTP_MAILSTORE_EXTERNAL_URL,
L"https://outlook.office365.com/mapi/emsmdb/?MailboxId=C1735C60-2BF1-4E66-A6AF-4CEB7B60F0ED@contoso.com"
},
{
PR_PROFILE_MAPIHTTP_ADDRESSBOOK_INTERNAL_URL,
L""
},
{
PR_PROFILE_MAPIHTTP_ADDRESSBOOK_EXTERNAL_URL,
L"https://outlook.office365.com/mapi/nspi/?MailboxId=C1735C60-2BF1-4E66-A6AF-4CEB7B60F0ED@contoso.com"
},
{
PR_PROFILE_AUTH_PACKAGE,
&lAuthPackage
}
};

int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hRes = S_OK; // Result from MAPI calls.
LPPROFADMIN lpProfAdmin = NULL; // Profile Admin object.
LPSERVICEADMIN lpSvcAdmin = NULL; // Service Admin object.
LPMAPITABLE lpMsgSvcTable = NULL; // Table to hold services.
LPSRowSet lpSvcRows = NULL; // Rowset to hold results of table query.
vector<SPropValue> rgvals;
SRestriction sres; // Restriction structure.
SPropValue SvcProps; // Property structure for restriction.
LPPROFSECT lpStoreProfSection = nullptr;
SPropValue spvVal;

wstring strDisplayName = wstring((LPWSTR)PropValueMap[PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W]);

strDisplayName = strDisplayName.substr(wcslen(L"SMTP:"), (strDisplayName.size() - wcslen(L"SMTP:")));

PropValueMap[PR_DISPLAY_NAME_W] = (LPVOID)strDisplayName.c_str();

// This indicates columns we want returned from HrQueryAllRows.
enum { iSvcName, iSvcUID, cptaSvc };
SizedSPropTagArray(cptaSvc, sptCols) = { cptaSvc, PR_SERVICE_NAME, PR_SERVICE_UID };

string ProfileName = "MAPIHTTP_Profile";
// Initialize MAPI.

if (FAILED(hRes = MAPIInitialize(NULL)))
{
cout << "Error initializing MAPI.";
goto error;
}

// Get an IProfAdmin interface.

if (FAILED(hRes = MAPIAdminProfiles(0, // Flags.
&lpProfAdmin))) // Pointer to new IProfAdmin.
{
cout << "Error getting IProfAdmin interface.";
goto error;
}

// Create a new profile.
if (FAILED(hRes = lpProfAdmin->CreateProfile((LPTSTR)ProfileName.c_str(), // Name of new profile.
nullptr, // Password for profile.
0, // Handle to parent window.
0))) // Flags.
{
if (FAILED(hRes = lpProfAdmin->DeleteProfile((LPTSTR)ProfileName.c_str(), 0)))
{
cout << "Error deleting duplicate profile.";
goto error;
}
// Try once more!
if (FAILED(hRes = lpProfAdmin->CreateProfile((LPTSTR)ProfileName.c_str(), // Name of new profile.
nullptr, // Password for profile.
0, // Handle to parent window.
0))) // Flags.
{
cout << "Error creating profile even after retry.";
goto error;
}

}

// Get an IMsgServiceAdmin interface off of the IProfAdmin interface.
if (FAILED(hRes = lpProfAdmin->AdminServices((LPTSTR)ProfileName.c_str(), // Profile that we want to modify.
nullptr, // Password for that profile.
0, // Handle to parent window.
0, // Flags.
&lpSvcAdmin))) // Pointer to new IMsgServiceAdmin.
{
cout << "Error getting IMsgServiceAdmin interface.";
goto error;
}

// Create the new message service for Exchange.

if (FAILED(hRes = lpSvcAdmin->CreateMsgService("MSEMS", // Name of service from MAPISVC.INF.
NULL, // Display name of service.
NULL, // Handle to parent window.
NULL))) // Flags.
{
cout << "Error creating Exchange message service.";
goto error;
}

// You now have to obtain the entry id for the new service.
// You can do this by getting the message service table
// and getting the entry that corresponds to the new service.

if (FAILED(hRes = lpSvcAdmin->GetMsgServiceTable(0, // Flags.
&lpMsgSvcTable))) // Pointer to table.
{
cout << "Error getting Message Service Table.";
goto error;
}

// Set up restriction to query table.

sres.rt = RES_CONTENT;
sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
sres.res.resContent.ulPropTag = PR_SERVICE_NAME;
sres.res.resContent.lpProp = &SvcProps;

SvcProps.ulPropTag = PR_SERVICE_NAME;
SvcProps.Value.lpszA = "MSEMS";

// Query the table to obtain the entry for the newly created message service.

if (FAILED(hRes = HrQueryAllRows(lpMsgSvcTable,
(LPSPropTagArray)&sptCols,
&sres,
NULL,
0,
&lpSvcRows)))
{
cout << "Error querying table for new message service.";
goto error;
}

if (FAILED(hRes = GetStoreProvidersSection(
lpSvcAdmin,
(LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb,
&lpStoreProfSection)) || lpStoreProfSection == nullptr)
{
cout << "Error attempting to get the Store Providers Profile Section.";
goto error;
}

for (auto& propVal : PropValueMap)
{
ULONG ulPropTag = propVal.first;
LONG l = 0;

// Don't add the NSPI URLs for now since we first set the
// Store provider section which doesn't need this,
// we'll add it later.
if (ulPropTag == PR_PROFILE_MAPIHTTP_ADDRESSBOOK_EXTERNAL_URL ||
ulPropTag == PR_PROFILE_MAPIHTTP_ADDRESSBOOK_INTERNAL_URL)
continue;
ZeroMemory(&spvVal, sizeof(spvVal));
if (propVal.second == nullptr)
continue;
spvVal.ulPropTag = ulPropTag;
switch (PROP_TYPE(ulPropTag))
{
case PT_LONG:
l = *((LONG*)propVal.second);
if (l == 0)
continue;
spvVal.Value.l = l;
break;
case PT_STRING8:
if (strlen((LPSTR)propVal.second) == 0)
continue;
spvVal.Value.lpszA = (LPSTR)propVal.second;
break;
case PT_UNICODE:
if (wcslen((LPWSTR)propVal.second) == 0)
continue;
spvVal.Value.lpszW = (LPWSTR)propVal.second;
break;
default:
break;
}
rgvals.push_back(spvVal);
}

if (FAILED(hRes = lpStoreProfSection->SetProps(
rgvals.size(),
rgvals.data(),
nullptr)))
{
cout << "Error attempting to set the properties on the Store Provider profile section.";
goto error;
}

if (FAILED(hRes = lpStoreProfSection->SaveChanges(
KEEP_OPEN_READWRITE)))
{
cout << "Error attempting to save the properties on the Store Provider section.";
goto error;
}

// Ok, now add the NSPI endpoint since we are
// Confirm they aren't null or an empty string
// This can be the case if you were pulling these values from an Autodiscover response
if (PropValueMap[PR_PROFILE_MAPIHTTP_ADDRESSBOOK_EXTERNAL_URL] != nullptr && wcslen((LPWSTR)PropValueMap[PR_PROFILE_MAPIHTTP_ADDRESSBOOK_EXTERNAL_URL]) > 0)
{
ZeroMemory(&spvVal, sizeof(spvVal));
spvVal.ulPropTag = PR_PROFILE_MAPIHTTP_ADDRESSBOOK_EXTERNAL_URL;
spvVal.Value.lpszW = (LPWSTR)PropValueMap[PR_PROFILE_MAPIHTTP_ADDRESSBOOK_EXTERNAL_URL];
rgvals.push_back(spvVal);
}

if (PropValueMap[PR_PROFILE_MAPIHTTP_ADDRESSBOOK_INTERNAL_URL] != nullptr && wcslen((LPWSTR)PropValueMap[PR_PROFILE_MAPIHTTP_ADDRESSBOOK_INTERNAL_URL]) > 0)
{
ZeroMemory(&spvVal, sizeof(spvVal));
spvVal.ulPropTag = PR_PROFILE_MAPIHTTP_ADDRESSBOOK_INTERNAL_URL;
spvVal.Value.lpszW = (LPWSTR)PropValueMap[PR_PROFILE_MAPIHTTP_ADDRESSBOOK_INTERNAL_URL];
rgvals.push_back(spvVal);
}

// Configure the message service by using the previous properties.
if (FAILED(hRes = lpSvcAdmin->ConfigureMsgService(
(LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb, // Entry ID of service to configure.
NULL, // Handle to parent window.
0, // Flags.
rgvals.size(), // Number of properties we are setting.
rgvals.data()))) // Pointer to SPropValue array.
{
cout << "Error configuring message service.";
goto error;
}

cleanup:
// Clean up
if (lpStoreProfSection) lpStoreProfSection->Release();
if (lpSvcRows) FreeProws(lpSvcRows);
if (lpMsgSvcTable) lpMsgSvcTable->Release();
if (lpSvcAdmin) lpSvcAdmin->Release();
if (lpProfAdmin) lpProfAdmin->Release();

MAPIUninitialize();
return 0;
error:
cout << " hRes = 0x" << hex << hRes << dec << endl;
goto cleanup;
}

STDMETHODIMP GetStoreProvidersSection(LPSERVICEADMIN lpSvcAdmin, LPMAPIUID lpMapiUid, LPPROFSECT * lppProfSect)
{
HRESULT hRes = MAPI_E_CALL_FAILED;
LPPROFSECT lpProfSect = nullptr;
ULONG cValues = 0;
LPSPropValue lpProps = nullptr;
static SizedSPropTagArray(2, spta) = { 1, { PR_STORE_PROVIDERS } };

if (lpSvcAdmin == nullptr || lpMapiUid == nullptr || lppProfSect == nullptr)
return E_INVALIDARG;

*lppProfSect = nullptr;

hRes = lpSvcAdmin->OpenProfileSection(lpMapiUid,
0,
MAPI_FORCE_ACCESS,
&lpProfSect);

if (FAILED(hRes) || lpProfSect == nullptr)
{
return hRes;
}

hRes = lpProfSect->GetProps((LPSPropTagArray)&spta, 0, &cValues, &lpProps);

if (FAILED(hRes) || lpProps == nullptr || cValues == 0)
{
return hRes;
}

if (lpProps != nullptr && cValues > 0 && lpProps[0].ulPropTag == PR_STORE_PROVIDERS)
{
hRes = lpSvcAdmin->OpenProfileSection((LPMAPIUID)lpProps[0].Value.bin.lpb,
0, MAPI_FORCE_ACCESS | MAPI_MODIFY,
lppProfSect);
if (FAILED(hRes) || (*lppProfSect) == nullptr)
{
// failed
}
}
if (lpProps)
{
MAPIFreeBuffer(lpProps);
}
return hRes;
}

Skip to main content