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.
7091 lines
238 KiB
7091 lines
238 KiB
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
///
|
|
/// MPSWAB.C
|
|
///
|
|
/// Microsoft Property Store - WAB Dll
|
|
///
|
|
/// Contains implementations of File managment functions
|
|
///
|
|
/// Exposed Functions:
|
|
/// OpenPropertyStore
|
|
/// ClosePropertyStore
|
|
/// BackupPropertyStore
|
|
/// LockPropertyStore
|
|
/// UnlockPropertyStore
|
|
/// ReadRecord
|
|
/// WriteRecord
|
|
/// FindRecords
|
|
/// DeleteRecords
|
|
/// ReadIndex
|
|
/// ReadPropArray
|
|
/// HrFindFuzzyRecordMatches
|
|
///
|
|
/// Private:
|
|
/// UnlockFileAccess
|
|
/// LockFileAccess
|
|
/// ReloadMPSWabFileInfoTmp
|
|
/// bTagWriteTransaction
|
|
/// bUntagWriteTransaction
|
|
///
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
#include "_apipch.h"
|
|
|
|
BOOL fTrace = TRUE;
|
|
BOOL fDebugTrap = FALSE;
|
|
TCHAR szDebugOutputFile[MAX_PATH] = TEXT("");
|
|
|
|
|
|
BOOL bUntagWriteTransaction(LPMPSWab_FILE_HEADER lpMPSWabFileHeader,
|
|
HANDLE hMPSWabFile);
|
|
|
|
BOOL bTagWriteTransaction(LPMPSWab_FILE_HEADER lpMPSWabFileHeader,
|
|
HANDLE hMPSWabFile);
|
|
|
|
HRESULT GetFolderEIDs(HANDLE hMPSWabFile,
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo,
|
|
LPSBinary pmbinFold,
|
|
ULONG * lpulFolderEIDs,
|
|
LPDWORD * lppdwFolderEIDs);
|
|
|
|
BOOL bIsFolderMember(HANDLE hMPSWabFile,
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo,
|
|
DWORD dwEntryID, ULONG * lpulObjType);
|
|
|
|
extern int nCountSubStrings(LPTSTR lpszSearchStr);
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////
|
|
//
|
|
// OpenPropertyStore - searches for Property Store and or creates it
|
|
// based on flags.
|
|
//
|
|
// IN - lpszFileName - file name specified by client
|
|
// IN - ulFlags - AB_CREATE_NEW
|
|
// AB_CREATE_ALWAYS
|
|
// AB_OPEN_ALWAYS
|
|
// AB_OPEN_EXISTING
|
|
// AB_READ_ONLY
|
|
// AB_SET_DEFAULT (?)
|
|
// AB_DONT_RESTORE
|
|
// IN - hWnd - In the event of data corruption, use this hWnd for a message box
|
|
// if it exists, or show the message box on the desktop window
|
|
// OUT- lphPropertyStore - Handle to opened property store
|
|
//
|
|
// This routine also scans the file and attempts to fix errors if it finds any.
|
|
// including recovering from backup. When opening the file with OpenPropertyStore
|
|
// specify AB_DONT_RESTORE to prevent the restoration operation
|
|
// This should especially be done when opening files that are not the default
|
|
// property store.
|
|
//
|
|
// Return Value:
|
|
// HRESULT -
|
|
// S_OK Success
|
|
// E_FAIL Failure
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
HRESULT OpenPropertyStore( IN LPTSTR lpszFileName,
|
|
IN ULONG ulFlags,
|
|
IN HWND hWnd,
|
|
OUT LPHANDLE lphPropertyStore)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
ULONG i=0,j=0;
|
|
DWORD dwNumofBytes = 0;
|
|
WIN32_FIND_DATA FileData;
|
|
DWORD dwIndexBlockSize = 0;
|
|
LPTSTR lpszBuffer = NULL;
|
|
BOOL bFileLocked = FALSE;
|
|
ULONG cchSize;
|
|
|
|
//
|
|
// the following pointer will be returned back as the handle to the property store
|
|
//
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = NULL;
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
if(!lphPropertyStore)
|
|
{
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
// A file name overrides an outlook session
|
|
if(pt_bIsWABOpenExSession && !(ulFlags & AB_IGNORE_OUTLOOK))
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
if(!lpfnWABOpenStorageProvider)
|
|
return MAPI_E_NOT_INITIALIZED;
|
|
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = NULL;
|
|
|
|
hr = lpfnWABOpenStorageProvider(hWnd, pmsessOutlookWabSPI,
|
|
lpfnAllocateBufferExternal ? lpfnAllocateBufferExternal : (LPALLOCATEBUFFER) (MAPIAllocateBuffer),
|
|
lpfnAllocateMoreExternal ? lpfnAllocateMoreExternal : (LPALLOCATEMORE) (MAPIAllocateMore),
|
|
lpfnFreeBufferExternal ? lpfnFreeBufferExternal : MAPIFreeBuffer,
|
|
0,
|
|
&lpWSP);
|
|
|
|
DebugTrace(TEXT("Outlook WABOpenStorageProvider returned:%x\n"),hr);
|
|
|
|
if(HR_FAILED(hr))
|
|
return hr;
|
|
|
|
(*lphPropertyStore) = (HANDLE) lpWSP;
|
|
|
|
return(hr);
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo = LocalAlloc(LMEM_ZEROINIT,sizeof(MPSWab_FILE_INFO));
|
|
|
|
if (!lpMPSWabFileInfo)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
|
|
DebugTrace(( TEXT("-----------\nOpenPropertyStore: Entry\n")));
|
|
|
|
lpMPSWabFileInfo->hDataAccessMutex = CreateMutex(NULL,FALSE,TEXT("MPSWabDataAccessMutex"));
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
|
|
//
|
|
// Initialize
|
|
//
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader = NULL;
|
|
lpMPSWabFileInfo->lpszMPSWabFileName = NULL;
|
|
lpMPSWabFileInfo->lpMPSWabIndexStr = NULL;
|
|
lpMPSWabFileInfo->lpMPSWabIndexEID = NULL;
|
|
|
|
*lphPropertyStore = NULL;
|
|
|
|
//
|
|
// No file name ???
|
|
//
|
|
if (lpszFileName == NULL) goto out;
|
|
|
|
|
|
//
|
|
// Allocate space for the file header
|
|
//
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader = LocalAlloc(LMEM_ZEROINIT,sizeof(MPSWab_FILE_HEADER));
|
|
|
|
if (!lpMPSWabFileInfo->lpMPSWabFileHeader)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// retain file name for future use
|
|
//
|
|
cchSize = lstrlen(lpszFileName) + 1;
|
|
lpMPSWabFileInfo->lpszMPSWabFileName = (LPTSTR) LocalAlloc(LMEM_ZEROINIT,sizeof(TCHAR)*cchSize);
|
|
|
|
if (!lpMPSWabFileInfo->lpszMPSWabFileName)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
StrCpyN(lpMPSWabFileInfo->lpszMPSWabFileName,lpszFileName,cchSize);
|
|
|
|
|
|
if(((ulFlags & AB_OPEN_ALWAYS)) || ((ulFlags & AB_OPEN_EXISTING)))
|
|
{
|
|
//
|
|
// If file exists, open it - if it doesnt exist, create a new one
|
|
//
|
|
hMPSWabFile = FindFirstFile(lpMPSWabFileInfo->lpszMPSWabFileName, &FileData);
|
|
if (hMPSWabFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
//
|
|
// File Not Found
|
|
//
|
|
if ((ulFlags & AB_OPEN_ALWAYS))
|
|
{
|
|
//
|
|
// create a new one
|
|
//
|
|
if (!CreateMPSWabFile( IN lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
IN lpMPSWabFileInfo->lpszMPSWabFileName,
|
|
IN MAX_INITIAL_INDEX_ENTRIES,
|
|
IN NAMEDPROP_STORE_SIZE))
|
|
{
|
|
DebugTrace(TEXT("Could Not Create File %s!\n"),lpMPSWabFileInfo->lpszMPSWabFileName);
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Nothing to do .. exit
|
|
//
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// found the file ... just close the handle ...
|
|
FindClose(hMPSWabFile);
|
|
hMPSWabFile = NULL;
|
|
}
|
|
}
|
|
else if (((ulFlags & AB_CREATE_NEW)) || ((ulFlags & AB_CREATE_ALWAYS)))
|
|
{
|
|
//
|
|
// Create a new file - overwrite any existing file
|
|
//
|
|
if ((ulFlags & AB_CREATE_NEW))
|
|
{
|
|
hMPSWabFile = FindFirstFile(lpMPSWabFileInfo->lpszMPSWabFileName, &FileData);
|
|
if (hMPSWabFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
//
|
|
// Dont overwrite if flag is CREATE_NEW
|
|
//
|
|
DebugTrace(TEXT("Specified file %s found\n"),lpMPSWabFileInfo->lpszMPSWabFileName);
|
|
hr = MAPI_E_NOT_FOUND;
|
|
|
|
//Close the handle since we dont need it
|
|
FindClose(hMPSWabFile);
|
|
hMPSWabFile = NULL;
|
|
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a new one ... over-write if neccessary
|
|
//
|
|
if (!CreateMPSWabFile( IN lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
IN lpMPSWabFileInfo->lpszMPSWabFileName,
|
|
IN MAX_INITIAL_INDEX_ENTRIES,
|
|
IN NAMEDPROP_STORE_SIZE))
|
|
{
|
|
DebugTrace(TEXT("Could Not Create File %s!\n"),lpMPSWabFileInfo->lpszMPSWabFileName);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we have a valid file, even though the file is new ... load the structures from the file
|
|
//
|
|
|
|
//
|
|
// check that we have a valid hWnd if we need to show message boxes
|
|
//
|
|
if (hWnd == NULL)
|
|
hWnd = GetDesktopWindow();
|
|
|
|
// reentrancy point for bug 16681
|
|
TryOpeningWABFileOnceAgain:
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
// Verify the WAB version, and migrate the file from an old version
|
|
// to a new version if required
|
|
hr = HrVerifyWABVersionAndUpdate( hWnd,
|
|
hMPSWabFile,
|
|
lpMPSWabFileInfo);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
//
|
|
// Bug 16681:
|
|
// Check the special case error for the blank-wab problem
|
|
// If this error exists, then rename to file to *.w-b
|
|
// and try creating a new wab file or restoring from
|
|
// backup ...
|
|
if(hr == MAPI_E_VERSION)
|
|
{
|
|
TCHAR szSaveAsFileName[MAX_PATH];
|
|
ULONG nLen = lstrlen(lpMPSWabFileInfo->lpszMPSWabFileName);
|
|
StrCpyN(szSaveAsFileName, lpMPSWabFileInfo->lpszMPSWabFileName, ARRAYSIZE(szSaveAsFileName));
|
|
|
|
szSaveAsFileName[nLen-2]='\0';
|
|
StrCatBuff(szSaveAsFileName, TEXT("~b"), ARRAYSIZE(szSaveAsFileName));
|
|
|
|
DeleteFile(szSaveAsFileName); //just in case it exists
|
|
|
|
DebugTrace(TEXT("Blank WAB file found. Being saved as %s\n"), szSaveAsFileName);
|
|
|
|
if (hMPSWabFile && INVALID_HANDLE_VALUE != hMPSWabFile)
|
|
{
|
|
IF_WIN32(CloseHandle(hMPSWabFile);)
|
|
IF_WIN16(CloseFile(hMPSWabFile);)
|
|
hMPSWabFile = NULL;
|
|
}
|
|
|
|
if(!MoveFile(lpMPSWabFileInfo->lpszMPSWabFileName, szSaveAsFileName))
|
|
{
|
|
// Just in case MoveFile failed,
|
|
if(!DeleteFile(lpMPSWabFileInfo->lpszMPSWabFileName))
|
|
{
|
|
// and if delete file failed too, we dont want to get
|
|
// caught in a loop so exit ..
|
|
goto out;
|
|
}
|
|
}
|
|
hr = E_FAIL;
|
|
|
|
goto TryOpeningWABFileOnceAgain;
|
|
}
|
|
|
|
// There is a catch here that if the GUID of the file is mangled
|
|
// we will never be able to access the file with the WAB
|
|
DebugTrace(TEXT("hrVerifyWABVersionAndUpdate failed: %x\n"), hr);
|
|
goto out;
|
|
// else fall through
|
|
}
|
|
|
|
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags == WAB_CLEAR)
|
|
{
|
|
// so it is a wab file - if there are no errors tagged to a quick check
|
|
hr = HrDoQuickWABIntegrityCheck(lpMPSWabFileInfo, hMPSWabFile);
|
|
if (HR_FAILED(hr))
|
|
DebugTrace(TEXT("HrDoQuickWABIntegrityCheck failed:%x\n"),hr);
|
|
else
|
|
{
|
|
// Reload whatever new info we added as a result of the above.
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the quick check failed or some errors are tagged then rebuild the
|
|
// indexes
|
|
if( (HR_FAILED(hr)) ||
|
|
(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_ERROR_DETECTED) ||
|
|
(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_WRITE_IN_PROGRESS) )
|
|
{
|
|
hr = HrDoDetailedWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("HrDoDetailedWABIntegrityCheck failed:%x\n"),hr);
|
|
if(hr == MAPI_E_CORRUPT_DATA)
|
|
{
|
|
if (ulFlags & AB_DONT_RESTORE)
|
|
{
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
// Restore from Backup
|
|
ShowMessageBoxParam(hWnd, idsWABIntegrityError, MB_ICONHAND | MB_OK, lpMPSWabFileInfo->lpszMPSWabFileName);
|
|
|
|
hr = HrRestoreFromBackup(lpMPSWabFileInfo, hMPSWabFile);
|
|
if(!HR_FAILED(hr))
|
|
ShowMessageBox(NULL, idsWABRestoreSucceeded, MB_OK | MB_ICONEXCLAMATION);
|
|
else
|
|
{
|
|
ShowMessageBoxParam(NULL, idsWABUnableToRestoreBackup, MB_ICONHAND | MB_OK, lpMPSWabFileInfo->lpszMPSWabFileName);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo->bReadOnlyAccess = ((ulFlags & AB_OPEN_READ_ONLY)) ? TRUE : FALSE;
|
|
|
|
hr = S_OK;
|
|
|
|
|
|
out:
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
if (hMPSWabFile && INVALID_HANDLE_VALUE != hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if (!(FAILED(hr)))
|
|
{
|
|
lpMPSWabFileInfo->bMPSWabInitialized = TRUE;
|
|
*lphPropertyStore = (HANDLE) lpMPSWabFileInfo;
|
|
}
|
|
else
|
|
{
|
|
LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabFileHeader);
|
|
|
|
LocalFreeAndNull(&lpMPSWabFileInfo->lpszMPSWabFileName);
|
|
|
|
LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabIndexStr);
|
|
|
|
LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabIndexEID);
|
|
|
|
//Close our handle on this mutex
|
|
CloseHandle(lpMPSWabFileInfo->hDataAccessMutex);
|
|
|
|
LocalFreeAndNull(&lpMPSWabFileInfo);
|
|
}
|
|
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
DebugTrace(( TEXT("OpenPropertyStore: Exit\n-----------\n")));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ClosePropertyStore
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
// IN ulFlags - AB_DONT_BACKUP prevents automatic backup. Should be called for
|
|
// for non-default property stores.
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT ClosePropertyStore(HANDLE hPropertyStore, ULONG ulFlags)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
TCHAR szBackupFileName[MAX_PATH];
|
|
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = NULL;
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
DebugTrace(( TEXT("-----------\nClosePropertyStore: Entry\n")));
|
|
|
|
if(pt_bIsWABOpenExSession && !(ulFlags & AB_IGNORE_OUTLOOK))
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
// Dont need to do anything in here ...
|
|
if(!hPropertyStore)
|
|
return MAPI_E_NOT_INITIALIZED;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
lpMPSWabFileInfo = hPropertyStore;
|
|
|
|
if (NULL == lpMPSWabFileInfo) goto out;
|
|
|
|
if(!(ulFlags & AB_DONT_BACKUP))
|
|
{
|
|
szBackupFileName[0]='\0';
|
|
|
|
GetWABBackupFileName(lpMPSWabFileInfo->lpszMPSWabFileName,szBackupFileName,ARRAYSIZE(szBackupFileName));
|
|
|
|
if(lstrlen(szBackupFileName))
|
|
{
|
|
//
|
|
// We do a backup operation here and some cleanup
|
|
//
|
|
hr = BackupPropertyStore( hPropertyStore,
|
|
szBackupFileName);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("BackupPropertyStore failed: %x\n"),hr);
|
|
//ignore errors and keep going on with this shutdown ...
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabFileHeader);
|
|
|
|
LocalFreeAndNull(&lpMPSWabFileInfo->lpszMPSWabFileName);
|
|
|
|
LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabIndexStr);
|
|
|
|
LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabIndexEID);
|
|
|
|
//Close our handle on this mutex
|
|
CloseHandle(lpMPSWabFileInfo->hDataAccessMutex);
|
|
|
|
LocalFreeAndNull(&lpMPSWabFileInfo);
|
|
|
|
|
|
hr = S_OK;
|
|
|
|
|
|
out:
|
|
|
|
DebugTrace(( TEXT("ClosePropertyStore: Exit\n-----------\n")));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetContainerObjectType
|
|
//
|
|
// In this IE5 WAB, we are saving RECORD_CONTAINER type objects to the WAB store
|
|
// However, the previous IE4- wabs dont understand this object and will barf and
|
|
// fail. For purposes of backward compatibility, we need to make sure that they
|
|
// dont fail - to do this, we mark the object-type of record container objects
|
|
// from MAPI_ABCONT to MAPI_MAILUSER - that way a IE4- wab will treat the folder
|
|
// as a spurious mail user but wont exactly crash .. we'll let the RecordHeader.ulObjType
|
|
// remain as a RECORD_CONTAINER so we can still do quick searches for it
|
|
// When reading the object, we will reset the object type in IE5(this) WAB
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
void SetContainerObjectType(ULONG ulcProps, LPSPropValue lpProps, BOOL bSetToMailUser)
|
|
{
|
|
ULONG i = 0;
|
|
for(i=0;i<ulcProps;i++)
|
|
{
|
|
if(lpProps[i].ulPropTag == PR_OBJECT_TYPE)
|
|
{
|
|
lpProps[i].Value.l = bSetToMailUser ? MAPI_MAILUSER : MAPI_ABCONT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// WriteRecord
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
// IN pmbinFold - <Outlook> EntryID of folder to search in (NULL for default)
|
|
// IN lppsbEID - EntryId of record to write.
|
|
// *lppsbEID should be null to create and return new entryID
|
|
// IN ulRecordType - RECORD_CONTACT, RECORD_DISTLIST, RECORD_CONTAINER
|
|
// IN ulcPropCount - number of props in prop array
|
|
// IN lpPropArray - Array of LPSPropValues
|
|
// IN ulFlags - reserved - 0
|
|
//
|
|
// Two cases -
|
|
// writing a new record or
|
|
// modifying/editing an old record
|
|
//
|
|
// In the first case we create all the proper header structures and
|
|
// tack them onto the end of the file, updating the indexes and the
|
|
// file header structure.
|
|
//
|
|
// In the second case, when record is edited, it could become smaller or larger
|
|
// To avoid too much complication, we invalidate the old record header in the file and
|
|
// write the edited record to a new location. The accesscount in the file header
|
|
// is updated so that after too many edits we can re-write the file to a cleaner
|
|
// file. The original entryid is retained and the offset/data updated in the indexes.
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT WriteRecord(IN HANDLE hPropertyStore,
|
|
IN LPSBinary pmbinFold,
|
|
IN LPSBinary * lppsbEID,
|
|
IN ULONG ulFlags,
|
|
IN ULONG ulRecordType,
|
|
IN ULONG ulcPropCount,
|
|
IN LPPROPERTY_ARRAY lpPropArray)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPULONG lpPropTagArray = NULL;
|
|
TCHAR * lpRecordData = NULL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
DWORD dwNumofBytes = 0;
|
|
ULONG ulRecordDataSize = 0;
|
|
BOOL bIsNewRecord = TRUE;
|
|
ULONG nIndexPos;
|
|
ULONG i=0,j=0,k=0;
|
|
BOOL bFileLocked = FALSE;
|
|
DWORD dwTempEID = 0;
|
|
SBinary sbEIDSave = {0};
|
|
BOOL bEIDSave = FALSE;
|
|
ULONG iEIDSave; // index of EID property in lpPropArray
|
|
ULONG ulcOldPropCount = 0;
|
|
LPSPropValue lpOldPropArray = NULL;
|
|
TCHAR lpszOldIndex[indexMax][MAX_INDEX_STRING];
|
|
DWORD dwEntryID = 0;
|
|
SBinary sbEID = {0};
|
|
LPSBinary lpsbEID = NULL;
|
|
|
|
ULONG ulRecordHeaderOffset = 0;
|
|
ULONG ulRecordPropTagOffset = 0;
|
|
ULONG ulRecordDataOffset = 0;
|
|
|
|
BOOL bPropSet[indexMax];
|
|
DWORD dwErr = 0;
|
|
|
|
ULONG nLen = 0;
|
|
|
|
LPBYTE lp = NULL;
|
|
|
|
//
|
|
// These structures temporarily hold the new entry info for us
|
|
//
|
|
MPSWab_INDEX_ENTRY_DATA_STRING MPSWabIndexEntryDataString[indexMax];
|
|
MPSWab_INDEX_ENTRY_DATA_ENTRYID MPSWabIndexEntryDataEntryID;
|
|
MPSWab_RECORD_HEADER MPSWabRecordHeader = {0};
|
|
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo;
|
|
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
#ifdef DEBUG
|
|
// _DebugProperties(lpPropArray, ulcPropCount, TEXT("WriteRecord Properties"));
|
|
#endif
|
|
|
|
if(pt_bIsWABOpenExSession)
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
if(!hPropertyStore)
|
|
return MAPI_E_NOT_INITIALIZED;
|
|
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
ULONG cb = 0;
|
|
LPSPropValue lpNewPropArray = NULL;
|
|
SCODE sc = 0;
|
|
|
|
if(!pt_bIsUnicodeOutlook)
|
|
{
|
|
// Need to convert these props back to ANSI for Outlook
|
|
// Since we don't know whether these props are localalloced or MapiAlloced,
|
|
// we can't convert them without leaking memory.
|
|
// Therefore, we need to create a copy of the props before we can save them ..
|
|
// what a waste of effort ..
|
|
// Allocate more for our return buffer
|
|
|
|
if (FAILED(sc = ScCountProps(ulcPropCount, lpPropArray, &cb)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(sc = MAPIAllocateBuffer(cb, &lpNewPropArray)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(sc = ScCopyProps(ulcPropCount, lpPropArray, lpNewPropArray, NULL)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
// Now we thunk the data back to ANSI for Outlook
|
|
if (FAILED(sc = ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), lpNewPropArray, ulcPropCount, 0)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
hr = lpWSP->lpVtbl->WriteRecord(lpWSP,
|
|
pmbinFold,
|
|
lppsbEID,
|
|
ulFlags,
|
|
ulRecordType,
|
|
ulcPropCount,
|
|
lpNewPropArray ? lpNewPropArray : lpPropArray);
|
|
|
|
DebugTrace(TEXT("WABStorageProvider::WriteRecord returned:%x\n"),hr);
|
|
exit:
|
|
FreeBufferAndNull(&lpNewPropArray);
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo = hPropertyStore;
|
|
|
|
if ( (NULL == lpMPSWabFileInfo) ||
|
|
(NULL == lpPropArray) ||
|
|
(0 == ulcPropCount) )
|
|
{
|
|
DebugTrace(TEXT("Invalid Parameter!!\n"));
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
if ((ulRecordType != RECORD_CONTACT) &&
|
|
(ulRecordType != RECORD_DISTLIST) &&
|
|
(ulRecordType != RECORD_CONTAINER))
|
|
goto out;
|
|
|
|
if(lppsbEID)
|
|
{
|
|
lpsbEID = *lppsbEID;
|
|
if(lpsbEID && lpsbEID->cb != SIZEOF_WAB_ENTRYID)
|
|
{
|
|
// this may be a WAB container .. reset the entryid to a WAB entryid
|
|
if(WAB_CONTAINER == IsWABEntryID(lpsbEID->cb, (LPENTRYID)lpsbEID->lpb,
|
|
NULL,NULL,NULL,NULL,NULL))
|
|
{
|
|
IsWABEntryID(lpsbEID->cb, (LPENTRYID)lpsbEID->lpb,
|
|
(LPVOID*)&sbEID.lpb,(LPVOID*)&sbEID.cb,NULL,NULL,NULL);
|
|
if(sbEID.cb == SIZEOF_WAB_ENTRYID)
|
|
lpsbEID = &sbEID;
|
|
}
|
|
}
|
|
}
|
|
if(!lppsbEID || (lpsbEID && lpsbEID->cb != SIZEOF_WAB_ENTRYID))
|
|
{
|
|
DebugTrace(TEXT("Invalid Parameter!!\n"));
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
|
|
if(lpsbEID && lpsbEID->cb && lpsbEID->lpb)
|
|
CopyMemory(&dwEntryID, lpsbEID->lpb, min(lpsbEID->cb, sizeof(dwEntryID)));
|
|
|
|
DebugTrace(TEXT("--WriteRecord: dwEntryID=%d\n"), dwEntryID);
|
|
|
|
if (lpMPSWabFileInfo->bReadOnlyAccess)
|
|
{
|
|
DebugTrace(TEXT("Access Permissions are Read-Only\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
|
|
if(ulRecordType == RECORD_CONTAINER)
|
|
SetContainerObjectType(ulcPropCount, lpPropArray, TRUE);
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
//
|
|
// Check that we have enough disk space before trying any disk writing operations
|
|
//
|
|
if(!WABHasFreeDiskSpace(lpMPSWabFileInfo->lpszMPSWabFileName, hMPSWabFile))
|
|
{
|
|
hr = MAPI_E_NOT_ENOUGH_DISK;
|
|
goto out;
|
|
}
|
|
|
|
hr = E_FAIL; //reset hr
|
|
|
|
//
|
|
// To ensure that file info is accurate,
|
|
// Any time we open a file, read the file info again ...
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Anytime we detect an error - try to fix it ...
|
|
//
|
|
if((lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_ERROR_DETECTED) ||
|
|
(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_WRITE_IN_PROGRESS))
|
|
{
|
|
if(HR_FAILED(HrDoQuickWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile)))
|
|
{
|
|
hr = HrDoDetailedWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("HrDoDetailedWABIntegrityCheck failed:%x\n"),hr);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = E_FAIL; //reset hr
|
|
|
|
|
|
//
|
|
// If this is an old record, we want to get its old propertys so we can compare the
|
|
// indexes to see if any of their values changed ... if the valuse changed then we
|
|
// have to update the indexes for the old record too ..
|
|
//
|
|
if (dwEntryID != 0)
|
|
{
|
|
//get pointers to old displayname, firstname, lastname
|
|
for(j=indexDisplayName;j<indexMax;j++)
|
|
{
|
|
lpszOldIndex[j][0]='\0';
|
|
if (!LoadIndex( IN lpMPSWabFileInfo,
|
|
IN j,
|
|
IN hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Error Loading Index!\n"));
|
|
goto out;
|
|
}
|
|
for(i=0;i<lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries;i++)
|
|
{
|
|
if(lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID == dwEntryID)
|
|
{
|
|
// an old index exists for this entry
|
|
// get its value
|
|
StrCpyN(lpszOldIndex[j],lpMPSWabFileInfo->lpMPSWabIndexStr[i].szIndex,ARRAYSIZE(lpszOldIndex[j]));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tag this file as undergoing a write operation
|
|
if(!bTagWriteTransaction( lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Taggin file write failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Irrespective of whether this is a new record or an old record, the
|
|
// data is going to the end of the file ... Get this new file position
|
|
//
|
|
ulRecordHeaderOffset = GetFileSize(hMPSWabFile, NULL);
|
|
|
|
if (dwEntryID != 0)
|
|
{
|
|
//
|
|
// we are not creating a new thing
|
|
// so we should first find the old header
|
|
// if the old entry doesnt exist then we
|
|
// should treat this as a new record and
|
|
// replace the entry id with a properly generated
|
|
// entryid
|
|
// if we find the existing record then we need to mark that as
|
|
// being defunct
|
|
//
|
|
|
|
//
|
|
// Search for given EntryID
|
|
// If not found, assign a new one
|
|
//
|
|
if (BinSearchEID( IN lpMPSWabFileInfo->lpMPSWabIndexEID,
|
|
IN dwEntryID,
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries,
|
|
OUT &nIndexPos))
|
|
{
|
|
//
|
|
// this entryid exists in the index - we will need to invalidate this record
|
|
//
|
|
bIsNewRecord = FALSE;
|
|
|
|
if(!ReadDataFromWABFile(hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos].ulOffset,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
(DWORD) sizeof(MPSWab_RECORD_HEADER)))
|
|
goto out;
|
|
|
|
|
|
//
|
|
// Set valid flag to false
|
|
//
|
|
MPSWabRecordHeader.bValidRecord = FALSE;
|
|
|
|
//
|
|
// Write it back
|
|
// Set File Pointer to this record
|
|
//
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos].ulOffset,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
sizeof(MPSWab_RECORD_HEADER)))
|
|
goto out;
|
|
|
|
//
|
|
// update the EntryID index so that it now points to the new offset
|
|
// instead of the old one
|
|
//
|
|
lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos].ulOffset = ulRecordHeaderOffset;
|
|
|
|
//
|
|
// Increment this count so we know that we invalidated one more record ...
|
|
//
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulModificationCount++;
|
|
}
|
|
else
|
|
{
|
|
bIsNewRecord = TRUE; //This tags whether or not to create a new Index entry
|
|
|
|
//
|
|
// assign a new entryid
|
|
//
|
|
dwEntryID = lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// we are creating a new thing
|
|
//
|
|
bIsNewRecord = TRUE;
|
|
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID++;
|
|
dwEntryID = lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID;
|
|
}
|
|
|
|
//
|
|
// Set the flag so we know when to backup
|
|
//
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags |= WAB_BACKUP_NOW;
|
|
|
|
|
|
//
|
|
// if bIsNewRecord, then the PR_ENTRYID field of the record, as passed
|
|
// into this function, is 0 and we want to change it to the new Entry ID
|
|
// prior to saving so that we can include the new EntryID in the record on file
|
|
// Hence we scan the records and update PR_ENTRYID
|
|
//
|
|
if (bIsNewRecord)
|
|
{
|
|
for(i=0;i < ulcPropCount; i++)
|
|
{
|
|
if (lpPropArray[i].ulPropTag == PR_ENTRYID)
|
|
{
|
|
// Save the value of the property for restoration later
|
|
sbEIDSave = lpPropArray[i].Value.bin;
|
|
iEIDSave = i;
|
|
bEIDSave = TRUE;
|
|
|
|
// Assert(! lpPropArray[i].Value.bin.cb);
|
|
if (! lpPropArray[i].Value.bin.cb) {
|
|
// No EntryID pointer... point to a temporary one.
|
|
lpPropArray[i].Value.bin.lpb = (LPVOID)&dwTempEID;
|
|
}
|
|
CopyMemory(lpPropArray[i].Value.bin.lpb,&dwEntryID,SIZEOF_WAB_ENTRYID);
|
|
lpPropArray[i].Value.bin.cb = SIZEOF_WAB_ENTRYID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Now we create a new Record Header structure to write to the file
|
|
//
|
|
MPSWabRecordHeader.bValidRecord = TRUE;
|
|
MPSWabRecordHeader.ulObjType = ulRecordType;
|
|
MPSWabRecordHeader.dwEntryID = dwEntryID;
|
|
MPSWabRecordHeader.ulcPropCount = ulcPropCount;
|
|
|
|
|
|
//
|
|
// write this empty record header to file so we can allocate file space now
|
|
// before filling in all the data
|
|
//
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
ulRecordHeaderOffset,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
sizeof(MPSWabRecordHeader)))
|
|
goto out;
|
|
|
|
|
|
//
|
|
// Now the File Pointer points to the end of the header which is the
|
|
// beginning of the PropTagArray
|
|
// ulRecordPropTagOffset is a relative offset from the start of the Record Header
|
|
//
|
|
ulRecordPropTagOffset = sizeof(MPSWab_RECORD_HEADER);
|
|
|
|
|
|
MPSWabRecordHeader.ulPropTagArraySize = sizeof(ULONG) * ulcPropCount;
|
|
|
|
//
|
|
// Allocate space for the prop tag array
|
|
//
|
|
lpPropTagArray = LocalAlloc(LMEM_ZEROINIT, MPSWabRecordHeader.ulPropTagArraySize);
|
|
|
|
if (!lpPropTagArray)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Fill in this array
|
|
//
|
|
for(i=0;i < ulcPropCount; i++)
|
|
{
|
|
lpPropTagArray[i] = lpPropArray[i].ulPropTag;
|
|
}
|
|
|
|
//
|
|
// write it
|
|
//
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPCVOID) lpPropTagArray,
|
|
(DWORD) MPSWabRecordHeader.ulPropTagArraySize ,
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing RecordPropArray failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
ulRecordDataOffset = sizeof(ULONG) * ulcPropCount;
|
|
|
|
|
|
if(HR_FAILED(hr = HrGetBufferFromPropArray(ulcPropCount,
|
|
lpPropArray,
|
|
&ulRecordDataSize,
|
|
&lp)))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
MPSWabRecordHeader.ulPropTagArrayOffset = ulRecordPropTagOffset;
|
|
MPSWabRecordHeader.ulRecordDataOffset = ulRecordDataOffset;
|
|
MPSWabRecordHeader.ulRecordDataSize = ulRecordDataSize;
|
|
|
|
//
|
|
// update the record header
|
|
// Write in the record header
|
|
// Set the filepointer to the RecordOffset
|
|
//
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
ulRecordHeaderOffset,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
sizeof(MPSWab_RECORD_HEADER)))
|
|
goto out;
|
|
|
|
//
|
|
// Write a data block
|
|
// Now we can write this block of data into the file
|
|
//
|
|
if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
|
|
ulRecordDataOffset,
|
|
NULL,
|
|
FILE_CURRENT))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Now write the RecordData
|
|
//
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPCVOID) lp,
|
|
(DWORD) ulRecordDataSize,
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing RecordHeader failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
LocalFreeAndNull(&lp);
|
|
|
|
|
|
|
|
//
|
|
// Update the indexes and write to file
|
|
// If this is a new record, we need to create and store new index
|
|
// entries in their proper place in the property store file
|
|
//
|
|
//// If this is not a new entry then we need to compare the index values to see if they
|
|
//// might have changed
|
|
//
|
|
// EntryID index in the file. Since we have already updated the actual
|
|
// offset in the Index in memory, all we really need to do is to
|
|
// store the index back into file. The string indexes are unchanged
|
|
// in this operation.
|
|
//
|
|
|
|
|
|
|
|
//
|
|
// Create the new index entries (only for new records)
|
|
//
|
|
|
|
MPSWabIndexEntryDataEntryID.dwEntryID = dwEntryID;
|
|
MPSWabIndexEntryDataEntryID.ulOffset = ulRecordHeaderOffset;
|
|
|
|
for(j=indexDisplayName;j<indexMax;j++)
|
|
{
|
|
MPSWabIndexEntryDataString[j].dwEntryID = dwEntryID;
|
|
MPSWabIndexEntryDataString[j].szIndex[0] = '\0';
|
|
bPropSet[j] = FALSE;
|
|
|
|
for(i=0;i<ulcPropCount;i++)
|
|
{
|
|
if(lpPropArray[i].ulPropTag == rgIndexArray[j])
|
|
{
|
|
bPropSet[j] = TRUE;
|
|
nLen = TruncatePos(lpPropArray[i].Value.LPSZ, MAX_INDEX_STRING-1);
|
|
CopyMemory(MPSWabIndexEntryDataString[j].szIndex,lpPropArray[i].Value.LPSZ,sizeof(TCHAR)*nLen);
|
|
MPSWabIndexEntryDataString[j].szIndex[nLen]='\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (bIsNewRecord)
|
|
{
|
|
|
|
DebugTrace(TEXT("Creating New Record: EntryID %d\n"), dwEntryID);
|
|
|
|
|
|
// Now write these indexes into file ...
|
|
|
|
// the indices in the file are already sorted so to add the new entry
|
|
// we will do the following:
|
|
//
|
|
// 1. Find out where in the index the entry would fit in
|
|
// 2. Write the entry into that position in the file
|
|
// 3. write the rest of the index from that point on into the file
|
|
// 4. reload the index
|
|
|
|
// do a bin search to find a match for the current index
|
|
// binsearch returns the matching position on match or it
|
|
// returns the position at which the match would exist were
|
|
// the match in the array. Thus whether
|
|
// there is a match or not we can assume ulPosition containts
|
|
// the index of the item at which the new entry should be entered
|
|
//
|
|
|
|
|
|
//
|
|
// do string indexes
|
|
//
|
|
for(j=indexDisplayName;j<indexMax;j++) //assumes a specific order defined in mpswab.h
|
|
{
|
|
|
|
if(!bPropSet[j])
|
|
continue;
|
|
|
|
//
|
|
// Get the index
|
|
//
|
|
if (!LoadIndex( IN lpMPSWabFileInfo,
|
|
IN j,
|
|
IN hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Error Loading Index!\n"));
|
|
goto out;
|
|
}
|
|
|
|
DebugTrace( TEXT("Index: %d Entry: %s\n"),j,MPSWabIndexEntryDataString[j].szIndex);
|
|
|
|
BinSearchStr( IN lpMPSWabFileInfo->lpMPSWabIndexStr,
|
|
IN MPSWabIndexEntryDataString[j].szIndex,
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries,
|
|
OUT &nIndexPos);
|
|
// nIndexPos will contain the position at which we can insert this entry into the file
|
|
|
|
//Set the filepointer to point to the above found point
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulOffset + (nIndexPos) * sizeof(MPSWab_INDEX_ENTRY_DATA_STRING),
|
|
(LPVOID) &MPSWabIndexEntryDataString[j],
|
|
sizeof(MPSWab_INDEX_ENTRY_DATA_STRING)))
|
|
goto out;
|
|
|
|
|
|
if (lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries != nIndexPos) //if not the last entry
|
|
{
|
|
//write the remaining entries in the array back to file
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPCVOID) &lpMPSWabFileInfo->lpMPSWabIndexStr[nIndexPos],
|
|
(DWORD) sizeof(MPSWab_INDEX_ENTRY_DATA_STRING)*(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries - nIndexPos),
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing Index[%d] failed.\n"), j);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries++;
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].UtilizedBlockSize += sizeof(MPSWab_INDEX_ENTRY_DATA_STRING);
|
|
|
|
|
|
}
|
|
|
|
//Do the same for the EntryID index also
|
|
BinSearchEID( IN lpMPSWabFileInfo->lpMPSWabIndexEID,
|
|
IN MPSWabIndexEntryDataEntryID.dwEntryID,
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries,
|
|
OUT &nIndexPos);
|
|
// nIndexPos will contain the position at which we can insert this entry into the file
|
|
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulOffset + (nIndexPos) * sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID),
|
|
(LPVOID) &MPSWabIndexEntryDataEntryID,
|
|
sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID)))
|
|
goto out;
|
|
|
|
|
|
if (lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries != nIndexPos) //if not the last entry
|
|
{
|
|
//write the remaining entries in the array back to file
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPCVOID) &lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos],
|
|
(DWORD) sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID)*(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries - nIndexPos),
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing Index[%d] failed.\n"), j);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries++;
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].UtilizedBlockSize += sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID);
|
|
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries++;
|
|
}
|
|
else
|
|
{
|
|
DebugTrace(TEXT("Modifying Existing Record: EntryID %d\n"), dwEntryID);
|
|
|
|
// We have to compare the old props with the new props to see if we need to change any of the string
|
|
// indexes ...
|
|
for(j=indexDisplayName;j<indexMax;j++)
|
|
{
|
|
|
|
BOOL bUpdateStringIndex = FALSE;
|
|
BOOL bRemoveOldStringIndex = FALSE;
|
|
BOOL bAddNewStringIndex = FALSE;
|
|
|
|
DebugTrace(TEXT("Index: %d Entry: %s\n"),j,MPSWabIndexEntryDataString[j].szIndex);
|
|
|
|
if (lstrlen(MPSWabIndexEntryDataString[j].szIndex))
|
|
bAddNewStringIndex = TRUE;
|
|
|
|
if (lstrlen(lpszOldIndex[j]))
|
|
bRemoveOldStringIndex = TRUE;
|
|
|
|
// if there is no old index and there is a new index
|
|
// or there is an old index and there is a new index but they are different
|
|
// or there is an old index but no new index then
|
|
if( (!bRemoveOldStringIndex && bAddNewStringIndex)
|
|
|| (bRemoveOldStringIndex && bAddNewStringIndex && (lstrcmpi(lpszOldIndex[j],MPSWabIndexEntryDataString[j].szIndex)!=0))
|
|
|| (bRemoveOldStringIndex && !bAddNewStringIndex) )
|
|
{
|
|
bUpdateStringIndex = TRUE;
|
|
}
|
|
|
|
if(!bUpdateStringIndex)
|
|
continue;
|
|
|
|
|
|
if (bRemoveOldStringIndex)
|
|
{
|
|
ULONG nIndex =0;
|
|
int nStartPos=0,nEndPos=0;
|
|
LPTSTR lpsz = lpszOldIndex[j];
|
|
ULONG nTotal = 0;
|
|
|
|
if (!LoadIndex( IN lpMPSWabFileInfo,
|
|
IN j,
|
|
IN hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Error Loading Index!\n"));
|
|
goto out;
|
|
}
|
|
|
|
nTotal = lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries;
|
|
|
|
// Find the position of the old string index
|
|
// There is one problem where there are multiple entries with the same name
|
|
// BinSearch can potentially hand us back the wrong entry if we just
|
|
// search by name alone .. we need to look at both names and
|
|
// entry ids before accepting a position as the correct one
|
|
//
|
|
BinSearchStr( IN lpMPSWabFileInfo->lpMPSWabIndexStr,
|
|
IN lpszOldIndex[j],
|
|
IN nTotal,
|
|
OUT &nIndexPos);
|
|
|
|
// nIndexPos contains the position of a particular entry matching the old index
|
|
// This may not necessarily be the correct entry if there are multiple identical
|
|
// display name entries ... Hence we look in out sorted Index array for the start
|
|
// of such names and the end of such names and then look at the entry ids
|
|
// of all such entries to get the right one
|
|
if(nTotal > 0)
|
|
{
|
|
nStartPos = (int) nIndexPos;
|
|
nEndPos = (int) nIndexPos;
|
|
|
|
while((nStartPos>=0) && !lstrcmpi(lpsz,lpMPSWabFileInfo->lpMPSWabIndexStr[nStartPos].szIndex))
|
|
nStartPos--;
|
|
|
|
nStartPos++;
|
|
|
|
while((nEndPos<(int)nTotal) && !lstrcmpi(lpsz,lpMPSWabFileInfo->lpMPSWabIndexStr[nEndPos].szIndex))
|
|
nEndPos++;
|
|
|
|
nEndPos--;
|
|
|
|
if (nStartPos != nEndPos)
|
|
{
|
|
// there is more than one ...
|
|
for(nIndex=(ULONG)nStartPos;nIndex<=(ULONG)nEndPos;nIndex++)
|
|
{
|
|
if (lpMPSWabFileInfo->lpMPSWabIndexStr[nIndex].dwEntryID == dwEntryID)
|
|
{
|
|
nIndexPos = nIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
// At this point nIndexPos will contain the correctposition of this entry
|
|
|
|
//Set the filepointer to point to the above found point
|
|
if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulOffset + (nIndexPos) * sizeof(MPSWab_INDEX_ENTRY_DATA_STRING),
|
|
NULL,
|
|
FILE_BEGIN))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
//remove the entry by overwriting it ...
|
|
if (lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries != nIndexPos) //if not the last entry
|
|
{
|
|
//write the remaining entries in the array back to file
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPCVOID) &lpMPSWabFileInfo->lpMPSWabIndexStr[nIndexPos+1],
|
|
(DWORD) sizeof(MPSWab_INDEX_ENTRY_DATA_STRING)*(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries - nIndexPos-1),
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing Index[%d] failed.\n"), j);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries>0)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries--;
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].UtilizedBlockSize>0)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].UtilizedBlockSize -= sizeof(MPSWab_INDEX_ENTRY_DATA_STRING);
|
|
|
|
}
|
|
|
|
if (bAddNewStringIndex)
|
|
{
|
|
//Now find where the new entry would go ..
|
|
//
|
|
// Get the index
|
|
//
|
|
if (!LoadIndex( IN lpMPSWabFileInfo,
|
|
IN j,
|
|
IN hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Error Loading Index!\n"));
|
|
goto out;
|
|
}
|
|
|
|
BinSearchStr( IN lpMPSWabFileInfo->lpMPSWabIndexStr,
|
|
IN MPSWabIndexEntryDataString[j].szIndex,
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries,
|
|
OUT &nIndexPos);
|
|
// nIndexPos will contain the position at which we can insert this entry into the file
|
|
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulOffset + (nIndexPos) * sizeof(MPSWab_INDEX_ENTRY_DATA_STRING),
|
|
(LPVOID) &MPSWabIndexEntryDataString[j],
|
|
sizeof(MPSWab_INDEX_ENTRY_DATA_STRING)))
|
|
goto out;
|
|
|
|
|
|
if (lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries != nIndexPos) //if not the last entry
|
|
{
|
|
//write the remaining entries in the array back to file
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPCVOID) &lpMPSWabFileInfo->lpMPSWabIndexStr[nIndexPos],
|
|
(DWORD) sizeof(MPSWab_INDEX_ENTRY_DATA_STRING)*(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries - nIndexPos),
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing Index[%d] failed.\n"), j);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries++;
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].UtilizedBlockSize += sizeof(MPSWab_INDEX_ENTRY_DATA_STRING);
|
|
|
|
}
|
|
}
|
|
|
|
// Not a new item index-entry but just a modification of an old one
|
|
// in this case we just need to save the EntryID index back to file
|
|
if (!BinSearchEID( IN lpMPSWabFileInfo->lpMPSWabIndexEID,
|
|
IN dwEntryID,
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries,
|
|
OUT &nIndexPos))
|
|
{
|
|
DebugTrace(TEXT("EntryID not found\n")); //No way should this ever happen
|
|
hr = MAPI_E_INVALID_ENTRYID;
|
|
goto out;
|
|
}
|
|
|
|
//Set the filepointer to point to the start of the entryid index
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulOffset + (nIndexPos) * sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID),
|
|
(LPVOID) &lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos],
|
|
sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID)))
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
// update the file header
|
|
if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
|
|
0,
|
|
NULL,
|
|
FILE_BEGIN))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
DebugTrace(TEXT("ulcNum: %d\t"),lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries);
|
|
for(i=indexDisplayName;i<indexMax-2;i++)
|
|
DebugTrace(TEXT("index %d: %d\t"),i, lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[i].ulcNumEntries);
|
|
DebugTrace(TEXT("\n"));
|
|
#endif
|
|
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries != lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags |= WAB_ERROR_DETECTED;
|
|
|
|
for(i=indexDisplayName+1;i<indexMax;i++)
|
|
{
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[i].ulcNumEntries > lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags |= WAB_ERROR_DETECTED;
|
|
}
|
|
|
|
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPCVOID) lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
(DWORD) sizeof(MPSWab_FILE_HEADER),
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing FileHeader failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
if ((lpMPSWabFileInfo->lpMPSWabFileHeader->ulcMaxNumEntries - lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries) < 10)
|
|
{
|
|
//
|
|
// if we are within 10 entries of exhausting the allocated space for the property
|
|
// store, its time to grow the store.
|
|
//
|
|
if (!CompressFile( lpMPSWabFileInfo,
|
|
hMPSWabFile,
|
|
NULL,
|
|
TRUE,
|
|
AB_GROW_INDEX))
|
|
{
|
|
DebugTrace(TEXT("Growing the file failed\n"));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Notify other processes and our UI
|
|
{
|
|
NOTIFICATION Notification;
|
|
|
|
Notification.ulEventType = bIsNewRecord ? fnevObjectCreated : fnevObjectModified;
|
|
Notification.info.obj.cbEntryID = SIZEOF_WAB_ENTRYID;
|
|
Notification.info.obj.lpEntryID = (LPENTRYID)&dwEntryID;
|
|
switch (ulRecordType) {
|
|
case RECORD_CONTACT:
|
|
Notification.info.obj.ulObjType = MAPI_MAILUSER;
|
|
break;
|
|
case RECORD_DISTLIST:
|
|
Notification.info.obj.ulObjType = MAPI_DISTLIST;
|
|
break;
|
|
case RECORD_CONTAINER:
|
|
Notification.info.obj.ulObjType = MAPI_ABCONT;
|
|
break;
|
|
default:
|
|
Assert(FALSE);
|
|
break;
|
|
}
|
|
Notification.info.obj.cbParentID = 0;
|
|
Notification.info.obj.lpParentID = NULL;
|
|
Notification.info.obj.cbOldID = 0;
|
|
Notification.info.obj.lpOldID = NULL;
|
|
Notification.info.obj.cbOldParentID = 0;
|
|
Notification.info.obj.lpOldParentID = NULL;
|
|
Notification.info.obj.lpPropTagArray = (LPSPropTagArray)lpPropArray;
|
|
|
|
HrFireNotification(&Notification);
|
|
}
|
|
*/
|
|
//
|
|
// if we're still here it was all fun and games ...
|
|
//
|
|
|
|
if(!*lppsbEID) // if there was a null LPSBinary entryid provided, return one
|
|
{
|
|
LPSBinary lpsb = LocalAlloc(LMEM_ZEROINIT, sizeof(SBinary));
|
|
if(!lpsb)
|
|
{
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
lpsb->cb = SIZEOF_WAB_ENTRYID;
|
|
lpsb->lpb = LocalAlloc(LMEM_ZEROINIT, SIZEOF_WAB_ENTRYID);
|
|
if(!lpsb->lpb)
|
|
{
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(lpsb->lpb, &dwEntryID, lpsb->cb);
|
|
*lppsbEID = lpsb;
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
|
|
out:
|
|
// UnTag this file as undergoing a write operation
|
|
// We only want the flag to stay there during crashes not during
|
|
// normal operations
|
|
//
|
|
if(lpMPSWabFileInfo)
|
|
{
|
|
if(!bUntagWriteTransaction( lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Untaggin file write failed\n"));
|
|
}
|
|
}
|
|
|
|
if (bEIDSave) {
|
|
// Restore the original EID property in the input property array
|
|
lpPropArray[iEIDSave].Value.bin = sbEIDSave;
|
|
}
|
|
|
|
LocalFreeAndNull(&lpPropTagArray);
|
|
|
|
LocalFreePropArray(hPropertyStore, ulcOldPropCount, &lpOldPropArray);
|
|
|
|
if (hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
// Some special case error codes for generic fails
|
|
if(HR_FAILED(hr) && hr == E_FAIL)
|
|
{
|
|
dwErr = GetLastError();
|
|
switch(dwErr)
|
|
{
|
|
case ERROR_DISK_FULL:
|
|
hr = MAPI_E_NOT_ENOUGH_DISK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// in case we changed the object type here, reset it
|
|
if(ulRecordType == RECORD_CONTAINER)
|
|
SetContainerObjectType(ulcPropCount, lpPropArray, FALSE);
|
|
|
|
DebugTrace(( TEXT("WriteRecord: Exit\n-----------\n")));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
/*
|
|
-
|
|
- HrDupePropResWCtoA
|
|
*
|
|
* Dupes the PropRes passed into FindRecords and ReadPropArray
|
|
* and converts it from WC to A in the process so we can feed
|
|
* it to outlook
|
|
*
|
|
* Note the *lppPropResA->lpProp needs to be freed seperately from *lppPropResA
|
|
*/
|
|
HRESULT HrDupePropResWCtoA(ULONG ulFlags, LPSPropertyRestriction lpPropRes,LPSPropertyRestriction * lppPropResA)
|
|
{
|
|
SCODE sc = 0;
|
|
HRESULT hr = S_OK;
|
|
LPSPropValue lpNewPropArray = NULL;
|
|
|
|
LPSPropertyRestriction lpPropResA = NULL;
|
|
ULONG cb = 0;
|
|
|
|
if(!(ulFlags & AB_MATCH_PROP_ONLY)) // means Restriction has some data part
|
|
{
|
|
if (FAILED(sc = ScCountProps(1, lpPropRes->lpProp, &cb)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(sc = MAPIAllocateBuffer(cb, &lpNewPropArray)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(sc = ScCopyProps(1, lpPropRes->lpProp, lpNewPropArray, NULL)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
// Now we thunk the data back to ANSI for Outlook
|
|
if (FAILED(sc = ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), lpNewPropArray, 1, 0)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(sc = MAPIAllocateBuffer(sizeof(SPropValue), &lpNewPropArray)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
ZeroMemory(lpNewPropArray, sizeof(SPropValue));
|
|
if(PROP_TYPE(lpPropRes->ulPropTag)==PT_UNICODE)
|
|
lpNewPropArray->ulPropTag = CHANGE_PROP_TYPE(lpPropRes->ulPropTag, PT_STRING8);
|
|
else if(PROP_TYPE(lpPropRes->ulPropTag)==PT_MV_UNICODE)
|
|
lpNewPropArray->ulPropTag = CHANGE_PROP_TYPE(lpPropRes->ulPropTag, PT_MV_STRING8);
|
|
else
|
|
lpNewPropArray->ulPropTag = lpPropRes->ulPropTag;
|
|
}
|
|
|
|
if (FAILED(sc = MAPIAllocateBuffer(sizeof(SPropertyRestriction), &lpPropResA)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
lpPropResA->relop = lpPropRes->relop;
|
|
lpPropResA->ulPropTag = lpNewPropArray->ulPropTag;
|
|
lpPropResA->lpProp = lpNewPropArray;
|
|
*lppPropResA = lpPropResA;
|
|
|
|
exit:
|
|
if(HR_FAILED(hr))
|
|
{
|
|
FreeBufferAndNull(&lpPropResA);
|
|
FreeBufferAndNull(&lpNewPropArray);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FindRecords
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
// IN pmbinFold - <Outlook> EntryID of folder to search in (NULL for default)
|
|
// IN ulFlags - AB_MATCH_PROP_ONLY - checks for existence of a certain prop only
|
|
// Does not check/compare the value of the Prop
|
|
// Used for unindexed properties only. Works only
|
|
// with RELOP_EQ and RELOP_NE
|
|
// e.g. caller says - give me a list of all entryids who have
|
|
// an email address - in this case we dont care what the
|
|
// email address is. Or he could say, give me a list of
|
|
// all entries who dont have URLs
|
|
//
|
|
// AB_IGNORE_OUTLOOK - works against WAB file even if OLK is running
|
|
//
|
|
// IN lpPropRes - pointer to SPropRes structure
|
|
// IN bLockFile - This function is also called internally in cases where we dont
|
|
// want to lock the file - In such cases we set the value to False. For
|
|
// external callers (outside MPSWAB.c) this value must always be TRUE
|
|
// IN OUT lpulcEIDCount - Count of how many to get and how many actually returned
|
|
// if Zero is specified, we have to get all matches.
|
|
//
|
|
//
|
|
// OUT rgsbEntryIDs - array of SBinary structures containing matching entryids
|
|
//
|
|
// lpPropRes will specify one of the following operators
|
|
// RELOP_GE (>=) RELOP_GT (>) RELOP_LE (<=)
|
|
// RELOP_LT (<) RELOP_NE (!=) RELOP_EQ (==)
|
|
//
|
|
// Implicit in this function is the fact that it should not be called for
|
|
// finding EntryIDs based on a given entryid value i.e. lpPropRes cannot
|
|
// contain an EntryID value, reason being that entryids aer unique and it doesnt
|
|
// make sense to find entryids. Hence if an entryid is specified, this
|
|
// function will just return the specified entryid back ...
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT FindRecords(IN HANDLE hPropertyStore,
|
|
IN LPSBinary pmbinFold,
|
|
IN ULONG ulFlags,
|
|
IN BOOL bLockFile,
|
|
IN LPSPropertyRestriction lpPropRes,
|
|
IN OUT LPULONG lpulcEIDCount,
|
|
OUT LPSBinary * lprgsbEntryIDs)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPDWORD lprgdwTmp = NULL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
ULONG nCurrentIndex =0;
|
|
ULONG i=0,j=0,k=0,min=0,n=0;
|
|
DWORD nCurrentEID = 0;
|
|
ULONG ulMaxCount;
|
|
DWORD dwNumofBytes = 0;
|
|
ULONG ret;
|
|
BOOL bMatchFound;
|
|
TCHAR lpszValue[MAX_INDEX_STRING+1];
|
|
ULONG ulRangeStart = 0;
|
|
ULONG ulRangeEnd = 0;
|
|
ULONG ulRelOp = 0;
|
|
ULONG ulcNumEntries =0;
|
|
ULONG ulPreviousRecordOffset = 0,ulCurrentRecordOffset = 0;
|
|
ULONG ulRecordCount = 0;
|
|
LPULONG lpulPropTagArray = NULL;
|
|
TCHAR * szBuf = NULL;
|
|
TCHAR * lp = NULL;
|
|
int nComp = 0;
|
|
BOOL bFileLocked = 0;
|
|
BOOL bErrorDetected = FALSE;
|
|
|
|
LPDWORD lpdwEntryIDs = NULL;
|
|
|
|
ULONG ulcEIDCount = 0;
|
|
LPDWORD lpdwEID = NULL;
|
|
|
|
SPropValue TmpProp;
|
|
ULONG ulcTmpValues;
|
|
ULONG ulcTmpDataSize;
|
|
ULONG ulFileSize = 0;
|
|
|
|
MPSWab_RECORD_HEADER MPSWabRecordHeader;
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo;
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
if(pt_bIsWABOpenExSession && !(ulFlags & AB_IGNORE_OUTLOOK))
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
if(!hPropertyStore)
|
|
return MAPI_E_NOT_INITIALIZED;
|
|
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
LPSPropertyRestriction lpPropResA = NULL;
|
|
|
|
if( !pt_bIsUnicodeOutlook)
|
|
{
|
|
// Need to thunk the restriction down to ANSI
|
|
HrDupePropResWCtoA(ulFlags, lpPropRes, &lpPropResA);
|
|
}
|
|
|
|
hr = lpWSP->lpVtbl->FindRecords(lpWSP,
|
|
(pmbinFold && pmbinFold->cb && pmbinFold->lpb) ? pmbinFold : NULL,
|
|
ulFlags,
|
|
lpPropResA ? lpPropResA : lpPropRes,
|
|
lpulcEIDCount,
|
|
lprgsbEntryIDs);
|
|
|
|
DebugTrace(TEXT("WABStorageProvider::FindRecords returned:%x\n"),hr);
|
|
|
|
if(lpPropResA)
|
|
{
|
|
FreeBufferAndNull(&lpPropResA->lpProp);
|
|
FreeBufferAndNull(&lpPropResA);
|
|
}
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo = hPropertyStore;
|
|
|
|
if (NULL==lpMPSWabFileInfo) goto out;
|
|
if (NULL==lpPropRes) goto out;
|
|
|
|
//
|
|
// If we are looking for property matching only, the lpProp can be null
|
|
// Just remember not to reference it in this case...
|
|
//
|
|
if ( !((ulFlags & AB_MATCH_PROP_ONLY)) && (NULL==lpPropRes->lpProp))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
lpdwEntryIDs = NULL;
|
|
ulMaxCount = *lpulcEIDCount;
|
|
*lpulcEIDCount = 0;
|
|
|
|
ulRelOp = lpPropRes->relop;
|
|
|
|
|
|
if(bLockFile)
|
|
{
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
ulFileSize = GetFileSize(hMPSWabFile, NULL);
|
|
|
|
//
|
|
// To ensure that file info is accurate,
|
|
// Any time we open a file, read the file info again ...
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
//
|
|
// Anytime we detect an error - try to fix it ...
|
|
//
|
|
if((lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_ERROR_DETECTED) ||
|
|
(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_WRITE_IN_PROGRESS))
|
|
{
|
|
if(HR_FAILED(HrDoQuickWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile)))
|
|
{
|
|
hr = HrDoDetailedWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("HrDoDetailedWABIntegrityCheck failed:%x\n"),hr);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//There are 2 main cases for this FindRecord function:
|
|
// 1. The specified property type to find is an index and we just
|
|
// need to search the indexes.
|
|
// 2. The specified property type is not an index, so we need to search
|
|
// the whole file.
|
|
// Each case is treated seperately.
|
|
//
|
|
|
|
//
|
|
// Of course, first we check if mistakenly an EntryID was sought. If
|
|
// so, just return the entry id itself.
|
|
//
|
|
if (rgIndexArray[indexEntryID] == lpPropRes->ulPropTag)
|
|
{
|
|
lpdwEntryIDs = LocalAlloc(LMEM_ZEROINIT,SIZEOF_WAB_ENTRYID);
|
|
|
|
if (!lpdwEntryIDs)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
*lpdwEntryIDs = lpPropRes->lpProp->Value.ul;
|
|
*lpulcEIDCount = 1;
|
|
hr = S_OK;
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Now Check if the specified property type is indexed or not indexed
|
|
//
|
|
for (i = indexDisplayName; i<indexMax; i++) //assumes that indexEntryID = 0 and ignores it
|
|
{
|
|
//
|
|
// first check if the prop tag we are searching for is indexed or not
|
|
//
|
|
if (rgIndexArray[i] == lpPropRes->ulPropTag)
|
|
{
|
|
ulcNumEntries = lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[i].ulcNumEntries;
|
|
|
|
if (ulcNumEntries == 0)
|
|
{
|
|
//
|
|
// if nothing to search in, then report a success and return
|
|
//
|
|
hr = S_OK;
|
|
goto out;
|
|
}
|
|
|
|
if ((ulFlags & AB_MATCH_PROP_ONLY))
|
|
{
|
|
//
|
|
// We dont need to look at the data
|
|
// We can assume that every single record has the indexed properties
|
|
// and therefore every record is eligible for returning
|
|
//
|
|
// So if RELOP_EQ is specified, we can just return a array of all
|
|
// the existing entryids .. if RELOP_NE is specified, then we cant
|
|
// return anything ...
|
|
//
|
|
|
|
if (lpPropRes->relop == RELOP_NE)
|
|
{
|
|
ulcEIDCount = 0;//*lpulcEIDCount = 0;
|
|
lpdwEID = NULL; //lpdwEntryIDs = NULL;
|
|
hr = S_OK;
|
|
}
|
|
else if(lpPropRes->relop == RELOP_EQ)
|
|
{
|
|
|
|
//*lpulcEIDCount = ulcNumEntries;
|
|
ulcEIDCount = ulcNumEntries;
|
|
|
|
//Allocate enough memory for returned array
|
|
//lpdwEntryIDs = LocalAlloc(LMEM_ZEROINIT,SIZEOF_WAB_ENTRYID * (*lpulcEIDCount));
|
|
lpdwEID = LocalAlloc(LMEM_ZEROINIT,SIZEOF_WAB_ENTRYID * ulcEIDCount);
|
|
|
|
//if (!lpdwEntryIDs)
|
|
if (!lpdwEID)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// Make sure this index is loaded into memory
|
|
if (!LoadIndex(lpMPSWabFileInfo,i,hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Could not load index %x\n"),rgIndexArray[i]);
|
|
goto out;
|
|
}
|
|
|
|
for(j=0;j<ulcEIDCount;j++)
|
|
{
|
|
lpdwEID[j] = lpMPSWabFileInfo->lpMPSWabIndexStr[j].dwEntryID;
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
DebugTrace(TEXT("Unsupported find parameters\n"));
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
}
|
|
goto filterFolderMembers;
|
|
|
|
}
|
|
|
|
//
|
|
// We need to look at the Data
|
|
//
|
|
|
|
//
|
|
// The index strings are only MAX_INDEX_STRING long
|
|
// If the value to search for is longer we need to truncate it to
|
|
// MAX_INDEX_STRING length. There is a caveat that now we will
|
|
// return spurious matches but lets leave it here for now
|
|
// and tag it as TBD!!!
|
|
//
|
|
if (lstrlen(lpPropRes->lpProp->Value.LPSZ) >= MAX_INDEX_STRING-1) // >= 31 chars (so won't include trailing null)
|
|
{
|
|
ULONG nLen = TruncatePos(lpPropRes->lpProp->Value.LPSZ, MAX_INDEX_STRING-1);
|
|
CopyMemory(lpszValue,lpPropRes->lpProp->Value.LPSZ,sizeof(TCHAR)*nLen);
|
|
lpszValue[nLen]='\0';
|
|
}
|
|
else
|
|
{
|
|
StrCpyN(lpszValue,lpPropRes->lpProp->Value.LPSZ,ARRAYSIZE(lpszValue));
|
|
}
|
|
|
|
//
|
|
// Load the appropriate index into memory
|
|
//
|
|
if (!LoadIndex(lpMPSWabFileInfo,i,hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Could not load index %x\n"),rgIndexArray[i]);
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
//if it is indexed, search this index for a match
|
|
//
|
|
bMatchFound = BinSearchStr( IN lpMPSWabFileInfo->lpMPSWabIndexStr,
|
|
IN lpszValue,
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[i].ulcNumEntries,
|
|
OUT &ret);
|
|
|
|
//
|
|
// 'ret' now contains the position at which this entry exists, if it exists
|
|
//
|
|
|
|
//
|
|
// Now we have to deal with all the relational operators
|
|
// There are several Permutations and Combinations of the operators and
|
|
// the success of the search.
|
|
//
|
|
// Rel_OP MatchFound=TRue MatchFound=False
|
|
//
|
|
// EQ == Find all that match Return Nothing
|
|
// NE != Find all that match and Return all
|
|
// exclude them
|
|
// LE <=, LT < Return Everything in index Return everything
|
|
// including and/or before including and before
|
|
// GT >, GE >= Return Everything in index Return Everything
|
|
// including and/or after including and after
|
|
//
|
|
// Since our sting arrays are sorted, the matched string could
|
|
// be one of many duplicates and we dont know where the duplicate lies
|
|
// so we have to find all the duplicates to the matched string
|
|
// This is easy - e.g.
|
|
// index array -> A,B,D,G,G,G,G,G,S,U,V,Y,Z and we matched G in the
|
|
// middle position ^
|
|
// we can just move forward and backward from there and find the range
|
|
// of indexes that match and treat that range as the matched range
|
|
// and then follow the above combinations ...
|
|
|
|
ulRangeStart = ret;
|
|
ulRangeEnd = ret;
|
|
|
|
//
|
|
// If no match is found, then we can use the above values for ulRangeStart
|
|
// and ulRangeEnd otherwise if match was found we have to seek the
|
|
// borders of the duplicate list ...
|
|
//
|
|
if (bMatchFound)
|
|
{
|
|
for(;;)
|
|
{
|
|
ulRangeStart--;
|
|
if ( (0xffffffff == ulRangeStart) ||
|
|
(lstrcmpi(lpMPSWabFileInfo->lpMPSWabIndexStr[ulRangeStart].szIndex,lpszValue) ) )
|
|
break;
|
|
}
|
|
for(;;)
|
|
{
|
|
ulRangeEnd++;
|
|
if ( (ulRangeEnd == ulcNumEntries) ||
|
|
(lstrcmpi(lpMPSWabFileInfo->lpMPSWabIndexStr[ulRangeEnd].szIndex,lpszValue) ) )
|
|
break;
|
|
}
|
|
|
|
// Fix off-by-one ..
|
|
ulRangeStart++;
|
|
ulRangeEnd--;
|
|
|
|
}
|
|
|
|
//
|
|
// Now ulRangeStart points to start of the matched entries and
|
|
// ulRangeEnd to end of the matched entries.
|
|
// e.g. 0 1 ... ... ulcNumEntries-1
|
|
// A,B,C,D,G,G,G,G,G,G,H,J,J,K,L,Z
|
|
// ^ ^
|
|
// | |
|
|
// ulRangeStart ulRangeEnd
|
|
//
|
|
// Now we need to calculate the number of values we are returning in the array
|
|
//
|
|
if (bMatchFound)
|
|
{
|
|
switch(ulRelOp)
|
|
{
|
|
case RELOP_GT:
|
|
//include everything from RangeEnd+1 to end
|
|
*lpulcEIDCount = ulcNumEntries - (ulRangeEnd + 1);
|
|
break;
|
|
case RELOP_GE:
|
|
//include everything from RangeStart to end
|
|
*lpulcEIDCount = ulcNumEntries - ulRangeStart;
|
|
break;
|
|
case RELOP_LT:
|
|
*lpulcEIDCount = ulRangeStart;
|
|
break;
|
|
case RELOP_LE:
|
|
*lpulcEIDCount = ulRangeEnd + 1;
|
|
break;
|
|
case RELOP_NE:
|
|
*lpulcEIDCount = ulcNumEntries - (ulRangeEnd+1 - ulRangeStart);
|
|
break;
|
|
case RELOP_EQ:
|
|
*lpulcEIDCount = (ulRangeEnd+1 - ulRangeStart);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Assumes ulRangeStart = ulRangeEnd
|
|
switch(ulRelOp)
|
|
{
|
|
case RELOP_GT:
|
|
case RELOP_GE:
|
|
//include everything from RangeEnd/RangeStart to end
|
|
*lpulcEIDCount = ulcNumEntries - ulRangeEnd;
|
|
break;
|
|
case RELOP_LT:
|
|
case RELOP_LE:
|
|
*lpulcEIDCount = ulRangeStart;
|
|
break;
|
|
case RELOP_NE:
|
|
*lpulcEIDCount = ulcNumEntries;
|
|
break;
|
|
case RELOP_EQ:
|
|
*lpulcEIDCount = 0;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (*lpulcEIDCount == 0)
|
|
{
|
|
//
|
|
// nothing to return - goodbye
|
|
//
|
|
hr = S_OK;
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// dont return more than Max asked for (where Max != 0)...
|
|
//
|
|
if ( (*lpulcEIDCount > ulMaxCount) && (ulMaxCount != 0) )
|
|
*lpulcEIDCount = ulMaxCount;
|
|
|
|
//
|
|
// Allocate enough memory for returned array
|
|
//
|
|
lpdwEntryIDs = LocalAlloc(LMEM_ZEROINIT,SIZEOF_WAB_ENTRYID * (*lpulcEIDCount));
|
|
|
|
if (!lpdwEntryIDs)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
|
|
//
|
|
// Now copy over the EntryIDs from the index to the returned array
|
|
// Each operator needs different treatment
|
|
//
|
|
if (bMatchFound)
|
|
{
|
|
switch(ulRelOp)
|
|
{
|
|
case RELOP_GT:
|
|
for(i=0;i<(*lpulcEIDCount);i++)
|
|
{
|
|
//include everything from RangeEnd+1 to end
|
|
lpdwEntryIDs[i] = lpMPSWabFileInfo->lpMPSWabIndexStr[i+ulRangeEnd+1].dwEntryID;
|
|
}
|
|
break;
|
|
case RELOP_GE:
|
|
for(i=0;i<(*lpulcEIDCount);i++)
|
|
{
|
|
//include everything from RangeStart to end
|
|
lpdwEntryIDs[i] = lpMPSWabFileInfo->lpMPSWabIndexStr[i+ulRangeStart].dwEntryID;
|
|
}
|
|
break;
|
|
case RELOP_LT:
|
|
case RELOP_LE:
|
|
for(i=0;i<(*lpulcEIDCount);i++)
|
|
{
|
|
//include everything from before RangeEnd/RangeStart
|
|
lpdwEntryIDs[i] = lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID;
|
|
}
|
|
break;
|
|
case RELOP_NE:
|
|
i = 0;
|
|
if ( (ulcNumEntries > ulMaxCount) && (ulMaxCount != 0) )
|
|
ulcNumEntries = ulMaxCount;
|
|
for(j=0;j<ulcNumEntries;j++)
|
|
{
|
|
//include everything from before RangeStart and after RangeEnd
|
|
if ( (j<ulRangeStart) || (j>ulRangeEnd) )
|
|
{
|
|
lpdwEntryIDs[i] = lpMPSWabFileInfo->lpMPSWabIndexStr[j].dwEntryID;
|
|
i++;
|
|
}
|
|
}
|
|
break;
|
|
case RELOP_EQ:
|
|
i = 0;
|
|
for(j=0;j<(*lpulcEIDCount);j++)
|
|
{
|
|
//include everything between RangeStart and RangeEnd
|
|
lpdwEntryIDs[i] = lpMPSWabFileInfo->lpMPSWabIndexStr[j+ulRangeStart].dwEntryID;
|
|
i++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//assumes that RangeStart = RangeEnd
|
|
switch(ulRelOp)
|
|
{
|
|
case RELOP_GT:
|
|
case RELOP_GE:
|
|
for(i=0;i<(*lpulcEIDCount);i++)
|
|
{
|
|
//include everything from RangeStart to end
|
|
lpdwEntryIDs[i] = lpMPSWabFileInfo->lpMPSWabIndexStr[i+ulRangeStart].dwEntryID;
|
|
}
|
|
break;
|
|
case RELOP_LT:
|
|
case RELOP_LE:
|
|
case RELOP_NE:
|
|
for(i=0;i<(*lpulcEIDCount);i++)
|
|
{
|
|
//include first 'n' entries
|
|
lpdwEntryIDs[i] = lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID;
|
|
}
|
|
break;
|
|
case RELOP_EQ:
|
|
//This case should never happen cause we checked for it before (when total found=0)
|
|
DebugTrace(TEXT("Unexpected RELOP_EQ case\n"));
|
|
break;
|
|
}
|
|
}
|
|
//if we're here we've got our data
|
|
hr = S_OK;
|
|
if(!pmbinFold)
|
|
{
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
ulcEIDCount = *lpulcEIDCount;
|
|
lpdwEID = lpdwEntryIDs;
|
|
lpdwEntryIDs = NULL;
|
|
*lpulcEIDCount = 0;
|
|
goto filterFolderMembers;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// If we're here then we didnt find anything in the indices ...
|
|
// Time to search the whole file
|
|
// Mechanism for this search is to go through all the entries in an index
|
|
// read in the record corresponding to that entry, read in the prop tag
|
|
// array, search in it for the specified property based on REL_OP and then if
|
|
// it meets our criteria, we can store the entryid of the record and return it
|
|
|
|
|
|
// For the time being lets also ignore Multivalued properties because they are too much of a headache
|
|
if ( ((lpPropRes->ulPropTag & MV_FLAG)) && (!((ulFlags & AB_MATCH_PROP_ONLY))) )
|
|
{
|
|
DebugTrace(TEXT("Searching for MultiValued prop data not supported in this version\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
|
|
// The maximum number of entryIDs we can return = maximum number of entries
|
|
// So we will allocate some working space for ourselves here
|
|
lpdwEID = LocalAlloc(LMEM_ZEROINIT, SIZEOF_WAB_ENTRYID*lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries);
|
|
if (!lpdwEID)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
|
|
ulcEIDCount = 0;
|
|
|
|
|
|
|
|
ulPreviousRecordOffset = 0;
|
|
if (0xFFFFFFFF== SetFilePointer ( hMPSWabFile,
|
|
ulPreviousRecordOffset,
|
|
NULL,
|
|
FILE_BEGIN))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
for(n=0;n<lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries;n++)
|
|
{
|
|
ulCurrentRecordOffset = lpMPSWabFileInfo->lpMPSWabIndexEID[n].ulOffset;
|
|
|
|
if (0xFFFFFFFF== SetFilePointer ( hMPSWabFile,
|
|
ulCurrentRecordOffset - ulPreviousRecordOffset,
|
|
NULL,
|
|
FILE_CURRENT))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
ulPreviousRecordOffset = ulCurrentRecordOffset;
|
|
|
|
//Read in the record header
|
|
if(!ReadFile( hMPSWabFile,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
(DWORD) sizeof(MPSWab_RECORD_HEADER),
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Reading Record header failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
if(dwNumofBytes == 0)
|
|
{
|
|
DebugTrace(TEXT("Passed the end of file\n"));
|
|
break;
|
|
}
|
|
|
|
ulPreviousRecordOffset += dwNumofBytes;
|
|
|
|
if(!bIsValidRecord( MPSWabRecordHeader,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID,
|
|
ulCurrentRecordOffset,
|
|
ulFileSize))
|
|
// if (MPSWabRecordHeader.bValidRecord != TRUE)
|
|
{
|
|
//
|
|
// skip to next record
|
|
//
|
|
bErrorDetected = TRUE;
|
|
continue;
|
|
}
|
|
|
|
|
|
// Do a special case for PR_OBJECT_TYPE searches since these can be easily
|
|
// determined from the record header without having to read the entire record
|
|
//
|
|
if( (lpPropRes->ulPropTag == PR_OBJECT_TYPE) &&
|
|
(lpPropRes->relop == RELOP_EQ) )
|
|
{
|
|
LONG ulObjType = 0;
|
|
|
|
if(MPSWabRecordHeader.ulObjType == RECORD_DISTLIST)
|
|
ulObjType = MAPI_DISTLIST;
|
|
else if(MPSWabRecordHeader.ulObjType == RECORD_CONTAINER)
|
|
ulObjType = MAPI_ABCONT;
|
|
else
|
|
ulObjType = MAPI_MAILUSER;
|
|
|
|
if(lpPropRes->lpProp->Value.l == ulObjType)
|
|
{
|
|
//save this entry id in our master list
|
|
lpdwEID[ulcEIDCount++] = MPSWabRecordHeader.dwEntryID;
|
|
}
|
|
|
|
// goto next record - whether it was a match or not ...
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Read in the PropTagArray
|
|
//
|
|
|
|
//
|
|
// Allocate space for the PropTagArray
|
|
//
|
|
LocalFreeAndNull(&lpulPropTagArray);
|
|
lpulPropTagArray = LocalAlloc(LMEM_ZEROINIT, MPSWabRecordHeader.ulPropTagArraySize);
|
|
|
|
if (!lpulPropTagArray)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
|
|
//
|
|
// Read in the Prop tag array
|
|
//
|
|
if(!ReadFile( hMPSWabFile,
|
|
(LPVOID) lpulPropTagArray,
|
|
(DWORD) MPSWabRecordHeader.ulPropTagArraySize,
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Reading Record PropTagArray failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
ulPreviousRecordOffset += dwNumofBytes;
|
|
|
|
//
|
|
// if AB_MATCH_PROP_ONLY is specified, then we limit our search to determining whether or
|
|
// not the property exists. if AB_MATCH_PROP_ONLY is not specified, we first look
|
|
// for the prop tag and then we look at the data behind the tag.
|
|
//
|
|
|
|
// if AB_MATCH_PROP is specified, we only search for existence or non-existence of the
|
|
// prop. All other Relational Operators are defunct.
|
|
|
|
// As long as we are not searching for multi-valued properties, we can have realtional operator
|
|
// based searching.
|
|
|
|
|
|
if ((ulFlags & AB_MATCH_PROP_ONLY) && (ulRelOp != RELOP_EQ) && (ulRelOp != RELOP_NE))
|
|
{
|
|
DebugTrace(TEXT("Unsupported relational operator for Property Matching\n"));
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
if ((PROP_TYPE(lpPropRes->ulPropTag) == PT_CLSID) && (ulRelOp != RELOP_EQ) && (ulRelOp != RELOP_NE))
|
|
{
|
|
DebugTrace(TEXT("Unsupported relational operator for finding GUIDs \n"));
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
|
|
bMatchFound = FALSE;
|
|
|
|
//
|
|
// scan the existing props for our tag
|
|
//
|
|
for (j=0;j<MPSWabRecordHeader.ulcPropCount;j++)
|
|
{
|
|
if (lpulPropTagArray[j]==lpPropRes->ulPropTag)
|
|
{
|
|
bMatchFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// At this point we know whether or not the record contains this property of interest
|
|
// Now we look at the flags and relational operators to see what to do.
|
|
|
|
if ((ulFlags & AB_MATCH_PROP_ONLY))
|
|
{
|
|
// We are interested only in the presence or absence of this property
|
|
if ( ( (ulRelOp == RELOP_EQ) && (bMatchFound) ) ||
|
|
( (ulRelOp == RELOP_NE) && (!bMatchFound) ) )
|
|
{
|
|
//save this entry id in our master list
|
|
lpdwEID[ulcEIDCount++] = MPSWabRecordHeader.dwEntryID;
|
|
|
|
}
|
|
|
|
// goto next record
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// want to compare the values ...
|
|
|
|
// if we are trying to compare value data and the property doesnt even exist in the record,
|
|
// bail out now ...
|
|
if (!bMatchFound)
|
|
{
|
|
//nothing of interest - go to next record
|
|
continue;
|
|
}
|
|
|
|
LocalFreeAndNull(&szBuf);
|
|
|
|
szBuf = LocalAlloc(LMEM_ZEROINIT,MPSWabRecordHeader.ulRecordDataSize);
|
|
|
|
if (!szBuf)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
if(!ReadFile( hMPSWabFile,
|
|
(LPVOID) szBuf,
|
|
(DWORD) MPSWabRecordHeader.ulRecordDataSize,
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Reading Record Data failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
ulPreviousRecordOffset += dwNumofBytes;
|
|
|
|
lp = szBuf;
|
|
|
|
//reset bMatchFound - used again later in this routine
|
|
bMatchFound = FALSE;
|
|
|
|
//go through all the property values
|
|
for(i=0;i< MPSWabRecordHeader.ulcPropCount;i++)
|
|
{
|
|
//Read Property Tag
|
|
CopyMemory(&TmpProp.ulPropTag,lp,sizeof(ULONG));
|
|
lp+=sizeof(ULONG) / sizeof(TCHAR);
|
|
|
|
//Check if it is MultiValued
|
|
if ((TmpProp.ulPropTag & MV_FLAG))
|
|
{
|
|
//Read cValues
|
|
CopyMemory(&ulcTmpValues,lp,sizeof(ULONG));
|
|
lp+=sizeof(ULONG) / sizeof(TCHAR);
|
|
}
|
|
|
|
//read DataSize
|
|
CopyMemory(&ulcTmpDataSize,lp,sizeof(ULONG));
|
|
lp+=sizeof(ULONG) / sizeof(TCHAR);
|
|
|
|
if (TmpProp.ulPropTag != lpPropRes->ulPropTag)
|
|
{
|
|
//skip this prop
|
|
lp += ulcTmpDataSize;
|
|
// go check next Prop Tag
|
|
continue;
|
|
}
|
|
|
|
if ((TmpProp.ulPropTag & MV_FLAG))
|
|
{
|
|
//skip this prop
|
|
lp += ulcTmpDataSize;
|
|
//go check next prop tag
|
|
continue;
|
|
}
|
|
|
|
// copy the requisite number of bytes into memory
|
|
switch(PROP_TYPE(TmpProp.ulPropTag))
|
|
{
|
|
case(PT_I2):
|
|
case(PT_LONG):
|
|
case(PT_APPTIME):
|
|
case(PT_SYSTIME):
|
|
case(PT_R4):
|
|
case(PT_BOOLEAN):
|
|
case(PT_CURRENCY):
|
|
case(PT_I8):
|
|
CopyMemory(&TmpProp.Value.i,lp,min(ulcTmpDataSize,sizeof(TmpProp.Value.i)));
|
|
break;
|
|
|
|
case(PT_CLSID):
|
|
case(PT_TSTRING):
|
|
TmpProp.Value.LPSZ = LocalAlloc(LMEM_ZEROINIT,ulcTmpDataSize);
|
|
if (!TmpProp.Value.LPSZ)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(TmpProp.Value.LPSZ,lp,ulcTmpDataSize);
|
|
break;
|
|
|
|
case(PT_BINARY):
|
|
TmpProp.Value.bin.lpb = LocalAlloc(LMEM_ZEROINIT,ulcTmpDataSize);
|
|
if (!TmpProp.Value.bin.lpb)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(TmpProp.Value.bin.lpb,lp,ulcTmpDataSize);
|
|
TmpProp.Value.bin.cb = ulcTmpDataSize;
|
|
break;
|
|
|
|
default:
|
|
// something I dont understand .. skip
|
|
lp += ulcTmpDataSize;
|
|
//go check next prop tag
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
lp += ulcTmpDataSize;
|
|
|
|
// Do the comparison
|
|
switch(PROP_TYPE(TmpProp.ulPropTag))
|
|
{
|
|
case(PT_I2):
|
|
nComp = TmpProp.Value.i - lpPropRes->lpProp->Value.i;
|
|
break;
|
|
case(PT_LONG):
|
|
nComp = TmpProp.Value.l - lpPropRes->lpProp->Value.l;
|
|
break;
|
|
case(PT_R4):
|
|
if ((TmpProp.Value.flt - lpPropRes->lpProp->Value.flt) < 0)
|
|
{
|
|
nComp = -1;
|
|
}
|
|
else if ((TmpProp.Value.flt - lpPropRes->lpProp->Value.flt) == 0)
|
|
{
|
|
nComp = 0;
|
|
}
|
|
else
|
|
{
|
|
nComp = 1;
|
|
}
|
|
break;
|
|
case(PT_DOUBLE):
|
|
if ((TmpProp.Value.dbl - lpPropRes->lpProp->Value.dbl) < 0)
|
|
{
|
|
nComp = -1;
|
|
}
|
|
else if ((TmpProp.Value.dbl - lpPropRes->lpProp->Value.dbl) == 0)
|
|
{
|
|
nComp = 0;
|
|
}
|
|
else
|
|
{
|
|
nComp = 1;
|
|
}
|
|
break;
|
|
case(PT_BOOLEAN):
|
|
nComp = TmpProp.Value.b - lpPropRes->lpProp->Value.b;
|
|
break;
|
|
case(PT_CURRENCY):
|
|
// ???TBD: nComp = TmpProp.Value.cur - lpPropRes->lpProp->Value.cur;
|
|
if((TmpProp.Value.cur.Hi - lpPropRes->lpProp->Value.cur.Hi) < 0)
|
|
{
|
|
nComp = -1;
|
|
}
|
|
else if((TmpProp.Value.cur.Hi - lpPropRes->lpProp->Value.cur.Hi) > 0)
|
|
{
|
|
nComp = +1;
|
|
}
|
|
else
|
|
{
|
|
if(TmpProp.Value.cur.Lo < lpPropRes->lpProp->Value.cur.Lo)
|
|
{
|
|
nComp = -1;
|
|
}
|
|
else if((TmpProp.Value.cur.Lo - lpPropRes->lpProp->Value.cur.Lo) > 0)
|
|
{
|
|
nComp = +1;
|
|
}
|
|
else
|
|
{
|
|
nComp = 0;
|
|
}
|
|
}
|
|
break;
|
|
case(PT_APPTIME):
|
|
if ((TmpProp.Value.at - lpPropRes->lpProp->Value.at) < 0)
|
|
{
|
|
nComp = -1;
|
|
}
|
|
else if ((TmpProp.Value.at - lpPropRes->lpProp->Value.at) == 0)
|
|
{
|
|
nComp = 0;
|
|
}
|
|
else
|
|
{
|
|
nComp = 1;
|
|
}
|
|
break;
|
|
|
|
case(PT_SYSTIME):
|
|
nComp = CompareFileTime(&(TmpProp.Value.ft), (FILETIME *) (&(lpPropRes->lpProp->Value.ft)));
|
|
break;
|
|
|
|
case(PT_TSTRING):
|
|
nComp = lstrcmpi(TmpProp.Value.LPSZ,lpPropRes->lpProp->Value.LPSZ);
|
|
break;
|
|
|
|
case(PT_BINARY):
|
|
min = (TmpProp.Value.bin.cb < lpPropRes->lpProp->Value.bin.cb) ? TmpProp.Value.bin.cb : lpPropRes->lpProp->Value.bin.cb;
|
|
k=0;
|
|
nComp=0;
|
|
while((k<min) && ((int)TmpProp.Value.bin.lpb[k] == (int)lpPropRes->lpProp->Value.bin.lpb[k]))
|
|
k++; //find first difference
|
|
if (k!=min)
|
|
nComp = (int) TmpProp.Value.bin.lpb[k] - (int) lpPropRes->lpProp->Value.bin.lpb[k];
|
|
break;
|
|
|
|
case(PT_CLSID):
|
|
nComp = IsEqualGUID(TmpProp.Value.lpguid,lpPropRes->lpProp->Value.lpguid);
|
|
break;
|
|
|
|
case(PT_I8):
|
|
// ??? TBD how to do this one ??
|
|
if((TmpProp.Value.li.HighPart - lpPropRes->lpProp->Value.li.HighPart) < 0)
|
|
{
|
|
nComp = -1;
|
|
}
|
|
else if((TmpProp.Value.li.HighPart - lpPropRes->lpProp->Value.li.HighPart) > 0)
|
|
{
|
|
nComp = +1;
|
|
}
|
|
else
|
|
{
|
|
if(TmpProp.Value.li.LowPart < lpPropRes->lpProp->Value.li.LowPart)
|
|
{
|
|
nComp = -1;
|
|
}
|
|
else if((TmpProp.Value.li.LowPart - lpPropRes->lpProp->Value.li.LowPart) > 0)
|
|
{
|
|
nComp = +1;
|
|
}
|
|
else
|
|
{
|
|
nComp = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
// If we get what we are looking for then there is no need to look at the
|
|
// rest of the record. In that case we go to the next record.
|
|
//
|
|
switch(ulRelOp)
|
|
{
|
|
case(RELOP_EQ):
|
|
if (nComp == 0)
|
|
{
|
|
// We got atleast one match, so we can store this entryID and
|
|
// skip to next record
|
|
lpdwEID[ulcEIDCount++] = MPSWabRecordHeader.dwEntryID;
|
|
bMatchFound = TRUE;
|
|
}
|
|
break;
|
|
case(RELOP_NE):
|
|
// We can only declare success for the != operator if and only if all values
|
|
// of this property in the record do not meet the given value.
|
|
// This means that we have to scan the whole record before we can declare success.
|
|
// Thus, instead of marking the flag on success, we actually mark it on
|
|
// failure. At the end of the 'for' loop, if there was even 1 failure in the
|
|
// test, we can mark the record as having failed our test.
|
|
if (nComp == 0)
|
|
{
|
|
bMatchFound = TRUE;
|
|
}
|
|
break;
|
|
case(RELOP_GT):
|
|
if (nComp > 0)
|
|
{
|
|
// We got atleast one match, so we can store this entryID and
|
|
// skip to next record
|
|
lpdwEID[ulcEIDCount++] = MPSWabRecordHeader.dwEntryID;
|
|
bMatchFound = TRUE;
|
|
}
|
|
break;
|
|
case(RELOP_GE):
|
|
if (nComp >= 0)
|
|
{
|
|
// We got atleast one match, so we can store this entryID and
|
|
// skip to next record
|
|
lpdwEID[ulcEIDCount++] = MPSWabRecordHeader.dwEntryID;
|
|
bMatchFound = TRUE;
|
|
}
|
|
break;
|
|
case(RELOP_LT):
|
|
if (nComp < 0)
|
|
{
|
|
// We got atleast one match, so we can store this entryID and
|
|
// skip to next record
|
|
lpdwEID[ulcEIDCount++] = MPSWabRecordHeader.dwEntryID;
|
|
bMatchFound = TRUE;
|
|
}
|
|
break;
|
|
case(RELOP_LE):
|
|
if (nComp <= 0)
|
|
{
|
|
// We got atleast one match, so we can store this entryID and
|
|
// skip to next record
|
|
lpdwEID[ulcEIDCount++] = MPSWabRecordHeader.dwEntryID;
|
|
bMatchFound = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch(PROP_TYPE(TmpProp.ulPropTag))
|
|
{
|
|
case(PT_CLSID):
|
|
case(PT_TSTRING):
|
|
LocalFreeAndNull((LPVOID *) (&TmpProp.Value.LPSZ));
|
|
break;
|
|
case(PT_BINARY):
|
|
LocalFreeAndNull((LPVOID *) (&TmpProp.Value.bin.lpb));
|
|
break;
|
|
}
|
|
|
|
// if we got a match above, we dont look in this record anymore
|
|
if (bMatchFound)
|
|
break;
|
|
|
|
} //(for i= ...
|
|
|
|
if ((ulRelOp == RELOP_NE) && (bMatchFound == FALSE))
|
|
{
|
|
//We exited the for loop legitimately and still didnt find a match
|
|
//so we can finally declare a success for this one relop
|
|
lpdwEID[ulcEIDCount++] = MPSWabRecordHeader.dwEntryID;
|
|
}
|
|
|
|
} //else
|
|
|
|
|
|
if ((ulcEIDCount == ulMaxCount) && (ulMaxCount != 0))
|
|
{
|
|
// got enough records to return
|
|
// break out of do loop
|
|
break;
|
|
}
|
|
|
|
LocalFreeAndNull(&szBuf);
|
|
|
|
|
|
LocalFreeAndNull(&lpulPropTagArray);
|
|
|
|
}//for loop
|
|
|
|
|
|
filterFolderMembers:
|
|
#define WAB_IGNORE_ENTRY 0xFFFFFFFF
|
|
// if a folder was specified, only return the entries that are part of this folder
|
|
// pmbinFold will be NULL when there is no Outlook and no profiles
|
|
// otherwise it will have something in it
|
|
// If pmbinFold->cb and ->lpb are empty, then this is the virtual PAB folder and
|
|
// we want to return EVERYTHING in it
|
|
if(pmbinFold)// && pmbinFold->cb && pmbinFold->lpb)
|
|
{
|
|
// if it is the virtual root folder, only accept entries that dont have
|
|
// PR_WAB_FOLDER_PARENT set on it
|
|
// if it is not the root virtual folder, only return this entry if it is
|
|
// a member of the folder
|
|
/***/ if(!pmbinFold->cb && !pmbinFold->lpb)
|
|
{
|
|
// only accept entries that dont have PR_WAB_FOLDER_PARENT
|
|
for(i=0;i<ulcEIDCount;i++)
|
|
{
|
|
ULONG ulObjType = 0;
|
|
if(bIsFolderMember(hMPSWabFile, lpMPSWabFileInfo, lpdwEID[i], &ulObjType))
|
|
lpdwEID[i] = WAB_IGNORE_ENTRY;
|
|
//if(ulObjType == RECORD_CONTAINER)
|
|
// lpdwEID[i] = WAB_IGNORE_ENTRY;
|
|
}
|
|
}
|
|
else if(pmbinFold->cb && pmbinFold->lpb)
|
|
/****/ {
|
|
LPDWORD lpdwFolderEIDs = NULL;
|
|
ULONG ulFolderEIDs = 0;
|
|
if(!HR_FAILED(GetFolderEIDs( hMPSWabFile, lpMPSWabFileInfo,
|
|
pmbinFold, &ulFolderEIDs, &lpdwFolderEIDs)))
|
|
{
|
|
if(ulFolderEIDs && lpdwFolderEIDs)
|
|
{
|
|
for(i=0;i<ulcEIDCount;i++)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
for(j=0;j<ulFolderEIDs;j++)
|
|
{
|
|
if(lpdwEID[i] == lpdwFolderEIDs[j])
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if(!bFound)
|
|
lpdwEID[i] = WAB_IGNORE_ENTRY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// empty folder so dont return anything
|
|
ulcEIDCount = 0;
|
|
if(lpdwEID)
|
|
{
|
|
LocalFree(lpdwEID);
|
|
lpdwEID = NULL;
|
|
}
|
|
}
|
|
}
|
|
if(lpdwFolderEIDs)
|
|
LocalFree(lpdwFolderEIDs);
|
|
}
|
|
}
|
|
|
|
*lpulcEIDCount = 0;
|
|
if(lpdwEID && ulcEIDCount)
|
|
{
|
|
//So now if we got here, we can return the array
|
|
lpdwEntryIDs = LocalAlloc(LMEM_ZEROINIT, ulcEIDCount * SIZEOF_WAB_ENTRYID);
|
|
if (!lpdwEntryIDs)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
for(i=0;i<ulcEIDCount;i++)
|
|
{
|
|
if(lpdwEID[i]!=WAB_IGNORE_ENTRY)
|
|
{
|
|
lpdwEntryIDs[*lpulcEIDCount]=lpdwEID[i];
|
|
(*lpulcEIDCount)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
out:
|
|
|
|
if(!HR_FAILED(hr) &&
|
|
lpdwEntryIDs &&
|
|
*lpulcEIDCount)
|
|
{
|
|
// Convert to the array of SBinarys we will return
|
|
(*lprgsbEntryIDs) = LocalAlloc(LMEM_ZEROINIT, sizeof(SBinary) * (*lpulcEIDCount));
|
|
if(*lprgsbEntryIDs)
|
|
{
|
|
for(i=0;i<*lpulcEIDCount;i++)
|
|
{
|
|
(*lprgsbEntryIDs)[i].lpb = LocalAlloc(LMEM_ZEROINIT, SIZEOF_WAB_ENTRYID);
|
|
if((*lprgsbEntryIDs)[i].lpb)
|
|
{
|
|
(*lprgsbEntryIDs)[i].cb = SIZEOF_WAB_ENTRYID;
|
|
CopyMemory((*lprgsbEntryIDs)[i].lpb, &(lpdwEntryIDs[i]), SIZEOF_WAB_ENTRYID);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
*lpulcEIDCount = 0; // out of memory
|
|
}
|
|
|
|
if(lpdwEntryIDs)
|
|
LocalFree(lpdwEntryIDs);
|
|
|
|
LocalFreeAndNull(&szBuf);
|
|
|
|
LocalFreeAndNull(&lpulPropTagArray);
|
|
|
|
LocalFreeAndNull(&lpdwEID);
|
|
|
|
if(bErrorDetected)
|
|
TagWABFileError(lpMPSWabFileInfo->lpMPSWabFileHeader, hMPSWabFile);
|
|
|
|
if (hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if(bLockFile)
|
|
{
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DeleteRecord
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
// IN dwEntryID - EntryID of record to delete
|
|
//
|
|
// Basically, we invalidate the existing record specified by the EntryID
|
|
// and we also reduce the total count, update the modification count,
|
|
// and remove the corresponding indexes from all the 4 indexes
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT DeleteRecord( IN HANDLE hPropertyStore,
|
|
IN LPSBinary lpsbEID)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
ULONG nIndexPos = 0, j = 0, i = 0;
|
|
HANDLE hMPSWabFile = NULL;
|
|
DWORD dwNumofBytes = 0;
|
|
ULONG index = 0;
|
|
BOOL bFileLocked = FALSE;
|
|
BOOL bEntryAlreadyDeleted = FALSE;
|
|
DWORD dwEntryID = 0;
|
|
MPSWab_RECORD_HEADER MPSWabRecordHeader;
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo;
|
|
SBinary sbEID = {0};
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
if(pt_bIsWABOpenExSession)
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
if(!hPropertyStore)
|
|
return MAPI_E_NOT_INITIALIZED;
|
|
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
|
|
hr = lpWSP->lpVtbl->DeleteRecord(lpWSP,
|
|
lpsbEID);
|
|
|
|
DebugTrace(TEXT("WABStorageProvider::DeleteRecord returned:%x\n"),hr);
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo = hPropertyStore;
|
|
|
|
if(lpsbEID && lpsbEID->cb != SIZEOF_WAB_ENTRYID)
|
|
{
|
|
// this may be a WAB container .. reset the entryid to a WAB entryid
|
|
if(WAB_CONTAINER == IsWABEntryID(lpsbEID->cb, (LPENTRYID)lpsbEID->lpb,
|
|
NULL,NULL,NULL,NULL,NULL))
|
|
{
|
|
IsWABEntryID(lpsbEID->cb, (LPENTRYID)lpsbEID->lpb,
|
|
(LPVOID*)&sbEID.lpb,(LPVOID*)&sbEID.cb,NULL,NULL,NULL);
|
|
if(sbEID.cb == SIZEOF_WAB_ENTRYID)
|
|
lpsbEID = &sbEID;
|
|
}
|
|
}
|
|
if(!lpsbEID || lpsbEID->cb != SIZEOF_WAB_ENTRYID)
|
|
{
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
CopyMemory(&dwEntryID, lpsbEID->lpb, min(lpsbEID->cb, sizeof(dwEntryID)));
|
|
|
|
DebugTrace(TEXT("----Thread:%x\tDeleteRecord: Entry\n----EntryID:%d\n"),GetCurrentThreadId(),dwEntryID);
|
|
|
|
//
|
|
// If we had started this whole session requesting read-only access
|
|
// make sure we dont mistakenly try to violate it ...
|
|
//
|
|
if (lpMPSWabFileInfo->bReadOnlyAccess)
|
|
{
|
|
DebugTrace(TEXT("Access Permissions are Read-Only"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
|
|
//Open the file
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// To ensure that file info is accurate,
|
|
// Any time we open a file, read the file info again ...
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Anytime we detect an error - try to fix it ...
|
|
//
|
|
if((lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_ERROR_DETECTED) ||
|
|
(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_WRITE_IN_PROGRESS))
|
|
{
|
|
if(HR_FAILED(HrDoQuickWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile)))
|
|
{
|
|
hr = HrDoDetailedWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("HrDoDetailedWABIntegrityCheck failed:%x\n"),hr);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Tag this file as undergoing a write operation
|
|
if(!bTagWriteTransaction( lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Taggin file write failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// First check if this is a valid entryID
|
|
//
|
|
if (!BinSearchEID( IN lpMPSWabFileInfo->lpMPSWabIndexEID,
|
|
IN dwEntryID,
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries,
|
|
OUT &nIndexPos))
|
|
{
|
|
DebugTrace(TEXT("Specified EntryID: %d doesnt exist!"),dwEntryID);
|
|
hr = MAPI_E_INVALID_ENTRYID;
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Yes it's valid. Go to this record and invalidate the record.
|
|
//
|
|
if(!ReadDataFromWABFile(hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos].ulOffset,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
(DWORD) sizeof(MPSWab_RECORD_HEADER)))
|
|
goto out;
|
|
|
|
|
|
if ((MPSWabRecordHeader.bValidRecord == FALSE) && (MPSWabRecordHeader.bValidRecord != TRUE))
|
|
{
|
|
//
|
|
// this should never happen but who knows
|
|
//
|
|
DebugTrace(TEXT("Specified entry has already been invalidated ...\n"));
|
|
// hr = S_OK;
|
|
// goto out;
|
|
// if we hit an invalid entryid through the index, then we need to remove that link from the index
|
|
// so we'll go ahead and pretend that its all fine and continue like nothing happened.
|
|
// This will ensure that the entryid reference is also removed ...
|
|
bEntryAlreadyDeleted = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Set valid flag to false
|
|
//
|
|
MPSWabRecordHeader.bValidRecord = FALSE;
|
|
|
|
//
|
|
// Write it back
|
|
// Set File Pointer to this record
|
|
//
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos].ulOffset,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
sizeof(MPSWab_RECORD_HEADER)))
|
|
goto out;
|
|
|
|
|
|
//
|
|
// Now we need to remove this entry from the EntryID index and also remove this
|
|
// entry from the other indexes
|
|
//
|
|
// Set File Pointer to the Pt. in the EntryID index on file
|
|
// at which this record appears
|
|
//
|
|
if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulOffset + (nIndexPos)*sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID),
|
|
NULL,
|
|
FILE_BEGIN))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
// Write the remainder of the array back to disk to overwrite this entry
|
|
|
|
if (lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries > (nIndexPos+1))
|
|
{
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPVOID) &lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos+1],
|
|
(DWORD) sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID)*(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries - nIndexPos - 1),
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing Index failed.\n"));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries>0)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries--;
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].UtilizedBlockSize>0)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].UtilizedBlockSize -= sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID);
|
|
|
|
// DebugTrace(TEXT("Thread:%x\tIndex: %d\tulNumEntries: %d\n"),GetCurrentThreadId(),indexEntryID,lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries);
|
|
|
|
//
|
|
// Similarly scan the str index arrays
|
|
//
|
|
for (index = indexDisplayName; index < indexMax; index++)
|
|
{
|
|
if (!LoadIndex( IN lpMPSWabFileInfo,
|
|
IN index,
|
|
IN hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Error Loading Index!"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
nIndexPos = 0xFFFFFFFF;
|
|
|
|
for(j=0;j<lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[index].ulcNumEntries;j++)
|
|
{
|
|
if (lpMPSWabFileInfo->lpMPSWabIndexStr[j].dwEntryID == dwEntryID)
|
|
{
|
|
nIndexPos = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if the entry doesnt exist .. no problem
|
|
// if it does - delete it ...
|
|
|
|
if (index == indexDisplayName)
|
|
Assert(nIndexPos != 0xFFFFFFFF);
|
|
|
|
if (nIndexPos != 0xFFFFFFFF)
|
|
{
|
|
|
|
if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[index].ulOffset + (nIndexPos)*sizeof(MPSWab_INDEX_ENTRY_DATA_STRING),
|
|
NULL,
|
|
FILE_BEGIN))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
// Write the remainder of the array back to disk to overwrite this entry
|
|
|
|
if (lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[index].ulcNumEntries > (nIndexPos+1))
|
|
{
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPVOID) &lpMPSWabFileInfo->lpMPSWabIndexStr[nIndexPos+1],
|
|
(DWORD) sizeof(MPSWab_INDEX_ENTRY_DATA_STRING)*(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[index].ulcNumEntries - nIndexPos - 1),
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing Index failed.\n"));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[index].ulcNumEntries>0)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[index].ulcNumEntries--;
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[index].UtilizedBlockSize>0)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[index].UtilizedBlockSize -= sizeof(MPSWab_INDEX_ENTRY_DATA_STRING);
|
|
|
|
//DebugTrace(TEXT("Thread:%x\tIndex: %d\tulNumEntries: %d\n"),GetCurrentThreadId(),index,lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[index].ulcNumEntries);
|
|
}
|
|
}
|
|
|
|
|
|
// Save the fileheader back to the file
|
|
if(!bEntryAlreadyDeleted)
|
|
{
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries>0)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries--;
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulModificationCount++;
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags |= WAB_BACKUP_NOW;
|
|
}
|
|
|
|
if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
|
|
0,
|
|
NULL,
|
|
FILE_BEGIN))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries != lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags |= WAB_ERROR_DETECTED;
|
|
|
|
|
|
for(i=indexDisplayName;i<indexMax;i++)
|
|
{
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[i].ulcNumEntries > lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries)
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags |= WAB_ERROR_DETECTED;
|
|
}
|
|
|
|
if(!WriteFile( hMPSWabFile,
|
|
(LPVOID) lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
(DWORD) sizeof(MPSWab_FILE_HEADER),
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Writing FileHeader Failed failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
if ( (lpMPSWabFileInfo->lpMPSWabFileHeader->ulModificationCount > MAX_ALLOWABLE_WASTED_SPACE_ENTRIES) ) // ||
|
|
{
|
|
// above condition means that if more than space for 50 entries is wasted
|
|
// of if number of modifications are more than the number of entries
|
|
// we should clean up the file
|
|
if (!CompressFile( lpMPSWabFileInfo,
|
|
hMPSWabFile,
|
|
NULL,
|
|
FALSE,
|
|
0))
|
|
{
|
|
DebugTrace(TEXT("Thread:%x\tCompress file failed\n"),GetCurrentThreadId());
|
|
hr = E_FAIL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
hr = S_OK;
|
|
|
|
|
|
out:
|
|
|
|
// UnTag this file as undergoing a write operation
|
|
// We only want the flag to stay there during crashes not during
|
|
// normal operations
|
|
//
|
|
if(lpMPSWabFileInfo)
|
|
{
|
|
if(!bUntagWriteTransaction( lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Untaggin file write failed\n"));
|
|
}
|
|
}
|
|
|
|
if (hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
//DebugTrace(TEXT("----Thread:%x\tDeleteRecords: Exit\n"),GetCurrentThreadId());
|
|
|
|
return(hr);
|
|
}
|
|
|
|
/*
|
|
-
|
|
- ReadRecordFreePropArray
|
|
*
|
|
* Memory from ReadRecord can be obtained through a convoluted plethora of different
|
|
* allocation types .. we therefore need to free it much more safely than other memory types
|
|
*
|
|
*/
|
|
void ReadRecordFreePropArray(HANDLE hPropertyStore, ULONG ulcPropCount, LPSPropValue * lppPropArray)
|
|
{
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
if( pt_bIsWABOpenExSession && //outlook session
|
|
!pt_bIsUnicodeOutlook && //outlook doesn't support Unicode
|
|
!lpfnAllocateMoreExternal ) //don't have an outlook allocator
|
|
{
|
|
// this is special case MAPI Allocated memory
|
|
FreeBufferAndNull(lppPropArray);
|
|
}
|
|
else
|
|
LocalFreePropArray(hPropertyStore, ulcPropCount, lppPropArray);
|
|
}
|
|
|
|
|
|
/*
|
|
-
|
|
- HrDupeOlkPropsAtoWC
|
|
*
|
|
* Outlook properties are unmungable without having the outlook allocators
|
|
* In an independent WAB session, the Outlook allocators are not available, hence
|
|
* we have to recreate the property arrays with the WAB allocators so we can modify
|
|
* them and turn them from Outlooks non-unicode to the WAB's needed unicode format.
|
|
*/
|
|
HRESULT HrDupeOlkPropsAtoWC(ULONG ulCount, LPSPropValue lpPropArray, LPSPropValue * lppSPVNew)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SCODE sc = 0;
|
|
LPSPropValue lpSPVNew = NULL;
|
|
ULONG cb = 0;
|
|
|
|
if (FAILED(sc = ScCountProps(ulCount, lpPropArray, &cb)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(sc = MAPIAllocateBuffer(cb, &lpSPVNew)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(sc = ScCopyProps(ulCount, lpPropArray, lpSPVNew, NULL)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
// [PaulHi] Raid 73237 @hack
|
|
// Outlook marks the contact as mail or DL (group) through the PR_DISPLAY_TYPE
|
|
// property tag. However, the WAB relies on the PR_OBJECT_TYPE tag to determine
|
|
// how the contact appears in the listview. If there is no PR_OBJECT_TYPE tag
|
|
// but there is a PR_DISPLAY_TYPE tag then convert it to PR_OBJECT_TYPE.
|
|
{
|
|
ULONG ul;
|
|
ULONG ulDpType = (ULONG)(-1);
|
|
BOOL bConvert = TRUE;
|
|
|
|
for (ul=0; ul<ulCount; ul++)
|
|
{
|
|
if (lpSPVNew[ul].ulPropTag == PR_OBJECT_TYPE)
|
|
{
|
|
bConvert = FALSE;
|
|
break;
|
|
}
|
|
else if (lpSPVNew[ul].ulPropTag == PR_DISPLAY_TYPE)
|
|
ulDpType = ul;
|
|
}
|
|
if ( bConvert && (ulDpType != (ULONG)(-1)) )
|
|
{
|
|
// Convert PR_DISPLAY_TYPE to PR_OBJECT_TYPE
|
|
lpSPVNew[ulDpType].ulPropTag = PR_OBJECT_TYPE;
|
|
if ( (lpSPVNew[ulDpType].Value.ul == DT_PRIVATE_DISTLIST) ||
|
|
(lpSPVNew[ulDpType].Value.ul == DT_DISTLIST) )
|
|
{
|
|
lpSPVNew[ulDpType].Value.ul = MAPI_DISTLIST;
|
|
}
|
|
else
|
|
{
|
|
lpSPVNew[ulDpType].Value.ul = MAPI_MAILUSER;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(FAILED(sc = ScConvertAPropsToW((LPALLOCATEMORE)(&MAPIAllocateMore), lpSPVNew, ulCount, 0)))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
*lppSPVNew = lpSPVNew;
|
|
|
|
exit:
|
|
if(HR_FAILED(hr))
|
|
{
|
|
FreeBufferAndNull(&lpSPVNew);
|
|
*lppSPVNew = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ReadRecord
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
// IN dwEntryID - EntryID of record to read
|
|
// IN ulFlags
|
|
// OUT ulcPropCount - number of props returned
|
|
// OUT lpPropArray - Array of Property values
|
|
//
|
|
// Basically, we find the record offset, read in the record, copy the data
|
|
// into SPropValue arrays and return the arrays
|
|
//
|
|
// IMPORTANT NOTE: To free memory allocated from here call ReadRecordFreePropArray
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT ReadRecord( IN HANDLE hPropertyStore,
|
|
IN LPSBinary lpsbEntryID,
|
|
IN ULONG ulFlags,
|
|
OUT LPULONG lpulcPropCount,
|
|
OUT LPPROPERTY_ARRAY * lppPropArray)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
BOOL bFileLocked = FALSE;
|
|
DWORD dwEntryID = 0;
|
|
MPSWab_RECORD_HEADER MPSWabRecordHeader = {0};
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = hPropertyStore;
|
|
SBinary sbEID = {0};
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
if(pt_bIsWABOpenExSession && !(ulFlags & AB_IGNORE_OUTLOOK))
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
if(!hPropertyStore)
|
|
return MAPI_E_NOT_INITIALIZED;
|
|
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
|
|
hr = lpWSP->lpVtbl->ReadRecord( lpWSP,
|
|
lpsbEntryID,
|
|
ulFlags,
|
|
lpulcPropCount,
|
|
lppPropArray);
|
|
|
|
DebugTrace(TEXT("WABStorageProvider::ReadRecord returned:%x\n"),hr);
|
|
|
|
if(!HR_FAILED(hr) && *lpulcPropCount && *lppPropArray && !pt_bIsUnicodeOutlook)
|
|
{
|
|
// Map all the contacts props to Unicode if needed since Outlook9 and older don't
|
|
// support unicode
|
|
SCODE sc = 0;
|
|
if(lpfnAllocateMoreExternal)
|
|
{
|
|
// the memory that comes from outlook is allocated using outlooks allocators
|
|
// and we can't mess with it .. unless we have the allocators passed in through
|
|
// wabopenex
|
|
if(sc = ScConvertAPropsToW(lpfnAllocateMoreExternal, *lppPropArray, *lpulcPropCount, 0))
|
|
hr = ResultFromScode(sc);
|
|
}
|
|
else
|
|
{
|
|
// we don't have external allocators, which means we need to muck with reallocating memory etc
|
|
// therefore we'll need to duplicate the prop array and then convert it
|
|
//
|
|
// Because of this mess, we need to have a special way of releasing this memory so
|
|
// we don't leak all over the place
|
|
ULONG ulCount = *lpulcPropCount;
|
|
LPSPropValue lpSPVNew = NULL;
|
|
|
|
if(HR_FAILED(hr = HrDupeOlkPropsAtoWC(ulCount, *lppPropArray, &lpSPVNew)))
|
|
goto exit;
|
|
|
|
// Free the old props
|
|
LocalFreePropArray(hPropertyStore, *lpulcPropCount, lppPropArray);
|
|
*lppPropArray = lpSPVNew;
|
|
*lpulcPropCount = ulCount;
|
|
}
|
|
}
|
|
exit:
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
|
|
if(lpsbEntryID && lpsbEntryID->cb != SIZEOF_WAB_ENTRYID)
|
|
{
|
|
// this may be a WAB container .. reset the entryid to a WAB entryid
|
|
if(WAB_CONTAINER == IsWABEntryID(lpsbEntryID->cb, (LPENTRYID)lpsbEntryID->lpb,
|
|
NULL,NULL,NULL,NULL,NULL))
|
|
{
|
|
IsWABEntryID(lpsbEntryID->cb, (LPENTRYID)lpsbEntryID->lpb,
|
|
(LPVOID*)&sbEID.lpb, (LPVOID*)&sbEID.cb, NULL,NULL,NULL);
|
|
if(sbEID.cb == SIZEOF_WAB_ENTRYID)
|
|
lpsbEntryID = &sbEID;
|
|
}
|
|
}
|
|
if(!lpsbEntryID || lpsbEntryID->cb != SIZEOF_WAB_ENTRYID)
|
|
{
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
|
|
CopyMemory(&dwEntryID, lpsbEntryID->lpb, min(lpsbEntryID->cb, sizeof(dwEntryID)));
|
|
|
|
//DebugTrace(TEXT("--ReadRecord: dwEntryID=%d\n"), dwEntryID);
|
|
|
|
*lpulcPropCount = 0;
|
|
*lppPropArray = NULL;
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
|
|
//Open the file
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// To ensure that file info is accurate,
|
|
// Any time we open a file, read the file info again ...
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Anytime we detect an error - try to fix it ...
|
|
//
|
|
if((lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_ERROR_DETECTED) ||
|
|
(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_WRITE_IN_PROGRESS))
|
|
{
|
|
if(HR_FAILED(HrDoQuickWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile)))
|
|
{
|
|
hr = HrDoDetailedWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("HrDoDetailedWABIntegrityCheck failed:%x\n"),hr);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = ReadRecordWithoutLocking(
|
|
hMPSWabFile,
|
|
lpMPSWabFileInfo,
|
|
dwEntryID,
|
|
lpulcPropCount,
|
|
lppPropArray);
|
|
|
|
|
|
out:
|
|
|
|
//a little cleanup on failure
|
|
if (FAILED(hr))
|
|
{
|
|
if ((*lppPropArray) && (MPSWabRecordHeader.ulcPropCount > 0))
|
|
{
|
|
LocalFreePropArray(hPropertyStore, MPSWabRecordHeader.ulcPropCount, lppPropArray);
|
|
*lppPropArray = NULL;
|
|
}
|
|
}
|
|
|
|
if(hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
|
|
//DebugTrace(( TEXT("ReadRecord: Exit\n-----------\n")));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
#ifdef OLD_STUFF /*
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ReadIndex - Given a specified proptag, returns an array containing all the
|
|
// data in the addressbook that corresponds to the supplied proptag
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
// IN ulPropTag - EntryID of record to read
|
|
// OUT lpulEIDCount - number of props returned
|
|
// OUT lppdwIndex - Array of Property values
|
|
//
|
|
// Basically, we find the record offset, read in the record, copy the data
|
|
// into SPropValue arrays and return the arrays.
|
|
//
|
|
// Each SPropValue within the array corresponds to data for that prop in
|
|
// the property store. The SPropVal.Value holds the data and the
|
|
// SPropVal.ulPropTag holds the **ENTRY-ID** of the record containing
|
|
// the data and not any prop tag value
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT ReadIndex( IN HANDLE hPropertyStore,
|
|
IN PROPERTY_TAG ulPropTag,
|
|
OUT LPULONG lpulEIDCount,
|
|
OUT LPPROPERTY_ARRAY * lppdwIndex)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
SPropertyRestriction PropRes;
|
|
ULONG ulPropCount = 0;
|
|
ULONG ulEIDCount = 0;
|
|
//ULONG ulArraySize = 0;
|
|
LPDWORD lpdwEntryIDs = NULL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
DWORD dwNumofBytes = 0;
|
|
LPPROPERTY_ARRAY lpPropArray = NULL;
|
|
TCHAR * szBuf = NULL;
|
|
TCHAR * lp = NULL;
|
|
ULONG i=0,j=0,k=0;
|
|
ULONG nIndexPos=0,ulRecordOffset = 0;
|
|
BOOL bFileLocked = FALSE;
|
|
BOOL bMatchFound = FALSE;
|
|
ULONG ulDataSize = 0;
|
|
ULONG ulcValues = 0;
|
|
ULONG ulTmpPropTag = 0;
|
|
ULONG ulFileSize = 0;
|
|
BOOL bErrorDetected = FALSE;
|
|
|
|
|
|
MPSWab_RECORD_HEADER MPSWabRecordHeader = {0};
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = hPropertyStore;
|
|
|
|
DebugTrace(( TEXT("-----------\nReadIndex: Entry\n")));
|
|
|
|
*lpulEIDCount = 0;
|
|
*lppdwIndex = NULL;
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
|
|
PropRes.ulPropTag = ulPropTag;
|
|
PropRes.relop = RELOP_EQ;
|
|
PropRes.lpProp = NULL;
|
|
|
|
hr = FindRecords( IN hPropertyStore,
|
|
IN AB_MATCH_PROP_ONLY,
|
|
FALSE,
|
|
&PropRes,
|
|
&ulEIDCount,
|
|
&lpdwEntryIDs);
|
|
|
|
if (FAILED(hr))
|
|
goto out;
|
|
|
|
//reset hr
|
|
hr = E_FAIL;
|
|
|
|
if (ulEIDCount == 0)
|
|
{
|
|
DebugTrace(TEXT("No Records Found\n"));
|
|
hr = MAPI_E_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
|
|
// We now know that we are going to get ulEIDCount records
|
|
// We will assume that each record has only 1 property which we are interested in
|
|
|
|
lpPropArray = LocalAlloc(LMEM_ZEROINIT, ulEIDCount * sizeof(SPropValue));
|
|
if (!lpPropArray)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
|
|
//Open the file
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
ulFileSize = GetFileSize(hMPSWabFile, NULL);
|
|
|
|
//
|
|
// To ensure that file info is accurate,
|
|
// Any time we open a file, read the file info again ...
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Anytime we detect an error - try to fix it ...
|
|
//
|
|
if((lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_ERROR_DETECTED) ||
|
|
(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_WRITE_IN_PROGRESS))
|
|
{
|
|
if(HR_FAILED(HrDoQuickWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile)))
|
|
{
|
|
hr = HrDoDetailedWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("HrDoDetailedWABIntegrityCheck failed:%x\n"),hr);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ulArraySize = 0;
|
|
*lpulEIDCount = 0;
|
|
|
|
ulPropCount = 0;
|
|
for(i = 0; i < ulEIDCount; i++)
|
|
{
|
|
|
|
//Get offset for this entryid
|
|
if (!BinSearchEID( IN lpMPSWabFileInfo->lpMPSWabIndexEID,
|
|
IN lpdwEntryIDs[i],
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries,
|
|
OUT &nIndexPos))
|
|
{
|
|
DebugTrace(TEXT("Specified EntryID doesnt exist!\n"));
|
|
continue;
|
|
//goto out;
|
|
}
|
|
|
|
ulRecordOffset = lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos].ulOffset;
|
|
|
|
if(!ReadDataFromWABFile(hMPSWabFile,
|
|
ulRecordOffset,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
(DWORD) sizeof(MPSWab_RECORD_HEADER)))
|
|
goto out;
|
|
|
|
|
|
if(!bIsValidRecord( MPSWabRecordHeader,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID,
|
|
ulRecordOffset,
|
|
ulFileSize))
|
|
// if ((MPSWabRecordHeader.bValidRecord == FALSE) && (MPSWabRecordHeader.bValidRecord != TRUE))
|
|
{
|
|
//this should never happen but who knows
|
|
DebugTrace(TEXT("Error: Obtained an invalid record ...\n"));
|
|
bErrorDetected = TRUE;
|
|
//hr = MAPI_E_INVALID_OBJECT;
|
|
//goto out;
|
|
//ignore it and continue
|
|
continue;
|
|
}
|
|
|
|
|
|
//ReadData
|
|
LocalFreeAndNull(&szBuf);
|
|
|
|
szBuf = LocalAlloc(LMEM_ZEROINIT, MPSWabRecordHeader.ulRecordDataSize);
|
|
if (!szBuf)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// Set File Pointer to beginning of Data Section
|
|
if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
|
|
MPSWabRecordHeader.ulPropTagArraySize,
|
|
NULL,
|
|
FILE_CURRENT))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
//Read in the data
|
|
// Read record header
|
|
if(!ReadFile( hMPSWabFile,
|
|
(LPVOID) szBuf,
|
|
(DWORD) MPSWabRecordHeader.ulRecordDataSize,
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Reading Record Header failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
lp = szBuf;
|
|
|
|
// Go through all the properties in this record searching for the
|
|
// desired one ...
|
|
|
|
bMatchFound = FALSE;
|
|
ulDataSize = 0;
|
|
ulcValues = 0;
|
|
ulTmpPropTag = 0;
|
|
|
|
for (j = 0; j< MPSWabRecordHeader.ulcPropCount; j++)
|
|
{
|
|
|
|
CopyMemory(&ulTmpPropTag,lp,sizeof(ULONG));
|
|
lp+=sizeof(ULONG);
|
|
|
|
if ((ulTmpPropTag & MV_FLAG))
|
|
{
|
|
CopyMemory(&ulcValues,lp,sizeof(ULONG));
|
|
lp += sizeof(ULONG); //skip cValues
|
|
}
|
|
CopyMemory(&ulDataSize,lp,sizeof(ULONG));
|
|
lp+=sizeof(ULONG);
|
|
|
|
// if the tag doesnt match, skip this property
|
|
if (ulTmpPropTag != ulPropTag) //skip
|
|
{
|
|
lp += ulDataSize; //skip over data
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
bMatchFound = TRUE;
|
|
break;
|
|
}
|
|
} // for j ...
|
|
|
|
if (bMatchFound)
|
|
{
|
|
|
|
//
|
|
// if we are here, the property matched and we want its data
|
|
//
|
|
|
|
//
|
|
// **** note: ***** we store the entryid in the proptag variable
|
|
//
|
|
lpPropArray[ulPropCount].ulPropTag = lpdwEntryIDs[i];
|
|
|
|
if ((ulPropTag & MV_FLAG))
|
|
{
|
|
//now get the data
|
|
switch(PROP_TYPE(ulPropTag))
|
|
{
|
|
case(PT_MV_I2):
|
|
case(PT_MV_LONG):
|
|
case(PT_MV_R4):
|
|
case(PT_MV_DOUBLE):
|
|
case(PT_MV_CURRENCY):
|
|
case(PT_MV_APPTIME):
|
|
case(PT_MV_SYSTIME):
|
|
case(PT_MV_CLSID):
|
|
case(PT_MV_I8):
|
|
lpPropArray[ulPropCount].Value.MVi.lpi = LocalAlloc(LMEM_ZEROINIT,ulDataSize);
|
|
if (!(lpPropArray[ulPropCount].Value.MVi.lpi))
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
lpPropArray[ulPropCount].Value.MVi.cValues = ulcValues;
|
|
CopyMemory(lpPropArray[ulPropCount].Value.MVi.lpi, lp, ulDataSize);
|
|
lp += ulDataSize;
|
|
break;
|
|
|
|
case(PT_MV_BINARY):
|
|
lpPropArray[ulPropCount].Value.MVbin.lpbin = LocalAlloc(LMEM_ZEROINIT, ulcValues * sizeof(SBinary));
|
|
if (!(lpPropArray[ulPropCount].Value.MVbin.lpbin))
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
lpPropArray[ulPropCount].Value.MVbin.cValues = ulcValues;
|
|
for (k=0;k<ulcValues;k++)
|
|
{
|
|
ULONG nLen;
|
|
// copy cBytes
|
|
CopyMemory(&nLen, lp, sizeof(ULONG));
|
|
lp += sizeof(ULONG);
|
|
lpPropArray[ulPropCount].Value.MVbin.lpbin[k].cb = nLen;
|
|
lpPropArray[ulPropCount].Value.MVbin.lpbin[k].lpb = LocalAlloc(LMEM_ZEROINIT, nLen);
|
|
if (!(lpPropArray[ulPropCount].Value.MVbin.lpbin[k].lpb))
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(lpPropArray[ulPropCount].Value.MVbin.lpbin[k].lpb, lp, nLen);
|
|
lp += nLen;
|
|
}
|
|
lpPropArray[ulPropCount].Value.MVbin.cValues = ulcValues;
|
|
break;
|
|
|
|
case(PT_MV_TSTRING):
|
|
lpPropArray[ulPropCount].Value.MVSZ.LPPSZ = LocalAlloc(LMEM_ZEROINIT, ulcValues * sizeof(LPTSTR));
|
|
if (!(lpPropArray[ulPropCount].Value.MVSZ.LPPSZ))
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
for (k=0;k<ulcValues;k++)
|
|
{
|
|
ULONG nLen;
|
|
// get string length (includes terminating zero)
|
|
CopyMemory(&nLen, lp, sizeof(ULONG));
|
|
lp += sizeof(ULONG);
|
|
lpPropArray[ulPropCount].Value.MVSZ.LPPSZ[k] = LocalAlloc(LMEM_ZEROINIT, nLen);
|
|
if (!(lpPropArray[ulPropCount].Value.MVSZ.LPPSZ[k]))
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(lpPropArray[ulPropCount].Value.MVSZ.LPPSZ[k], lp, nLen);
|
|
lp += nLen;
|
|
}
|
|
lpPropArray[ulPropCount].Value.MVSZ.cValues = ulcValues;
|
|
break;
|
|
|
|
} //switch
|
|
}
|
|
else
|
|
{
|
|
//Single Valued
|
|
switch(PROP_TYPE(ulPropTag))
|
|
{
|
|
case(PT_I2):
|
|
case(PT_LONG):
|
|
case(PT_APPTIME):
|
|
case(PT_SYSTIME):
|
|
case(PT_R4):
|
|
case(PT_BOOLEAN):
|
|
case(PT_CURRENCY):
|
|
case(PT_I8):
|
|
CopyMemory(&(lpPropArray[ulPropCount].Value.i),lp,ulDataSize);
|
|
lp+=ulDataSize;
|
|
break;
|
|
case(PT_CLSID):
|
|
case(PT_TSTRING):
|
|
lpPropArray[ulPropCount].Value.LPSZ = LocalAlloc(LMEM_ZEROINIT,ulDataSize);
|
|
if (!(lpPropArray[ulPropCount].Value.LPSZ))
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(lpPropArray[ulPropCount].Value.LPSZ,lp,ulDataSize);
|
|
lp+=ulDataSize;
|
|
break;
|
|
case(PT_BINARY):
|
|
lpPropArray[ulPropCount].Value.bin.lpb = LocalAlloc(LMEM_ZEROINIT,ulDataSize);
|
|
if (!(lpPropArray[ulPropCount].Value.bin.lpb))
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(lpPropArray[ulPropCount].Value.bin.lpb,lp,ulDataSize);
|
|
lpPropArray[ulPropCount].Value.bin.cb = ulDataSize;
|
|
lp+=ulDataSize;
|
|
break;
|
|
|
|
} //switch
|
|
|
|
} // if MV_PROP
|
|
|
|
ulPropCount++;
|
|
|
|
|
|
} // if bMatchFound
|
|
|
|
} //for i=
|
|
|
|
DebugTrace(( TEXT("ulPropCount: %d\tulEIDCount: %d\n"),ulPropCount, ulEIDCount));
|
|
|
|
//Got all the prop tags
|
|
if (lpPropArray)
|
|
{
|
|
*lpulEIDCount = ulPropCount;
|
|
*lppdwIndex = lpPropArray;
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
out:
|
|
|
|
LocalFreeAndNull(&lpdwEntryIDs);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if((lpPropArray) && (ulEIDCount > 0))
|
|
LocalFreePropArray(hPropertyStore, ulEIDCount,&lpPropArray);
|
|
*lppdwIndex = NULL;
|
|
*lpulEIDCount = 0;
|
|
}
|
|
|
|
if(bErrorDetected)
|
|
TagWABFileError(lpMPSWabFileInfo->lpMPSWabFileHeader, hMPSWabFile);
|
|
|
|
if(hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
DebugTrace(( TEXT("ReadIndex: Exit\n-----------\n")));
|
|
|
|
return(hr);
|
|
}
|
|
*/
|
|
#endif // OLD_STUFF
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// BackupPropertyStore - Creates a clean, backup version of the property store
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
// IN lpszBackupFileName - name to back up in ...
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT BackupPropertyStore(HANDLE hPropertyStore, LPTSTR lpszBackupFileName)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
BOOL bWFileLocked = FALSE;
|
|
DWORD dwNumofBytes = 0;
|
|
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = hPropertyStore;
|
|
|
|
HCURSOR hOldCur = SetCursor(LoadCursor(NULL,IDC_WAIT));
|
|
|
|
DebugTrace(( TEXT("BackupPropertyStore: Entry\n")));
|
|
|
|
if (lpszBackupFileName == NULL)
|
|
{
|
|
DebugTrace(TEXT("Invalid backup file name\n"));
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bWFileLocked = TRUE;
|
|
}
|
|
|
|
hMPSWabFile = CreateFile( lpMPSWabFileInfo->lpszMPSWabFileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_RANDOM_ACCESS,
|
|
(HANDLE) NULL);
|
|
if (hMPSWabFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// We dont want to back up this file if it has errors in it so first
|
|
// check for errors
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
if(!(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_BACKUP_NOW))
|
|
{
|
|
DebugTrace(( TEXT("No need to backup!\n")));
|
|
hr = S_OK;
|
|
goto out;
|
|
}
|
|
|
|
if(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & (WAB_ERROR_DETECTED | WAB_WRITE_IN_PROGRESS))
|
|
{
|
|
DebugTrace(TEXT("Errors in file - Won't backup!\n"));
|
|
goto out;
|
|
}
|
|
|
|
DebugTrace( TEXT("Backing up to %s\n"),lpszBackupFileName);
|
|
|
|
//
|
|
// reset the backup flag before backing up
|
|
//
|
|
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags &= ~WAB_BACKUP_NOW;
|
|
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
0,
|
|
(LPVOID) lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
sizeof(MPSWab_FILE_HEADER)))
|
|
goto out;
|
|
|
|
if (!CompressFile( lpMPSWabFileInfo,
|
|
hMPSWabFile,
|
|
lpszBackupFileName,
|
|
FALSE,
|
|
0))
|
|
{
|
|
DebugTrace(TEXT("Compress file failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
//SetFileAttributes(lpszBackupFileName, FILE_ATTRIBUTE_HIDDEN);
|
|
|
|
hr = S_OK;
|
|
|
|
|
|
out:
|
|
|
|
if (hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if(bWFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
SetCursor(hOldCur);
|
|
|
|
DebugTrace(( TEXT("BackupPropertyStore: Exit\n")));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// UnlockFileAccess - UnLocks Exclusive Access to the property store
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL UnLockFileAccessTmp(LPMPSWab_FILE_INFO lpMPSWabFileInfo)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
DebugTrace(( TEXT("\t\tUnlockFileAccess: Entry\n")));
|
|
|
|
if(lpMPSWabFileInfo)
|
|
bRet = ReleaseMutex(lpMPSWabFileInfo->hDataAccessMutex);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LockFileAccess - Gives exclusive access to the Property Store
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL LockFileAccessTmp(LPMPSWab_FILE_INFO lpMPSWabFileInfo)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
DWORD dwWait = 0;
|
|
|
|
DebugTrace(( TEXT("\t\tLockFileAccess: Entry\n")));
|
|
|
|
if(lpMPSWabFileInfo)
|
|
{
|
|
dwWait = WaitForSingleObject(lpMPSWabFileInfo->hDataAccessMutex,MAX_LOCK_FILE_TIMEOUT);
|
|
|
|
if ((dwWait == WAIT_TIMEOUT) || (dwWait == WAIT_FAILED))
|
|
{
|
|
DebugTrace(TEXT("Thread:%x\tWaitFOrSingleObject failed.\n"),GetCurrentThreadId());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
return(bRet);
|
|
|
|
}
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ReloadMPSWabFileInfo - Reloads the MPSWabFileHeader and reloads the
|
|
// memory indexes. This is a performance hit but cant be helped since it
|
|
// is the most reliable way to ensure we are working with the latest
|
|
// valid information
|
|
//
|
|
// Thus a write by one program cannot mess up the read by another program
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL ReloadMPSWabFileInfoTmp(HANDLE hPropertyStore)
|
|
{
|
|
HANDLE hMPSWabFile = NULL;
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = hPropertyStore;
|
|
|
|
BOOL bRet = FALSE;
|
|
DWORD dwNumofBytes = 0;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
if(0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
|
|
0,
|
|
NULL,
|
|
FILE_BEGIN))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
bRet = ReloadMPSWabFileInfo(lpMPSWabFileInfo,hMPSWabFile);
|
|
|
|
out:
|
|
|
|
if (hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LockPropertyStore - Locks the property store and reloads the indexes so we have
|
|
// the most current ones ...
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT LockPropertyStore(IN HANDLE hPropertyStore)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = (LPMPSWab_FILE_INFO) hPropertyStore;
|
|
|
|
if (!LockFileAccessTmp(lpMPSWabFileInfo))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
// reload the indexes
|
|
if(!ReloadMPSWabFileInfoTmp(hPropertyStore))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
hr = hrSuccess;
|
|
|
|
out:
|
|
return hr;
|
|
}
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// UnLockPropertyStore -
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT UnlockPropertyStore(IN HANDLE hPropertyStore)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = (LPMPSWab_FILE_INFO) hPropertyStore;
|
|
|
|
if (!UnLockFileAccessTmp(lpMPSWabFileInfo))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
hr = hrSuccess;
|
|
|
|
out:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ReadPropArray - Given a specified array of proptags and a search key,
|
|
// finds all records with that search key, and reads the records for
|
|
// all the props specified in the proptagarray.
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
// IN pmbinFold - <Outlook> EntryID of folder to search in (NULL for default)
|
|
// IN SPropRes - property restriction set specifying what we're searching for
|
|
// IN ulFlags - search flags - only acceptable one is AB_MATCH_PROP_ONLY
|
|
// IN ulcPropTagCount - number of props per record requested
|
|
// IN lpPropTagArray - array of ulPropTags to return
|
|
// OUT lpContentList - List of AdrEntry structures corresponding to each matched record.
|
|
// SPropValue of each structure contains the requested props.
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT ReadPropArray( IN HANDLE hPropertyStore,
|
|
IN LPSBinary pmbinFold,
|
|
IN SPropertyRestriction * lpPropRes,
|
|
IN ULONG ulSearchFlags,
|
|
IN ULONG ulcPropTagCount,
|
|
IN LPULONG lpPTArray,
|
|
OUT LPCONTENTLIST * lppContentList)
|
|
{
|
|
LPULONG lpPropTagArray = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
SCODE sc = SUCCESS_SUCCESS;
|
|
ULONG ulcEIDCount = 0;
|
|
LPSBinary rgsbEntryIDs = NULL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
DWORD dwNumofBytes = 0;
|
|
LPBYTE szBuf = NULL;
|
|
LPBYTE lp = NULL;
|
|
LPCONTENTLIST lpContentList = NULL;
|
|
ULONG i=0,j=0,k=0;
|
|
ULONG nIndexPos=0,ulRecordOffset = 0;
|
|
BOOL bFileLocked = FALSE;
|
|
LPSPropValue lpPropArray = NULL;
|
|
ULONG ulcFoundPropCount = 0; //Counts the number of finds so it can exit early
|
|
BOOL * lpbFoundProp = NULL;
|
|
ULONG ulFileSize = 0;
|
|
int nCount=0;
|
|
BOOL bErrorDetected = FALSE;
|
|
|
|
|
|
MPSWab_RECORD_HEADER MPSWabRecordHeader = {0};
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo;
|
|
|
|
|
|
//DebugTrace(("-----------\nReadPropArray: Entry\n"));
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
// Check arguments
|
|
if(ulcPropTagCount < 1)
|
|
return(MAPI_E_INVALID_PARAMETER);
|
|
|
|
if(pt_bIsWABOpenExSession)
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
ULONG ulFlags = ulSearchFlags;
|
|
|
|
if(!hPropertyStore)
|
|
return MAPI_E_NOT_INITIALIZED;
|
|
|
|
if(ulFlags & AB_UNICODE && !pt_bIsUnicodeOutlook)
|
|
ulFlags &= ~AB_UNICODE;
|
|
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
LPSPropertyRestriction lpPropResA = NULL;
|
|
|
|
if( !pt_bIsUnicodeOutlook)
|
|
{
|
|
// Need to thunk the restriction down to ANSI
|
|
HrDupePropResWCtoA(ulFlags, lpPropRes, &lpPropResA);
|
|
|
|
// Since the native Outlook properties are all non-UNICODE, if someone is requesting
|
|
// Unicode data, we need to convert requsted Unicode props in the PropTagArray into ANSI props
|
|
//
|
|
if(ulSearchFlags & AB_UNICODE)
|
|
{
|
|
if(!(lpPropTagArray = LocalAlloc(LMEM_ZEROINIT, sizeof(ULONG)*ulcPropTagCount)))
|
|
return MAPI_E_NOT_ENOUGH_MEMORY;
|
|
for(i=0;i<ulcPropTagCount;i++)
|
|
{
|
|
if(PROP_TYPE(lpPTArray[i]) == PT_UNICODE)
|
|
lpPropTagArray[i] = CHANGE_PROP_TYPE(lpPTArray[i], PT_STRING8);
|
|
else if(PROP_TYPE(lpPTArray[i]) == PT_MV_UNICODE)
|
|
lpPropTagArray[i] = CHANGE_PROP_TYPE(lpPTArray[i], PT_MV_STRING8);
|
|
else
|
|
lpPropTagArray[i] = lpPTArray[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lpPropTagArray = lpPTArray;
|
|
}
|
|
|
|
}
|
|
|
|
hr = lpWSP->lpVtbl->ReadPropArray(lpWSP,
|
|
(pmbinFold && pmbinFold->cb && pmbinFold->lpb) ? pmbinFold : NULL,
|
|
lpPropResA ? lpPropResA : lpPropRes,
|
|
ulFlags,
|
|
ulcPropTagCount,
|
|
lpPTArray,
|
|
lppContentList);
|
|
|
|
DebugTrace(TEXT("WABStorageProvider::ReadPropArray returned:%x\n"),hr);
|
|
|
|
if(lpPropResA)
|
|
{
|
|
FreeBufferAndNull(&lpPropResA->lpProp);
|
|
FreeBufferAndNull(&lpPropResA);
|
|
}
|
|
|
|
if(ulSearchFlags & AB_UNICODE && !pt_bIsUnicodeOutlook)
|
|
{
|
|
// Sender specifically requested Unicode data which Outlook doesn't return
|
|
// so need to modify the returned data list ..
|
|
if(!HR_FAILED(hr) && *lppContentList)
|
|
{
|
|
LPCONTENTLIST lpAdrList = *lppContentList;
|
|
for(i=0;lpAdrList->cEntries;i++)
|
|
{
|
|
// Now we thunk the data back to ANSI for Outlook
|
|
// ignore errors for now
|
|
if(lpAdrList->aEntries[i].rgPropVals)
|
|
ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), lpAdrList->aEntries[i].rgPropVals, lpAdrList->aEntries[i].cValues, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(lpPropTagArray != lpPTArray)
|
|
LocalFreeAndNull(&lpPropTagArray);
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo = hPropertyStore;
|
|
|
|
|
|
if ((ulSearchFlags & ~(AB_MATCH_PROP_ONLY|AB_UNICODE) ) ||
|
|
(!(lpPTArray)) ||
|
|
(!(lpPropRes)) ||
|
|
( ulcPropTagCount == 0 ) ||
|
|
( hPropertyStore == NULL))
|
|
{
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
// Since the native properties are all UNICODE, if someone is NOT requesting
|
|
// Unicode data, we need to convert ANSI props in the PropTagArray into UNICODE props
|
|
//
|
|
if(!(ulSearchFlags & AB_UNICODE))
|
|
{
|
|
if(!(lpPropTagArray = LocalAlloc(LMEM_ZEROINIT, sizeof(ULONG)*ulcPropTagCount)))
|
|
{
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
for(i=0;i<ulcPropTagCount;i++)
|
|
{
|
|
if(PROP_TYPE(lpPTArray[i]) == PT_STRING8)
|
|
lpPropTagArray[i] = CHANGE_PROP_TYPE(lpPTArray[i], PT_UNICODE);
|
|
else if(PROP_TYPE(lpPTArray[i]) == PT_MV_STRING8)
|
|
lpPropTagArray[i] = CHANGE_PROP_TYPE(lpPTArray[i], PT_MV_UNICODE);
|
|
else
|
|
lpPropTagArray[i] = lpPTArray[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lpPropTagArray = lpPTArray;
|
|
}
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
|
|
hr = FindRecords( IN hPropertyStore,
|
|
pmbinFold,
|
|
IN ulSearchFlags,
|
|
FALSE,
|
|
lpPropRes,
|
|
&ulcEIDCount,
|
|
&rgsbEntryIDs);
|
|
|
|
if (FAILED(hr))
|
|
goto out;
|
|
|
|
if (ulcEIDCount == 0)
|
|
{
|
|
DebugTrace(TEXT("No Records Found\n"));
|
|
hr = MAPI_E_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
|
|
//reset hr
|
|
hr = E_FAIL;
|
|
|
|
|
|
//Open the file
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
ulFileSize = GetFileSize(hMPSWabFile, NULL);
|
|
|
|
|
|
//
|
|
// To ensure that file info is accurate,
|
|
// Any time we open a file, read the file info again ...
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Anytime we detect an error - try to fix it ...
|
|
//
|
|
if((lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_ERROR_DETECTED) ||
|
|
(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_WRITE_IN_PROGRESS))
|
|
{
|
|
if(HR_FAILED(HrDoQuickWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile)))
|
|
{
|
|
hr = HrDoDetailedWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("HrDoDetailedWABIntegrityCheck failed:%x\n"),hr);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
*lppContentList = NULL;
|
|
|
|
// we know we matched ulcEIDCount records so
|
|
// pre-create an array of that many records
|
|
|
|
*lppContentList = LocalAlloc(LMEM_ZEROINIT, sizeof(CONTENTLIST) + ulcEIDCount * sizeof(ADRENTRY));
|
|
if(!(*lppContentList))
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed to allocate memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
lpContentList = (*lppContentList);
|
|
lpContentList->cEntries = ulcEIDCount;
|
|
nCount = 0;
|
|
|
|
for (i = 0; i < ulcEIDCount; i++)
|
|
{
|
|
DWORD dwEID = 0;
|
|
LPADRENTRY lpAdrEntry = &(lpContentList->aEntries[nCount]);
|
|
|
|
CopyMemory(&dwEID, rgsbEntryIDs[i].lpb, min(rgsbEntryIDs[i].cb, sizeof(dwEID)));
|
|
|
|
//Get offset for this entryid
|
|
if (!BinSearchEID( IN lpMPSWabFileInfo->lpMPSWabIndexEID,
|
|
IN dwEID,
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries,
|
|
OUT &nIndexPos))
|
|
{
|
|
DebugTrace(TEXT("Specified EntryID doesnt exist!\n"));
|
|
// skip this entry ... we'd rather ignore than fail ...
|
|
continue;
|
|
//goto out;
|
|
}
|
|
|
|
ulRecordOffset = lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos].ulOffset;
|
|
|
|
if(!ReadDataFromWABFile(hMPSWabFile,
|
|
ulRecordOffset,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
(DWORD) sizeof(MPSWab_RECORD_HEADER)))
|
|
goto out;
|
|
|
|
|
|
if(!bIsValidRecord( MPSWabRecordHeader,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID,
|
|
ulRecordOffset,
|
|
ulFileSize))
|
|
{
|
|
//this should never happen but who knows
|
|
DebugTrace(TEXT("Error: Obtained an invalid record ...\n"));
|
|
bErrorDetected = TRUE;
|
|
// skip rather than fail
|
|
continue;
|
|
}
|
|
|
|
if(MPSWabRecordHeader.ulObjType == RECORD_CONTAINER)
|
|
continue; //skip the container records - dont want them in our contents tables
|
|
|
|
|
|
|
|
//Allocate each AdrEntry Structure
|
|
lpAdrEntry->cValues = ulcPropTagCount;
|
|
|
|
lpAdrEntry->rgPropVals = LocalAlloc(LMEM_ZEROINIT, ulcPropTagCount * sizeof(SPropValue));
|
|
if(!(lpAdrEntry->rgPropVals))
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed to allocate memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
|
|
// Initialize this rgPropVals empty array.
|
|
// We set all the proptypes to PT_ERROR so that if any property
|
|
// could not be found in the record, its corresponding property is already
|
|
// initialized to an Error
|
|
for(j=0;j<ulcPropTagCount;j++)
|
|
{
|
|
lpAdrEntry->rgPropVals[j].ulPropTag = PROP_TAG(PT_ERROR,0x0000);
|
|
}
|
|
|
|
|
|
//ReadData
|
|
LocalFreeAndNull(&szBuf);
|
|
szBuf = LocalAlloc(LMEM_ZEROINIT, MPSWabRecordHeader.ulRecordDataSize);
|
|
if (!szBuf)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// Set File Pointer to beginning of Data Section
|
|
if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
|
|
MPSWabRecordHeader.ulPropTagArraySize,
|
|
NULL,
|
|
FILE_CURRENT))
|
|
{
|
|
DebugTrace(TEXT("SetFilePointer Failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
//Read in the data
|
|
// Read record header
|
|
if(!ReadFile( hMPSWabFile,
|
|
(LPVOID) szBuf,
|
|
(DWORD) MPSWabRecordHeader.ulRecordDataSize,
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Reading Record Header failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
lp = szBuf;
|
|
|
|
// Go through all the properties in this record searching for the
|
|
// desired ones ...
|
|
|
|
|
|
// We also initialize a bool array that tracks if each individual
|
|
// property has been set ... this prevents us from overwriting a prop
|
|
// once it has been found
|
|
LocalFreeAndNull(&lpbFoundProp);
|
|
|
|
lpbFoundProp = LocalAlloc(LMEM_ZEROINIT, sizeof(BOOL) * ulcPropTagCount);
|
|
if(!lpbFoundProp)
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed to allocate memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
|
|
for(j=0;j<ulcPropTagCount;j++)
|
|
lpbFoundProp[j]=FALSE;
|
|
|
|
ulcFoundPropCount = 0;
|
|
|
|
|
|
|
|
for (j = 0; j< MPSWabRecordHeader.ulcPropCount; j++)
|
|
{
|
|
LPSPropValue lpSPropVal=NULL;
|
|
ULONG ulDataSize = 0;
|
|
ULONG ulcValues = 0;
|
|
ULONG ulTmpPropTag = 0;
|
|
BOOL bPropMatch = FALSE;
|
|
ULONG ulPropMatchIndex = 0;
|
|
|
|
// Did we find as many props as we were looking for?
|
|
// if yes, then dont look for any more
|
|
if (ulcFoundPropCount == ulcPropTagCount)
|
|
break;
|
|
|
|
// Get the fresh property tag
|
|
CopyMemory(&ulTmpPropTag,lp,sizeof(ULONG));
|
|
lp+=sizeof(ULONG);
|
|
|
|
if ((ulTmpPropTag & MV_FLAG)) // MVProps have an additional cValues thrown in
|
|
{
|
|
CopyMemory(&ulcValues,lp,sizeof(ULONG));
|
|
lp += sizeof(ULONG);
|
|
}
|
|
|
|
//Get the prop data size
|
|
CopyMemory(&ulDataSize,lp,sizeof(ULONG));
|
|
lp+=sizeof(ULONG);
|
|
|
|
// Check if we want this property
|
|
for(k=0;k<ulcPropTagCount;k++)
|
|
{
|
|
if (ulTmpPropTag == lpPropTagArray[k])
|
|
{
|
|
bPropMatch = TRUE;
|
|
ulPropMatchIndex = k;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//skip if no match
|
|
if ((!bPropMatch))
|
|
{
|
|
lp += ulDataSize; //skip over data
|
|
continue;
|
|
}
|
|
|
|
//if we already found this property and filled it, skip
|
|
if (lpbFoundProp[ulPropMatchIndex] == TRUE)
|
|
{
|
|
lp += ulDataSize; //skip over data
|
|
continue;
|
|
}
|
|
|
|
//Set this prop in the array we will return
|
|
lpSPropVal = &(lpAdrEntry->rgPropVals[ulPropMatchIndex]);
|
|
|
|
lpSPropVal->ulPropTag = ulTmpPropTag;
|
|
|
|
|
|
//Single Valued
|
|
switch(PROP_TYPE(ulTmpPropTag))
|
|
{
|
|
case(PT_I2):
|
|
case(PT_LONG):
|
|
case(PT_APPTIME):
|
|
case(PT_SYSTIME):
|
|
case(PT_R4):
|
|
case(PT_BOOLEAN):
|
|
case(PT_CURRENCY):
|
|
case(PT_I8):
|
|
CopyMemory(&(lpSPropVal->Value.i),lp,min(ulDataSize,sizeof(lpSPropVal->Value.i)));
|
|
lp+=ulDataSize;
|
|
break;
|
|
|
|
case(PT_CLSID):
|
|
case(PT_TSTRING):
|
|
lpSPropVal->Value.LPSZ = LocalAlloc(LMEM_ZEROINIT,ulDataSize);
|
|
if (!(lpSPropVal->Value.LPSZ))
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(lpSPropVal->Value.LPSZ,lp,ulDataSize);
|
|
lp+=ulDataSize;
|
|
break;
|
|
|
|
case(PT_BINARY):
|
|
lpSPropVal->Value.bin.lpb = LocalAlloc(LMEM_ZEROINIT,ulDataSize);
|
|
if (!(lpSPropVal->Value.bin.lpb))
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(lpSPropVal->Value.bin.lpb,lp,ulDataSize);
|
|
lpSPropVal->Value.bin.cb = ulDataSize;
|
|
lp+=ulDataSize;
|
|
break;
|
|
|
|
|
|
// Multi-valued
|
|
case PT_MV_TSTRING:
|
|
lpSPropVal->Value.MVSZ.LPPSZ = LocalAlloc(LMEM_ZEROINIT, ulcValues * sizeof(LPTSTR));
|
|
if (!lpSPropVal->Value.MVSZ.LPPSZ)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
lpSPropVal->Value.MVSZ.cValues = ulcValues;
|
|
for (k=0;k<ulcValues;k++)
|
|
{
|
|
ULONG nLen;
|
|
// get string length (includes terminating zero)
|
|
CopyMemory(&nLen, lp, sizeof(ULONG));
|
|
lp += sizeof(ULONG);
|
|
lpSPropVal->Value.MVSZ.LPPSZ[k] = LocalAlloc(LMEM_ZEROINIT, nLen);
|
|
if (!lpSPropVal->Value.MVSZ.LPPSZ[k])
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(lpSPropVal->Value.MVSZ.LPPSZ[k], lp, nLen);
|
|
lp += nLen;
|
|
}
|
|
break;
|
|
|
|
case PT_MV_BINARY:
|
|
lpSPropVal->Value.MVbin.lpbin = LocalAlloc(LMEM_ZEROINIT, ulcValues * sizeof(SBinary));
|
|
if (!lpSPropVal->Value.MVbin.lpbin)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
lpSPropVal->Value.MVbin.cValues = ulcValues;
|
|
for (k=0;k<ulcValues;k++)
|
|
{
|
|
ULONG nLen;
|
|
CopyMemory(&nLen, lp, sizeof(ULONG));
|
|
lp += sizeof(ULONG);
|
|
lpSPropVal->Value.MVbin.lpbin[k].cb = nLen;
|
|
lpSPropVal->Value.MVbin.lpbin[k].lpb = LocalAlloc(LMEM_ZEROINIT, nLen);
|
|
if (!lpSPropVal->Value.MVbin.lpbin[k].lpb)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
CopyMemory(lpSPropVal->Value.MVbin.lpbin[k].lpb, lp, nLen);
|
|
lp += nLen;
|
|
}
|
|
break;
|
|
|
|
} //switch
|
|
|
|
|
|
ulcFoundPropCount++;
|
|
lpbFoundProp[ulPropMatchIndex]=TRUE;
|
|
|
|
}// for j
|
|
|
|
if(!(ulSearchFlags & AB_UNICODE)) //default DATA is in UNICODE, switch it to ANSI
|
|
ConvertWCPropsToALocalAlloc(lpAdrEntry->rgPropVals, lpAdrEntry->cValues);
|
|
|
|
LocalFreeAndNull(&szBuf);
|
|
|
|
LocalFreeAndNull(&lpbFoundProp);
|
|
|
|
nCount++;
|
|
|
|
}//for i
|
|
|
|
lpContentList->cEntries = nCount;
|
|
|
|
hr = S_OK;
|
|
|
|
out:
|
|
|
|
if(lpPropTagArray && lpPropTagArray!=lpPTArray)
|
|
LocalFree(lpPropTagArray);
|
|
|
|
LocalFreeAndNull(&szBuf);
|
|
|
|
LocalFreeAndNull(&lpbFoundProp);
|
|
|
|
FreeEntryIDs(hPropertyStore,
|
|
ulcEIDCount,
|
|
rgsbEntryIDs);
|
|
|
|
if(bErrorDetected)
|
|
TagWABFileError(lpMPSWabFileInfo->lpMPSWabFileHeader, hMPSWabFile);
|
|
|
|
if(hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if (HR_FAILED(hr))
|
|
{
|
|
if (*lppContentList)
|
|
{
|
|
FreePcontentlist(hPropertyStore, *lppContentList);
|
|
(*lppContentList) = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
//DebugTrace(("ReadPropArray: Exit\n-----------\n"));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
typedef struct _tagWabEIDList
|
|
{
|
|
WAB_ENTRYID dwEntryID;
|
|
struct _tagWabEIDList * lpNext;
|
|
} WAB_EID_LIST, * LPWAB_EID_LIST;
|
|
|
|
//$$private swap routine
|
|
void my_swap(LPWAB_ENTRYID lpdwEID, int left, int right)
|
|
{
|
|
WAB_ENTRYID temp;
|
|
temp = lpdwEID[left];
|
|
lpdwEID[left] = lpdwEID[right];
|
|
lpdwEID[right] = temp;
|
|
return;
|
|
}
|
|
//$$ private quick sort routine
|
|
// copied from Kernighan and Ritchie p.87
|
|
void my_qsort(LPWAB_ENTRYID lpdwEID, int left, int right)
|
|
{
|
|
int i, last;
|
|
|
|
if(left >= right)
|
|
return;
|
|
|
|
my_swap(lpdwEID, left, (left+right)/2);
|
|
|
|
last = left;
|
|
for(i=left+1;i<=right;i++)
|
|
if(lpdwEID[i]<lpdwEID[left])
|
|
my_swap(lpdwEID, ++last, i);
|
|
|
|
my_swap(lpdwEID, left, last);
|
|
my_qsort(lpdwEID, left, last-1);
|
|
my_qsort(lpdwEID, last+1, right);
|
|
|
|
return;
|
|
}
|
|
|
|
//$$//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// HrFindFuzzyRecordMatches - given a str to search for, goes throught the
|
|
// indexes and looks for partial matches. Returns a DWORD array of entry ids
|
|
// of all records that met the criteria ... if the flag AB_FAIL_AMBIGUOUS is
|
|
// supplied the function bails out if it finds more than 1 result (this
|
|
// is advantageous for ResolveNames since we have to call the function
|
|
// again and this way we avoid duplicate work
|
|
// If the search string contains spaces - we break it down into substrings
|
|
// and find only those targets that have all the sub strings. Reason for
|
|
// doing this is that if we have a Display Name of Thomas A. Edison, we should
|
|
// be able to search for Tom Edison and succeed. Caveat: we will also succeed
|
|
// for Ed Mas.<TBD> fix it
|
|
//
|
|
// One final addendum - if we get 1 exact match and multiple fuzzy matches and
|
|
// AB_FAIL_AMBIGUOUS is set then we give the 1 exact match precedence
|
|
// over the rest and declare it the unique result
|
|
//
|
|
// IN hPropertyStore - handle to property store
|
|
// IN pmbinFold - <Outlook> EntryID of folder to search in (NULL for default)
|
|
// IN lpszSearchStr - String to search for ...
|
|
// IN ulFlags - 0
|
|
// AB_FAIL_AMBIGUOUS // Means fail if no exact match
|
|
// And any combination of
|
|
// AB_FUZZY_FIND_NAME // search display name index
|
|
// AB_FUZZY_FIND_EMAIL // search email address index
|
|
// AB_FUZZY_FIND_ALIAS // search nickname index
|
|
// AB_FUZZY_FIND_ALL // search all three indexes
|
|
//
|
|
// OUT lpcValues - number of records matched
|
|
// OUT rgsbEntryIDs - array of SBinary EntryIDs of matching records
|
|
//
|
|
// TBD: This implementation Doesnt support Multi Valued propertys right now
|
|
//
|
|
//
|
|
//
|
|
// Returns
|
|
// Success: S_OK
|
|
// Failure: E_FAIL, MAPI_E_AMBIGUOUS_RECIP if AB_FUZZY_FAIL_AMBIGUOUS specified
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT HrFindFuzzyRecordMatches( HANDLE hPropertyStore,
|
|
LPSBinary pmbinFold,
|
|
LPTSTR lpszSearchForThisString,
|
|
ULONG ulFlags,
|
|
ULONG * lpcValues,
|
|
LPSBinary * lprgsbEntryIDs)
|
|
{
|
|
HRESULT hr= E_FAIL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
BOOL bFileLocked = FALSE;
|
|
ULONG j = 0;
|
|
ULONG i = 0,k=0;
|
|
ULONG cValues = 0;
|
|
LPWAB_EID_LIST lpHead = NULL,lpCurrent = NULL;
|
|
ULONG ulNumIndexesToSearch = 0;
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo;
|
|
LPWAB_ENTRYID lpdwEntryIDs = NULL;
|
|
LPTSTR * lppszSubStr = NULL;
|
|
ULONG ulSubStrCount = 0;
|
|
LPTSTR lpszSearchStr = NULL;
|
|
ULONG ulUniqueMatchCount = 0;
|
|
DWORD dwUniqueEID = 0;
|
|
LPDWORD lpdwFolderEIDs = NULL;
|
|
ULONG ulFolderEIDs = 0;
|
|
BOOL bSearchVirtualRootFolder = FALSE;
|
|
ULONG cchSize;
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
if(pt_bIsWABOpenExSession)
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
if(!hPropertyStore)
|
|
return MAPI_E_NOT_INITIALIZED;
|
|
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
LPSTR lpSearchString = ConvertWtoA(lpszSearchForThisString);
|
|
hr = lpWSP->lpVtbl->FindFuzzyRecordMatches( lpWSP,
|
|
pmbinFold,
|
|
lpSearchString,
|
|
ulFlags,
|
|
lpcValues,
|
|
lprgsbEntryIDs);
|
|
LocalFreeAndNull(&lpSearchString);
|
|
DebugTrace(TEXT("WABStorageProvider::FindFuzzyRecordMatches returned:%x\n"),hr);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
lpMPSWabFileInfo = hPropertyStore;
|
|
|
|
//DebugTrace(("//////////\nHrFindFuzzyRecordMatches: Entry\n"));
|
|
|
|
|
|
if ((!lpszSearchForThisString) ||
|
|
(!lprgsbEntryIDs) ||
|
|
( hPropertyStore == NULL) )
|
|
{
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
DebugTrace(TEXT("Invalid Parameters\n"));
|
|
goto out;
|
|
}
|
|
|
|
*lprgsbEntryIDs = NULL;
|
|
*lpcValues = 0;
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
|
|
// Parse the search string for spaces and break it down into substrings
|
|
cchSize = lstrlen(lpszSearchForThisString)+1;
|
|
lpszSearchStr = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
|
|
if(!lpszSearchStr)
|
|
{
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
StrCpyN(lpszSearchStr, lpszSearchForThisString, cchSize);
|
|
|
|
TrimSpaces(lpszSearchStr);
|
|
ulSubStrCount = 0;
|
|
|
|
{
|
|
// Count the spaces
|
|
LPTSTR lpTemp = lpszSearchStr;
|
|
LPTSTR lpStart = lpszSearchStr;
|
|
|
|
ulSubStrCount = nCountSubStrings(lpszSearchStr);
|
|
|
|
lppszSubStr = LocalAlloc(LMEM_ZEROINIT, sizeof(LPTSTR) * ulSubStrCount);
|
|
if(!lppszSubStr)
|
|
{
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// Fill in the substrings
|
|
i=0;
|
|
lpTemp = lpszSearchStr;
|
|
while(*lpTemp)
|
|
{
|
|
if (IsSpace(lpTemp) &&
|
|
! IsSpace(CharNext(lpTemp))) {
|
|
LPTSTR lpNextString = CharNext(lpTemp);
|
|
*lpTemp = '\0';
|
|
lpTemp = lpNextString;
|
|
cchSize = lstrlen(lpStart)+1;
|
|
lppszSubStr[i] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
|
|
if(!lppszSubStr[i])
|
|
{
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
StrCpyN(lppszSubStr[i], lpStart, cchSize);
|
|
lpStart = lpTemp;
|
|
i++;
|
|
}
|
|
else
|
|
lpTemp = CharNext(lpTemp);
|
|
}
|
|
|
|
if(i==ulSubStrCount-1)
|
|
{
|
|
//we're off by one
|
|
cchSize = lstrlen(lpStart)+1;
|
|
lppszSubStr[i] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
|
|
if(!lppszSubStr[i])
|
|
{
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
StrCpyN(lppszSubStr[i], lpStart, cchSize);
|
|
}
|
|
|
|
for(i=0;i<ulSubStrCount;i++)
|
|
TrimSpaces(lppszSubStr[i]);
|
|
}
|
|
|
|
|
|
//Open the file
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// To ensure that file info is accurate,
|
|
// Any time we open a file, read the file info again ...
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
//
|
|
// Anytime we detect an error - try to fix it ...
|
|
//
|
|
if((lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_ERROR_DETECTED) ||
|
|
(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_WRITE_IN_PROGRESS))
|
|
{
|
|
if(HR_FAILED(HrDoQuickWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile)))
|
|
{
|
|
hr = HrDoDetailedWABIntegrityCheck(lpMPSWabFileInfo,hMPSWabFile);
|
|
if(HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("HrDoDetailedWABIntegrityCheck failed:%x\n"),hr);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// If a WAB folder EID is specified, then we only want to search within the contents
|
|
// of that particular WAB folder .. that way we don't have to search through the whole WAB
|
|
// So we will open the WAB folder and get a list of it's member EIDs and check that a entryid
|
|
// is a member of that folder before we search through it
|
|
if(ulFlags & AB_FUZZY_FIND_PROFILEFOLDERONLY)
|
|
{
|
|
if(pmbinFold && pmbinFold->cb && pmbinFold->lpb)
|
|
{
|
|
// We need to look through the specified folder only
|
|
hr = GetFolderEIDs(hMPSWabFile, lpMPSWabFileInfo, pmbinFold,
|
|
&ulFolderEIDs, &lpdwFolderEIDs);
|
|
if(!HR_FAILED(hr) && !ulFolderEIDs && !lpdwFolderEIDs)
|
|
goto out; //empty container - nothing to search
|
|
}
|
|
else
|
|
{
|
|
// we need to look through the virtual folder
|
|
// It's harder to assemble a list of virtual folder contents
|
|
// without looking at each entry .. so instead we will just look
|
|
// at the entry prior to searching through it and if it's not in th
|
|
// virtual folder, we will ignore it ..
|
|
bSearchVirtualRootFolder = TRUE;
|
|
}
|
|
}
|
|
|
|
// If we can always assume that the Display Name is made up of
|
|
// First and Last name .. then by searching only the display name
|
|
// we dont need to search the other indexes.
|
|
// later when we have email implemented as an index - we can think about searching
|
|
// the email also ...
|
|
|
|
if (ulFlags & AB_FUZZY_FIND_NAME)
|
|
ulNumIndexesToSearch++;
|
|
if (ulFlags & AB_FUZZY_FIND_EMAIL)
|
|
ulNumIndexesToSearch++;
|
|
if (ulFlags & AB_FUZZY_FIND_ALIAS)
|
|
ulNumIndexesToSearch++;
|
|
|
|
for(k=0;k<ulNumIndexesToSearch;k++)
|
|
{
|
|
if(ulFlags & AB_FUZZY_FIND_NAME)
|
|
{
|
|
ulFlags &= ~AB_FUZZY_FIND_NAME;
|
|
j = indexDisplayName;
|
|
}
|
|
else if(ulFlags & AB_FUZZY_FIND_EMAIL)
|
|
{
|
|
ulFlags &= ~AB_FUZZY_FIND_EMAIL;
|
|
j = indexEmailAddress;
|
|
}
|
|
else if(ulFlags & AB_FUZZY_FIND_ALIAS)
|
|
{
|
|
ulFlags &= ~AB_FUZZY_FIND_ALIAS;
|
|
j = indexAlias;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the index
|
|
//
|
|
if (!LoadIndex( IN lpMPSWabFileInfo,
|
|
IN j,
|
|
IN hMPSWabFile) )
|
|
{
|
|
DebugTrace(TEXT("Error Loading Index!\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
for(i=0;i<lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[j].ulcNumEntries;i++)
|
|
{
|
|
// if there is a match we will store it in a linked list for now
|
|
// since that is simpler to implement ...
|
|
// later on we can clean up the action .. TBD
|
|
LPTSTR lpszTarget = lpMPSWabFileInfo->lpMPSWabIndexStr[i].szIndex;
|
|
ULONG n = 0;
|
|
|
|
// Before looking at any particular entry, check that it is part of the
|
|
// current folder
|
|
if(ulFolderEIDs && lpdwFolderEIDs)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
for(n=0;n<ulFolderEIDs;n++)
|
|
{
|
|
if(lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID == lpdwFolderEIDs[n])
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if(!bFound)
|
|
continue;
|
|
}
|
|
else
|
|
if(bSearchVirtualRootFolder)
|
|
{
|
|
// Discard this entry if it belongs to any folder .. we only want to
|
|
// consider entries that don't belong to any folder ..
|
|
ULONG ulObjType = 0;
|
|
if(bIsFolderMember( hMPSWabFile, lpMPSWabFileInfo,
|
|
lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID, &ulObjType) ||
|
|
(ulObjType == RECORD_CONTAINER) )
|
|
continue;
|
|
}
|
|
|
|
for(n=0;n<ulSubStrCount;n++)
|
|
{
|
|
if(j == indexEmailAddress && IsInternetAddress(lppszSubStr[n], NULL))
|
|
{
|
|
// Bug 33422 - we are resolving correct email addresses to incorrect email addresses
|
|
// If the address looks like a valid internet address, we should do a starts with search
|
|
// This way [email protected] doesnt resolve to [email protected]
|
|
if(lstrlen(lppszSubStr[n]) > lstrlen(lpszTarget))
|
|
break;
|
|
|
|
// Bug 7881: need to do a caseinsensitive search here ..
|
|
{
|
|
LPTSTR lp = lppszSubStr[n], lpT = lpszTarget;
|
|
while(lp && *lp && lpT && *lpT &&
|
|
( ( (TCHAR)CharLower( (LPTSTR)(DWORD_PTR)MAKELONG(*lp, 0)) == *lpT) ||
|
|
( (TCHAR)CharUpper( (LPTSTR)(DWORD_PTR)MAKELONG(*lp, 0)) == *lpT) ) )
|
|
{
|
|
lp++;
|
|
lpT++;
|
|
}
|
|
if(*lp) // which means didnt reach the end of the string
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
if (!SubstringSearch(lpszTarget, lppszSubStr[n]))
|
|
break;
|
|
}
|
|
|
|
{
|
|
BOOL bExactMatch = FALSE;
|
|
|
|
// look for exact matches too
|
|
if(lstrlen(lpszSearchForThisString) > MAX_INDEX_STRING-1)
|
|
{
|
|
// this is a really long string so we can't really compare it correctly
|
|
// so for starters we will compare the first 32 chars
|
|
TCHAR sz[MAX_INDEX_STRING];
|
|
CopyMemory(sz, lpszSearchForThisString, min(sizeof(TCHAR)*lstrlen(lpszTarget),sizeof(sz)));
|
|
sz[min(lstrlen(lpszTarget),ARRAYSIZE(sz)-1)] = '\0'; // depending on the language target string may or maynot be 32 chars - might be less
|
|
if(!lstrcmpi(sz, lpszTarget))
|
|
{
|
|
// Match .. now to check the whole string ...
|
|
ULONG ulcProps = 0;
|
|
LPSPropValue lpProps = NULL;
|
|
if(!HR_FAILED(ReadRecordWithoutLocking(hMPSWabFile, lpMPSWabFileInfo,
|
|
lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID,
|
|
&ulcProps, &lpProps)))
|
|
{
|
|
ULONG k = 0;
|
|
ULONG ulProp = (j==indexDisplayName) ? PR_DISPLAY_NAME : ((j==indexEmailAddress) ? PR_EMAIL_ADDRESS : PR_NICKNAME);
|
|
for(k=0;k<ulcProps;k++)
|
|
{
|
|
if(lpProps[k].ulPropTag == ulProp)
|
|
{
|
|
if(!lstrcmpi(lpszSearchForThisString, lpProps[k].Value.LPSZ))
|
|
{
|
|
bExactMatch = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
LocalFreePropArray(hMPSWabFile, ulcProps, &lpProps);
|
|
}
|
|
}
|
|
}
|
|
else if(!lstrcmpi(lpszSearchForThisString, lpszTarget))
|
|
bExactMatch = TRUE;
|
|
|
|
if(bExactMatch)
|
|
{
|
|
//exact match
|
|
ulUniqueMatchCount++;
|
|
dwUniqueEID = lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID;
|
|
if( ulFlags == AB_FUZZY_FAIL_AMBIGUOUS && ulUniqueMatchCount > 1 )
|
|
{
|
|
// more than two - genuine fail
|
|
hr = MAPI_E_AMBIGUOUS_RECIP;
|
|
DebugTrace(TEXT("Found multiple exact matches: Ambiguous search\n"));
|
|
goto out;
|
|
} //if
|
|
}
|
|
else // not an exact match - revert back to regular error check
|
|
if(n != ulSubStrCount) // something didnt match
|
|
continue;
|
|
}
|
|
|
|
// if (SubstringSearch(lpszTarget, lpszSearchStr))
|
|
{
|
|
// Yes a partial match ...
|
|
LPWAB_EID_LIST lpTemp = NULL;
|
|
BOOL bDupe = FALSE;
|
|
|
|
// before adding this to the list, make sure it is not a FOLDER .. if it is a folder,
|
|
// we need to ignore it ..
|
|
{
|
|
ULONG ulObjType = 0;
|
|
bIsFolderMember( hMPSWabFile, lpMPSWabFileInfo, lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID, &ulObjType);
|
|
if(ulObjType == RECORD_CONTAINER)
|
|
continue;
|
|
}
|
|
// before adding this entryid to the list, make sure that it isnt already in the list
|
|
if(lpHead)
|
|
{
|
|
lpTemp = lpHead;
|
|
while(lpTemp)
|
|
{
|
|
if(lpTemp->dwEntryID == lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID)
|
|
{
|
|
bDupe = TRUE;
|
|
break;
|
|
}
|
|
lpTemp = lpTemp->lpNext;
|
|
}
|
|
}
|
|
|
|
if(bDupe)
|
|
continue;
|
|
|
|
lpTemp = LocalAlloc(LMEM_ZEROINIT,sizeof(WAB_EID_LIST));
|
|
|
|
if(!lpTemp)
|
|
{
|
|
DebugTrace(TEXT("Local Alloc Failed\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
lpTemp->lpNext = NULL;
|
|
lpTemp->dwEntryID = lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID;
|
|
|
|
if (lpCurrent)
|
|
{
|
|
lpCurrent->lpNext = lpTemp;
|
|
lpCurrent = lpTemp;
|
|
}
|
|
else
|
|
lpCurrent = lpTemp;
|
|
|
|
if(!lpHead)
|
|
lpHead = lpCurrent;
|
|
|
|
cValues++;
|
|
|
|
// if we have to give exact match precedence over fuzzy match then
|
|
// this means that we have to search everything and can't bail just yet
|
|
//
|
|
/*
|
|
if( (ulFlags == AB_FUZZY_FAIL_AMBIGUOUS) &&
|
|
(cValues > 1) )
|
|
{
|
|
// There is always the possibility that the same element
|
|
// has been found twice, once under display name and once
|
|
// under e-mail (e.g. Joe Smith, [email protected], searching for Joe)
|
|
// So if we have two elements and the entryids are the same,
|
|
// this is no cause for failure
|
|
if(cValues==2)
|
|
{
|
|
if(lpHead && lpCurrent)
|
|
{
|
|
if(lpHead->dwEntryID == lpCurrent->dwEntryID)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// more than two - genuine fail
|
|
hr = MAPI_E_AMBIGUOUS_RECIP;
|
|
DebugTrace(TEXT("Found multiple matches: Ambiguous search\n"));
|
|
goto out;
|
|
|
|
} //if
|
|
*/
|
|
|
|
}//if Substring search
|
|
}//for(i= ..
|
|
}//for k=..
|
|
|
|
lpCurrent = lpHead;
|
|
|
|
if (lpCurrent == NULL)
|
|
{
|
|
// nothing found
|
|
hr = hrSuccess;
|
|
*lpcValues = 0;
|
|
DebugTrace(( TEXT("No matches found\n")));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// if we want this search to fail when it's ambiguous, this means
|
|
// we give preference to exact matches. Hence if we have a single exact match,
|
|
// then we should return only that single exact match..
|
|
if( ulFlags==AB_FUZZY_FAIL_AMBIGUOUS && ulUniqueMatchCount==1 )
|
|
{
|
|
*lpcValues = 1;
|
|
*lprgsbEntryIDs = LocalAlloc(LMEM_ZEROINIT, sizeof(SBinary));
|
|
if(!(*lprgsbEntryIDs))
|
|
{
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
(*lprgsbEntryIDs)[0].lpb = LocalAlloc(LMEM_ZEROINIT, SIZEOF_WAB_ENTRYID);
|
|
if((*lprgsbEntryIDs)[0].lpb)
|
|
{
|
|
(*lprgsbEntryIDs)[0].cb = SIZEOF_WAB_ENTRYID;
|
|
CopyMemory((*lprgsbEntryIDs)[0].lpb,&dwUniqueEID, SIZEOF_WAB_ENTRYID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// At the end of the above loops, we should have a linked list of
|
|
// entry ids - if we are searching through more than one index, then
|
|
// chances are that we have duplicates in this above list or entryids.
|
|
// We need to weed out the duplicates before we return this array
|
|
// First we turn the linked list into an array, freeing the linked list in the
|
|
// process. Then we quick sort the array of entryids
|
|
// Then we remove the duplicates and return another cleaned up array
|
|
|
|
lpdwEntryIDs = LocalAlloc(LMEM_ZEROINIT,cValues * SIZEOF_WAB_ENTRYID);
|
|
if(!lpdwEntryIDs)
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed to allocate memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
for(j=0;j<cValues;j++)
|
|
{
|
|
if(lpCurrent)
|
|
{
|
|
lpdwEntryIDs[j]=lpCurrent->dwEntryID;
|
|
lpHead = lpCurrent->lpNext;
|
|
LocalFreeAndNull(&lpCurrent);
|
|
lpCurrent = lpHead;
|
|
}
|
|
}
|
|
|
|
lpCurrent = NULL;
|
|
|
|
// Now quicksort this array
|
|
my_qsort(lpdwEntryIDs, 0, cValues-1);
|
|
|
|
// Now we have a quicksorted array - scan it and remove duplicates
|
|
*lpcValues = 1;
|
|
for(i=0;i<cValues-1;i++)
|
|
{
|
|
if(lpdwEntryIDs[i] == lpdwEntryIDs[i+1])
|
|
lpdwEntryIDs[i] = 0;
|
|
else
|
|
(*lpcValues)++;
|
|
|
|
}
|
|
|
|
*lprgsbEntryIDs = LocalAlloc(LMEM_ZEROINIT,(*lpcValues) * sizeof(SBinary));
|
|
if(!(*lprgsbEntryIDs))
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed to allocate memory\n"));
|
|
hr = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
*lpcValues = 0;
|
|
|
|
for(j=0;j<cValues;j++)
|
|
{
|
|
if(lpdwEntryIDs[j] > 0)
|
|
{
|
|
int index = *lpcValues;
|
|
(*lprgsbEntryIDs)[index].lpb = LocalAlloc(LMEM_ZEROINIT, SIZEOF_WAB_ENTRYID);
|
|
if((*lprgsbEntryIDs)[index].lpb)
|
|
{
|
|
(*lprgsbEntryIDs)[index].cb = SIZEOF_WAB_ENTRYID;
|
|
CopyMemory((*lprgsbEntryIDs)[index].lpb,&(lpdwEntryIDs[j]), SIZEOF_WAB_ENTRYID);
|
|
(*lpcValues)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = hrSuccess;
|
|
|
|
out:
|
|
|
|
if(lpdwFolderEIDs)
|
|
LocalFree(lpdwFolderEIDs);
|
|
|
|
if(lpCurrent)
|
|
{
|
|
while(lpCurrent)
|
|
{
|
|
lpHead = lpCurrent->lpNext;
|
|
LocalFreeAndNull(&lpCurrent);
|
|
lpCurrent = lpHead;
|
|
}
|
|
}
|
|
|
|
if(lppszSubStr)
|
|
{
|
|
for(i=0;i<ulSubStrCount;i++)
|
|
LocalFreeAndNull(&lppszSubStr[i]);
|
|
LocalFree(lppszSubStr);
|
|
}
|
|
|
|
LocalFreeAndNull(&lpszSearchStr);
|
|
|
|
LocalFreeAndNull(&lpdwEntryIDs);
|
|
|
|
if(hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
DebugTrace(TEXT("HrFindFuzzyRecordMatches: Exit: %x cValues: %d\n"),hr,*lpcValues);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//$$////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// bTagWriteTransaction -
|
|
//
|
|
// During a write transaction, we tag the header as write-in-progress so that
|
|
// if the transaction shuts down in the middle we dont get messed up the next
|
|
// time we open up and so we can attepmt a repair the next time we open up
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
BOOL bTagWriteTransaction(LPMPSWab_FILE_HEADER lpMPSWabFileHeader,
|
|
HANDLE hMPSWabFile)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
DWORD dwNumofBytes = 0;
|
|
|
|
if(!lpMPSWabFileHeader || !hMPSWabFile)
|
|
{
|
|
DebugTrace(TEXT("Invalid Parameter\n"));
|
|
goto out;
|
|
}
|
|
|
|
lpMPSWabFileHeader->ulFlags |= WAB_WRITE_IN_PROGRESS;
|
|
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
0,
|
|
(LPVOID) lpMPSWabFileHeader,
|
|
sizeof(MPSWab_FILE_HEADER)))
|
|
goto out;
|
|
|
|
bRet = TRUE;
|
|
|
|
out:
|
|
return bRet;
|
|
}
|
|
|
|
|
|
|
|
//$$////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// bUntagWriteTransaction -
|
|
//
|
|
// During a write transaction, we tag the header as write-in-progress so that
|
|
// if the transaction shuts down in the middle we dont get messed up the next
|
|
// time we open up and so we can attepmt a repair the next time we open up
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
BOOL bUntagWriteTransaction(LPMPSWab_FILE_HEADER lpMPSWabFileHeader,
|
|
HANDLE hMPSWabFile)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
DWORD dwNumofBytes = 0;
|
|
|
|
if(!lpMPSWabFileHeader || !hMPSWabFile)
|
|
{
|
|
DebugTrace(TEXT("Invalid Parameter\n"));
|
|
goto out;
|
|
}
|
|
|
|
lpMPSWabFileHeader->ulFlags &= ~WAB_WRITE_IN_PROGRESS;
|
|
|
|
// update the file header
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
0,
|
|
(LPVOID) lpMPSWabFileHeader,
|
|
sizeof(MPSWab_FILE_HEADER)))
|
|
goto out;
|
|
|
|
|
|
bRet = TRUE;
|
|
|
|
out:
|
|
return bRet;
|
|
}
|
|
|
|
/*
|
|
- GetNamedPropsFromBuffer
|
|
-
|
|
* bDoAtoWConversion - when importing from an old-non-unicode WAB file, we need to
|
|
* update the 'name' strings from ASCII to Unicode .. this flag tells us to do so
|
|
*
|
|
*/
|
|
BOOL GetNamedPropsFromBuffer(LPBYTE szBuf,
|
|
ULONG ulcGUIDCount,
|
|
BOOL bDoAtoWConversion,
|
|
OUT LPGUID_NAMED_PROPS * lppgnp)
|
|
{
|
|
LPBYTE lp = szBuf;
|
|
LPGUID_NAMED_PROPS lpgnp = NULL;
|
|
ULONG i = 0,j=0;
|
|
|
|
lpgnp = LocalAlloc(LMEM_ZEROINIT, ulcGUIDCount * sizeof(GUID_NAMED_PROPS));
|
|
if(!lpgnp)
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
for(i=0;i<ulcGUIDCount;i++)
|
|
{
|
|
lpgnp[i].lpGUID = LocalAlloc(LMEM_ZEROINIT, sizeof(GUID));
|
|
if(!lpgnp[i].lpGUID)
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
CopyMemory(lpgnp[i].lpGUID, lp, sizeof(GUID));
|
|
lp += sizeof(GUID); // for GUID
|
|
|
|
CopyMemory(&(lpgnp[i].cValues), lp, sizeof(ULONG));
|
|
lp += sizeof(ULONG); // for cValues
|
|
|
|
lpgnp[i].lpnm = LocalAlloc(LMEM_ZEROINIT, (lpgnp[i].cValues)*sizeof(NAMED_PROP));
|
|
if(!lpgnp[i].lpnm)
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
for(j=0;j<lpgnp[i].cValues;j++)
|
|
{
|
|
ULONG nLen;
|
|
LPWSTR lpW = NULL;
|
|
|
|
CopyMemory(&(lpgnp[i].lpnm[j].ulPropTag), lp, sizeof(ULONG));
|
|
lp += sizeof(ULONG); //saves PropTag
|
|
|
|
// nLen includes trailing zero
|
|
CopyMemory(&nLen, lp, sizeof(ULONG));
|
|
lp += sizeof(ULONG); //saves lstrlen
|
|
|
|
if(!bDoAtoWConversion)
|
|
{
|
|
if(!(lpW = LocalAlloc(LMEM_ZEROINIT, nLen)))
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed\n"));
|
|
goto out;
|
|
}
|
|
CopyMemory(lpW, lp, nLen);
|
|
}
|
|
else
|
|
{
|
|
LPSTR lpA = NULL;
|
|
if(!(lpA = LocalAlloc(LMEM_ZEROINIT, nLen)))
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed\n"));
|
|
goto out;
|
|
}
|
|
CopyMemory(lpA, lp, nLen);
|
|
lpW = ConvertAtoW(lpA);
|
|
LocalFreeAndNull(&lpA);
|
|
}
|
|
lpgnp[i].lpnm[j].lpsz = lpW;
|
|
|
|
// [PaulHi] HACK 3/25/99 The wabimprt.c code expects lpW to ALWAYS be at least
|
|
// two characters in length, and skips the first character. If this is
|
|
// less than or equal to one character then create a two character buffer filled
|
|
// with zeros.
|
|
if (nLen <= 2) // Length is in bytes
|
|
{
|
|
LocalFreeAndNull(&(lpgnp[i].lpnm[j].lpsz));
|
|
lpgnp[i].lpnm[j].lpsz = LocalAlloc(LMEM_ZEROINIT, (2 * sizeof(WCHAR)));
|
|
if (!lpgnp[i].lpnm[j].lpsz)
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed\n"));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
lp += nLen;
|
|
}
|
|
}
|
|
|
|
*lppgnp = lpgnp;
|
|
|
|
return TRUE;
|
|
|
|
out:
|
|
if(lpgnp)
|
|
FreeGuidnamedprops(ulcGUIDCount, lpgnp);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//$$////////////////////////////////////////////////////////////////////////
|
|
////
|
|
//// GetNamedPropsFromPropStore -
|
|
////
|
|
//// Used for retreiving the named props to the property store
|
|
//// The supplied lppgn pointer is filled with GUID_NAMED_PROP array
|
|
////
|
|
//// IN hPropertyStore - handle to the property store
|
|
//// OUT lpulcGUIDCount - number of different GUIDs in the lpgnp array
|
|
//// OUT lppgnp - returned LPGUID_NAMED_PROP structure array
|
|
////
|
|
//// The lppgnp Structure is LocalAlloced. Caller should calle
|
|
//// FreeGuidnamedprop to free this structure
|
|
////
|
|
////////////////////////////////////////////////////////////////////////////
|
|
HRESULT GetNamedPropsFromPropStore( IN HANDLE hPropertyStore,
|
|
OUT LPULONG lpulcGUIDCount,
|
|
OUT LPGUID_NAMED_PROPS * lppgnp)
|
|
{
|
|
HRESULT hr= E_FAIL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
BOOL bFileLocked = FALSE;
|
|
ULONG j = 0;
|
|
ULONG i = 0,k=0;
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = (LPMPSWab_FILE_INFO) hPropertyStore;
|
|
DWORD dwNumofBytes = 0;
|
|
ULONG ulSize = 0;
|
|
LPGUID_NAMED_PROPS lpgnp = NULL;
|
|
ULONG ulcGUIDCount = 0;
|
|
LPBYTE szBuf = NULL;
|
|
LPBYTE lp = NULL;
|
|
|
|
if ((!lppgnp) ||
|
|
( hPropertyStore == NULL) )
|
|
{
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
DebugTrace(TEXT("Invalid Parameters\n"));
|
|
goto out;
|
|
}
|
|
|
|
*lppgnp = NULL;
|
|
*lpulcGUIDCount = 0;
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
|
|
//Open the file
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// To ensure that file info is accurate,
|
|
// Any time we open a file, read the file info again ...
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// First we need to figure out how much space we need to save the named
|
|
// properties structure
|
|
//
|
|
ulSize = lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.AllocatedBlockSize;
|
|
ulcGUIDCount = lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.ulcNumEntries;
|
|
|
|
// Now the file is big enough, create the memory block for the named props
|
|
// and fill the block with the given Data
|
|
szBuf = LocalAlloc(LMEM_ZEROINIT, ulSize);
|
|
if(!szBuf)
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
if(!ReadDataFromWABFile(hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.ulOffset,
|
|
(LPVOID) szBuf,
|
|
ulSize))
|
|
goto out;
|
|
|
|
if(!GetNamedPropsFromBuffer(szBuf, ulcGUIDCount, FALSE, lppgnp))
|
|
goto out;
|
|
|
|
*lpulcGUIDCount = ulcGUIDCount;
|
|
|
|
// done
|
|
hr = S_OK;
|
|
|
|
out:
|
|
|
|
if(HR_FAILED(hr))
|
|
{
|
|
FreeGuidnamedprops(ulcGUIDCount, lpgnp);
|
|
}
|
|
|
|
LocalFreeAndNull(&szBuf);
|
|
|
|
if(hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
- SetNamedPropsToBuffer
|
|
-
|
|
*
|
|
*/
|
|
BOOL SetNamedPropsToBuffer( ULONG ulcGUIDCount,
|
|
LPGUID_NAMED_PROPS lpgnp,
|
|
ULONG * lpulSize,
|
|
LPBYTE * lpp)
|
|
{
|
|
ULONG ulSize = 0, i =0, j=0;
|
|
LPBYTE szBuf = NULL, lp = NULL;
|
|
|
|
//
|
|
// First we need to figure out how much space we need to save the named
|
|
// properties structure
|
|
//
|
|
ulSize = 0;
|
|
for(i=0;i<ulcGUIDCount;i++)
|
|
{
|
|
if(lpgnp[i].lpGUID)
|
|
{
|
|
ulSize += sizeof(GUID); // for GUID
|
|
ulSize += sizeof(ULONG); // for cValues
|
|
for(j=0;j<lpgnp[i].cValues;j++)
|
|
{
|
|
ulSize += sizeof(ULONG); //saves PropTag
|
|
if(lpgnp[i].lpnm[j].lpsz)
|
|
{
|
|
ulSize += sizeof(ULONG); //saves lstrlen
|
|
ulSize += sizeof(TCHAR)*(lstrlen(lpgnp[i].lpnm[j].lpsz)+1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Now the file is big enough, create the memory block for the named props
|
|
// and fill the block with the given Data
|
|
szBuf = LocalAlloc(LMEM_ZEROINIT, ulSize);
|
|
if(!szBuf)
|
|
{
|
|
DebugTrace(TEXT("LocalAlloc failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
lp = szBuf;
|
|
for(i=0;i<ulcGUIDCount;i++)
|
|
{
|
|
if(lpgnp[i].lpGUID)
|
|
{
|
|
CopyMemory(lp, lpgnp[i].lpGUID, sizeof(GUID));
|
|
lp += sizeof(GUID); // for GUID
|
|
CopyMemory(lp, &(lpgnp[i].cValues), sizeof(ULONG));
|
|
lp += sizeof(ULONG); // for cValues
|
|
for(j=0;j<lpgnp[i].cValues;j++)
|
|
{
|
|
ULONG nLen;
|
|
CopyMemory(lp, &(lpgnp[i].lpnm[j].ulPropTag), sizeof(ULONG));
|
|
lp += sizeof(ULONG); //saves PropTag
|
|
|
|
// This assumes that there is always a string to save
|
|
nLen = sizeof(TCHAR)*(lstrlen(lpgnp[i].lpnm[j].lpsz)+1);
|
|
CopyMemory(lp, &nLen, sizeof(ULONG));
|
|
lp += sizeof(ULONG); //saves lstrlen
|
|
|
|
CopyMemory(lp, lpgnp[i].lpnm[j].lpsz, nLen);
|
|
lp += nLen;
|
|
}
|
|
}
|
|
}
|
|
|
|
*lpp = szBuf;
|
|
*lpulSize = ulSize;
|
|
return TRUE;
|
|
out:
|
|
if(szBuf)
|
|
LocalFree(szBuf);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//$$////////////////////////////////////////////////////////////////////////
|
|
////
|
|
//// SetNamedPropsToPropStore -
|
|
////
|
|
//// Used for writing the named props to the property store
|
|
//// The input lpgnp pointer contents will overwrite whatever exists in the
|
|
//// property store hence this should be used to replace not to add.
|
|
//// For each application GUID, there can be any number of properties
|
|
//// The number of application GUIDs is stored in the
|
|
//// FileHeader.NamedPropData.ulcNumEntries field. The actual data is of the
|
|
//// form:
|
|
//// GUID.#-of-Named-Prop-Tags.proptag.strlen.string.proptag.strlen.string etc.
|
|
////
|
|
//// This function will grow the property store as needed to fit the given
|
|
//// data.
|
|
////
|
|
//// IN hPropertyStore - handle to the property store
|
|
//// IN ulcGUIDCount - number of different GUIDs in the lpgnp array
|
|
//// IN lpgnp - LPGUID_NAMED_PROP structure array
|
|
////////////////////////////////////////////////////////////////////////////
|
|
HRESULT SetNamedPropsToPropStore( IN HANDLE hPropertyStore,
|
|
IN ULONG ulcGUIDCount,
|
|
OUT LPGUID_NAMED_PROPS lpgnp)
|
|
{
|
|
HRESULT hr= E_FAIL;
|
|
HANDLE hMPSWabFile = NULL;
|
|
BOOL bFileLocked = FALSE;
|
|
ULONG j = 0;
|
|
ULONG i = 0,k=0;
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = (LPMPSWab_FILE_INFO) hPropertyStore;
|
|
DWORD dwNumofBytes = 0;
|
|
ULONG ulSize = 0;
|
|
LPBYTE szBuf = NULL;
|
|
LPBYTE lp = NULL;
|
|
|
|
DebugTrace(TEXT("\tSetNamedPropsToPropStore: Entry\n"));
|
|
|
|
if ((!lpgnp) ||
|
|
(lpgnp && !ulcGUIDCount) ||
|
|
( hPropertyStore == NULL) )
|
|
{
|
|
hr = MAPI_E_INVALID_PARAMETER;
|
|
DebugTrace(TEXT("Invalid Parameters\n"));
|
|
goto out;
|
|
}
|
|
|
|
if(!LockFileAccess(lpMPSWabFileInfo))
|
|
{
|
|
DebugTrace(TEXT("LockFileAccess Failed\n"));
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
bFileLocked = TRUE;
|
|
}
|
|
|
|
//Open the file
|
|
hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
|
|
|
|
if ( (hMPSWabFile == INVALID_HANDLE_VALUE) ||
|
|
HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// To ensure that file info is accurate,
|
|
// Any time we open a file, read the file info again ...
|
|
//
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
|
|
if(!SetNamedPropsToBuffer(ulcGUIDCount, lpgnp,
|
|
&ulSize, &szBuf))
|
|
goto out;
|
|
|
|
|
|
// We now know we need ulSize bytes of space.
|
|
// Do we have this much space in the store ? if not, grow the store
|
|
|
|
while(lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.AllocatedBlockSize < ulSize)
|
|
{
|
|
if (!CompressFile( lpMPSWabFileInfo,
|
|
hMPSWabFile,
|
|
NULL,
|
|
TRUE,
|
|
AB_GROW_NAMEDPROP))
|
|
{
|
|
DebugTrace(TEXT("Growing the file failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
if(!ReloadMPSWabFileInfo(
|
|
lpMPSWabFileInfo,
|
|
hMPSWabFile))
|
|
{
|
|
DebugTrace(TEXT("Reading file info failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Write this buffer into the file
|
|
//
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.ulOffset,
|
|
(LPVOID) szBuf,
|
|
ulSize))
|
|
goto out;
|
|
|
|
//
|
|
// Update the file header and write it
|
|
//
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.UtilizedBlockSize = ulSize;
|
|
lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.ulcNumEntries = ulcGUIDCount;
|
|
|
|
if(!WriteDataToWABFile( hMPSWabFile,
|
|
0,
|
|
(LPVOID) lpMPSWabFileInfo->lpMPSWabFileHeader,
|
|
sizeof(MPSWab_FILE_HEADER)))
|
|
goto out;
|
|
|
|
|
|
// done
|
|
hr = S_OK;
|
|
|
|
out:
|
|
|
|
LocalFreeAndNull(&szBuf);
|
|
|
|
if(hMPSWabFile)
|
|
IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
|
|
|
|
if (bFileLocked)
|
|
UnLockFileAccess(lpMPSWabFileInfo);
|
|
|
|
//DebugTrace(TEXT("//////////\nSetNamedPropsToPropStore: Exit\n"));
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
-
|
|
- GetOutlookRefreshCountData
|
|
*
|
|
* Outlook notifications are a bit funky in that Outlook sets an event and the first
|
|
* WAB process to get that event resets it to the mutual exclusion of all other WAB
|
|
* processes ... so we do an event count through the registry .. each process will make
|
|
* a registry check of the latest event count and fire a refresh if their copy is older
|
|
* than the count in the registry
|
|
*/
|
|
static const LPTSTR lpOlkContactRefresh = TEXT("OlkContactRefresh");
|
|
static const LPTSTR lpOlkFolderRefresh = TEXT("OlkFolderRefresh");
|
|
void GetOutlookRefreshCountData(LPDWORD lpdwOlkRefreshCount,LPDWORD lpdwOlkFolderRefreshCount)
|
|
{
|
|
HKEY hKey = NULL;
|
|
DWORD dwDisposition = 0,dwSize = 0,dwType = 0;
|
|
// begin registry stuff
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, lpNewWABRegKey, 0, //reserved
|
|
NULL, REG_OPTION_NON_VOLATILE, KEY_READ,
|
|
NULL, &hKey, &dwDisposition))
|
|
{
|
|
goto exit;
|
|
}
|
|
dwSize = sizeof(DWORD);
|
|
dwType = REG_DWORD;
|
|
RegQueryValueEx(hKey,lpOlkContactRefresh,NULL,&dwType,(LPBYTE)lpdwOlkRefreshCount, &dwSize);
|
|
dwSize = sizeof(DWORD);
|
|
dwType = REG_DWORD;
|
|
RegQueryValueEx(hKey,lpOlkFolderRefresh,NULL,&dwType,(LPBYTE)lpdwOlkFolderRefreshCount, &dwSize);
|
|
exit:
|
|
if(hKey)
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
/*
|
|
-
|
|
- SetOutlookRefreshCountData
|
|
*
|
|
*/
|
|
void SetOutlookRefreshCountData(DWORD dwOlkRefreshCount,DWORD dwOlkFolderRefreshCount)
|
|
{
|
|
HKEY hKey = NULL;
|
|
DWORD dwDisposition = 0,dwSize = 0;
|
|
// begin registry stuff
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, lpNewWABRegKey, 0, //reserved
|
|
NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
|
|
NULL, &hKey, &dwDisposition))
|
|
{
|
|
goto exit;
|
|
}
|
|
dwSize = sizeof(DWORD);
|
|
RegSetValueEx(hKey,lpOlkContactRefresh, 0, REG_DWORD, (LPBYTE)&dwOlkRefreshCount, dwSize);
|
|
RegSetValueEx(hKey,lpOlkFolderRefresh, 0, REG_DWORD, (LPBYTE)&dwOlkFolderRefreshCount, dwSize);
|
|
exit:
|
|
if(hKey)
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
/*
|
|
-
|
|
- Copies a set of container info from Outlook to the WAB
|
|
*
|
|
* The difference is that Outlook always returns ANSI while WAB may
|
|
* want Unicode in some cases
|
|
*
|
|
*/
|
|
void ConvertOlkConttoWABCont(ULONG * lpcolk, OutlookContInfo ** lprgolk,
|
|
ULONG * lpcolkci, OlkContInfo ** lprgolkci)
|
|
{
|
|
ULONG i = 0;
|
|
SCODE sc = S_OK;
|
|
ULONG cVal = *lpcolk;
|
|
OlkContInfo * rgolkci = NULL;
|
|
if(!(sc = MAPIAllocateBuffer(sizeof(OlkContInfo)*(cVal), &rgolkci)))
|
|
{
|
|
for(i = 0; i < *lpcolk ; i++)
|
|
{
|
|
if(!(sc = MAPIAllocateMore(sizeof(SBinary), rgolkci, (LPVOID*)(&rgolkci[i].lpEntryID))))
|
|
{
|
|
rgolkci[i].lpEntryID->cb = ((*lprgolk)[i]).lpEntryID->cb;
|
|
if(!(sc = MAPIAllocateMore(rgolkci[i].lpEntryID->cb, rgolkci, (LPVOID*)(&(rgolkci[i].lpEntryID->lpb)))))
|
|
{
|
|
CopyMemory(rgolkci[i].lpEntryID->lpb, ((*lprgolk)[i]).lpEntryID->lpb, rgolkci[i].lpEntryID->cb);
|
|
}
|
|
}
|
|
sc = ScAnsiToWCMore((LPALLOCATEMORE) (&MAPIAllocateMore), rgolkci,((*lprgolk)[i]).lpszName, &rgolkci[i].lpszName);
|
|
}
|
|
}
|
|
*lpcolkci = *lpcolk;
|
|
*lprgolkci = rgolkci;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CheckChangedWAB
|
|
|
|
Purpose : Has the file been written since we last checked?
|
|
|
|
Parameters: hPropertyStore = open property store handle
|
|
lpftLast -> Last file time for this dialog
|
|
|
|
Returns : TRUE if property store has changed since last check
|
|
|
|
Comment : The first time this function is called is regarded as
|
|
initialization and will always return FALSE.
|
|
|
|
***************************************************************************/
|
|
BOOL CheckChangedWAB(LPPROPERTY_STORE lpPropertyStore, HANDLE hMutex,
|
|
LPDWORD lpdwContact, LPDWORD lpdwFolder, LPFILETIME lpftLast)
|
|
{
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
BOOL fChanged = FALSE;
|
|
HANDLE hPropertyStore = lpPropertyStore->hPropertyStore;
|
|
|
|
if(!pt_bIsWABOpenExSession)
|
|
{
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = (LPMPSWab_FILE_INFO)hPropertyStore;
|
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
|
WIN32_FIND_DATA FindData;
|
|
|
|
if (lpMPSWabFileInfo) {
|
|
if (INVALID_HANDLE_VALUE == (hFind = FindFirstFile(
|
|
lpMPSWabFileInfo->lpszMPSWabFileName, // pointer to name of file to search for
|
|
&FindData))) {
|
|
DebugTrace(TEXT("CheckWABRefresh:FindFirstFile -> %u\n"), GetLastError());
|
|
} else {
|
|
if (lpftLast->dwHighDateTime < FindData.ftLastWriteTime.dwHighDateTime ||
|
|
(lpftLast->dwHighDateTime == FindData.ftLastWriteTime.dwHighDateTime &&
|
|
lpftLast->dwLowDateTime < FindData.ftLastWriteTime.dwLowDateTime)) {
|
|
fChanged = TRUE;
|
|
if (lpftLast->dwLowDateTime == 0 && lpftLast->dwHighDateTime == 0) {
|
|
fChanged = FALSE;
|
|
}
|
|
*lpftLast = FindData.ftLastWriteTime;
|
|
}
|
|
|
|
FindClose(hFind);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// WABOpenEx Session (ie Outlook session)
|
|
// Check our 2 events to see if anything needs updating
|
|
BOOL fContact = FALSE, fFolders = FALSE;
|
|
DWORD dwContact = 0, dwFolder = 0;
|
|
|
|
|
|
if(WAIT_OBJECT_0 == WaitForSingleObject(hMutex,0))
|
|
{
|
|
|
|
if(WAIT_OBJECT_0 == WaitForSingleObject(ghEventOlkRefreshContacts, 0))
|
|
fContact = TRUE;
|
|
|
|
if(WAIT_OBJECT_0 == WaitForSingleObject(ghEventOlkRefreshFolders, 0))
|
|
fFolders = TRUE;
|
|
|
|
if(!fContact && !fFolders)
|
|
{
|
|
// Didn't catch an event .. check if we missed any in the past by looking at the registry settings
|
|
GetOutlookRefreshCountData(&dwContact,&dwFolder);
|
|
if(*lpdwContact < dwContact)
|
|
{
|
|
fContact = TRUE;
|
|
*lpdwContact = dwContact;
|
|
}
|
|
if(*lpdwFolder < dwFolder)
|
|
{
|
|
fFolders = TRUE;
|
|
*lpdwFolder = dwFolder;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Caught an event .. update the registry
|
|
if(fContact)
|
|
{
|
|
DebugTrace(TEXT("####>> Got Outlook Contact Refresh Event\n"));
|
|
ResetEvent(ghEventOlkRefreshContacts);
|
|
}
|
|
if(fFolders)
|
|
{
|
|
DebugTrace(TEXT("####>> Got Outlook Folder Refresh Event\n"));
|
|
ResetEvent(ghEventOlkRefreshFolders);
|
|
}
|
|
|
|
GetOutlookRefreshCountData(&dwContact,&dwFolder);
|
|
*lpdwContact = dwContact + (fContact ? 1 : 0);
|
|
*lpdwFolder = dwFolder + (fFolders ? 1 : 0);
|
|
SetOutlookRefreshCountData(*lpdwContact, *lpdwFolder);
|
|
}
|
|
|
|
if(fContact)
|
|
{
|
|
SYSTEMTIME st = {0};
|
|
GetSystemTime(&st);
|
|
SystemTimeToFileTime(&st, lpftLast);
|
|
}
|
|
if(fFolders)
|
|
{
|
|
// Need to specifically update the folders list in the rgolkci ..
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
HRESULT hr = E_FAIL;
|
|
ULONG colk = 0;
|
|
OutlookContInfo * rgolk = NULL;
|
|
|
|
FreeBufferAndNull(&(lpPropertyStore->rgolkci));
|
|
hr = lpWSP->lpVtbl->GetContainerList(lpWSP, &colk, &rgolk);
|
|
if(!HR_FAILED(hr))
|
|
{
|
|
DebugTrace(TEXT("WABStorageProvider::GetContainerList returns:%x\n"),hr);
|
|
ConvertOlkConttoWABCont(&colk, &rgolk, &lpPropertyStore->colkci, &lpPropertyStore->rgolkci);
|
|
FreeBufferAndNull(&rgolk);
|
|
}
|
|
}
|
|
fChanged = fContact | fFolders;
|
|
|
|
ReleaseMutex(hMutex);
|
|
}
|
|
}
|
|
return(fChanged);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : FreeEntryIDs
|
|
|
|
Purpose : Frees any LPSBinary structures allocated and returned
|
|
by funtions in thie file (e.g. WriteRecord, etc)
|
|
|
|
Parameters: lpsbEID - SBinary structure containing an entryid
|
|
|
|
Returns : void
|
|
|
|
***************************************************************************/
|
|
HRESULT FreeEntryIDs(IN HANDLE hPropertyStore,
|
|
IN ULONG ulCount,
|
|
IN LPSBinary rgsbEIDs)
|
|
{
|
|
ULONG i = 0;
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
if(pt_bIsWABOpenExSession)
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
if(!hPropertyStore)
|
|
return MAPI_E_NOT_INITIALIZED;
|
|
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = lpWSP->lpVtbl->FreeEntryIDs( lpWSP,
|
|
ulCount,
|
|
rgsbEIDs);
|
|
|
|
DebugTrace(TEXT("WABStorageProvider::FreeEntryIDs returned:%x\n"),hr);
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if(ulCount && rgsbEIDs)
|
|
{
|
|
for(i=0;i<ulCount;i++)
|
|
LocalFree(rgsbEIDs[i].lpb);
|
|
LocalFree(rgsbEIDs);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
const LPTSTR szOutlook = TEXT("Outlook Contact Store");
|
|
//$$////////////////////////////////////////////////////////////////////////
|
|
////
|
|
//// GetWABFileName()
|
|
////
|
|
//// If this is a WAB File then returns a pointer to the file name
|
|
//// If running against outlook, returns szEmpty
|
|
//// Caller should not free this
|
|
////////////////////////////////////////////////////////////////////////////
|
|
LPTSTR GetWABFileName(IN HANDLE hPropertyStore, BOOL bRetOutlookStr)
|
|
{
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = (LPMPSWab_FILE_INFO) hPropertyStore;
|
|
|
|
if(pt_bIsWABOpenExSession)
|
|
{
|
|
return (bRetOutlookStr ? szOutlook : szEmpty);
|
|
}
|
|
|
|
return lpMPSWabFileInfo->lpszMPSWabFileName;
|
|
}
|
|
|
|
//$$////////////////////////////////////////////////////////////////////////
|
|
////
|
|
//// GetWABFileEntryCount() - returns actual number of entries in a WAB
|
|
/// This number includes all contacts, groups, and foldesr
|
|
////
|
|
////////////////////////////////////////////////////////////////////////////
|
|
DWORD GetWABFileEntryCount(IN HANDLE hPropertyStore)
|
|
{
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo = (LPMPSWab_FILE_INFO) hPropertyStore;
|
|
|
|
return lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries;
|
|
}
|
|
|
|
|
|
|
|
//$$////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LocalFreePropArray - Frees an SPropValue structure allocated using LocalAlloc
|
|
// instead of MAPIAllocateBuffer.
|
|
//
|
|
// When called internally from property store functions, hPropertyStore can be
|
|
// NULL ...
|
|
// If this is a Outlook session and there is a hPropertyStore then we release through
|
|
// Outlook.
|
|
// If there is no hPropertyStore then this was locally allocated memory when we
|
|
// LocalFree ...
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void LocalFreePropArray(HANDLE hPropertyStore, ULONG ulcPropCount, LPPROPERTY_ARRAY * lppPropArray)
|
|
{
|
|
ULONG i=0,j=0,k=0;
|
|
LPSPropValue lpPropArray = *lppPropArray;
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
if(!lpPropArray || !ulcPropCount)
|
|
goto out;
|
|
|
|
if(pt_bIsWABOpenExSession && hPropertyStore)
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = lpWSP->lpVtbl->FreePropArray( lpWSP,
|
|
ulcPropCount,
|
|
*lppPropArray);
|
|
|
|
DebugTrace(TEXT("WABStorageProvider::FreePropArray returned:%x\n"),hr);
|
|
|
|
*lppPropArray = NULL;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i<ulcPropCount;i++)
|
|
{
|
|
// we only care to free the sub-level pointers which we
|
|
// might have allocated
|
|
switch(PROP_TYPE(lpPropArray[i].ulPropTag))
|
|
{
|
|
case PT_CLSID:
|
|
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i].Value.lpguid)));
|
|
break;
|
|
|
|
case PT_STRING8:
|
|
if (lpPropArray[i].Value.lpszA)// && lpPropArray[i].Value.lpszA != szEmpty)
|
|
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i].Value.lpszA)));
|
|
break;
|
|
|
|
case PT_UNICODE:
|
|
if (lpPropArray[i].Value.lpszW && lpPropArray[i].Value.lpszW != szEmpty)
|
|
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i].Value.lpszW)));
|
|
break;
|
|
|
|
case PT_BINARY:
|
|
if (lpPropArray[i].Value.bin.cb)
|
|
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i].Value.bin.lpb)));
|
|
break;
|
|
|
|
case PT_MV_STRING8:
|
|
j = lpPropArray[i].Value.MVszA.cValues;
|
|
for(k = 0; k < j; k++)
|
|
{
|
|
if (lpPropArray[i].Value.MVszA.lppszA[k])// && lpPropArray[i].Value.MVszA.lppszA[k] != szEmpty)
|
|
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i].Value.MVszA.lppszA[k])));
|
|
}
|
|
LocalFree(lpPropArray[i].Value.MVszA.lppszA);
|
|
break;
|
|
|
|
case PT_MV_UNICODE:
|
|
j = lpPropArray[i].Value.MVszW.cValues;
|
|
for(k = 0; k < j; k++)
|
|
{
|
|
if (lpPropArray[i].Value.MVszW.lppszW[k] && lpPropArray[i].Value.MVszW.lppszW[k] != szEmpty)
|
|
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i].Value.MVszW.lppszW[k])));
|
|
}
|
|
LocalFree(lpPropArray[i].Value.MVszW.lppszW);
|
|
break;
|
|
|
|
case PT_MV_BINARY:
|
|
j = lpPropArray[i].Value.MVbin.cValues;
|
|
for(k = 0; k < j; k++)
|
|
{
|
|
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i].Value.MVbin.lpbin[k].lpb)));
|
|
}
|
|
LocalFree(lpPropArray[i].Value.MVbin.lpbin);
|
|
break;
|
|
|
|
case(PT_MV_I2):
|
|
case(PT_MV_LONG):
|
|
case(PT_MV_R4):
|
|
case(PT_MV_DOUBLE):
|
|
case(PT_MV_CURRENCY):
|
|
case(PT_MV_APPTIME):
|
|
case(PT_MV_SYSTIME):
|
|
case(PT_MV_CLSID):
|
|
case(PT_MV_I8):
|
|
LocalFree(lpPropArray[i].Value.MVi.lpi);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
LocalFreeAndNull((LPVOID *) (lppPropArray)); // yes, no &
|
|
out:
|
|
return;
|
|
}
|
|
|
|
//$$///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
// FreePcontentlist is used to free LPCONTENTLIST structures
|
|
// Even though the LPCONTENTLIST is exactly the same as LPADRLIST
|
|
// there is a difference in how the 2 are created with the former
|
|
// being created using LocalAlloc and the latter thru MAPIAllocateBuffer
|
|
//
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void FreePcontentlist(HANDLE hPropertyStore,
|
|
IN OUT LPCONTENTLIST lpContentList)
|
|
{
|
|
ULONG i=0;
|
|
|
|
LPPTGDATA lpPTGData=GetThreadStoragePointer();
|
|
|
|
if(pt_bIsWABOpenExSession)
|
|
{
|
|
// This is a WABOpenEx session using outlooks storage provider
|
|
if(!hPropertyStore)
|
|
return;// MAPI_E_NOT_INITIALIZED;
|
|
|
|
{
|
|
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) hPropertyStore;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = lpWSP->lpVtbl->FreeContentList(lpWSP,
|
|
lpContentList);
|
|
|
|
DebugTrace(TEXT("WABStorageProvider::FreeContentList returned:%x\n"),hr);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!lpContentList) goto out;
|
|
|
|
for (i=0;i<lpContentList->cEntries;i++)
|
|
{
|
|
if (lpContentList->aEntries[i].rgPropVals)
|
|
LocalFreePropArray(hPropertyStore, lpContentList->aEntries[i].cValues, (LPPROPERTY_ARRAY *) (&(lpContentList->aEntries[i].rgPropVals)));
|
|
}
|
|
|
|
LocalFreeAndNull(&lpContentList);
|
|
|
|
out:
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
-
|
|
- GetFolderEIDs
|
|
-
|
|
* Returns a list of EIDs that are a member of a given folder
|
|
*
|
|
*/
|
|
HRESULT GetFolderEIDs(HANDLE hMPSWabFile,
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo,
|
|
LPSBinary pmbinFold,
|
|
ULONG * lpulFolderEIDs,
|
|
LPDWORD * lppdwFolderEIDs)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG ulcPropCount = 0;
|
|
LPSPropValue lpPropArray = NULL;
|
|
DWORD dwEntryID = 0;
|
|
ULONG i = 0,j=0;
|
|
LPDWORD lpdwFolderEIDs = NULL;
|
|
SBinary sbEID = {0};
|
|
|
|
if(pmbinFold && pmbinFold->cb != SIZEOF_WAB_ENTRYID)
|
|
{
|
|
// this may be a WAB container .. reset the entryid to a WAB entryid
|
|
if(WAB_CONTAINER == IsWABEntryID(pmbinFold->cb, (LPENTRYID)pmbinFold->lpb,
|
|
NULL,NULL,NULL,NULL,NULL))
|
|
{
|
|
IsWABEntryID(pmbinFold->cb, (LPENTRYID)pmbinFold->lpb,
|
|
(LPVOID*)&sbEID.lpb,(LPVOID*)&sbEID.cb,NULL,NULL,NULL);
|
|
if(sbEID.cb == SIZEOF_WAB_ENTRYID)
|
|
pmbinFold = &sbEID;
|
|
}
|
|
}
|
|
if(!pmbinFold || pmbinFold->cb != SIZEOF_WAB_ENTRYID)
|
|
{
|
|
return MAPI_E_INVALID_PARAMETER;
|
|
}
|
|
|
|
CopyMemory(&dwEntryID, pmbinFold->lpb, min(pmbinFold->cb,sizeof(dwEntryID)));
|
|
|
|
if(HR_FAILED(hr = ReadRecordWithoutLocking( hMPSWabFile,
|
|
lpMPSWabFileInfo,
|
|
dwEntryID,
|
|
&ulcPropCount,
|
|
&lpPropArray)))
|
|
return hr;
|
|
|
|
|
|
for(i=0;i<ulcPropCount;i++)
|
|
{
|
|
if(lpPropArray[i].ulPropTag == PR_WAB_FOLDER_ENTRIES)
|
|
{
|
|
*lpulFolderEIDs = lpPropArray[i].Value.MVbin.cValues;
|
|
if(*lpulFolderEIDs)
|
|
{
|
|
lpdwFolderEIDs = LocalAlloc(LMEM_ZEROINIT, *lpulFolderEIDs * sizeof(DWORD));
|
|
if(lpdwFolderEIDs)
|
|
{
|
|
for(j=0;j<*lpulFolderEIDs;j++)
|
|
{
|
|
CopyMemory(&(lpdwFolderEIDs[j]), lpPropArray[i].Value.MVbin.lpbin[j].lpb, min(lpPropArray[i].Value.MVbin.lpbin[j].cb, sizeof(lpdwFolderEIDs[0])));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(*lpulFolderEIDs && lpdwFolderEIDs)
|
|
*lppdwFolderEIDs = lpdwFolderEIDs;
|
|
|
|
LocalFreePropArray(NULL, ulcPropCount, &lpPropArray);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
-
|
|
- bIsFolderMember
|
|
-
|
|
* Returns TRUE if specified entry is a member of a folder
|
|
*
|
|
*/
|
|
BOOL bIsFolderMember(HANDLE hMPSWabFile,
|
|
LPMPSWab_FILE_INFO lpMPSWabFileInfo,
|
|
DWORD dwEntryID, ULONG * lpulObjType)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
ULONG ulRecordOffset = 0;
|
|
ULONG nIndexPos = 0;
|
|
ULONG * lpulPropTags = NULL;
|
|
|
|
//
|
|
// First check if this is a valid entryID
|
|
//
|
|
if (!BinSearchEID( IN lpMPSWabFileInfo->lpMPSWabIndexEID,
|
|
IN dwEntryID,
|
|
IN lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries,
|
|
OUT &nIndexPos))
|
|
{
|
|
DebugTrace(TEXT("Specified EntryID doesnt exist!\n"));
|
|
goto out;
|
|
}
|
|
|
|
//if entryid exists, we can start reading the record
|
|
ulRecordOffset = lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos].ulOffset;
|
|
|
|
{
|
|
MPSWab_RECORD_HEADER MPSWabRecordHeader = {0};
|
|
DWORD dwNumofBytes = 0;
|
|
ULONG i = 0;
|
|
|
|
if(!ReadDataFromWABFile(hMPSWabFile,
|
|
ulRecordOffset,
|
|
(LPVOID) &MPSWabRecordHeader,
|
|
(DWORD) sizeof(MPSWab_RECORD_HEADER)))
|
|
goto out;
|
|
|
|
if(lpulObjType)
|
|
*lpulObjType = MPSWabRecordHeader.ulObjType;
|
|
|
|
lpulPropTags = LocalAlloc(LMEM_ZEROINIT, MPSWabRecordHeader.ulcPropCount * sizeof(ULONG));
|
|
if(!lpulPropTags)
|
|
{
|
|
DebugTrace(TEXT("Error allocating memory\n"));
|
|
goto out;
|
|
}
|
|
|
|
//Read in the data
|
|
if(!ReadFile( hMPSWabFile,
|
|
(LPVOID) lpulPropTags,
|
|
(DWORD) MPSWabRecordHeader.ulPropTagArraySize,
|
|
&dwNumofBytes,
|
|
NULL))
|
|
{
|
|
DebugTrace(TEXT("Reading Record Header failed.\n"));
|
|
goto out;
|
|
}
|
|
|
|
for(i=0;i<MPSWabRecordHeader.ulcPropCount;i++)
|
|
{
|
|
if(lpulPropTags[i] == PR_WAB_FOLDER_PARENT || lpulPropTags[i] == PR_WAB_FOLDER_PARENT_OLDPROP)
|
|
{
|
|
bRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
|
|
if(lpulPropTags)
|
|
LocalFree(lpulPropTags);
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*
|
|
-
|
|
- ConvertWCPropsToALocalAlloc()
|
|
-
|
|
* Takes a SPropValue array and converts Unicode strings to ANSI equivalents
|
|
* Uses LocalAlloc for the new strings .. unlike the ScWCtoAnsiMore which uses the
|
|
* internal memory allocators
|
|
*/
|
|
void ConvertWCPropsToALocalAlloc(LPSPropValue lpProps, ULONG ulcValues)
|
|
{
|
|
ULONG i = 0, j = 0, ulCount = 0;
|
|
LPSTR * lppszA = NULL;
|
|
LPSTR lpszA = NULL;
|
|
|
|
for(i=0;i<ulcValues;i++)
|
|
{
|
|
switch(PROP_TYPE(lpProps[i].ulPropTag))
|
|
{
|
|
case PT_UNICODE:
|
|
lpszA = ConvertWtoA(lpProps[i].Value.lpszW);
|
|
LocalFreeAndNull((LPVOID *) (&lpProps[i].Value.lpszW));
|
|
lpProps[i].Value.lpszA = lpszA;
|
|
lpProps[i].ulPropTag = CHANGE_PROP_TYPE( lpProps[i].ulPropTag, PT_STRING8);
|
|
break;
|
|
case PT_MV_UNICODE:
|
|
ulCount = lpProps[i].Value.MVszW.cValues;
|
|
if(lppszA = LocalAlloc(LMEM_ZEROINIT, sizeof(LPSTR)*ulCount))
|
|
{
|
|
for(j=0;j<ulCount;j++)
|
|
{
|
|
lppszA[j] = ConvertWtoA(lpProps[i].Value.MVszW.lppszW[j]);
|
|
LocalFreeAndNull((LPVOID*)&(lpProps[i].Value.MVszW.lppszW[j]));
|
|
}
|
|
LocalFreeAndNull((LPVOID*)(&lpProps[i].Value.MVszW.lppszW));
|
|
lpProps[i].Value.MVszW.cValues = 0;
|
|
lpProps[i].Value.MVszA.cValues = ulCount;
|
|
lpProps[i].Value.MVszA.lppszA = lppszA;
|
|
lppszA = NULL;
|
|
lpProps[i].ulPropTag = CHANGE_PROP_TYPE( lpProps[i].ulPropTag, PT_MV_STRING8);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
-
|
|
- ConvertAPropsToWCLocalAlloc()
|
|
-
|
|
* Takes a SPropValue array and converts Unicode strings to ANSI equivalents
|
|
* Uses LocalAlloc for the new strings .. unlike the ScWCtoAnsiMore which uses the
|
|
* internal memory allocators
|
|
*/
|
|
void ConvertAPropsToWCLocalAlloc(LPSPropValue lpProps, ULONG ulcValues)
|
|
{
|
|
ULONG i = 0, j = 0, ulCount = 0;
|
|
LPWSTR * lppszW = NULL;
|
|
LPWSTR lpszW = NULL;
|
|
|
|
for(i=0;i<ulcValues;i++)
|
|
{
|
|
switch(PROP_TYPE(lpProps[i].ulPropTag))
|
|
{
|
|
case PT_STRING8:
|
|
lpszW = ConvertAtoW(lpProps[i].Value.lpszA);
|
|
LocalFreeAndNull((LPVOID *) (&lpProps[i].Value.lpszA));
|
|
lpProps[i].Value.lpszW = lpszW;
|
|
lpProps[i].ulPropTag = CHANGE_PROP_TYPE( lpProps[i].ulPropTag, PT_UNICODE);
|
|
break;
|
|
case PT_MV_STRING8:
|
|
ulCount = lpProps[i].Value.MVszA.cValues;
|
|
if(lppszW = LocalAlloc(LMEM_ZEROINIT, sizeof(LPWSTR)*ulCount))
|
|
{
|
|
for(j=0;j<ulCount;j++)
|
|
{
|
|
lppszW[j] = ConvertAtoW(lpProps[i].Value.MVszA.lppszA[j]);
|
|
LocalFreeAndNull((LPVOID *) (&lpProps[i].Value.MVszA.lppszA[j]));
|
|
}
|
|
LocalFreeAndNull((LPVOID *)&(lpProps[i].Value.MVszW.lppszW));
|
|
lpProps[i].Value.MVszA.cValues = 0;
|
|
lpProps[i].Value.MVszW.cValues = ulCount;
|
|
lpProps[i].Value.MVszW.lppszW = lppszW;
|
|
lppszW = NULL;
|
|
lpProps[i].ulPropTag = CHANGE_PROP_TYPE( lpProps[i].ulPropTag, PT_MV_UNICODE);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|