//********************************************************************* //* Microsoft Windows ** //* Copyright(c) Microsoft Corp., 1994-1995 ** //********************************************************************* // // MAPICALL.C - Functions to call MAPI for internet mail profile configuration // // // HISTORY: // // 1/25/95 jeremys Created. // 96/03/09 markdu Added a wait cursor during loading of MAPI // #include "wizard.h" /********************************************************************** Terminology: profile - a collection of settings for Exchange that determine the services that are used and the address book and message store service - a MAPI plug-in that talks to a mail back end (or address book or message store) There can be a number of profiles installed on a particular machine. Each profile contains a set of services. Stategy: To configure Microsoft Exchange, we need to do the following: 1) Establish a profile to modify - If any profiles currently exist, find the default profile. Otherwise create a profile, which will be initially empty. 2) Populate the profile with the required services. - The profile needs to contain the Internet Mail service, an address book and a message store. If any of these items are not present, we add them to the profile. 3) Configure the internet mail service for this profile. - stick in the user's email address, email server, etc. **********************************************************************/ // instance handle must be in per-instance data segment #pragma data_seg(DATASEG_PERINSTANCE) HINSTANCE hInstMAPIDll=NULL; // handle to MAPI dll we load explicitly DWORD dwMAPIRefCount = 0; // global function pointers for MAPI apis LPMAPIINITIALIZE lpMAPIInitialize = NULL; LPMAPIADMINPROFILES lpMAPIAdminProfiles = NULL; LPMAPIUNINITIALIZE lpMAPIUninitialize = NULL; LPMAPIALLOCATEBUFFER lpMAPIAllocateBuffer = NULL; LPMAPIFREEBUFFER lpMAPIFreeBuffer = NULL; LPHRQUERYALLROWS lpHrQueryAllRows = NULL; #define NUM_MAPI_PROCS 6 // API table for function addresses to fetch APIFCN MAPIApiList[NUM_MAPI_PROCS] = { { (PVOID *) &lpMAPIInitialize,szMAPIInitialize}, { (PVOID *) &lpMAPIUninitialize,szMAPIUninitialize}, { (PVOID *) &lpMAPIAdminProfiles,szMAPIAdminProfiles}, { (PVOID *) &lpMAPIAllocateBuffer,szMAPIAllocateBuffer}, { (PVOID *) &lpMAPIFreeBuffer,szMAPIFreeBuffer}, { (PVOID *) &lpHrQueryAllRows,szHrQueryAllRows}}; #pragma data_seg(DATASEG_DEFAULT) // function prototypes HRESULT GetProfileList(LPPROFADMIN lpProfAdmin,LPSRowSet * ppRowSet); HRESULT GetServicesList(LPSERVICEADMIN lpServiceAdmin, LPSRowSet *ppRowSet); HRESULT CreateProfileIfNecessary(LPPROFADMIN lpProfAdmin,TCHAR * pszSelProfileName); HRESULT InstallRequiredServices(LPSERVICEADMIN pServiceAdmin, LPSRowSet pServiceRowSet); VOID FreeSRowSet(LPSRowSet prws); BOOL ValidateProperty(LPSPropValue pval, ULONG cVal, ULONG ulPropTag); BOOL DoesFileExist(TCHAR * pszPath,TCHAR * pszFileName); HRESULT ConfigInternetService(MAILCONFIGINFO * pMailConfigInfo, LPSERVICEADMIN lpServiceAdmin); HRESULT GetServiceUID(TCHAR * pszName,LPSERVICEADMIN lpServiceAdmin, LPMAPIUID *ppMapiUID); BOOL MakeUniqueFilename(UINT uIDFilename,UINT uIDAltFilename, TCHAR * pszFilename,DWORD cbFilename); extern BOOL GetApiProcAddresses(HMODULE hModDLL,APIFCN * pApiProcList, UINT nApiProcs); HRESULT ConfigNewService(LPSERVICEADMIN lpServiceAdmin,LPMAPIUID lpMapiUID, UINT uIDFilename,UINT uIDFilename1,UINT uPropValID); // enums enum { ivalDisplayName, ivalServiceName, ivalResourceFlags, ivalServiceDllName, ivalServiceEntryName, ivalServiceUID, ivalServiceSupportFiles, cvalMsgSrvMax }; /******************************************************************* NAME: InitMAPI SYNOPSIS: Loads the MAPI dll, gets proc addresses and initializes MAPI EXIT: TRUE if successful, or FALSE if fails. Displays its own error message upon failure. NOTES: We load MAPI explicitly because the DLL may not be installed when we begin the wizard... we may have to install it and then load it. ********************************************************************/ BOOL InitMAPI(HWND hWnd) { TCHAR szMAPIDll[SMALL_BUF_LEN+1]; HRESULT hr; // load MAPI only if not already loaded... otherwise just increment // reference count if (!hInstMAPIDll) { // set an hourglass cursor WAITCURSOR WaitCursor; // get the filename (MAPI32.DLL) out of resource LoadSz(IDS_MAPIDLL_FILENAME,szMAPIDll,ARRAYSIZE(szMAPIDll)); // load the MAPI dll DEBUGMSG("Loading MAPI DLL"); hInstMAPIDll = LoadLibrary(szMAPIDll); if (!hInstMAPIDll) { UINT uErr = GetLastError(); DisplayErrorMessage(hWnd,IDS_ERRLoadMAPIDll1,uErr,ERRCLS_STANDARD, MB_ICONSTOP,szMAPIDll); return FALSE; } // cycle through the API table and get proc addresses for all the APIs we // need BOOL fSuccess = GetApiProcAddresses(hInstMAPIDll,MAPIApiList,NUM_MAPI_PROCS); if (!fSuccess) { MsgBoxParam(hWnd,IDS_ERRLoadMAPIDll2,MB_ICONSTOP,MB_OK,szMAPIDll); DeInitMAPI(); return FALSE; } // initialize MAPI ASSERT(lpMAPIInitialize); hr = lpMAPIInitialize(NULL); if (HR_FAILED(hr)) { MsgBox(hWnd,IDS_ERRInitMAPI,MB_ICONSTOP,MB_OK); DeInitMAPI(); return FALSE; } gpWizardState->fMAPIActive = TRUE; } dwMAPIRefCount ++; return TRUE; } /******************************************************************* NAME: DeInitMAPI SYNOPSIS: Uninitializes MAPI and unloads MAPI dlls ********************************************************************/ VOID DeInitMAPI(VOID) { // decrease reference count if (dwMAPIRefCount) { dwMAPIRefCount--; } // shut down and unload MAPI if reference count hits zero if (!dwMAPIRefCount) { // uninitialize MAPI if (gpWizardState->fMAPIActive && lpMAPIUninitialize) { lpMAPIUninitialize(); gpWizardState->fMAPIActive = FALSE; } // free the MAPI dll if (hInstMAPIDll) { DEBUGMSG("Unloading MAPI DLL"); FreeLibrary(hInstMAPIDll); hInstMAPIDll = NULL; } // set function pointers to NULL for (UINT nIndex = 0;nIndexfMAPIActive,"MAPI not initialized!"); ASSERT(pMailConfigInfo); // get a pointer to the interface to administer profiles ASSERT(lpMAPIAdminProfiles); hr = lpMAPIAdminProfiles(0,&pProfAdmin); if (HR_FAILED(hr)) { DEBUGMSG("MAPIAdminProfiles returned 0x%lx",hr); return (hr); } ASSERT(pProfAdmin); // release this interface when we leave the function RELEASE_ME_LATER ReleaseProfAdminLater(pProfAdmin); // get profile name from passed-in struct, if specified if (pMailConfigInfo->pszProfileName && lstrlen(pMailConfigInfo->pszProfileName)) { lstrcpy(szSelProfileName,pMailConfigInfo->pszProfileName); } else { // no profile specified, use default name LoadSz(IDS_DEFAULT_PROFILE_NAME,szSelProfileName,ARRAYSIZE(szSelProfileName)); } // create profile if we need to hr = CreateProfileIfNecessary(pProfAdmin,szSelProfileName); if (HR_FAILED(hr)) return hr; // set this profile as default if appropriate if (pMailConfigInfo->fSetProfileAsDefault) { hr = pProfAdmin->SetDefaultProfile(szSelProfileName,0); if (HR_FAILED(hr)) return hr; } ASSERT(lstrlen(szSelProfileName)); // should have profile name at this point DEBUGMSG("Modifying MAPI profile: %s",szSelProfileName); // get a pointer to the interface to administer services for this profile hr = pProfAdmin->AdminServices(szSelProfileName,NULL,NULL,0, &pServiceAdmin); if (HR_FAILED(hr)) return hr; ASSERT(pServiceAdmin); // release pServiceAdmin interface when done RELEASE_ME_LATER rlServiceAdmin(pServiceAdmin); // get a list of services for this profile hr = GetServicesList(pServiceAdmin,&pServiceRowSet); if (HR_FAILED(hr)) return hr; ASSERT(pServiceRowSet); // install any services we need which aren't already present in the profile hr = InstallRequiredServices(pServiceAdmin,pServiceRowSet); // done with profile row set, free the table FreeSRowSet(pServiceRowSet); pServiceRowSet = NULL; if (HR_FAILED(hr)) return hr; // configure the internet mail service with the passed in email name, // server, etc. hr = ConfigInternetService(pMailConfigInfo,pServiceAdmin); if (HR_FAILED(hr)) { DEBUGMSG("ConfigInternetService returned 0x%x" , hr); return hr; } return hr; } /******************************************************************* NAME: GetProfileList SYNOPSIS: retrieves a list of MAPI profiles ENTRY: lpProfAdmin - pointer to profile admin interface ppRowSet - pointer to an SRowSet pointer that is filled in EXIT: returns an HRESULT. If successful, *ppRowSet contains pointer to SRowSet with profile list. NOTES: Cloned from MAPI profile control panel code. Caller MUST call MAPIFreeBuffer to free *ppRowSet when done. ********************************************************************/ HRESULT GetProfileList(LPPROFADMIN lpProfAdmin,LPSRowSet * ppRowSet) { HRESULT hr; LPMAPITABLE pMapiTable=NULL; SPropTagArray TagArray= {3,{PR_DISPLAY_NAME, PR_COMMENT,PR_DEFAULT_PROFILE}}; ASSERT(lpProfAdmin); ASSERT(ppRowSet); // call the lpProfAdmin interface to get the MAPI profile table hr = lpProfAdmin->GetProfileTable(0,&pMapiTable); if (HR_FAILED(hr)) return hr; ASSERT(pMapiTable); // release this interface when we leave the function RELEASE_ME_LATER ReleaseMapiTableLater(pMapiTable); // set properties of table columns hr = pMapiTable->SetColumns(&TagArray,0); if (!HR_FAILED(hr)) { // get row set information from table hr = pMapiTable->QueryRows(4000,0,ppRowSet); } return hr; } /******************************************************************* NAME: GetServicesList SYNOPSIS: retrieves a list of MAPI services in a profile ENTRY: lpProfAdmin - pointer to service admin interface ppRowSet - pointer to an SRowSet pointer that is filled in EXIT: returns an HRESULT. If successful, *ppRowSet contains pointer to SRowSet with service list. NOTES: Cloned from MAPI profile control panel code. Caller MUST call MAPIFreeBuffer to free *ppRowSet when done. ********************************************************************/ HRESULT GetServicesList(LPSERVICEADMIN lpServiceAdmin, LPSRowSet *ppRowSet) { HRESULT hr; ULONG iRow; LPMAPITABLE pMapiTable = NULL; SCODE sc = S_OK; static SPropTagArray taga = {7, { PR_DISPLAY_NAME, PR_SERVICE_NAME, PR_RESOURCE_FLAGS, PR_SERVICE_DLL_NAME, PR_SERVICE_ENTRY_NAME, PR_SERVICE_UID, PR_SERVICE_SUPPORT_FILES }}; *ppRowSet = NULL; hr = lpServiceAdmin->GetMsgServiceTable(0, &pMapiTable); if (HR_FAILED(hr)) return hr; // free this interface when function exits RELEASE_ME_LATER rlTable(pMapiTable); hr = pMapiTable->SetColumns(&taga, 0); if (!HR_FAILED(hr)) { // BUGBUG get rid of 'magic number' (appears in MAPI // ctrl panel code, need to find out what it is) jeremys 1/30/95 hr = pMapiTable->QueryRows(4000,0,ppRowSet); } if (HR_FAILED(hr)) return hr; for(iRow = 0; iRow < (*ppRowSet)->cRows; iRow++) { // make sure properties are valid, if not then slam something in ValidateProperty((*ppRowSet)->aRow[iRow].lpProps, 0, PR_DISPLAY_NAME); ValidateProperty((*ppRowSet)->aRow[iRow].lpProps, 1, PR_SERVICE_NAME); } return hr; } /******************************************************************* NAME: CreateProfileIfNecessary SYNOPSIS: Creates profile if it doesn't already exist ENTRY: lpProfAdmin - pointer to profile admin interface pszSelProfileName - name of profile to create EXIT: returns an HRESULT. ********************************************************************/ HRESULT CreateProfileIfNecessary(LPPROFADMIN pProfAdmin,TCHAR * pszSelProfileName) { HRESULT hr = hrSuccess; LPTSTR lpProfileName=NULL; BOOL fDefault; ASSERT(pProfAdmin); ASSERT(pszSelProfileName); ENUM_MAPI_PROFILE EnumMAPIProfile; // walk through the profile names, see if we have a match while (EnumMAPIProfile.Next(&lpProfileName,&fDefault)) { ASSERT(lpProfileName); if (!lstrcmpi(lpProfileName,pszSelProfileName)) { return hrSuccess; // found a match, nothing to do } } // no match, need to create profile DEBUGMSG("Creating MAPI profile: %s",pszSelProfileName); // call MAPI to create the profile hr = pProfAdmin->CreateProfile(pszSelProfileName, NULL, (ULONG) 0, (ULONG) 0); return hr; } /******************************************************************* NAME: InstallRequiredServices SYNOPSIS: Installs the 3 services we need (message store, address book, internet mail) in the profile if they're not already present. Calls functions to configure message store and address book (they both need a filename to use) if we're adding them. ENTRY: lpServiceAdmin - pointer to service admin interface pServiceRowSet - MAPI table with list of installed services EXIT: returns an HRESULT. NOTES: We deliberately don't configure internet mail service here-- we do that in the main routine. The reason is that we need to configure internet mail whether it's already installed or not, address book and message store we only need to configure if they're brand new. ********************************************************************/ HRESULT InstallRequiredServices(LPSERVICEADMIN pServiceAdmin, LPSRowSet pServiceRowSet) { ULONG iRow,iService; TCHAR szServiceName[SMALL_BUF_LEN+1]; LPMAPIUID pMapiUID=NULL; HRESULT hr=hrSuccess; // table for MAPI services we need to make sure are installed in profile MSGSERVICE MAPIServiceList[NUM_SERVICES] = { { FALSE, IDS_INTERNETMAIL_SERVICENAME, IDS_INTERNETMAIL_DESCRIPTION,FALSE, 0,0,0}, { FALSE, IDS_MESSAGESTORE_SERVICENAME, IDS_MESSAGESTORE_DESCRIPTION,TRUE, IDS_MESSAGESTORE_FILENAME,IDS_MESSAGESTORE_FILENAME1,PR_PST_PATH}, { FALSE, IDS_ADDRESSBOOK_SERVICENAME, IDS_ADDRESSBOOK_DESCRIPTION,TRUE, IDS_ADDRESSBOOK_FILENAME,IDS_ADDRESSBOOK_FILENAME1,PR_PAB_PATH}}; // walk through the list of services for (iRow = 0;iRow < pServiceRowSet->cRows;iRow ++) { DEBUGMSG("Profile contains service: %s (%s)", pServiceRowSet->aRow[iRow].lpProps[ivalDisplayName].Value.LPSZ, pServiceRowSet->aRow[iRow].lpProps[ivalServiceName].Value.LPSZ); // for each service, walk through our array of required services, // and see if there's a match for (iService = 0;iService < NUM_SERVICES;iService ++) { // load the name for this service out of resource LoadSz(MAPIServiceList[iService].uIDServiceName, szServiceName,ARRAYSIZE(szServiceName)); // compare it to the service name in the table of // installed services for this profile if (!lstrcmpi(szServiceName, pServiceRowSet->aRow[iRow].lpProps[ivalServiceName].Value.LPSZ)) { // this is a match! MAPIServiceList[iService].fPresent = TRUE; break; // break the inner 'for' loop } } } // install any services we need which are not already present for (iService = 0;iService < NUM_SERVICES;iService ++) { if (!MAPIServiceList[iService].fPresent) { TCHAR szServiceDesc[MAX_RES_LEN+1]; MSGSERVICE * pMsgService = &MAPIServiceList[iService]; // load the service name and description LoadSz(pMsgService->uIDServiceName, szServiceName,ARRAYSIZE(szServiceName)); LoadSz(pMsgService->uIDServiceDescription, szServiceDesc,ARRAYSIZE(szServiceDesc)); DEBUGMSG("Adding service: %s (%s)", szServiceDesc,szServiceName); // create the service hr = pServiceAdmin->CreateMsgService(szServiceName, szServiceDesc,0,0); if (HR_FAILED(hr)) return hr; // call a creation-time config procedure if specified if (pMsgService->fNeedConfig) { // get the UID (identifier) for this service // based on service name, APIs downstream need this hr = GetServiceUID(szServiceName,pServiceAdmin, &pMapiUID); if (HR_FAILED(hr)) return hr; ASSERT(pMapiUID); // call the proc to configure newly-created service hr = ConfigNewService(pServiceAdmin,pMapiUID, pMsgService->uIDStoreFilename,pMsgService->uIDStoreFilename1, pMsgService->uPropID); // free the buffer with the UID ASSERT(lpMAPIFreeBuffer); lpMAPIFreeBuffer(pMapiUID); pMapiUID = NULL; } } } return hr; } #define NUM_MAIL_PROPS 11 /******************************************************************* NAME: ConfigInternetService SYNOPSIS: Configures the Internet Mail service (route 66) with user's email name, email server, etc. ENTRY: pMailConfigInfo - pointer to struct with configuration info pServiceAdmin - pointer to service admin interface EXIT: returns an HRESULT NOTES: will stomp any existing settings for properties that it sets. ********************************************************************/ HRESULT ConfigInternetService(MAILCONFIGINFO * pMailConfigInfo, LPSERVICEADMIN pServiceAdmin) { HRESULT hr; SPropValue PropValue[NUM_MAIL_PROPS]; TCHAR szServiceName[SMALL_BUF_LEN+1]; LPMAPIUID pMapiUID=NULL; UINT nProps = NUM_MAIL_PROPS; ASSERT(pMailConfigInfo); ASSERT(pServiceAdmin); // get service UID for internet mail service LoadSz(IDS_INTERNETMAIL_SERVICENAME,szServiceName,ARRAYSIZE(szServiceName)); hr = GetServiceUID(szServiceName,pServiceAdmin,&pMapiUID); if (HR_FAILED(hr)) { return hr; } ASSERT(pMapiUID); // set the property value for each property. Note that the order // of items in the array doesn't mattter. The ulPropTag member indicates // what property the PropValue item is for, and the lpszA, b or l member // contains the data for that property. // need to "encrypt" mail account password with xor bit mask. Mail client // expects it to be thusly "encrypted" when it reads it out. It's stored // in the registry in this securely "encrypted" format. Somebody pretty // smart must have thought of this. // configure mail service properties PropValue[0].ulPropTag = PR_CFG_EMAIL_ADDRESS; PropValue[0].Value.LPSZ = pMailConfigInfo->pszEmailAddress; PropValue[1].ulPropTag = PR_CFG_EMAIL_DISPLAY_NAME; PropValue[1].Value.LPSZ = pMailConfigInfo->pszEmailDisplayName; PropValue[2].ulPropTag = PR_CFG_SERVER_PATH; PropValue[2].Value.LPSZ = pMailConfigInfo->pszEmailServer; PropValue[3].ulPropTag = PR_CFG_EMAIL_ACCOUNT; PropValue[3].Value.LPSZ = pMailConfigInfo->pszEmailAccountName; PropValue[4].ulPropTag = PR_CFG_PASSWORD; PropValue[4].Value.LPSZ = (LPTSTR) szNull; PropValue[5].ulPropTag = PR_CFG_REMEMBER; PropValue[5].Value.b = (USHORT) TRUE; // configure for RNA or LAN as appropriate PropValue[6].ulPropTag = PR_CFG_RNA_PROFILE; PropValue[7].ulPropTag = PR_CFG_CONN_TYPE; PropValue[8].ulPropTag = PR_CFG_DELIVERY_OPTIONS; if (pMailConfigInfo->pszConnectoidName && lstrlen(pMailConfigInfo->pszConnectoidName)) { PropValue[6].Value.LPSZ = pMailConfigInfo->pszConnectoidName; PropValue[7].Value.l = (long) CONNECT_TYPE_REMOTE; // set transfer mode for "selective".. PropValue[8].Value.l = DOWNLOAD_OPTION_HEADERS; } else { PropValue[6].Value.LPSZ = (LPTSTR) szNull; PropValue[7].Value.l = (long) CONNECT_TYPE_LAN; // set automatic transfer mode... mail guys made up the weird // define name, not me! PropValue[8].Value.l = DOWNLOAD_OPTION_MAIL_DELETE; } PropValue[9].ulPropTag = PR_CFG_REMOTE_USERNAME; PropValue[9].Value.LPSZ = pMailConfigInfo->pszEmailAccountName; PropValue[10].ulPropTag = PR_CFG_REMOTE_PASSWORD; PropValue[10].Value.LPSZ = pMailConfigInfo->pszEmailAccountPwd; // call the service admin interface to configure the service with these // properties hr = pServiceAdmin->ConfigureMsgService(pMapiUID,NULL,0, nProps,PropValue); if (HR_FAILED(hr)) { DEBUGMSG("ConfigureMsgService returned 0x%x", hr); } // free the buffer with the UID ASSERT(lpMAPIFreeBuffer); lpMAPIFreeBuffer(pMapiUID); pMapiUID = NULL; return hr; } /******************************************************************* NAME: ConfigMessageStore SYNOPSIS: Generates a unique filename and sets it as the message store ENTRY: lpServiceAdmin - pointer to service admin interface lpMapiUID - UID for this service (message store) EXIT: returns an HRESULT NOTES: This code expects to be called only when the service is newly created. Calling it on an existing service will cause it to stomp existing settings. ********************************************************************/ HRESULT ConfigNewService(LPSERVICEADMIN lpServiceAdmin,LPMAPIUID lpMapiUID, UINT uIDFilename,UINT uIDFilename1,UINT uPropValID) { TCHAR szMsgStorePath[MAX_PATH+1]; HRESULT hr=hrSuccess; ASSERT(lpServiceAdmin); ASSERT(lpMapiUID); // build a path for the message store if (!MakeUniqueFilename(uIDFilename,uIDFilename1, szMsgStorePath,sizeof(szMsgStorePath))) { DEBUGTRAP("Unable to create unique filename"); return MAPI_E_COLLISION; } DEBUGMSG("Creating MAPI store %s",szMsgStorePath); // set this filename for the message store SPropValue PropVal; PropVal.ulPropTag = uPropValID; PropVal.Value.LPSZ = szMsgStorePath; hr = lpServiceAdmin->ConfigureMsgService(lpMapiUID,NULL,0,1,&PropVal); if (HR_FAILED(hr)) { DEBUGMSG("ConfigureMsgService returned 0x%x", hr); } return hr; } /******************************************************************* NAME: FindInternetMailService SYNOPSIS: Detects if internet mail is installed, returns email address and email server if it is. ********************************************************************/ BOOL FindInternetMailService(TCHAR * pszEmailAddress,DWORD cbEmailAddress, TCHAR * pszEmailServer, DWORD cbEmailServer) { ASSERT(pszEmailAddress); ASSERT(pszEmailServer); if (!hInstMAPIDll && !InitMAPI(NULL)) return FALSE; // look through all profiles. For each profile, look through all // services. If we find an instance of the internet mail service, // then return email address and password to caller. If there is // more than one profile with the internet mail service, we // will return the first one we find. ENUM_MAPI_PROFILE EnumMAPIProfile; LPTSTR lpProfileName,lpServiceName; BOOL fDefault; // walk through the list of profiles... while (EnumMAPIProfile.Next(&lpProfileName,&fDefault)) { ASSERT(lpProfileName); DEBUGMSG("Found profile: %s",lpProfileName); // for each profile, walk through the list of services... ENUM_MAPI_SERVICE EnumMAPIService(lpProfileName); while (EnumMAPIService.Next(&lpServiceName)) { TCHAR szSmallBuf[SMALL_BUF_LEN+1]; DEBUGMSG("Found service: %s",lpServiceName); if (!lstrcmpi(lpServiceName,LoadSz(IDS_INTERNETMAIL_SERVICENAME, szSmallBuf,ARRAYSIZE(szSmallBuf)))) { //BUGBUG 21-May-1995 jeremys Get e-mail server & address from MAPI profile return TRUE; } } } return FALSE; } ENUM_MAPI_PROFILE::ENUM_MAPI_PROFILE(VOID) { LPPROFADMIN pProfAdmin=NULL; // interface to administer profiles HRESULT hr; ASSERTSZ(gpWizardState->fMAPIActive,"MAPI not initialized!"); _pProfileRowSet = NULL; _nEntries = 0; _iRow = 0; // get a pointer to the interface to administer profiles ASSERT(lpMAPIAdminProfiles); hr = lpMAPIAdminProfiles(0,&pProfAdmin); if (HR_FAILED(hr)) { DEBUGMSG("MAPIAdminProfiles returned 0x%lx",hr); return; } ASSERT(pProfAdmin); // release this interface when we leave the function RELEASE_ME_LATER ReleaseProfAdminLater(pProfAdmin); // get the rows in the profile table hr = GetProfileList(pProfAdmin,&_pProfileRowSet); if (HR_FAILED(hr)) return; ASSERT(_pProfileRowSet); _nEntries = _pProfileRowSet->cRows; } ENUM_MAPI_PROFILE::~ENUM_MAPI_PROFILE(VOID) { if (_pProfileRowSet) { // done with profile row set, free the table FreeSRowSet(_pProfileRowSet); _pProfileRowSet = NULL; } } BOOL ENUM_MAPI_PROFILE::Next(LPTSTR * ppProfileName,BOOL * pfDefault) { ASSERT(pfDefault); if (!_pProfileRowSet) return FALSE; if (_iRow < _pProfileRowSet->cRows) { LPSPropValue pPropVal = _pProfileRowSet->aRow[_iRow].lpProps; ASSERT(pPropVal); // get pointer to profile name *ppProfileName = pPropVal[0].Value.LPSZ; ASSERT(*ppProfileName); // set 'this profile is default' flag *pfDefault = pPropVal[2].Value.b; _iRow++; return TRUE; } return FALSE; } ENUM_MAPI_SERVICE::ENUM_MAPI_SERVICE(LPTSTR pszProfileName) { LPPROFADMIN pProfAdmin=NULL; // interface to administer profiles LPSERVICEADMIN pServiceAdmin=NULL; // interface to administer services HRESULT hr; ASSERT(pszProfileName); ASSERTSZ(gpWizardState->fMAPIActive,"MAPI not initialized!"); _pServiceRowSet = NULL; _nEntries = 0; _iRow = 0; // get a pointer to the interface to administer profiles ASSERT(lpMAPIAdminProfiles); hr = lpMAPIAdminProfiles(0,&pProfAdmin); if (HR_FAILED(hr)) { DEBUGMSG("MAPIAdminProfiles returned 0x%lx",hr); return; } ASSERT(pProfAdmin); // release this interface when we leave the function RELEASE_ME_LATER ReleaseProfAdminLater(pProfAdmin); // get a pointer to the interface to administer services for this profile hr = pProfAdmin->AdminServices(pszProfileName,NULL,NULL,0, &pServiceAdmin); if (HR_FAILED(hr)) { DEBUGMSG("AdminServices returned 0x%lx",hr); return; } // release this interface when we leave the function RELEASE_ME_LATER ReleaseServiceAdminLater(pServiceAdmin); // get the rows in the profile table hr = GetServicesList(pServiceAdmin,&_pServiceRowSet); if (HR_FAILED(hr)) return; ASSERT(_pServiceRowSet); _nEntries = _pServiceRowSet->cRows; } ENUM_MAPI_SERVICE::~ENUM_MAPI_SERVICE(VOID) { if (_pServiceRowSet) { // done with profile row set, free the table FreeSRowSet(_pServiceRowSet); _pServiceRowSet = NULL; } } BOOL ENUM_MAPI_SERVICE::Next(LPTSTR * ppServiceName) { if (!_pServiceRowSet) return FALSE; if (_iRow < _pServiceRowSet->cRows) { LPSPropValue pPropVal = _pServiceRowSet->aRow[_iRow].lpProps; ASSERT(pPropVal); // get pointer to profile name *ppServiceName = pPropVal[ivalServiceName].Value.LPSZ; ASSERT(*ppServiceName); _iRow++; return TRUE; } return FALSE; } /******************************************************************* NAME: MakeUniqueFilename SYNOPSIS: Generates a filename in the Windows directory that does not already exist ENTRY: uIDFilename - ID of string resource for desired name for the file uIDAltFilename - ID of string resource with template for filename to use if file with uIDFilename's name already exists. Template should contain %u into which numbers will be inserted to make filename unique. pszFilename - buffer to return path and filename into cbFilename - size of pszFilename buffer EXIT: returns TRUE if successful, FALSE if couldn't make unique filename within MAX_FILENAME_TRIES tries ********************************************************************/ // number of times we'll try to create a unique filename before giving up #define MAX_MAKEFILENAME_TRIES 20 BOOL MakeUniqueFilename(UINT uIDFilename,UINT uIDAltFilename, TCHAR * pszFilename,DWORD cbFilename) { TCHAR szFileName[SMALL_BUF_LEN+1]; BOOL fSuccess = FALSE; ASSERT(pszFilename); // build a path for the filename UINT uRet=GetWindowsDirectory(pszFilename,cbFilename); ASSERTSZ(uRet,"GetWindowsDirectory failed"); // choose a file name that doesn't already exist // first, try using the string resource specified by uIDFilename LoadSz(uIDFilename,szFileName,ARRAYSIZE(szFileName)); if (DoesFileExist(pszFilename,szFileName)) { // if that file exists, then use the string resource uIDAltFilename // which has a replacable parameter. We'll try adding numbers to // the filename to make it unique. TCHAR szFileFmt[SMALL_BUF_LEN+1]; LoadSz(uIDAltFilename,szFileFmt,ARRAYSIZE(szFileFmt)); for (UINT nIndex = 0; nIndex < MAX_MAKEFILENAME_TRIES; nIndex ++) { // make a name e.g. "mailbox4.pst" wsprintf(szFileName,szFileFmt,nIndex); if (!DoesFileExist(pszFilename,szFileName)) { // OK, found a filename that doesn't exist fSuccess = TRUE; break; } } } else { // first try succeeded fSuccess = TRUE; } if (fSuccess) { // now we have unique filename, build the full path lstrcat(pszFilename,szSlash); lstrcat(pszFilename,szFileName); } return fSuccess; } /******************************************************************* NAME: DoesFileExist SYNOPSIS: Checks to see whether the specified file exists ENTRY: pszPath - path to directory pszFilename - name of file EXIT: returns TRUE if file exists, FALSE if it doesn't ********************************************************************/ BOOL DoesFileExist(TCHAR * pszPath,TCHAR * pszFileName) { CHAR szPath[MAX_PATH+1]; OFSTRUCT of; ASSERT(pszPath); ASSERT(pszFileName); // concatenate the path and file name #ifdef UNICODE TCHAR szTmp[MAX_PATH+1]; lstrcpy(szTmp,pszPath); lstrcat(szTmp,szSlash); lstrcat(szTmp,pszFileName); wcstombs(szPath, szTmp, MAX_PATH+1); #else lstrcpy(szPath,pszPath); lstrcat(szPath,szSlash); lstrcat(szPath,pszFileName); #endif // find out if this file exists return (OpenFile(szPath,&of,OF_EXIST) != HFILE_ERROR); } /******************************************************************* NAME: GetServiceUID SYNOPSIS: Given a MAPI service name, gets the MAPIUID associated with it. ENTRY: pszServiceName - name of MAPI service (e.g. "IMAIL","MSPST AB") lpServiceAdmin - pointer to service admin interface ppMapiUID - pointer to pointer for MAPIUID struct EXIT: returns an HRESULT NOTES: Cloned from MAPI profile wizard code, if you think this function is big and ugly now you should have seen it before I cleaned it up. This function allocates a MAPIUID, the caller is responsible for freeing this (use MAPIFreeBuffer) when done. ********************************************************************/ HRESULT GetServiceUID(TCHAR * pszServiceName,LPSERVICEADMIN lpServiceAdmin, LPMAPIUID *ppMapiUID) { HRESULT hr =hrSuccess; LPSPropValue pTblProp =NULL; DWORD iRow,iColumn; LPMAPITABLE pTable =NULL; LPSRowSet pRowSet =NULL; LPSRow pRow =NULL; int nFound =0; LPMAPIUID pMapiUID =NULL; BOOL fContinue = TRUE; SizedSPropTagArray(2, Tbltaga) = { 2, { PR_SERVICE_NAME, PR_SERVICE_UID }}; ASSERT(pszServiceName); ASSERT(lpServiceAdmin); ASSERT(ppMapiUID); // get table of message services hr = lpServiceAdmin->GetMsgServiceTable(0, &pTable); if (HR_FAILED(hr)) { DEBUGMSG("GetMsgServiceTable returned 0x%x", hr); return hr; } ASSERT(pTable); // release this table when we exit this function RELEASE_ME_LATER rlTable(pTable); ASSERT(lpHrQueryAllRows); hr = lpHrQueryAllRows(pTable, (LPSPropTagArray) &Tbltaga, NULL, NULL, 0, &pRowSet); if (HR_FAILED(hr)) { DEBUGMSG("HrQueryAllRows returned 0x%x", hr); return hr; } ASSERT(pRowSet); iRow =0; while (fContinue && iRow< pRowSet->cRows) { pRow = &pRowSet->aRow[iRow]; pTblProp = pRow->lpProps; nFound = 0; for (iColumn=0; iColumncValues; iColumn++) { //Check each property if (pTblProp->ulPropTag ==PR_SERVICE_UID) { nFound++; ASSERT(lpMAPIAllocateBuffer); lpMAPIAllocateBuffer(pTblProp->Value.bin.cb, (LPVOID FAR *) &pMapiUID); if (!pMapiUID) { hr = MAPI_E_NOT_ENOUGH_MEMORY; fContinue = FALSE; break; } memcpy(pMapiUID, pTblProp->Value.bin.lpb, (size_t) pTblProp->Value.bin.cb); *ppMapiUID = pMapiUID; } else if ((pTblProp->ulPropTag ==PR_SERVICE_NAME) && !lstrcmpi(pTblProp->Value.LPSZ, pszServiceName)) { nFound++; } pTblProp++; if (nFound == 2) { // found it! fContinue = FALSE; break; } } iRow++; if (nFound < 2) { // if one but not both items matched above, then deallocate buffer if (pMapiUID) { ASSERT(lpMAPIFreeBuffer); lpMAPIFreeBuffer(pMapiUID); pMapiUID =NULL; } if (*ppMapiUID) *ppMapiUID = NULL; } } if (HR_FAILED(hr) || nFound < 2) { // free buffer if we didn't find the UID if (pMapiUID) { ASSERT(lpMAPIFreeBuffer); lpMAPIFreeBuffer(pMapiUID); } if (*ppMapiUID) *ppMapiUID = NULL; } if (pRowSet) FreeSRowSet(pRowSet); return hr; } /******************************************************************* NAME: FreeSRowSet SYNOPSIS: Frees an SRowSet structure and the rows therein ENTRY: prws - the row set to free NOTES: Cloned from MAPI profile ctrl panel code ********************************************************************/ VOID FreeSRowSet(LPSRowSet prws) { ULONG irw; if (!prws) return; ASSERT(lpMAPIFreeBuffer); // Free each row for (irw = 0; irw < prws->cRows; irw++) lpMAPIFreeBuffer(prws->aRow[irw].lpProps); // Free the top level structure lpMAPIFreeBuffer(prws); } /* * ValidateProperty * * Purpose: * Given a string prop, make sure it contains a valid string. * * Arguments: * pval * cVal * ulPropTag * * Returns: * BOOL */ TCHAR szUnk[] = TEXT("???"); BOOL ValidateProperty(LPSPropValue pval, ULONG cVal, ULONG ulPropTag) { if(pval[cVal].ulPropTag != ulPropTag) { // make sure we're not stomping on good properties. ASSERT(PROP_TYPE(pval[cVal].ulPropTag) == PT_ERROR); pval[cVal].ulPropTag = ulPropTag; pval[cVal].Value.LPSZ = szUnk; return TRUE; } return FALSE; } #pragma data_seg(DATASEG_READONLY) // note: this array depends on errors in rc file being in this order // starting with S_OK. Don't rearrange one without rearranging the other. static SCODE mpIdsScode[] = { S_OK, MAPI_E_NO_ACCESS, E_NOINTERFACE, E_INVALIDARG, MAPI_E_CALL_FAILED, MAPI_E_NOT_FOUND, MAPI_E_NO_SUPPORT, MAPI_W_ERRORS_RETURNED, MAPI_W_PARTIAL_COMPLETION, MAPI_E_BAD_CHARWIDTH, MAPI_E_BAD_VALUE, MAPI_E_BUSY, MAPI_E_COLLISION, MAPI_E_COMPUTED, MAPI_E_CORRUPT_DATA, MAPI_E_CORRUPT_STORE, MAPI_E_DISK_ERROR, MAPI_E_HAS_FOLDERS, MAPI_E_HAS_MESSAGES, MAPI_E_INVALID_ENTRYID, MAPI_E_INVALID_OBJECT, MAPI_E_LOGON_FAILED, MAPI_E_NETWORK_ERROR, MAPI_E_NON_STANDARD, MAPI_E_NOT_ENOUGH_DISK, MAPI_E_NOT_ENOUGH_MEMORY, MAPI_E_NOT_ENOUGH_RESOURCES, MAPI_E_NOT_IN_QUEUE, MAPI_E_OBJECT_CHANGED, MAPI_E_OBJECT_DELETED, MAPI_E_STRING_TOO_LONG, MAPI_E_SUBMITTED, MAPI_E_TOO_BIG, MAPI_E_UNABLE_TO_ABORT, MAPI_E_UNCONFIGURED, MAPI_E_UNEXPECTED_TYPE, MAPI_E_UNKNOWN_FLAGS, MAPI_E_USER_CANCEL, MAPI_E_VERSION }; #pragma data_seg() /******************************************************************* NAME: GetMAPIErrorText SYNOPSIS: Gets text string corresponding to MAPI error code ENTRY: uErr - MAPI error code pszErrText - buffer to retrieve error text description cbErrText - size of pszErrText buffer NOTES: based on MapScodeSz from MAPI SDK. There is, alas, no function built into MAPI to do this. Every app must include all text strings for every error code, and will be broken on any error codes MAPI invents after the app ships. Numbskulls. ********************************************************************/ VOID GetMAPIErrorText(UINT uErr,TCHAR * pszErrText,DWORD cbErrText) { ASSERT(pszErrText); SCODE scErr = (SCODE) uErr; UINT nIndex,nMax; // get facility code, facility is bits 17-30 in scode DWORD dwFacility = (scErr >> 16) & (0x7FFF); // if this is a WIN32 code wrapped in an scode, call GetErrorDescription // to get the text, which will get the text out of Windows and do a much // better job than this crappy little function will if (dwFacility == FACILITY_WIN32) GetErrorDescription(pszErrText,cbErrText, (scErr & 0xFFFF),ERRCLS_STANDARD); /* Linear search in mpIdsScode for scArg. When found, index is IDS. */ nMax = sizeof mpIdsScode / sizeof mpIdsScode[0]; for (nIndex = 0; nIndex < nMax; nIndex++) { if (mpIdsScode[nIndex] == scErr) break; } if (nIndex < nMax) { // found the code in the table LoadSz(IDS_MAPIERROR_BASE + nIndex,pszErrText,cbErrText); } else { // didn't find the code in the table, make a generic string // with the error number TCHAR szFmt[SMALL_BUF_LEN+1]; LoadSz(IDS_GENERIC_MAPI_ERROR,szFmt,ARRAYSIZE(szFmt)); wsprintf(pszErrText,szFmt,scErr); } }