Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1332 lines
41 KiB

/*
* CSV.C
*
* Migrate CSV <-> WAB
*
* Copyright 1997 Microsoft Corporation. All Rights Reserved.
*/
#include "_comctl.h"
#include <windows.h>
#include <commctrl.h>
#include <mapix.h>
#include <wab.h>
#include <wabguid.h>
#include <wabdbg.h>
#include <wabmig.h>
#include <emsabtag.h>
#include <shlwapi.h>
#include "wabimp.h"
#include "..\..\wab32res\resrc2.h"
#include "dbgutil.h"
BOOL HandleImportError(HWND hwnd, ULONG ids, HRESULT hResult, LPTSTR lpDisplayName,
LPTSTR lpEmailAddress, LPWAB_IMPORT_OPTIONS lpImportOptions);
BOOL HandleExportError(HWND hwnd, ULONG ids, HRESULT hResult, LPTSTR lpDisplayName,
LPTSTR lpEmailAddress, LPWAB_EXPORT_OPTIONS lpExportOptions);
/***************************************************************************
Name : IsDomainName
Purpose : Is this domain correctly formatted for an Internet address?
Parameters: lpDomain -> Domain name to check
Returns : TRUE if the domain is a correct format for an Internet
address.
Comment : Valid domain names have this form:
bar[.bar]*
where bar must have non-empty contents
no high bits are allowed on any characters
no '@' allowed
***************************************************************************/
BOOL IsDomainName(LPTSTR lpDomain) {
BOOL fBar = FALSE;
if (lpDomain) {
if (*lpDomain == '\0' || *lpDomain == '.') {
// domain name must have contents and can't start with '.'
return(FALSE);
}
while (*lpDomain) {
// Internet addresses only allow pure ASCII. No high bits!
if (*lpDomain & 0x80 || *lpDomain == '@') {
return(FALSE);
}
if (*lpDomain == '.') {
// Recursively check this part of the domain name
return(IsDomainName(CharNext(lpDomain)));
}
lpDomain = CharNext(lpDomain);
}
return(TRUE);
}
return(FALSE);
}
/***************************************************************************
Name : IsInternetAddress
Purpose : Is this address correctly formatted for an Internet address
Parameters: lpAddress -> Address to check
Returns : TRUE if the address is a correct format for an Internet
address.
Comment : Valid addresses have this form:
foo@bar[.bar]*
where foo and bar must have non-empty contents
***************************************************************************/
BOOL IsInternetAddress(LPTSTR lpAddress) {
BOOL fDomain = FALSE;
// Step through the address looking for '@'. If there's an at sign in the middle
// of a string, this is close enough to being an internet address for me.
if (lpAddress) {
// Can't start with '@'
if (*lpAddress == '@') {
return(FALSE);
}
while (*lpAddress) {
// Internet addresses only allow pure ASCII. No high bits!
if (*lpAddress & 0x80) {
return(FALSE);
}
if (*lpAddress == '@') {
// Found the at sign. Is there anything following?
// (Must NOT be another '@')
return(IsDomainName(CharNext(lpAddress)));
}
lpAddress = CharNext(lpAddress);
}
}
return(FALSE);
}
/***************************************************************************
Name : OpenCSVFile
Purpose : Opens a CSV file for import
Parameters: hwnd = main dialog window
lpFileName = filename to create
lphFile -> returned file handle
Returns : HRESULT
Comment :
***************************************************************************/
HRESULT OpenCSVFile(HWND hwnd, LPTSTR lpFileName, LPHANDLE lphFile) {
LPTSTR lpFilter;
TCHAR szFileName[MAX_PATH + 1] = "";
OPENFILENAME ofn;
HANDLE hFile = INVALID_HANDLE_VALUE;
HRESULT hResult = hrSuccess;
DWORD ec;
if (INVALID_HANDLE_VALUE == (hFile = CreateFile(lpFileName,
GENERIC_READ,
0, // sharing
NULL,
CREATE_NEW,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL))) {
ec = GetLastError();
DebugTrace("CreateFile(%s) -> %u\n", lpFileName, ec);
switch (ec) {
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
default:
ShowMessageBoxParam(hwnd, IDE_CSV_EXPORT_FILE_ERROR, MB_ICONERROR, lpFileName);
hResult = ResultFromScode(MAPI_E_NOT_FOUND);
break;
}
}
if (! hResult) {
*lphFile = hFile;
}
return(hResult);
}
/***************************************************************************
Name : CountCSVRows
Purpose : Counts the rows in the CSV file
Parameters: hFile = open CSV file
szSep = list separator
lpulcEntries -> returned count of rows
Returns : HRESULT
Comment : File pointer should be positioned past the header row prior
to calling this function. This function leaves the file
pointer where it found it.
***************************************************************************/
HRESULT CountCSVRows(HANDLE hFile, LPTSTR szSep, LPULONG lpulcEntries) {
HRESULT hResult = hrSuccess;
PUCHAR * rgItems = NULL;
ULONG ulStart;
ULONG cProps, i;
*lpulcEntries = 0;
Assert(hFile != INVALID_HANDLE_VALUE);
if (0xFFFFFFFF == (ulStart = SetFilePointer(hFile, 0, NULL, FILE_CURRENT))) {
DebugTrace("CountCSVRows SetFilePointer -> %u\n", GetLastError());
return(ResultFromScode(MAPI_E_CALL_FAILED));
}
while (hResult == hrSuccess) {
// Read the line
if (ReadCSVLine(hFile, szSep, &cProps, &rgItems)) {
// End of file
break;
}
(*lpulcEntries)++;
if (rgItems) {
for (i = 0; i < cProps; i++) {
if (rgItems[i]) {
LocalFree(rgItems[i]);
}
}
LocalFree(rgItems);
rgItems = NULL;
}
}
if (0xFFFFFFFF == SetFilePointer(hFile, ulStart, NULL, FILE_BEGIN)) {
DebugTrace("CountCSVRows SetFilePointer -> %u\n", GetLastError());
}
return(hResult);
}
BOOL TestCSVName(ULONG index,
LPPROP_NAME lpImportMapping,
ULONG ulcFields,
PUCHAR * rgItems,
ULONG cProps,
BOOL fTryUnchosen) {
return((index != NOT_FOUND) &&
index < ulcFields &&
index < cProps &&
(fTryUnchosen || lpImportMapping[index].fChosen) &&
rgItems[index] &&
rgItems[index][0]);
}
/***************************************************************************
Name : MakeDisplayName
Purpose : Forms a display name based on the values of various props.
Parameters: lppDisplayName -> Returned display name. This should only
be used for certain purposes. It can be used for error
dialogs, but if it was generated from first/middle/last,
it should not be used for PR_DISPLAY_NAME!
lpImportMapping = import mapping table
ulcFields = size of import mapping table
rgItems = fields for this CSV item
cProps = number of fields in rgItems
iDisplayName = indicies of name related props
iNickname
iSurname
iGivenName
iMiddleName
iEmailAddress
iCompanyName
Returns : index of attribute forming the display name, or
if FML, return INDEX_FIRST_MIDDLE_LAST.
Comment : Form the display name based on these rules:
1. If there's already a display name and it's chosen,
use it.
2. if there's a chosen first, middle or last name, add them
together and use them.
3. if there's a chosen nickname, use it
4. if there's a chosen email-address, use it.
5. if there's a chosen company name, use it.
6. look again without regard to whether it was chosen or not.
***************************************************************************/
ULONG MakeDisplayName(LPTSTR * lppDisplayName,
LPPROP_NAME lpImportMapping,
ULONG ulcFields,
PUCHAR * rgItems,
ULONG cProps,
ULONG iDisplayName,
ULONG iNickname,
ULONG iSurname,
ULONG iGivenName,
ULONG iMiddleName,
ULONG iEmailAddress,
ULONG iCompanyName) {
BOOL fTryUnchosen = FALSE;
BOOL fSurname = FALSE;
BOOL fGivenName = FALSE;
BOOL fMiddleName = FALSE;
ULONG index = NOT_FOUND;
ULONG ulSize = 0;
LPTSTR lpDisplayName = NULL;
try_again:
if (TestCSVName(iDisplayName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
index = iDisplayName;
goto found;
}
if (TestCSVName(iSurname, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen) ||
TestCSVName(iGivenName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen) ||
TestCSVName(iMiddleName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
index = INDEX_FIRST_MIDDLE_LAST;
goto found;
}
if (TestCSVName(iNickname, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
index = iNickname;
goto found;
}
if (TestCSVName(iEmailAddress, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
index = iEmailAddress;
goto found;
}
if (TestCSVName(iCompanyName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
index = iCompanyName;
goto found;
}
if (! fTryUnchosen) {
fTryUnchosen = TRUE;
goto try_again;
}
found:
*lppDisplayName = NULL;
switch (index) {
case NOT_FOUND:
break;
case INDEX_FIRST_MIDDLE_LAST:
if (fSurname = TestCSVName(iSurname, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
ulSize += (lstrlen(rgItems[iSurname]) + 1);
}
if (fGivenName = TestCSVName(iGivenName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
ulSize += (lstrlen(rgItems[iGivenName]) + 1);
}
if (fMiddleName = TestCSVName(iMiddleName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
ulSize += (lstrlen(rgItems[iMiddleName]) + 1);
}
Assert(ulSize);
if (lpDisplayName = *lppDisplayName = LocalAlloc(LPTR, ulSize)) {
// BUGBUG: This does not localize. The effect is that in the collision/error
// dialogs, we will get the order of names wrong. It should not effect the properties
// actually stored on the object since we won't set PR_DISPLAY_NAME if it was
// generated by First/Middle/Last. I can live with this, but we'll see if the
// testers find it. BruceK
if (fGivenName) {
StrCatBuff(lpDisplayName, rgItems[iGivenName], ulSize);
}
if (fMiddleName) {
if (*lpDisplayName) {
StrCatBuff(lpDisplayName, " ", ulSize);
}
StrCatBuff(lpDisplayName, rgItems[iMiddleName], ulSize);
}
if (fSurname) {
if (*lpDisplayName) {
StrCatBuff(lpDisplayName, " ", ulSize);
}
StrCatBuff(lpDisplayName, rgItems[iSurname], ulSize);
}
}
break;
default:
ulSize = lstrlen(rgItems[index]) + 1;
if (*lppDisplayName = LocalAlloc(LPTR, ulSize)) {
StrCpyN(*lppDisplayName, rgItems[index], ulSize);
}
break;
}
return(index);
}
#define MAX_SEP 20
void GetListSeparator(LPTSTR szBuf)
{
// Buffer is assumed to be MAX_SEP chars long
if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLIST, szBuf, MAX_SEP))
{
szBuf[0] = TEXT(',');
szBuf[1] = 0;
}
}
HRESULT CSVImport(HWND hWnd,
LPADRBOOK lpAdrBook,
LPWABOBJECT lpWABObject,
LPWAB_PROGRESS_CALLBACK lpProgressCB,
LPWAB_EXPORT_OPTIONS lpOptions) {
HRESULT hResult = hrSuccess;
register ULONG i;
ULONG cbWABEID, ulObjType;
ULONG index;
ULONG ulLastChosenProp = 0;
ULONG ulcFields = 0;
ULONG cProps;
ULONG ulCreateFlags = CREATE_CHECK_DUP_STRICT;
TCHAR szBuffer[MAX_RESOURCE_STRING + 1];
WAB_PROGRESS Progress;
LPABCONT lpContainer = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
TCHAR rgFileName[MAX_PATH + 1] = "";
PUCHAR * rgItems = NULL;
REPLACE_INFO RI;
LPMAPIPROP lpMailUserWAB = NULL;
SPropValue sPropVal;
BOOL fSkipSetProps;
LPTSTR lpDisplayName = NULL, lpEmailAddress = NULL;
ULONG iEmailAddress = NOT_FOUND, iDisplayName = NOT_FOUND, iSurname = NOT_FOUND,
iGivenName = NOT_FOUND, iCompanyName = NOT_FOUND, iMiddleName = NOT_FOUND,
iNickname = NOT_FOUND, iDisplay = NOT_FOUND;
TCHAR szSep[MAX_SEP];
SetGlobalBufferFunctions(lpWABObject);
// Read in the Property Name strings to the PropNames array
for (i = 0; i < NUM_EXPORT_PROPS; i++) {
rgPropNames[i].lpszName = LoadAllocString(rgPropNames[i].ids);
Assert(rgPropNames[i].lpszName);
DebugTrace("Property 0x%08x name: %s\n", rgPropNames[i].ulPropTag, rgPropNames[i].lpszName);
}
GetListSeparator(szSep);
// Present UI Wizard
if (hResult = ImportWizard(hWnd, rgFileName, ARRAYSIZE(rgFileName), rgPropNames, szSep, &lpImportMapping, &ulcFields, &hFile)) {
goto exit;
}
Assert(hFile != INVALID_HANDLE_VALUE);
// Find name props and last chosen property
for (i = 0; i < ulcFields; i++) {
if (lpImportMapping[i].fChosen) {
ulLastChosenProp = i;
}
switch (lpImportMapping[i].ulPropTag) {
case PR_EMAIL_ADDRESS:
iEmailAddress = i;
break;
case PR_DISPLAY_NAME:
iDisplayName = i;
break;
case PR_SURNAME:
iSurname = i;
break;
case PR_GIVEN_NAME:
iGivenName = i;
break;
case PR_COMPANY_NAME:
iCompanyName = i;
break;
case PR_MIDDLE_NAME:
iMiddleName = i;
break;
case PR_NICKNAME:
iNickname = i;
break;
}
}
//
// Open the WAB's PAB container: fills global lpCreateEIDsWAB
//
if (hResult = LoadWABEIDs(lpAdrBook, &lpContainer)) {
goto exit;
}
//
// All set... now loop through the file lines, adding each to the WAB
//
// How many lines are there?
if (hResult = CountCSVRows(hFile, szSep, &ulcEntries)) {
goto exit;
}
DebugTrace("CSV file contains %u entries\n", ulcEntries);
// Initialize the Progress Bar
Progress.denominator = max(ulcEntries, 1);
Progress.numerator = 0;
if (LoadString(hInst, IDS_STATE_IMPORT_MU, szBuffer, sizeof(szBuffer))) {
DebugTrace("Status Message: %s\n", szBuffer);
Progress.lpText = szBuffer;
} else {
DebugTrace("Cannot load resource string %u\n", IDS_STATE_IMPORT_MU);
Progress.lpText = NULL;
}
lpProgressCB(hWnd, &Progress);
while (hResult == hrSuccess) {
// Read the CSV attributes
if (hResult = ReadCSVLine(hFile, szSep, &cProps, &rgItems)) {
DebugTrace("ReadCSVLine -> %x\n", GetScode(hResult));
if (GetScode(hResult) == MAPI_E_NOT_FOUND) {
// EOF
hResult = hrSuccess;
}
break; // nothing more to read
}
iDisplay = iDisplayName;
if (TestCSVName(iEmailAddress,
lpImportMapping,
ulcFields,
rgItems,
cProps,
TRUE)) {
lpEmailAddress = rgItems[iEmailAddress];
}
switch (index = MakeDisplayName(&lpDisplayName,
lpImportMapping,
ulcFields,
rgItems,
cProps,
iDisplayName,
iNickname,
iSurname,
iGivenName,
iMiddleName,
iEmailAddress,
iCompanyName)) {
case NOT_FOUND:
// No name props
// BUGBUG: Should give special error?
break;
case INDEX_FIRST_MIDDLE_LAST:
break;
default:
iDisplay = index;
break;
}
// Should be the same number of fields in every entry, but if not,
// we'll handle it below.
// Assert(cProps == ulcFields); // Outlook does this!
ulCreateFlags = CREATE_CHECK_DUP_STRICT;
if (lpOptions->ReplaceOption == WAB_REPLACE_ALWAYS) {
ulCreateFlags |= CREATE_REPLACE;
}
retry:
// Create a new wab mailuser
if (HR_FAILED(hResult = lpContainer->lpVtbl->CreateEntry(lpContainer,
lpCreateEIDsWAB[iconPR_DEF_CREATE_MAILUSER].Value.bin.cb,
(LPENTRYID)lpCreateEIDsWAB[iconPR_DEF_CREATE_MAILUSER].Value.bin.lpb,
ulCreateFlags,
&lpMailUserWAB))) {
DebugTrace("CreateEntry(WAB MailUser) -> %x\n", GetScode(hResult));
goto exit;
}
for (i = 0; i <= min(ulLastChosenProp, cProps); i++)
{
if (lpImportMapping[i].fChosen && lpImportMapping[i].lpszName)
{
if (rgItems[i] && *rgItems[i]) {
// Look it up in the WAB property names table
DebugTrace("Prop %u: <%s> %s\n", i, lpImportMapping[i].lpszName, rgItems[i]);
sPropVal.ulPropTag = lpImportMapping[i].ulPropTag;
Assert(PROP_TYPE(lpImportMapping[i].ulPropTag) == PT_TSTRING);
sPropVal.Value.LPSZ = rgItems[i];
fSkipSetProps = FALSE;
if (sPropVal.ulPropTag == PR_EMAIL_ADDRESS)
{
if (! IsInternetAddress(sPropVal.Value.LPSZ))
{
DebugTrace("Found non-SMTP address %s\n", sPropVal.Value.LPSZ);
if (HandleImportError(hWnd,
0,
WAB_W_BAD_EMAIL,
lpDisplayName,
lpEmailAddress,
lpOptions))
{
hResult = ResultFromScode(MAPI_E_USER_CANCEL);
goto exit;
}
lpEmailAddress = NULL;
fSkipSetProps = TRUE;
}
}
if (! fSkipSetProps)
{
// Set the property on the WAB entry
if (HR_FAILED(hResult = lpMailUserWAB->lpVtbl->SetProps(lpMailUserWAB,
1, // cValues
&sPropVal, // property array
NULL)))
{ // problems array
DebugTrace("ImportEntry:SetProps(WAB) -> %x\n", GetScode(hResult));
goto exit;
}
// [PaulHi] 3/4/99 Raid 73637
// If we have a valid email address then we need to also add the
// PR_ADDRTYPE property set to "SMTP".
if (sPropVal.ulPropTag == PR_EMAIL_ADDRESS)
{
sPropVal.ulPropTag = PR_ADDRTYPE;
sPropVal.Value.LPSZ = (LPTSTR)szSMTP;
hResult = lpMailUserWAB->lpVtbl->SetProps(
lpMailUserWAB,
1,
&sPropVal,
NULL);
if (HR_FAILED(hResult))
{
DebugTrace("CSV ImportEntry:SetProps(WAB) for PR_ADDRTYPE -> %x\n", GetScode(hResult));
goto exit;
}
}
}
}
}
}
if (index != iDisplayName && index != NOT_FOUND && index != INDEX_FIRST_MIDDLE_LAST) {
// Set the PR_DISPLAY_NAME
sPropVal.ulPropTag = PR_DISPLAY_NAME;
sPropVal.Value.LPSZ = rgItems[index];
// Set the property on the WAB entry
if (HR_FAILED(hResult = lpMailUserWAB->lpVtbl->SetProps(lpMailUserWAB,
1, // cValues
&sPropVal, // property array
NULL))) { // problems array
DebugTrace("ImportEntry:SetProps(WAB) -> %x\n", GetScode(hResult));
goto exit;
}
}
// Save the new wab mailuser or distlist
if (HR_FAILED(hResult = lpMailUserWAB->lpVtbl->SaveChanges(lpMailUserWAB,
KEEP_OPEN_READONLY | FORCE_SAVE))) {
if (GetScode(hResult) == MAPI_E_COLLISION) {
/*
// Find the display name
Assert(lpDisplayName);
if (! lpDisplayName) {
DebugTrace("Collision, but can't find PR_DISPLAY_NAME in entry\n");
goto exit;
}
*/ // WAB replaces no Display Names with Unknown
// Do we need to prompt?
if (lpOptions->ReplaceOption == WAB_REPLACE_PROMPT) {
// Prompt user with dialog. If they say YES, we should try again
RI.lpszDisplayName = lpDisplayName ? lpDisplayName : "";
RI.lpszEmailAddress = lpEmailAddress;
RI.ConfirmResult = CONFIRM_ERROR;
RI.lpImportOptions = lpOptions;
DialogBoxParam(hInst,
MAKEINTRESOURCE(IDD_ImportReplace),
hWnd,
ReplaceDialogProc,
(LPARAM)&RI);
switch (RI.ConfirmResult) {
case CONFIRM_YES:
case CONFIRM_YES_TO_ALL:
// YES
// NOTE: recursive Migrate will fill in the SeenList entry
// go try again!
lpMailUserWAB->lpVtbl->Release(lpMailUserWAB);
lpMailUserWAB = NULL;
ulCreateFlags |= CREATE_REPLACE;
goto retry;
break;
case CONFIRM_ABORT:
hResult = ResultFromScode(MAPI_E_USER_CANCEL);
goto exit;
default:
// NO
break;
}
}
hResult = hrSuccess;
} else {
DebugTrace("SaveChanges(WAB MailUser) -> %x\n", GetScode(hResult));
}
}
// Clean up
if (rgItems) {
for (i = 0; i < cProps; i++) {
if (rgItems[i]) {
LocalFree(rgItems[i]);
}
}
LocalFree(rgItems);
rgItems = NULL;
}
// Update progress bar
Progress.numerator++;
// TEST CODE!
if (Progress.numerator == Progress.denominator) {
// Done? Do I need to do anything?
}
lpProgressCB(hWnd, &Progress);
if (lpMailUserWAB) {
lpMailUserWAB->lpVtbl->Release(lpMailUserWAB);
lpMailUserWAB = NULL;
}
if (lpDisplayName) {
LocalFree(lpDisplayName);
lpDisplayName = NULL;
}
// if (hResult) {
// if (HandleExportError(hWnd, 0, hResult, lpRow->aRow[0].lpProps[iptaColumnsPR_DISPLAY_NAME].Value.LPSZ)) {
// hResult = ResultFromScode(MAPI_E_USER_CANCEL);
// } else {
// hResult = hrSuccess;
// }
// }
}
exit:
if (hFile) {
CloseHandle(hFile);
}
if (lpDisplayName) {
LocalFree(lpDisplayName);
}
// Don't free lpEmailAddress! It's part of the rgItems below.
// Free the WAB objects
if (lpMailUserWAB) {
lpMailUserWAB->lpVtbl->Release(lpMailUserWAB);
}
if (lpContainer) {
lpContainer->lpVtbl->Release(lpContainer);
}
// Free the prop name strings
for (i = 0; i < NUM_EXPORT_PROPS; i++) {
if (rgPropNames[i].lpszName) {
LocalFree(rgPropNames[i].lpszName);
}
}
// Free any CSV attributes left
if (rgItems) {
for (i = 0; i < cProps; i++) {
if (rgItems[i]) {
LocalFree(rgItems[i]);
}
}
LocalFree(rgItems);
}
if (lpCreateEIDsWAB) {
WABFreeBuffer(lpCreateEIDsWAB);
lpCreateEIDsWAB = NULL;
}
return(hResult);
}
/***************************************************************************
Name : CreateCSVFile
Purpose : Creates a CSV file for export
Parameters: hwnd = main dialog window
lpFileName = filename to create
lphFile -> returned file handle
Returns : HRESULT
Comment :
***************************************************************************/
HRESULT CreateCSVFile(HWND hwnd, LPTSTR lpFileName, LPHANDLE lphFile) {
LPTSTR lpFilter;
TCHAR szFileName[MAX_PATH + 1] = "";
OPENFILENAME ofn;
HANDLE hFile = INVALID_HANDLE_VALUE;
HRESULT hResult = hrSuccess;
if (INVALID_HANDLE_VALUE == (hFile = CreateFile(lpFileName,
GENERIC_WRITE,
0, // sharing
NULL,
CREATE_NEW,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL))) {
if (GetLastError() == ERROR_FILE_EXISTS) {
// Ask user if they want to overwrite
switch (ShowMessageBoxParam(hwnd, IDE_CSV_EXPORT_FILE_EXISTS, MB_ICONEXCLAMATION | MB_YESNO | MB_SETFOREGROUND, lpFileName)) {
case IDYES:
if (INVALID_HANDLE_VALUE == (hFile = CreateFile(lpFileName,
GENERIC_WRITE,
0, // sharing
NULL,
CREATE_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL))) {
ShowMessageBoxParam(hwnd, IDE_CSV_EXPORT_FILE_ERROR, MB_ICONERROR, lpFileName);
hResult = ResultFromScode(MAPI_E_NOT_FOUND);
}
break;
default:
DebugTrace("ShowMessageBoxParam gave unknown return\n");
case IDNO:
// nothing to do here
hResult = ResultFromScode(MAPI_E_USER_CANCEL);
break;
}
} else {
ShowMessageBoxParam(hwnd, IDE_CSV_EXPORT_FILE_ERROR, MB_ICONERROR, lpFileName);
hResult = ResultFromScode(MAPI_E_NOT_FOUND);
}
}
if (! hResult) {
*lphFile = hFile;
}
return(hResult);
}
/***************************************************************************
Name : WriteCSV
Purpose : Writes a string to a CSV file with fixups for special characters
Parameters: hFile = file handle
fFixup = TRUE if we should check for special characters
lpString = nul-terminated string to write
szSep = list separator (only needed if fFixup is TRUE)
Returns : HRESULT
Comment : CSV special characters are szSep, CR and LF. If they occur in
the string, we should wrap the entire string in quotes.
***************************************************************************/
HRESULT WriteCSV(HANDLE hFile, BOOL fFixup, const UCHAR * lpString, LPTSTR szSep) {
HRESULT hResult = hrSuccess;
ULONG cWrite = lstrlen((LPTSTR)lpString);
ULONG cbWritten;
BOOL fQuote = FALSE;
register ULONG i;
ULONG ec;
LPTSTR szSepT;
// Is there a szSep, a CR or a LF in the string?
// If so, enclose the string in quotes.
if (fFixup) {
szSepT = szSep;
for (i = 0; i < cWrite && ! fQuote; i++) {
if (lpString[i] == (UCHAR)(*szSepT)) {
szSepT++;
if (*szSepT == '\0')
fQuote = TRUE;
} else {
szSepT = szSep;
if ((lpString[i] == '\n') || (lpString[i] == '\r'))
fQuote = TRUE;
}
}
}
if (fQuote) {
if (! WriteFile(hFile,
szQuote,
1,
&cbWritten,
NULL)) {
ec = GetLastError();
DebugTrace("WriteCSV:WriteFile -> %u\n", ec);
if (ec == ERROR_HANDLE_DISK_FULL ||
ec == ERROR_DISK_FULL) {
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_DISK);
} else {
hResult = ResultFromScode(MAPI_E_DISK_ERROR);
}
goto exit;
}
}
if (! WriteFile(hFile,
lpString,
cWrite,
&cbWritten,
NULL)) {
ec = GetLastError();
DebugTrace("WriteCSV:WriteFile -> %u\n", ec);
if (ec == ERROR_HANDLE_DISK_FULL ||
ec == ERROR_DISK_FULL) {
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_DISK);
} else {
hResult = ResultFromScode(MAPI_E_DISK_ERROR);
}
goto exit;
}
if (fQuote) {
if (! WriteFile(hFile,
szQuote,
1,
&cbWritten,
NULL)) {
ec = GetLastError();
DebugTrace("WriteCSV:WriteFile -> %u\n", ec);
if (ec == ERROR_HANDLE_DISK_FULL ||
ec == ERROR_DISK_FULL) {
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_DISK);
} else {
hResult = ResultFromScode(MAPI_E_DISK_ERROR);
}
goto exit;
}
}
exit:
return(hResult);
}
HRESULT ExportCSVMailUser(HANDLE hFile,
ULONG ulPropNames,
ULONG ulLastProp,
LPPROP_NAME lpPropNames,
LPSPropTagArray lppta,
LPTSTR szSep,
LPADRBOOK lpAdrBook,
ULONG cbEntryID,
LPENTRYID lpEntryID) {
HRESULT hResult = hrSuccess;
LPMAILUSER lpMailUser = NULL;
ULONG ulObjType;
ULONG cProps;
LPSPropValue lpspv = NULL;
ULONG i;
const UCHAR szCRLF[] = "\r\n";
UCHAR szBuffer[11] = "";
if (hResult = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook,
cbEntryID,
lpEntryID,
NULL,
0,
&ulObjType,
(LPUNKNOWN *)&lpMailUser)) {
DebugTrace("WAB OpenEntry(mailuser) -> %x\n", GetScode(hResult));
goto exit;
}
if ((HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(lpMailUser,
lppta,
0,
&cProps,
&lpspv)))) {
DebugTrace("ExportCSVMailUser: GetProps() -> %x\n", GetScode(hResult));
goto exit;
}
for (i = 0; i < ulPropNames; i++) {
if (rgPropNames[i].fChosen) {
// Output the value
switch (PROP_TYPE(lpspv[i].ulPropTag)) {
case PT_TSTRING:
if (hResult = WriteCSV(hFile, TRUE, lpspv[i].Value.LPSZ, szSep)) {
goto exit;
}
break;
case PT_LONG:
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), "%u", lpspv[i].Value.l);
if (hResult = WriteCSV(hFile, TRUE, szBuffer, szSep)) {
goto exit;
}
break;
default:
DebugTrace("CSV export: unsupported property 0x%08x\n", lpspv[i].ulPropTag);
Assert(FALSE);
// fall through to skip
case PT_ERROR:
// skip it.
break;
}
if (i != ulLastProp) {
// Output the seperator
if (hResult = WriteCSV(hFile, FALSE, szSep, NULL)) {
goto exit;
}
}
}
}
if (hResult = WriteCSV(hFile, FALSE, szCRLF, NULL)) {
goto exit;
}
exit:
if (lpspv) {
WABFreeBuffer(lpspv);
}
if (lpMailUser) {
lpMailUser->lpVtbl->Release(lpMailUser);
}
return(hResult);
}
HRESULT CSVExport(HWND hWnd,
LPADRBOOK lpAdrBook,
LPWABOBJECT lpWABObject,
LPWAB_PROGRESS_CALLBACK lpProgressCB,
LPWAB_EXPORT_OPTIONS lpOptions) {
HRESULT hResult = hrSuccess;
register ULONG i;
ULONG cbWABEID, ulObjType;
ULONG ulLastChosenProp = 0;
WAB_PROGRESS Progress;
ULONG cRows = 0;
LPSRowSet lpRow = NULL;
ULONG ulCount = 0;
SRestriction restrictObjectType;
SPropValue spvObjectType;
LPENTRYID lpWABEID = NULL;
LPABCONT lpContainer = NULL;
LPMAPITABLE lpContentsTable = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
LPSPropTagArray lppta = NULL;
const UCHAR szCRLF[] = "\r\n";
TCHAR szSep[MAX_SEP];
TCHAR rgFileName[MAX_PATH + 1] = "";
SetGlobalBufferFunctions(lpWABObject);
// Read in the Property Name strings
for (i = 0; i < NUM_EXPORT_PROPS; i++) {
rgPropNames[i].lpszName = LoadAllocString(rgPropNames[i].ids);
Assert(rgPropNames[i].lpszName);
DebugTrace("Property 0x%08x name: %s\n", rgPropNames[i].ulPropTag, rgPropNames[i].lpszName);
}
// Present UI Wizard
if (hResult = ExportWizard(hWnd, rgFileName, ARRAYSIZE(rgFileName), rgPropNames)) {
goto exit;
}
// Find the last prop name chosen
for (i = NUM_EXPORT_PROPS - 1; i > 0; i--) {
if (rgPropNames[i].fChosen) {
ulLastChosenProp = i;
break;
}
}
//
// Open the WAB's PAB container
//
if (hResult = lpAdrBook->lpVtbl->GetPAB(lpAdrBook,
&cbWABEID,
&lpWABEID)) {
DebugTrace("WAB GetPAB -> %x\n", GetScode(hResult));
goto exit;
} else {
if (hResult = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook,
cbWABEID, // size of EntryID to open
lpWABEID, // EntryID to open
NULL, // interface
0, // flags
&ulObjType,
(LPUNKNOWN *)&lpContainer)) {
DebugTrace("WAB OpenEntry(PAB) -> %x\n", GetScode(hResult));
goto exit;
}
}
//
// All set... now loop through the WAB's entries, exporting each one
//
if (HR_FAILED(hResult = lpContainer->lpVtbl->GetContentsTable(lpContainer,
0, // ulFlags
&lpContentsTable))) {
DebugTrace("WAB GetContentsTable(PAB Table) -> %x\n", GetScode(hResult));
goto exit;
}
// Set the columns to those we're interested in
if (hResult = lpContentsTable->lpVtbl->SetColumns(lpContentsTable,
(LPSPropTagArray)&ptaColumns,
0)) {
DebugTrace("WAB SetColumns(PAB Table) -> %x\n", GetScode(hResult));
goto exit;
}
// Restrict the table to MAPI_MAILUSERs
spvObjectType.ulPropTag = PR_OBJECT_TYPE;
spvObjectType.Value.l = MAPI_MAILUSER;
restrictObjectType.rt = RES_PROPERTY;
restrictObjectType.res.resProperty.relop = RELOP_EQ;
restrictObjectType.res.resProperty.ulPropTag = PR_OBJECT_TYPE;
restrictObjectType.res.resProperty.lpProp = &spvObjectType;
if (HR_FAILED(hResult = lpContentsTable->lpVtbl->Restrict(lpContentsTable,
&restrictObjectType,
0))) {
DebugTrace("WAB Restrict (MAPI_MAILUSER) -> %x\n", GetScode(hResult));
goto exit;
}
// How many MailUser entries are there?
ulcEntries = CountRows(lpContentsTable, FALSE);
DebugTrace("WAB contains %u MailUser entries\n", ulcEntries);
if (ulcEntries == 0) {
DebugTrace("WAB has no entries, nothing to export.\n");
goto exit;
}
// Initialize the Progress Bar
Progress.denominator = max(ulcEntries, 1);
Progress.numerator = 0;
Progress.lpText = NULL;
lpProgressCB(hWnd, &Progress);
// Write out the property names
GetListSeparator(szSep);
// Create the file (and handle error UI)
if (hResult = CreateCSVFile(hWnd, rgFileName, &hFile)) {
goto exit;
}
for (i = 0; i < NUM_EXPORT_PROPS; i++) {
// Output the name
if (rgPropNames[i].fChosen) {
if (hResult = WriteCSV(hFile, TRUE, rgPropNames[i].lpszName, szSep)) {
goto exit;
}
if (i != ulLastChosenProp) {
// Output the seperator
if (hResult = WriteCSV(hFile, FALSE, szSep, NULL)) {
goto exit;
}
}
}
}
if (hResult = WriteCSV(hFile, FALSE, szCRLF, NULL)) {
goto exit;
}
// Map the prop name array to a SPropTagArray.
lppta = LocalAlloc(LPTR, CbNewSPropTagArray(NUM_EXPORT_PROPS));
lppta->cValues = NUM_EXPORT_PROPS;
for (i = 0; i < lppta->cValues; i++) {
lppta->aulPropTag[i] = rgPropNames[i].ulPropTag;
}
cRows = 1;
while (cRows && hResult == hrSuccess) {
// Get the next WAB entry
if (hResult = lpContentsTable->lpVtbl->QueryRows(lpContentsTable,
1, // one row at a time
0, // ulFlags
&lpRow)) {
DebugTrace("QueryRows -> %x\n", GetScode(hResult));
goto exit;
}
if (lpRow) {
if (cRows = lpRow->cRows) { // Yes, single '='
Assert(lpRow->cRows == 1);
Assert(lpRow->aRow[0].cValues == iptaColumnsMax);
Assert(lpRow->aRow[0].lpProps[iptaColumnsPR_ENTRYID].ulPropTag == PR_ENTRYID);
Assert(lpRow->aRow[0].lpProps[iptaColumnsPR_OBJECT_TYPE].ulPropTag == PR_OBJECT_TYPE);
if (cRows = lpRow->cRows) { // yes, single '='
// Export mailuser
if (hResult = ExportCSVMailUser(hFile,
NUM_EXPORT_PROPS,
ulLastChosenProp,
rgPropNames,
lppta,
szSep,
lpAdrBook,
lpRow->aRow[0].lpProps[iptaColumnsPR_ENTRYID].Value.bin.cb,
(LPENTRYID)lpRow->aRow[0].lpProps[iptaColumnsPR_ENTRYID].Value.bin.lpb)) {
goto exit;
}
// Update progress bar
Progress.numerator++;
lpProgressCB(hWnd, &Progress);
if (hResult) {
if (HandleExportError(hWnd,
0,
hResult,
lpRow->aRow[0].lpProps[iptaColumnsPR_DISPLAY_NAME].Value.LPSZ,
PropStringOrNULL(&lpRow->aRow[0].lpProps[iptaColumnsPR_EMAIL_ADDRESS]),
lpOptions)) {
hResult = ResultFromScode(MAPI_E_USER_CANCEL);
} else {
hResult = hrSuccess;
}
}
} // else, drop out of loop, we're done.
}
WABFreeProws(lpRow);
}
}
exit:
if (hFile) {
CloseHandle(hFile);
}
if (lppta) {
LocalFree(lppta);
}
// Free the WAB objects
WABFreeBuffer(lpWABEID);
lpWABEID = NULL;
if (lpContainer) {
lpContainer->lpVtbl->Release(lpContainer);
lpContainer = NULL;
}
if (lpContentsTable) {
lpContentsTable->lpVtbl->Release(lpContentsTable);
lpContentsTable = NULL;
}
// Free the prop name strings
for (i = 0; i < NUM_EXPORT_PROPS; i++) {
if (rgPropNames[i].lpszName) {
LocalFree(rgPropNames[i].lpszName);
}
}
return(hResult);
}