text: ////////////////////////////////////////////////////////////////////////////////
  ///
  ///
  /// MPSMisc.C
  ///
  /// Microsoft Property Store - WAB Dll - miscellaneous helper functions
  ///
  /// binSearchStr
  /// binSearchEID
  /// LoadIndex
  /// SizeOfSinglePropData
  /// SizeOfMultiPropData
  /// UnlockFileAccess
  /// LockFileAccess
  /// LocalFreePropArray
  /// FreePcontentlist
  /// ReadRecordWithoutLocking
  /// GetWABBackupFileName
  /// CopySrcFileToDestFile
  /// bIsValidRecord
  /// TagWABFileError
  /// ReloadMPSWabFileInfo
  ///
  /// CreateMPSWabFile
  /// CompressFile
  /// HrDoQuickWABIntegrityCheck
  /// HrResetWABFileContents
  /// HrRestoreFromBackup
  /// HrDoDetailedWABIntegrityCheck
  /////////////////////////////////////////////////////////////////////////////////
  #include "_apipch.h"
  
  extern BOOL fTrace;							// Set TRUE if you want debug traces
  extern BOOL fDebugTrap;						// Set TRUE to get int3's
  extern TCHAR szDebugOutputFile[MAX_PATH];	// the name of the debug output file
  extern BOOL SubstringSearch(LPTSTR pszTarget, LPTSTR pszSearch);
  
  BOOL CopySrcFileToDestFile(HANDLE hSrc, ULONG ulSrcStartOffset,
                             HANDLE hDest,ULONG ulDestStartOffset);
  
  
  
  //$$////////////////////////////////////////////////////////////////////////////
  //
  // Gets us a WAB specific temp file name to play with
  //
  //
  ////////////////////////////////////////////////////////////////////////////////
  void GetWABTempFileName(LPTSTR szFileName)
  {
      TCHAR   szBuf[MAX_PATH];
      TCHAR   szBufPath[MAX_PATH];
      szBufPath[0]='\0';
      GetTempPath(MAX_PATH,szBufPath);
      LoadString(hinstMapiX, IDS_WAB_TEMP_FILE_PREFIX, szBuf, CharSizeOf(szBuf));
      GetTempFileName(szBufPath,                   /* dir. for temp. files            */
                      szBuf, //"MPS",                /* temp. filename prefix           */
                      0,                    /* create unique name w/ sys. time */
                      (LPTSTR) szFileName); /* buffer for name                 */
  
      return;
  }
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  BOOL binSearchStr - binary search routine for scanning string indexes
  //
  // IN  struct _tagIndexOffset * Index - Index Array to Search in
  // IN  LPTSTR lpszValue - value to search for
  // IN  ULONG nArraySize - number of elements in array
  // OUT ULONG lpulMatchIndex - array index of matched item
  //
  // Returns:
  //      Nothing found: FALSE - lpulMatchIndex contains array position at which this entry
  //                              this entry would hypothetically exist, were it a part of the array
  //      Match found: TRUE - lpulMatchIndex contains array position of matched entry
  //
  // Comments:
  //      Algorithm from "Data Structures" by Reingold & Hansen, pg. 278.
  //
  ////////////////////////////////////////////////////////////////////////////////////
  BOOL BinSearchStr(  IN  struct  _tagMPSWabIndexEntryDataString * lpIndexStr,
                      IN  LPTSTR  lpszValue,   //used for searching strings
                      IN  ULONG   nArraySize,
                      OUT ULONG * lpulMatchIndex)
  {
      LONG    low = 0;
      LONG    high = nArraySize - 1;
      LONG    mid = (low + high) / 2;
      int     comp = 0;
      BOOL    bRet = FALSE;
  
      *lpulMatchIndex = 0;
  
      if (nArraySize == 0) return FALSE;
  
      while (low <= high && ! bRet) {
          mid = (low + high) / 2;
          comp = lstrcmpi(lpIndexStr[mid].szIndex, lpszValue);
          if (comp < 0) {
              low = mid + 1;
          } else if (comp > 0) {
              high = mid - 1;
          } else {
              bRet = TRUE;
          }
      }
  
      // Calculate found or insert position
      (ULONG)*lpulMatchIndex = bRet ? mid : low;
  
      // DebugTrace(TEXT("\tBinSearchSTR: Exit\n"));
  
      return bRet;
  }
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  BOOL binSearchEID - binary search routine for scanning EntryID index
  //
  // IN  lpIndexEID - Index Array to Search in
  // IN  LPTSTR dwValue - value to search for
  // IN  ULONG nArraySize - number of elements in array
  // OUT ULONG lpulMatchIndex - array index of matched item
  //
  // Returns:
  //      Nothing found: FALSE - lpulMatchIndex contains array position at which this entry
  //                              this entry would hypothetically exist, were it a part of the array
  //      Match found: TRUE - lpulMatchIndex contains array position of matched entry
  //
  ////////////////////////////////////////////////////////////////////////////////////
  BOOL BinSearchEID(  IN  struct  _tagMPSWabIndexEntryDataEntryID * lpIndexEID,
                      IN  DWORD   dwValue,     //used for comparing DWORDs
                      IN  ULONG   nArraySize,
                      OUT ULONG * lpulMatchIndex)
  {
      LONG    low = 0;
      LONG    high = nArraySize - 1;
      LONG    mid = (low + high) / 2;
      BOOL    bRet = FALSE;
  
      *lpulMatchIndex = 0;
  
  
      // The special cases for this algorithm are
      // nArraySize == 0
      if (nArraySize == 0) return FALSE;
  
      while (low <= high && ! bRet) {
          mid = (low + high) / 2;
  
          if (lpIndexEID[mid].dwEntryID < dwValue) 
              low = mid+1;
          else if (lpIndexEID[mid].dwEntryID > dwValue) 
              high = mid - 1;
          else //equal 
              bRet = TRUE;
      }
  
      // Calculate found or insert position
      (ULONG)*lpulMatchIndex = bRet ? mid : low;
  
      return bRet;
  }
  
  
  
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  CreateMPSWabFile
  //
  //  Internal function for creating the MPS Wab File - called from several places
  //
  //  IN ulcMaxEntries - this number determines how much space we put aside for
  //                      the indexes when we create the file. From time to time
  //                      we will need to grow the file so we can call this CreateFile
  //                      function to create the new file with the new size...
  //
  ////////////////////////////////////////////////////////////////////////////////////
  BOOL CreateMPSWabFile(IN    struct  _tagMPSWabFileHeader * lpMPSWabFileHeader,
                        IN    LPTSTR  lpszFileName,
                        IN    ULONG   ulcMaxEntries,
                        IN    ULONG   ulNamedPropSize)
  {
  
          HRESULT hr = E_FAIL;
          HANDLE  hMPSWabFile = NULL;
          DWORD   dwNumofBytesWritten;
          LPVOID  lpszBuffer = NULL;
          int     i = 0;
  
          DebugTrace(TEXT("\tCreateMPSWabFile: Entry\n"));
  
  
          //
          // Create the file - its assumed that calling function has worked out all the
          // logic for whether or not old file should be left alone or not.
          //
          hMPSWabFile = CreateFile(   lpszFileName,
                                      GENERIC_WRITE,
                                      0,
                                      (LPSECURITY_ATTRIBUTES) NULL,
                                      CREATE_ALWAYS,
                                      FILE_ATTRIBUTE_NORMAL,
                                      (HANDLE) NULL);
  
          if (hMPSWabFile == INVALID_HANDLE_VALUE)
          {
              DebugPrintError(( TEXT("Could not create file.\nExiting ...\n")));
              goto out;
          }
  
  
          lpMPSWabFileHeader->ulModificationCount = 0;
          lpMPSWabFileHeader->MPSWabGuid = MPSWab_GUID;
          lpMPSWabFileHeader->ulcNumEntries = 0;
          lpMPSWabFileHeader->ulcMaxNumEntries = ulcMaxEntries;
          lpMPSWabFileHeader->ulFlags = WAB_CLEAR;
          lpMPSWabFileHeader->ulReserved1 = 0;
          lpMPSWabFileHeader->ulReserved2 = 0;
          lpMPSWabFileHeader->ulReserved3 = 0;
          lpMPSWabFileHeader->ulReserved4 = 0;
          lpMPSWabFileHeader->dwNextEntryID = 1;
  
  
          // We will squeeze in the space to save the named property data betweeen th
          // File header and the First Index
          lpMPSWabFileHeader->NamedPropData.ulOffset = sizeof(MPSWab_FILE_HEADER);
          lpMPSWabFileHeader->NamedPropData.UtilizedBlockSize = 0;
          lpMPSWabFileHeader->NamedPropData.ulcNumEntries = 0;
          lpMPSWabFileHeader->NamedPropData.AllocatedBlockSize = ulNamedPropSize;
  
          // its important that this order matches  TEXT("enum _IndexType") in mpswab.h
          // or we'll have major read-write problems
          for(i=0;i<indexMax;i++)
          {
  
              lpMPSWabFileHeader->IndexData[i].UtilizedBlockSize = 0;
              lpMPSWabFileHeader->IndexData[i].ulcNumEntries = 0;
              if(i==indexEntryID)
              {
                  lpMPSWabFileHeader->IndexData[i].ulOffset = lpMPSWabFileHeader->NamedPropData.AllocatedBlockSize + lpMPSWabFileHeader->NamedPropData.ulOffset;
                  lpMPSWabFileHeader->IndexData[i].AllocatedBlockSize = ulcMaxEntries * sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID);
              }
              else
              {
                  lpMPSWabFileHeader->IndexData[i].ulOffset = lpMPSWabFileHeader->IndexData[i-1].ulOffset + lpMPSWabFileHeader->IndexData[i-1].AllocatedBlockSize;
                  lpMPSWabFileHeader->IndexData[i].AllocatedBlockSize = ulcMaxEntries * sizeof(MPSWab_INDEX_ENTRY_DATA_STRING);
              }
          }
  
  
          //Now we write this dummy structure to the file
          if(!WriteFile(  hMPSWabFile,
                          (LPCVOID) lpMPSWabFileHeader,
                          (DWORD) sizeof(MPSWab_FILE_HEADER),
                          &dwNumofBytesWritten,
                          NULL))
          {
              DebugPrintError(( TEXT("Writing FileHeader failed.\n")));
              goto out;
          }
  
  
          //Assuming that the entryid index is always smaller than the display name index
          // allocate enough empty space for a display name index and
          lpszBuffer = LocalAlloc(LMEM_ZEROINIT, lpMPSWabFileHeader->IndexData[indexDisplayName].AllocatedBlockSize);
          if(!lpszBuffer)
          {
              DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n")));
              hr = MAPI_E_NOT_ENOUGH_MEMORY;
              goto out;
          }
  
          // Write the dummy blank Named Prop data into the file
          // (this ensures that there are all zeros in the blank space)
          // Assumes that the NamedPropData will be less than the index space
          if(!WriteFile(  hMPSWabFile,
                          (LPCVOID) lpszBuffer,
                          (DWORD) lpMPSWabFileHeader->NamedPropData.AllocatedBlockSize,
                          &dwNumofBytesWritten,
                          NULL))
          {
              DebugPrintError(( TEXT("Writing Index No. %d failed.\n"),i));
              goto out;
          }
  
          for (i=0;i<indexMax;i++)
          {
              if(!WriteFile(  hMPSWabFile,
                              (LPCVOID) lpszBuffer,
                              (DWORD) lpMPSWabFileHeader->IndexData[i].AllocatedBlockSize,
                              &dwNumofBytesWritten,
                              NULL))
              {
                  DebugPrintError(( TEXT("Writing Index No. %d failed.\n"),i));
                  goto out;
              }
          }
  
          LocalFreeAndNull(&lpszBuffer);
  
          IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
          hMPSWabFile = NULL;
  
          hr = S_OK;
  
  
  out:
          LocalFreeAndNull(&lpszBuffer);
  
          DebugTrace(TEXT("\tCreateMPSWabFile: Exit\n"));
  
          return( (FAILED(hr)) ? FALSE : TRUE);
  
  }
  
  
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  LoadIndex - Only one of the string indexes is loaded at any given time
  //      If we need some other index in memory, we have to reload it from the file ...
  //
  //  This assumes that the ulcNumEntries and UtilizedBlockData for each index in the file header is up to date
  //  because that value is used to allocate memory for the index
  //
  //  We use this function as a generic load index function too
  //
  //
  ////////////////////////////////////////////////////////////////////////////////////
  BOOL LoadIndex( IN  struct  _tagMPSWabFileInfo * lpMPSWabFileInfo,
                  IN  ULONG   nIndexType,
                  IN  HANDLE  hMPSWabFile)
  {
      BOOL    bRet = FALSE;
      DWORD   dwNumofBytes = 0;
  
      // DebugTrace(TEXT("\tLoadIndex: Entry\n"));
  
      if (!lpMPSWabFileInfo) goto out;
  
      if (lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries==0) //assumes this is an accurate value
      {
          LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabIndexEID);
          LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabIndexStr);
  
          bRet = TRUE;
          goto out;
      }
  
      //otherwise we have to reload the index from file
  
      //
      //First free the existing index
      //
      if (nIndexType == indexEntryID)
      {
          LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabIndexEID);
      }
      else
      {
          LocalFreeAndNull(&lpMPSWabFileInfo->lpMPSWabIndexStr);
      }
  
  
      //Load the index into memory
      if(0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
                                        lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[nIndexType].ulOffset,
                                        NULL,
                                        FILE_BEGIN))
      {
          DebugPrintError(( TEXT("SetFilePointer Failed\n")));
          goto out;
      }
  
      if (nIndexType == indexEntryID)
      {
          lpMPSWabFileInfo->lpMPSWabIndexEID = LocalAlloc(LMEM_ZEROINIT, lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[nIndexType].UtilizedBlockSize);
          if(!(lpMPSWabFileInfo->lpMPSWabIndexEID))
          {
              DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n")));
              goto out;
          }
          if(!ReadFile(   hMPSWabFile,
                          (LPVOID) lpMPSWabFileInfo->lpMPSWabIndexEID,
                          (DWORD) lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[nIndexType].UtilizedBlockSize,
                          &dwNumofBytes,
                          NULL))
          {
              DebugPrintError(( TEXT("Reading Index failed.\n")));
              goto out;
          }
      }
      else
      {
          lpMPSWabFileInfo->lpMPSWabIndexStr = LocalAlloc(LMEM_ZEROINIT, lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[nIndexType].UtilizedBlockSize);
          if(!(lpMPSWabFileInfo->lpMPSWabIndexStr))
          {
              DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n")));
              goto out;
          }
          if(!ReadFile(   hMPSWabFile,
                          (LPVOID) lpMPSWabFileInfo->lpMPSWabIndexStr,
                          (DWORD) lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[nIndexType].UtilizedBlockSize,
                          &dwNumofBytes,
                          NULL))
          {
              DebugPrintError(( TEXT("Reading Index failed.\n")));
              goto out;
          }
      }
  
      if (nIndexType != indexEntryID)
          lpMPSWabFileInfo->nCurrentlyLoadedStrIndexType = nIndexType;
  
      bRet = TRUE;
  
  out:
  
      // DebugTrace(TEXT( TEXT("\tLoadIndex: Exit\n")));
      return bRet;
  }
  
  
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  SizeOfSinglePropData - returns the number of bytes of data in a given SPropValue ...
  //
  //
  ////////////////////////////////////////////////////////////////////////////////////
  ULONG SizeOfSinglePropData(SPropValue Prop)
  {
  
      ULONG   i = 0;
      ULONG   ulDataSize = 0;
  
      switch(PROP_TYPE(Prop.ulPropTag))
      {
      case PT_I2:
          ulDataSize = sizeof(short int);
          break;
      case PT_LONG:
          ulDataSize = sizeof(LONG);
          break;
      case PT_R4:
          ulDataSize = sizeof(float);
          break;
      case PT_DOUBLE:
          ulDataSize = sizeof(double);
          break;
      case PT_BOOLEAN:
          ulDataSize = sizeof(unsigned short int);
          break;
      case PT_CURRENCY:
          ulDataSize = sizeof(CURRENCY);
          break;
      case PT_APPTIME:
          ulDataSize = sizeof(double);
          break;
      case PT_SYSTIME:
          ulDataSize = sizeof(FILETIME);
          break;
      case PT_STRING8:
          ulDataSize = lstrlenA(Prop.Value.lpszA)+1;
          break;
      case PT_UNICODE:
          ulDataSize = sizeof(TCHAR)*(lstrlenW(Prop.Value.lpszW)+1);
          break;
      case PT_BINARY:
          ulDataSize = Prop.Value.bin.cb;
          break;
      case PT_CLSID:
          ulDataSize = sizeof(GUID);
          break;
      case PT_I8:
          ulDataSize = sizeof(LARGE_INTEGER);
          break;
      case PT_ERROR:
          ulDataSize = sizeof(SCODE);
          break;
      case PT_NULL:
          ulDataSize = sizeof(LONG);
          break;
      }
  
      return ulDataSize;
  
  }
  
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  SizeOfMultiPropData - returns the number of bytes of data in a given SPropValue ...
  //
  //
  ////////////////////////////////////////////////////////////////////////////////////
  ULONG SizeOfMultiPropData(SPropValue Prop)
  {
  
      ULONG   i = 0;
      ULONG   ulDataSize = 0;
  
      switch(PROP_TYPE(Prop.ulPropTag))
      {
      case PT_MV_I2:
          ulDataSize = sizeof(short int) * Prop.Value.MVi.cValues;
          break;
      case PT_MV_LONG:
          ulDataSize = sizeof(LONG) * Prop.Value.MVl.cValues;
          break;
      case PT_MV_R4:
          ulDataSize = sizeof(float) * Prop.Value.MVflt.cValues;
          break;
      case PT_MV_DOUBLE:
          ulDataSize = sizeof(double) * Prop.Value.MVdbl.cValues;
          break;
      case PT_MV_CURRENCY:
          ulDataSize = sizeof(CURRENCY) * Prop.Value.MVcur.cValues;
          break;
      case PT_MV_APPTIME:
          ulDataSize =  sizeof(double) * Prop.Value.MVat.cValues;
          break;
      case PT_MV_SYSTIME:
          ulDataSize = sizeof(FILETIME) * Prop.Value.MVft.cValues;
          break;
      case PT_MV_BINARY:
          ulDataSize = 0;
          // Note this data size includes, for each array entry, the sizeof(ULONG) that
          // contains the actual datasize (i.e cb)
          for(i=0;i<Prop.Value.MVbin.cValues;i++)
          {
              ulDataSize += sizeof(ULONG) + Prop.Value.MVbin.lpbin[i].cb;
          }
          break;
      case PT_MV_STRING8:
          ulDataSize = 0;
          DebugTrace(TEXT("where the heck are we getting ANSI data from\n"));
          // Note this data size includes, for each array entry, the sizeof(ULONG) that
          // contains the actual datasize (i.e cb)
          for(i=0;i<Prop.Value.MVszA.cValues;i++)
          {
              // Note the strlen is incremented by '+1' to include the terminating NULL for
              // each string
              ulDataSize += sizeof(ULONG) + lstrlenA(Prop.Value.MVszA.lppszA[i])+1;
          }
          break;
      case PT_MV_UNICODE:
          ulDataSize = 0;
          // Note this data size includes, for each array entry, the sizeof(ULONG) that
          // contains the actual datasize (i.e cb)
          for(i=0;i<Prop.Value.MVszW.cValues;i++)
          {
              // Note the strlen is incremented by '+1' to include the terminating NULL for
              // each string
              ulDataSize += sizeof(ULONG) + sizeof(TCHAR)*(lstrlenW(Prop.Value.MVszW.lppszW[i])+1);
          }
          break;
      case PT_MV_CLSID:
          ulDataSize = sizeof(GUID) * Prop.Value.MVguid.cValues;
          break;
      case PT_MV_I8:
          ulDataSize = sizeof(LARGE_INTEGER) * Prop.Value.MVli.cValues;
          break;
      }
  
      return ulDataSize;
  
  }
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  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 ReloadMPSWabFileInfo(
                      IN  struct  _tagMPSWabFileInfo * lpMPSWabFileInfo,
                      IN  HANDLE  hMPSWabFile)
  {
      BOOL bRet = TRUE;
      ULONG i = 0;
      DWORD dwNumofBytes = 0;
  
      if(!ReadDataFromWABFile(hMPSWabFile,
                              0,
                              (LPVOID) lpMPSWabFileInfo->lpMPSWabFileHeader,
                              sizeof(MPSWab_FILE_HEADER)))
         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(lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags & WAB_ERROR_DETECTED)
      {
          if(!WriteFile(  hMPSWabFile,
                          (LPCVOID) lpMPSWabFileInfo->lpMPSWabFileHeader,
                          (DWORD) sizeof(MPSWab_FILE_HEADER),
                          &dwNumofBytes,
                          NULL))
          {
              DebugPrintError(( TEXT("Writing FileHeader failed.\n")));
              goto out;
          }
  
      }
  
  
      //
      // Get the Entry iD index
      //
      if (!LoadIndex( IN  lpMPSWabFileInfo,
                      IN  indexEntryID,
                      IN  hMPSWabFile) )
      {
          DebugPrintError(( TEXT("Error Loading EntryID Index!\n")));
          goto out;
      }
  
      //
      // Get the current string index
      //
      if (!LoadIndex( IN  lpMPSWabFileInfo,
                      IN  lpMPSWabFileInfo->nCurrentlyLoadedStrIndexType,
                      IN  hMPSWabFile) )
      {
          DebugPrintError(( TEXT("Error Loading String Index!\n")));
          goto out;
      }
  
      bRet = TRUE;
  
  out:
  
      return bRet;
  
  }
  
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  UnlockFileAccess - UnLocks Exclusive Access to the property store
  //
  ////////////////////////////////////////////////////////////////////////////////////
  BOOL UnLockFileAccess(LPMPSWab_FILE_INFO lpMPSWabFileInfo)
  {
      BOOL bRet = FALSE;
  
      //DebugTrace(TEXT( TEXT("\t\tUnlockFileAccess\n")));
  
      if(lpMPSWabFileInfo)
      {
          bRet = ReleaseMutex(lpMPSWabFileInfo->hDataAccessMutex);
      }
  
      return bRet;
  }
  
  
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  LockFileAccess - Gives exclusive access to the Property Store
  //
  ////////////////////////////////////////////////////////////////////////////////////
  BOOL LockFileAccess(LPMPSWab_FILE_INFO lpMPSWabFileInfo)
  {
      BOOL bRet = FALSE;
      DWORD dwWait = 0;
  
      //DebugTrace(TEXT( TEXT("\t\tLockFileAccess\n")));
  
      if(lpMPSWabFileInfo)
      {
          dwWait = WaitForSingleObject(lpMPSWabFileInfo->hDataAccessMutex,MAX_LOCK_FILE_TIMEOUT);
  
          if ((dwWait == WAIT_TIMEOUT) || (dwWait == WAIT_FAILED))
          {
              DebugPrintError(( TEXT("Thread:%x\tWaitForSingleObject failed.\n"),GetCurrentThreadId()));
              bRet = FALSE;
          }
          else
              bRet = TRUE;
      }
  
      return(bRet);
  
  }
  
  
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  CompressFile - Creates a compressed version of the property store
  //      file that removes all the invlaid records.
  //
  //  The compressiong function is very similar to creating a backup and hence
  //  the exported Backup function calls CompressFile. The difference being that
  //  in Backup, a new file is created with a new name and in CompressFile, the
  //  newfile is renamed to the property store
  //
  //  Similarly, growing the file is very similar and the internal call to Growing
  //  the file calls CompressFile too
  //
  //
  //  IN  lpMPSWabFileInfo
  //  IN  lpsznewFileName - supplied by backup. if NULL, means that CompressFile
  //      should rename the new file as the property store
  //  IN BOOL bGrowFile - if specified, the new file is created with space for
  //      an additional MAX_INITIAL_INDEX_ENTRIES
  //  IN ULONG ulFlags - there are 2 things that can grow here - the index size and
  //              the named property storage size. Hence we have the following flags
  //              one or more of which can be used simultaneously
  //              AB_GROW_INDEX | AB_GROW_NAMEDPROP
  //
  //  Returns
  //      Success:    TRUE
  //      Failure:    FALSE
  //
  ////////////////////////////////////////////////////////////////////////////////////
  BOOL CompressFile(  IN  struct  _tagMPSWabFileInfo * lpMPSWabFileInfo,
                      IN  HANDLE  hMPSWabFile,
                      IN  LPTSTR  lpszFileName,
                      IN  BOOL    bGrowFile,
                      IN  ULONG   ulFlags)
  {
      BOOL    bRet = FALSE;
      BOOL    bBackup = FALSE;
      BOOL    bRFileLocked = FALSE;
      BOOL    bWFileLocked = FALSE;
      HANDLE  hTempFile = NULL;
      struct  _tagMPSWabFileHeader NewFileHeader = {0};
      ULONG   ulNewFileMaxEntries = 0;
      ULONG   ulNamedPropSize = 0;
      DWORD   dwNumofBytes = 0;
      struct  _tagMPSWabIndexEntryDataString * lpIndexStr = NULL;
      struct  _tagMPSWabIndexEntryDataEntryID NewMPSWabIndexEID;
      ULONG   ulNewRecordOffset = 0;
      ULONG   ulNewEIDIndexElementOffset = 0;
      ULONG   i = 0;
      LPULONG lpPropTagArray = NULL;
      struct  _tagMPSWabRecordHeader RecordHeader;
      LPVOID  lpRecordData = NULL;
  
      ULONG   ulBytesLeftToCopy = 0;
      ULONG   ulChunkSize = 8192; //copy 8k at a time
      LPVOID  lpv = NULL;
      TCHAR   szFileName[MAX_PATH];
  
      ULONG   ulFileSize = 0;
  
  
      DebugTrace(TEXT("----Thread:%x\tCompressFile: Entry\n"),GetCurrentThreadId());
  
      // if this is a backup operation we first backup to a temp file and
      // then rename the temp file to the backup - this way if the process
      // fails we dont lose our last made backup ...
  
      if (lpszFileName != NULL)
      {
          if (!lstrcmpi(lpszFileName,lpMPSWabFileInfo->lpszMPSWabFileName))
          {
              DebugPrintError(( TEXT("Cannot backup a file over itself. Please specify new backup file name.")));
              goto out;
          }
          bBackup = TRUE;
      }
      else
          bBackup = FALSE;
  
      GetWABTempFileName(szFileName);
  
      // Find the least multiple of MAX_INITIAL_INDEX_ENTRIES that can accomodate the
      // existing entries in the file and set ulNewFilMaxEntries to that number
  
      ulNewFileMaxEntries = 0;
  
      {
          int j=0;
          for( j = (lpMPSWabFileInfo->lpMPSWabFileHeader->ulcMaxNumEntries/MAX_INITIAL_INDEX_ENTRIES);j >= 0; j--)
          {
              if (lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries >= (ULONG) j*MAX_INITIAL_INDEX_ENTRIES)
              {
                  ulNewFileMaxEntries = (j+1)*MAX_INITIAL_INDEX_ENTRIES;
                  break;
              }
          }
  
          if (ulNewFileMaxEntries == 0) //this shouldnt happen
              ulNewFileMaxEntries = lpMPSWabFileInfo->lpMPSWabFileHeader->ulcMaxNumEntries;
  
          ulNamedPropSize = lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.AllocatedBlockSize;
      }
  
      if (bGrowFile)
      {
          if(ulFlags & AB_GROW_INDEX)
              ulNewFileMaxEntries += MAX_INITIAL_INDEX_ENTRIES;
  
          if(ulFlags & AB_GROW_NAMEDPROP)
              ulNamedPropSize += NAMEDPROP_STORE_INCREMENT_SIZE;
      }
  
      if (!CreateMPSWabFile(  IN  &NewFileHeader,
                              IN  szFileName,
                              IN  ulNewFileMaxEntries,
                              IN  ulNamedPropSize))
      {
          DebugPrintError(( TEXT("Could Not Create File %s!\n"),szFileName));
          goto out;
      }
  
      if (hMPSWabFile == INVALID_HANDLE_VALUE)
      {
          DebugPrintError(( TEXT("Could not open file.\nExiting ...\n")));
          goto out;
      }
  
  
      hTempFile = CreateFile(     szFileName,
                                  GENERIC_READ | GENERIC_WRITE,
                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                                  (LPSECURITY_ATTRIBUTES) NULL,
                                  OPEN_EXISTING,
                                  FILE_FLAG_RANDOM_ACCESS,
                                  (HANDLE) NULL);
  
      if (hTempFile == INVALID_HANDLE_VALUE)
      {
          DebugPrintError(( TEXT("Could not open file.\nExiting ...\n")));
          goto out;
      }
  
  
      ulFileSize = GetFileSize(hMPSWabFile, NULL);
  
      NewFileHeader.ulcNumEntries = lpMPSWabFileInfo->lpMPSWabFileHeader->ulcNumEntries;
      NewFileHeader.ulModificationCount = 0;
      NewFileHeader.dwNextEntryID = lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID;
      NewFileHeader.ulFlags = lpMPSWabFileInfo->lpMPSWabFileHeader->ulFlags;
  
      NewFileHeader.NamedPropData.UtilizedBlockSize = lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.UtilizedBlockSize;
      NewFileHeader.NamedPropData.ulcNumEntries = lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.ulcNumEntries;
  
      NewFileHeader.ulReserved1 = lpMPSWabFileInfo->lpMPSWabFileHeader->ulReserved1;
      NewFileHeader.ulReserved2 = lpMPSWabFileInfo->lpMPSWabFileHeader->ulReserved2;
      NewFileHeader.ulReserved3 = lpMPSWabFileInfo->lpMPSWabFileHeader->ulReserved3;
      NewFileHeader.ulReserved4 = lpMPSWabFileInfo->lpMPSWabFileHeader->ulReserved4;
  
  
      for(i=indexEntryID; i<indexMax; i++)
      {
          NewFileHeader.IndexData[i].UtilizedBlockSize = lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[i].UtilizedBlockSize;
          NewFileHeader.IndexData[i].ulcNumEntries = lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[i].ulcNumEntries;
      }
  
  
      // write the header info
      //
      if(!WriteDataToWABFile( hTempFile,
                              0,
                              (LPVOID) &NewFileHeader,
                              sizeof(MPSWab_FILE_HEADER)))
          goto out;
  
  
      //
      // Copy over Named Prop Data
      //
      {
          lpv = NULL;
          lpv = LocalAlloc(LMEM_ZEROINIT, NewFileHeader.NamedPropData.UtilizedBlockSize);
          if(!lpv)
          {
              DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n")));
              goto out;
          }
  
          if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
                                             lpMPSWabFileInfo->lpMPSWabFileHeader->NamedPropData.ulOffset,
                                             NULL,
                                             FILE_BEGIN))
          {
              DebugPrintError(( TEXT("SetFilePointer Failed\n")));
              goto out;
          }
  
          if (0xFFFFFFFF == SetFilePointer ( hTempFile,
                                             NewFileHeader.NamedPropData.ulOffset,
                                             NULL,
                                             FILE_BEGIN)  )
          {
              DebugPrintError(( TEXT("SetFilePointer Failed\n")));
              goto out;
          }
  
          if(!ReadFile(hMPSWabFile,
                       (LPVOID) lpv,
                       (DWORD) NewFileHeader.NamedPropData.UtilizedBlockSize,
                       &dwNumofBytes,
                        NULL) )
          {
              DebugPrintError(( TEXT("read file failed.\n")));
              goto out;
          }
  
          if(!WriteFile(   hTempFile,
                          (LPCVOID) lpv,
                          (DWORD) NewFileHeader.NamedPropData.UtilizedBlockSize,
                          &dwNumofBytes,
                          NULL))
          {
              DebugPrintError(( TEXT("write file failed.\n")));
              goto out;
          }
  
          LocalFreeAndNull(&lpv);
  
      } // Copy over named prop data
  
  
      //
      // Then copy over the string indexes
      //
      for(i=indexDisplayName; i<indexMax;i++)
      {
          LocalFreeAndNull(&lpIndexStr);
  
          lpIndexStr = LocalAlloc(LMEM_ZEROINIT, NewFileHeader.IndexData[i].UtilizedBlockSize);
          if(!lpIndexStr)
          {
              DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n")));
              goto out;
          }
  
          if (0xFFFFFFFF == SetFilePointer ( hMPSWabFile,
                                             lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[i].ulOffset,
                                             NULL,
                                             FILE_BEGIN))
          {
              DebugPrintError(( TEXT("SetFilePointer Failed\n")));
              goto out;
          }
  
          if (0xFFFFFFFF == SetFilePointer ( hTempFile,
                                             NewFileHeader.IndexData[i].ulOffset,
                                             NULL,
                                             FILE_BEGIN)  )
          {
              DebugPrintError(( TEXT("SetFilePointer Failed\n")));
              goto out;
          }
  
          if(!ReadFile(hMPSWabFile,
                       (LPVOID) lpIndexStr,
                       (DWORD) NewFileHeader.IndexData[i].UtilizedBlockSize,
                       &dwNumofBytes,
                        NULL) )
          {
              DebugPrintError(( TEXT("read file failed.\n")));
              goto out;
          }
  
          if(!WriteFile(   hTempFile,
                          (LPCVOID) lpIndexStr,
                          (DWORD) NewFileHeader.IndexData[i].UtilizedBlockSize,
                          &dwNumofBytes,
                          NULL))
          {
              DebugPrintError(( TEXT("write file failed.\n")));
              goto out;
          }
  
          LocalFreeAndNull(&lpIndexStr);
      }
  
      //
      // now load the entryid index from the old file
      //
      if (!LoadIndex( IN  lpMPSWabFileInfo,
                      IN  indexEntryID,
                      IN  hMPSWabFile) )
      {
          DebugPrintError(( TEXT("Error Loading EntryID Index!\n")));
          goto out;
      }
  
      ulNewRecordOffset = NewFileHeader.IndexData[indexMax - 1].ulOffset + NewFileHeader.IndexData[indexMax - 1].AllocatedBlockSize;
      ulNewEIDIndexElementOffset = NewFileHeader.IndexData[indexEntryID].ulOffset;
  
  
      //
      // Walk through the old file entry ID index reading the
      // valid records one by one and writing them to the new file. Also write the
      // new record offset and the new EID entry into the new file (so that if we
      // crash we have as up to date data in the new file as possible
      //
      for(i=0;i<NewFileHeader.IndexData[indexEntryID].ulcNumEntries;i++)
      {
          NewMPSWabIndexEID.dwEntryID = lpMPSWabFileInfo->lpMPSWabIndexEID[i].dwEntryID;
          NewMPSWabIndexEID.ulOffset = ulNewRecordOffset;
  
          if(!ReadDataFromWABFile(hMPSWabFile,
                                  lpMPSWabFileInfo->lpMPSWabIndexEID[i].ulOffset,
                                  (LPVOID) &RecordHeader,
                                  (DWORD) sizeof(MPSWab_RECORD_HEADER)))
             goto out;
  
  
          // if for some reason this was an invalid record .. skip it and go to next
          if(!bIsValidRecord( RecordHeader,
                              lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID,
                              lpMPSWabFileInfo->lpMPSWabIndexEID[i].ulOffset,
                              ulFileSize))
              continue;
  
  
          LocalFreeAndNull(&lpPropTagArray);
  
          lpPropTagArray = LocalAlloc(LMEM_ZEROINIT, RecordHeader.ulPropTagArraySize);
          if(!lpPropTagArray)
          {
              DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n")));
              goto out;
          }
  
  
          if(!ReadFile(   hMPSWabFile,
                          (LPVOID) lpPropTagArray,
                          (DWORD) RecordHeader.ulPropTagArraySize,
                          &dwNumofBytes,
                          NULL))
          {
              DebugPrintError(( TEXT("read file failed.\n")));
              goto out;
          }
  
          LocalFreeAndNull(&lpRecordData);
  
          lpRecordData = LocalAlloc(LMEM_ZEROINIT, RecordHeader.ulRecordDataSize);
          if(!lpRecordData)
          {
              DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n")));
              goto out;
          }
  
  
          if(!ReadFile(   hMPSWabFile,
                          (LPVOID) lpRecordData,
                          (DWORD) RecordHeader.ulRecordDataSize,
                          &dwNumofBytes,
                          NULL))
          {
              DebugPrintError(( TEXT("read file failed.\n")));
              goto out;
          }
  
  
  
  
          if(!WriteDataToWABFile(hTempFile,
                                  ulNewRecordOffset,
                                  (LPVOID) &RecordHeader,
                                  (DWORD) sizeof(MPSWab_RECORD_HEADER)))
             goto out;
  
          // assumes that file pointer will be at the correct spot
          if(!WriteFile(   hTempFile,
                          (LPCVOID) lpPropTagArray,
                          (DWORD) RecordHeader.ulPropTagArraySize,
                          &dwNumofBytes,
                          NULL))
          {
              DebugPrintError(( TEXT("write file failed.\n")));
              goto out;
          }
  
          // assumes that file pointer will be at the correct spot
          if(!WriteFile(  hTempFile,
                          (LPCVOID) lpRecordData,
                          (DWORD) RecordHeader.ulRecordDataSize,
                          &dwNumofBytes,
                          NULL))
          {
              DebugPrintError(( TEXT("write file failed.\n")));
              goto out;
          }
  
          ulNewRecordOffset += sizeof(MPSWab_RECORD_HEADER) + RecordHeader.ulPropTagArraySize + RecordHeader.ulRecordDataSize;
  
  
          LocalFreeAndNull(&lpPropTagArray);
          LocalFreeAndNull(&lpRecordData);
  
  
          //
          // Write the new entryID index element
          //
          if(!WriteDataToWABFile( hTempFile,
                                  ulNewEIDIndexElementOffset,
                                  (LPVOID) &NewMPSWabIndexEID,
                                  sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID)))
              goto out;
  
  
          ulNewEIDIndexElementOffset += sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID);
  
          // loop for next record
  
      }
  
      //
      // At this point in the process we have successfuly copied over all the
      // records from the old file to the new file
      //
  
      // If this is a backup operation - we delete the old backup and copy the new temp file
      // as the new backup ...
  
      //
      // If this is not a backup operation, we want to basically delete the old file
      // and rename the new temp file as the property store ..
      //
      // However, if we release our access to the property store, there is no gaurentee that
      // some other process will not grab up exclusive access to the store and we will fail in
      // our attempt to gain control and modify ...
      //
      // Hence the options are
      // (a) give up control and then hope we can regain control before someone else does something
      //      to the file
      // (b) copy in the new file contents and overwrite the existing file contents - this is going
      //      to be slower than rewriting the file but it will give us exclusive control on the
      //      modifications and makes the process much more robust ...
      //
  
      if (!bBackup) // Not a backup operation
      {
          //
          // Save the header in the new file
          //
          if(!WriteDataToWABFile( hTempFile,
                                  0,
                                  (LPVOID) &NewFileHeader,
                                  sizeof(MPSWab_FILE_HEADER)))
              goto out;
  
          //
          // Copy the New file into this WAB file thus replacing the old contents
          // and hope this never fails
          //
          if(!CopySrcFileToDestFile(hTempFile, 0, hMPSWabFile, 0))
          {
              DebugPrintError(( TEXT("Unable to copy files\n")));
              goto out;
          }
  
  
          //
          // Reload this so we have the new fileheader info in our structures
          //
          if(!ReloadMPSWabFileInfo(
                          lpMPSWabFileInfo,
                           hMPSWabFile))
          {
              DebugPrintError(( TEXT("Reading file info failed.\n")));
              goto out;
          }
  
  
  
          //
          // Thats it ..  we can close the files and party on ..
          //
      }
      else
      {
          //this is a backup operation ...
  
          // Close the temp file
          if (hTempFile)
          {
              IF_WIN32(CloseHandle(hTempFile);) IF_WIN16(CloseFile(hTempFile);)
              hTempFile = NULL;
          }
  
          if(!CopyFile( szFileName,
                        lpszFileName,
                        FALSE))
          {
              DebugPrintError(( TEXT("CopyFile %s to %s failed: %d\n"),szFileName,lpszFileName, GetLastError()));
              goto out;
          }
      }
  
      bRet = TRUE;
  
  
  
  out:
  
      if (hTempFile)
          IF_WIN32(CloseHandle(hTempFile);) IF_WIN16(CloseFile(hTempFile);)
  
      if( szFileName != NULL)
          DeleteFile(szFileName);
  
      LocalFreeAndNull(&lpv);
  
      LocalFreeAndNull(&lpPropTagArray);
  
      LocalFreeAndNull(&lpRecordData);
  
  
      DebugTrace(TEXT("----Thread:%x\tCompressFile: Exit\n"),GetCurrentThreadId());
  
      return bRet;
  }
  
  
  
  //$$//////////////////////////////////////////////////////////////////////////////////
  //
  //  ReadRecordWithoutLocking
  //
  //  IN  lpMPSWabFileInfo
  //  IN  dwEntryID - EntryID of record to read
  //  OUT ulcPropCount - number of props returned
  //  OUT lpPropArray - Array of Property values
  //
  //  Returns
  //      Success:    S_OK
  //      Failure:    E_FAIL
  //
  ////////////////////////////////////////////////////////////////////////////////////
  HRESULT ReadRecordWithoutLocking(
                      IN  HANDLE hMPSWabFile,
                      IN  struct _tagMPSWabFileInfo * lpMPSWabFileInfo,
                      IN  DWORD   dwEntryID,
                      OUT LPULONG lpulcPropCount,
                      OUT LPPROPERTY_ARRAY * lppPropArray)
  {
      HRESULT hr = E_FAIL;
      ULONG ulRecordOffset = 0;
      BOOL bErrorDetected = FALSE;
      ULONG nIndexPos = 0;
      ULONG ulObjType = 0;
  
      *lpulcPropCount = 0;
      *lppPropArray = NULL;
  
      //
      // First check if this is a valid entryID
      //
      if (!BinSearchEID(  IN  lpMPSWabFileInfo->lpMPSWabIndexEID,
                          IN  dwEntryID,
                          IN  lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries,
                          OUT &nIndexPos))
      {
          DebugPrintError(( TEXT("Specified EntryID doesnt exist!\n")));
          hr = MAPI_E_INVALID_ENTRYID;
          goto out;
      }
  
      //if entryid exists, we can start reading the record
      ulRecordOffset = lpMPSWabFileInfo->lpMPSWabIndexEID[nIndexPos].ulOffset;
  
      hr = HrGetPropArrayFromFileRecord(hMPSWabFile,
                                        ulRecordOffset,
                                        &bErrorDetected,
                                        &ulObjType,
                                        NULL,
                                        lpulcPropCount,
                                        lppPropArray);
  
      if(!HR_FAILED(hr))
      {
          // reset the backward compatibility thing-um-a-jig we did between
          // MAPI_ABCONT and MAPI_MAILUSER
          if(ulObjType == RECORD_CONTAINER)
              SetContainerObjectType(*lpulcPropCount, *lppPropArray, FALSE);
      }
  
  out:
  
      //a little cleanup on failure
      if (FAILED(hr))
      {
          if(bErrorDetected)
          {
              TagWABFileError(lpMPSWabFileInfo->lpMPSWabFileHeader,
                              hMPSWabFile);
          }
  
          if ((*lppPropArray) && (*lpulcPropCount > 0))
          {
              LocalFreePropArray(NULL, *lpulcPropCount, lppPropArray);
              *lppPropArray = NULL;
          }
      }
  
      return(hr);
  }
  
  //$$//////////////////////////////////////////////////////////////////////////////
  //
  // GetWABBackupFileName - derives the backup file name from the WAB file by changing
  //              the extension from WAB to BWB
  //
  // lpszWabFileName - WAB file name
  // lpszBackupFileName - Backup File name - points to a preallocated buffer big enough to
  //                  hold the backup file name
  //
  // This generates a backup name in which the last character is turned into a ~
  ////////////////////////////////////////////////////////////////////////////////////
  void GetWABBackupFileName(LPTSTR lpszWab, LPTSTR lpszBackup, ULONG cchBackup)
  {
      ULONG nLen;
  
      if(!lpszWab || !lpszBackup)
          goto out;
  
      nLen = lstrlen(lpszWab);
  
  //    if((nLen < 4) || (lpszWab[nLen-4] != '.'))
  //        goto out;
  
      StrCpyN(lpszBackup,lpszWab, cchBackup);
  
      lpszBackup[nLen-1]='\0';
  
      StrCpyN(lpszBackup,TEXT("~"), cchBackup);
  
  out:
  
      return;
  }
  
  
  //$$//////////////////////////////////////////////////////////////////////////////
  //
  // HrDoQuickWABIntegrityCheck - does a quick integrity check of the WAB indexes
  //              Verifies that:
  //
  //  - Indexes contain the correct number of entries which is equal to or less than
  //      the max number of entries
  //  - Indexes dont contain duplicate entry-ids
  //  - Indexes point to valid and existing data ...
  //
  //  If there are problems, this function attempts to fix them - if it cant fix them
  //  we fail and caller should call HrDoDetailedWABIntegrityCheck which will rebuild
  //  the indexes from the actual WAB data.
  //
  ////////////////////////////////////////////////////////////////////////////////////
  HRESULT HrDoQuickWABIntegrityCheck(LPMPSWab_FILE_INFO lpMPSWabFileInfo, HANDLE hMPSWabFile)
  {
      HRESULT hr = E_FAIL;
      BOOL bError = FALSE;
      ULONG ulcNumWABEntries = 0,ulcNumIndexEntries = 0;
      LPMPSWab_FILE_HEADER lpMPSWabFileHeader = NULL;
      MPSWab_RECORD_HEADER MPSWabRecordHeader = {0};
      ULONG i=0,j=0;
      DWORD dwNumofBytes = 0;
      ULONG ulFileSize = GetFileSize(hMPSWabFile,NULL);
  
      lpMPSWabFileHeader = lpMPSWabFileInfo->lpMPSWabFileHeader;
      ulcNumWABEntries = lpMPSWabFileHeader->ulcNumEntries;
  
      //
      // First check the EntryID index
      //
      if (!LoadIndex( IN  lpMPSWabFileInfo,
                      IN  indexEntryID,
                      IN  hMPSWabFile) )
      {
          DebugPrintError(( TEXT("Error Loading Index!\n")));
          goto out;
      }
  
      ulcNumIndexEntries = lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries;
  
      if(ulcNumIndexEntries != ulcNumWABEntries)
      {
          hr = MAPI_E_INVALID_ENTRYID;
          DebugPrintError(( TEXT("EntryID index has incorrect number of elements\n")));
          goto out;
      }
  
      if(ulcNumIndexEntries > 0)
      {
          for(i=0;i<ulcNumIndexEntries-1;i++)
          {
              // Since this is a sorted array, the indexes will be in sorted order
              // So we just compare one with the next
              if(lpMPSWabFileInfo->lpMPSWabIndexEID[i].dwEntryID == lpMPSWabFileInfo->lpMPSWabIndexEID[i+1].dwEntryID)
              {
                  hr = MAPI_E_INVALID_ENTRYID;
                  DebugPrintError(( TEXT("EntryID index has duplicate elements\n")));
                  goto out;
              }
          }
      }
  
  /* 
  // This is painfully slowing things down
  // Comment out for now
  //
      // Now we walk through the index and verify that each entry is a valid entry ....
      for(i=0;i<ulcNumIndexEntries;i++)
      {
          ULONG ulOffset = lpMPSWabFileInfo->lpMPSWabIndexEID[i].ulOffset;
  
          MPSWab_RECORD_HEADER MPSWabRecordHeader = {0};
  
          if(!ReadDataFromWABFile(hMPSWabFile,
                                  ulOffset,
                                  (LPVOID) &MPSWabRecordHeader,
                                  (DWORD) sizeof(MPSWab_RECORD_HEADER)))
             goto out;
  
  
          if(!bIsValidRecord( MPSWabRecordHeader,
                              lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID,
                              ulOffset,
                              ulFileSize))
          {
              DebugPrintError(( TEXT("Index points to an invalid record\n")));
              hr = MAPI_E_INVALID_ENTRYID;
              goto out;
          }
  
      }
  */
  
      // if we're here, then the entry id index checks out ok ...
  
      //
      // Check out the other indexes also ... we will start backwards since we want to fix potential
      // problems in the First/Last name indexes before we do the more stringent display name case
      //
      for(j=indexMax-1;j>=indexDisplayName;j--)
      {
          if (!LoadIndex( IN  lpMPSWabFileInfo,
                          IN  j,
                          IN  hMPSWabFile) )
          {
              DebugPrintError(( TEXT("Error Loading Index!\n")));
              goto out;
          }
  
  
          ulcNumIndexEntries = lpMPSWabFileHeader->IndexData[j].ulcNumEntries;
  
          if(j == indexDisplayName)
          {
              if(ulcNumIndexEntries != ulcNumWABEntries)
              {
                  DebugPrintError(( TEXT("Display Name index has incorrect number of elements\n")));
                  goto out;
              }
          }
          else if(ulcNumIndexEntries > ulcNumWABEntries)
          {
              bError = TRUE;
              goto endloop;
          }
  
          if(ulcNumIndexEntries > 0)
          {
              for(i=0;i<ulcNumIndexEntries-1;i++)
              {
                  // Since this is a sorted array, the indexes will be in sorted order
                  // So we just compare one with the next
                  if(lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID == lpMPSWabFileInfo->lpMPSWabIndexStr[i+1].dwEntryID)
                  {
                      DebugPrintError(( TEXT("String index has duplicate elements\n")));
                      if(j == indexDisplayName)
                          goto out;
                      else
                      {
                          bError = TRUE;
                          goto endloop;
                      }
                  }
              }
          }
  
          // Now we walk through the index and verify that each entry is a valid entry ....
          for(i=0;i<ulcNumIndexEntries;i++)
          {
              DWORD dwEntryID = lpMPSWabFileInfo->lpMPSWabIndexStr[i].dwEntryID;
              ULONG nIndexPos;
  
              // All we need to do is to check that the entry id exists in the EntryID index since we
              // have already verified the entryid index
              if (!BinSearchEID(  IN  lpMPSWabFileInfo->lpMPSWabIndexEID,
                                  IN  dwEntryID,
                                  IN  lpMPSWabFileInfo->lpMPSWabFileHeader->IndexData[indexEntryID].ulcNumEntries, //IndexEntries,
                                  OUT &nIndexPos))
              {
                  DebugPrintError(( TEXT("Specified EntryID: %d doesnt exist!\n"),dwEntryID));
                  hr = MAPI_E_NOT_FOUND;
                  if(j == indexDisplayName)
                      goto out;
                  else
                  {
                      bError = TRUE;
                      goto endloop;
                  }
              }
  
          }
  
  endloop:
          if(bError &&
             ( (j==indexFirstName) || (j==indexLastName) ))
          {
              // if the problem is in the first/last indexes, we can reset these indexes safely
              //Assert(FALSE);
              ulcNumIndexEntries = 0;
              lpMPSWabFileHeader->IndexData[j].ulcNumEntries = 0;
              lpMPSWabFileHeader->IndexData[j].ulcNumEntries = 0;
  
              if(!WriteDataToWABFile( hMPSWabFile,
                                      0,
                                      (LPVOID) lpMPSWabFileInfo->lpMPSWabFileHeader,
                                      sizeof(MPSWab_FILE_HEADER)))
                  goto out;
  
          }
  
      }
  
      hr = hrSuccess;
  
  out:
  
      return hr;
  }
  
  //$$///////////////////////////////////////////////////////////////////////////////////////////
  //
  // CopySrcFileToDestFile - replaces contents of Dest file with contents of source file
  //                  starting at the ulsrcStartOffset and ulDestStartOffset respectively
  // hSrc, hDest - handles to already open files
  // ulSrcStartOffset - start copying from this offset
  // ulDestStartOffset - start copying to this offset
  //
  //////////////////////////////////////////////////////////////////////////////////////////////////
  BOOL CopySrcFileToDestFile(HANDLE hSrc, ULONG ulSrcStartOffset,
                             HANDLE hDest,ULONG ulDestStartOffset)
  {
      BOOL bRet = FALSE;
      ULONG ulBytesLeftToCopy = 0;
      DWORD dwNumofBytes = 0;
      ULONG ulChunkSize = 8192; //size of block of bytes to copy from one file into the other
      LPVOID lpv = NULL;
  
      // copy contents of hSrc into hDest
      //
      // Set the hDest File to 0 length
      //
      if (0xFFFFFFFF == SetFilePointer (  hDest,
                                          ulDestStartOffset,
                                          NULL,
                                          FILE_BEGIN))
      {
          DebugPrintError(( TEXT("SetFilePointer Failed\n")));
          goto out;
      }
  
      //
      // Set end of file to the current file pointer position to discard everything
      // in the file after this point
      //
      if (!SetEndOfFile(hDest))
      {
          DebugPrintError(( TEXT("SetEndofFile Failed\n")));
          goto out;
      }
  
  
      //
      // Go to beginning of the source File
      //
      if (0xFFFFFFFF == SetFilePointer(   hSrc,
                                          ulSrcStartOffset,
                                          NULL,
                                          FILE_BEGIN))
      {
          DebugPrintError(( TEXT("SetFilePointer Failed\n")));
          goto out;
      }
  
  
      //
      // figure out how many bytes to read ...
      //
      ulBytesLeftToCopy = GetFileSize(hSrc, NULL);
  
      if (0xFFFFFFFF == ulBytesLeftToCopy)
      {
          DebugPrintError(( TEXT("GetFileSize Failed: %d\n"),GetLastError()));
          goto out;
      }
  
      if(ulSrcStartOffset > ulBytesLeftToCopy)
      {
          DebugPrintError(( TEXT("Error in File Sizes\n")));
          goto out;
      }
  
      ulBytesLeftToCopy -= ulSrcStartOffset;
  
      lpv = LocalAlloc(LMEM_ZEROINIT, ulChunkSize);
  
      if(!lpv)
      {
          DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n")));
          goto out;
      }
  
  
      //
      // Loop copying bytes from one file to the other
      while(ulBytesLeftToCopy > 0)
      {
          if (ulBytesLeftToCopy < ulChunkSize)
              ulChunkSize = ulBytesLeftToCopy;
  
          if(!ReadFile(hSrc,(LPVOID) lpv,(DWORD) ulChunkSize,&dwNumofBytes,NULL))
          {
              DebugPrintError(( TEXT("Read file failed.\n")));
              goto out;
          }
  
          if (dwNumofBytes != ulChunkSize)
          {
              DebugPrintError(( TEXT("Read file failed.\n")));
              goto out;
          }
  
          if(!WriteFile(hDest,(LPVOID) lpv,(DWORD) ulChunkSize,&dwNumofBytes,NULL))
          {
              DebugPrintError(( TEXT("Write file failed.\n")));
              goto out;
          }
  
          if (dwNumofBytes != ulChunkSize)
          {
              DebugPrintError(( TEXT("Write file failed.\n")));
              goto out;
          }
  
          ulBytesLeftToCopy -= ulChunkSize;
  
      }
  
  
  
      bRet = TRUE;
  
  out:
  
      LocalFreeAndNull(&lpv);
  
      return bRet;
  }
  
  //$$//////////////////////////////////////////////////////////////////////////////
  //
  // HrResetWABFileContents - This is called as a last ditch error recovery attempt at
  //  deleting the file and reseting its contents in the event that a recovery from
  //  backup also failed...
  //
  //
   ////////////////////////////////////////////////////////////////////////////////////
  HRESULT HrResetWABFileContents(LPMPSWab_FILE_INFO lpMPSWabFileInfo, HANDLE hMPSWabFile)
  {
      HRESULT hr = E_FAIL;
      TCHAR szFileName[MAX_PATH];
      MPSWab_FILE_HEADER NewFileHeader;
      HANDLE hTempFile = NULL;
  
      DebugTrace(TEXT("#####HrResetWABFileContents Entry\n"));
  
      // Get a temporary file name ...
      GetWABTempFileName(szFileName);
  
      if (!CreateMPSWabFile(  IN  &NewFileHeader,
                              IN  szFileName,
                              IN  MAX_INITIAL_INDEX_ENTRIES,
                              IN  NAMEDPROP_STORE_SIZE))
      {
          DebugTrace(TEXT("Could Not Create File %s!\n"),szFileName);
          goto out;
      }
  
  
      hTempFile = CreateFile(     szFileName,
                                  GENERIC_READ | GENERIC_WRITE,
                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                                  (LPSECURITY_ATTRIBUTES) NULL,
                                  OPEN_EXISTING,
                                  FILE_FLAG_RANDOM_ACCESS,
                                  (HANDLE) NULL);
  
      if (hTempFile == INVALID_HANDLE_VALUE)
      {
          DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
          goto out;
      }
  
  
      if(!CopySrcFileToDestFile(hTempFile, 0, hMPSWabFile, 0))
      {
          DebugTrace(TEXT("Unable to copy files\n"));
          goto out;
      }
  
      //
      // Reload this so we have the new fileheader info in our structures
      //
      if(!ReloadMPSWabFileInfo(
                      lpMPSWabFileInfo,
                       hMPSWabFile))
      {
          DebugTrace(TEXT("Reading file info failed.\n"));
          goto out;
      }
  
      hr = hrSuccess;
  
  out:
  
      if(HR_FAILED(hr))
      {
          // This is totally unexpected and basically we couldnt even fix the file so tell
          // the user to restart the application - meanwhile we will delete the file
          ShowMessageBox(NULL, idsWABUnexpectedError, MB_ICONHAND | MB_OK);
          // Hope that no one comes and locks this file between the next 2 calls
          IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
          hMPSWabFile = NULL;
          DeleteFile(lpMPSWabFileInfo->lpszMPSWabFileName);
      }
  
      if(hTempFile)
          IF_WIN32(CloseHandle(hTempFile);) IF_WIN16(CloseFile(hTempFile);)
  
      DebugTrace(TEXT("#####HrResetWABFileContents Exit\n"));
  
      return hr;
  
  }
  
  //$$//////////////////////////////////////////////////////////////////////////////
  //
  // HrRestoreFromBackup - attempts to replace WAB file contents from contents of
  // Backup file
  //
  //
  ////////////////////////////////////////////////////////////////////////////////////
  HRESULT HrRestoreFromBackup(LPMPSWab_FILE_INFO lpMPSWabFileInfo, HANDLE hMPSWabFile)
  {
      HRESULT hr = E_FAIL;
      HANDLE hBackupFile = NULL;
      TCHAR szBackupFileName[MAX_PATH];
      HCURSOR hOldCursor = SetCursor(LoadCursor(NULL,IDC_WAIT));
  
      // Steps to this process are:
      // - Open Backup File
      // - Reset contents of WABFile
      // - Copy Backup into WAB
      // - Close BackupFile
      // - Reload WAB Indexes
  
      DebugTrace(TEXT("+++++HrRestoreFromBackup Entry\n"));
  
      // Get the backup file name
      szBackupFileName[0]='\0';
      GetWABBackupFileName(lpMPSWabFileInfo->lpszMPSWabFileName, szBackupFileName, ARRAYSIZE(szBackupFileName));
  
      hBackupFile = CreateFile(   szBackupFileName,
                                  GENERIC_READ,
                                  FILE_SHARE_READ,
                                  (LPSECURITY_ATTRIBUTES) NULL,
                                  OPEN_EXISTING,
                                  FILE_FLAG_SEQUENTIAL_SCAN, //FILE_FLAG_RANDOM_ACCESS,
                                  (HANDLE) NULL);
  
      if (hBackupFile == INVALID_HANDLE_VALUE)
      {
          DebugTrace(TEXT("Could not open backup file.\nExiting ...\n"));
          hr = MAPI_E_DISK_ERROR;
          goto out;
      }
  
      if(!CopySrcFileToDestFile(hBackupFile, 0, hMPSWabFile, 0))
      {
          DebugTrace(TEXT("Unable to copy files\n"));
          goto out;
      }
  
  
      //
      // Reload this so we have the new fileheader info in our structures
      //
      if(!ReloadMPSWabFileInfo(
                      lpMPSWabFileInfo,
                       hMPSWabFile))
      {
          DebugTrace(TEXT("Reading file info failed.\n"));
          goto out;
      }
  
  
      hr = hrSuccess;
  
  out:
  
      // close the backup file
      if(hBackupFile)
          IF_WIN32(CloseHandle(hBackupFile);) IF_WIN16(CloseFile(hBackupFile);)
  
      SetCursor(hOldCursor);
  
      DebugTrace(TEXT("+++++HrRestoreFromBackup Exit\n"));
  
      return hr;
  }
  
  
  
  //$$//////////////////////////////////////////////////////////////////////////////
  //
  // HrDoDetailedWABIntegrityCheck - does a thorough integrity check
  //  This is triggered only when an index error was detected or if a write
  //  transaction failed.
  //
  //  - We step through all the records validating them and their size data
  //  - From each valid record we create the sorted entryid index
  //  - From the sorted entryid index we read the records and create the
  //  - display name index and reset the first last name indexes for now
  //
  // This function should not fail but should try to recover from errors
  // If this function fails we need to break out the backup of the wab file
  //
  ////////////////////////////////////////////////////////////////////////////////////
  HRESULT HrDoDetailedWABIntegrityCheck(LPMPSWab_FILE_INFO lpMPSWabFileInfo, HANDLE hMPSWabFile)
  {
      HRESULT hr = E_FAIL;
      BOOL bEID = FALSE;
      ULONG ulcNumWABEntries = 0,ulcNumIndexEntries = 0;
      MPSWab_FILE_HEADER MPSWabFileHeader = {0};
      MPSWab_FILE_HEADER NewMPSWabFileHeader = {0};
      MPSWab_RECORD_HEADER MPSWabRecordHeader = {0};
      ULONG i=0,j=0;
      DWORD dwNumofBytes = 0;
      DWORD dwEntryID = 0;
      ULONG ulcPropCount = 0;
      LPSPropValue lpPropArray = NULL;
  
  
      ULONG ulRecordOffset = 0;
      ULONG ulFileSize = 0;
      ULONG ulcWABEntryCount = 0;
      ULONG nIndexPos = 0;
  
      MPSWab_INDEX_ENTRY_DATA_ENTRYID MPSWabIndexEID = {0};
      LPMPSWab_INDEX_ENTRY_DATA_ENTRYID lpIndexEID = NULL;
  
      MPSWab_INDEX_ENTRY_DATA_STRING  MPSWabIndexString = {0};
      LPMPSWab_INDEX_ENTRY_DATA_STRING  lpIndexString = NULL;
  
      LPVOID lpTmp = NULL;
  
      HCURSOR hOldCur = SetCursor(LoadCursor(NULL,IDC_WAIT));
  
      DebugTrace(TEXT("---DoDetailedWABIntegrityCheck Entry\n"));
      //
      // We will go with the assumption that this WAB is currently a proper wab file
      // otherwise OpenPropertyStore would have failed while opening it.
      //
      // Consequently, we should be able to read the header of the file
      // update the file header
      if(!ReadDataFromWABFile(hMPSWabFile,
                              0,
                              (LPVOID) &MPSWabFileHeader,
                              (DWORD) sizeof(MPSWab_FILE_HEADER)))
         goto out;
  
  
      // We are going to reset the file header for now so that if this process fails,
      // this file will think that there is nothing in the file rather than crashing
      NewMPSWabFileHeader = MPSWabFileHeader;
      NewMPSWabFileHeader.ulModificationCount = 0;
      NewMPSWabFileHeader.ulcNumEntries = 0;
  
      for(i=0;i<indexMax;i++)
      {
          if(i != indexDisplayName)// Temp Temp TBD TBD
          {
              NewMPSWabFileHeader.IndexData[i].UtilizedBlockSize = 0;
              NewMPSWabFileHeader.IndexData[i].ulcNumEntries = 0;
          }
      }
  
      //
      // Write this NewMPSWabFileHeader to the file
      //
      if(!WriteDataToWABFile( hMPSWabFile,
                              0,
                              (LPVOID) &NewMPSWabFileHeader,
                              sizeof(MPSWab_FILE_HEADER)))
          goto out;
  
  
      ulFileSize = GetFileSize(hMPSWabFile, NULL);
  
      if(ulFileSize == 0xFFFFFFFF)
      {
          DebugTrace(TEXT("Error retrieving file size: %d"),GetLastError());
          hr = MAPI_E_DISK_ERROR;
          goto out;
      }
      //
      // Allocate some working space
      //
      lpIndexEID = LocalAlloc(LMEM_ZEROINIT,
                              MPSWabFileHeader.IndexData[indexEntryID].AllocatedBlockSize);
      if(!lpIndexEID)
      {
          DebugTrace(TEXT("Error allocating memory\n"));
          hr = MAPI_E_NOT_ENOUGH_MEMORY;
          goto out;
      }
      lpTmp = LocalAlloc(LMEM_ZEROINIT,
                         MPSWabFileHeader.IndexData[indexEntryID].AllocatedBlockSize);
      if(!lpTmp)
      {
          DebugTrace(TEXT("Error allocating memory\n"));
          hr = MAPI_E_NOT_ENOUGH_MEMORY;
          goto out;
      }
  
      //
      // Now start reading the records 1 by one
      //
  
      ulRecordOffset = MPSWabFileHeader.IndexData[indexMax-1].ulOffset +
                       MPSWabFileHeader.IndexData[indexMax-1].AllocatedBlockSize;
  
      ulcWABEntryCount = 0;
  
      while(ulRecordOffset < ulFileSize)
      {
          if(!ReadDataFromWABFile(hMPSWabFile,
                                  ulRecordOffset,
                                  (LPVOID) &MPSWabRecordHeader,
                                  (DWORD) sizeof(MPSWab_RECORD_HEADER)))
             goto out;
  
          //
          // if this is an invalid record ignore it
          //
          if( (MPSWabRecordHeader.bValidRecord != FALSE) &&
              (!bIsValidRecord(   MPSWabRecordHeader,
                                  lpMPSWabFileInfo->lpMPSWabFileHeader->dwNextEntryID,
                                  ulRecordOffset,
                                  ulFileSize)))
          {
              DebugTrace(TEXT("Something seriously screwed up in the file\n"));
              hr = MAPI_E_CORRUPT_DATA;
              goto out;
          }
          else if(MPSWabRecordHeader.bValidRecord == FALSE)
          {
              // if this is a deleted obsolete record, ignore it
              ulRecordOffset +=   sizeof(MPSWab_RECORD_HEADER) +
                                  MPSWabRecordHeader.ulPropTagArraySize +
                                  MPSWabRecordHeader.ulRecordDataSize;
              continue;
          }
  
          //
          // We have a live one ... create an entryid index structure for this
          //
          MPSWabIndexEID.dwEntryID = MPSWabRecordHeader.dwEntryID;
          MPSWabIndexEID.ulOffset = ulRecordOffset;
  
          //
          // We are creating a shadow index in memory in the lpIndexEID block
          // We then write this index into the file ..
          //
  
          //
          // Find the position in the index where this entry will go
          //
          bEID = BinSearchEID(lpIndexEID,
                       MPSWabIndexEID.dwEntryID,
                       ulcWABEntryCount,
                       &nIndexPos);
  
          if(bEID)
          {
              //
              // This means that the entryid exists in the index which is messed up
              // since we dont support duplicate entryids ...
              // In this case, just ignore this entry and continue
              ulRecordOffset +=   sizeof(MPSWab_RECORD_HEADER) +
                                  MPSWabRecordHeader.ulPropTagArraySize +
                                  MPSWabRecordHeader.ulRecordDataSize;
              continue;
          }
  
          if(nIndexPos != ulcWABEntryCount)
          {
              CopyMemory( lpTmp,
                          &(lpIndexEID[nIndexPos]),
                          sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID) * (ulcWABEntryCount - nIndexPos));
          }
  
          CopyMemory( &(lpIndexEID[nIndexPos]),
                      &(MPSWabIndexEID),
                      sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID));
  
          if(nIndexPos != ulcWABEntryCount)
          {
              CopyMemory( &(lpIndexEID[nIndexPos+1]),
                          lpTmp,
                          sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID) * (ulcWABEntryCount - nIndexPos));
          }
  
          ulcWABEntryCount++;
  
          // Write this entry id index from memory to file
          //
          if(!WriteDataToWABFile( hMPSWabFile,
                                  MPSWabFileHeader.IndexData[indexEntryID].ulOffset,
                                  (LPVOID) lpIndexEID,
                                  sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID)*ulcWABEntryCount))
              goto out;
  
  
          NewMPSWabFileHeader.ulcNumEntries = ulcWABEntryCount;
          NewMPSWabFileHeader.IndexData[indexEntryID].UtilizedBlockSize = sizeof(MPSWab_INDEX_ENTRY_DATA_ENTRYID)*ulcWABEntryCount;
          NewMPSWabFileHeader.IndexData[indexEntryID].ulcNumEntries = ulcWABEntryCount;
  
          //
          // Write this NewMPSWabFileHeader to the file
          //
          if(!WriteDataToWABFile( hMPSWabFile,
                                  0,
                                  (LPVOID) &NewMPSWabFileHeader,
                                  sizeof(MPSWab_FILE_HEADER)))
              goto out;
  
  
          // go onto next record
              ulRecordOffset +=   sizeof(MPSWab_RECORD_HEADER) +
                                  MPSWabRecordHeader.ulPropTagArraySize +
                                  MPSWabRecordHeader.ulRecordDataSize;
  
      } // while loop
  
      // Now we have the correct entryid index,
      // we want to build the display name index from scratch ...
      LocalFreeAndNull(&lpTmp);
  
      lpTmp = LocalAlloc( LMEM_ZEROINIT,
                          MPSWabFileHeader.IndexData[indexDisplayName].AllocatedBlockSize);
      if(!lpTmp)
      {
          DebugTrace(TEXT("LocalAlloc failed\n"));
          hr = MAPI_E_NOT_ENOUGH_MEMORY;
          goto out;
      }
  
      lpIndexString = LocalAlloc( LMEM_ZEROINIT,
                          MPSWabFileHeader.IndexData[indexDisplayName].AllocatedBlockSize);
      if(!lpIndexString)
      {
          DebugTrace(TEXT("LocalAlloc failed\n"));
          hr = MAPI_E_NOT_ENOUGH_MEMORY;
          goto out;
      }
  
  
      //
      // Get the Entry iD index
      //
      if (!LoadIndex( IN  lpMPSWabFileInfo,
                      IN  indexEntryID,
                      IN  hMPSWabFile) )
      {
          DebugTrace(TEXT("Error Loading EntryID Index!\n"));
          goto out;
      }
  
      if(!ReadDataFromWABFile(hMPSWabFile,
                              0,
                              (LPVOID) lpMPSWabFileInfo->lpMPSWabFileHeader,
                              (DWORD) sizeof(MPSWab_FILE_HEADER)))
         goto out;
  
      for(i=0;i<ulcWABEntryCount;i++)
      {
          DWORD dwEntryID = lpIndexEID[i].dwEntryID;
          ULONG j = 0;
          LPTSTR lpszDisplayName = NULL;
  
          hr = ReadRecordWithoutLocking(  hMPSWabFile,
                                          lpMPSWabFileInfo,
                                          dwEntryID,
                                          &ulcPropCount,
                                          &lpPropArray);
  
          if (HR_FAILED(hr))
          {
              // Since there are a lot of implicit expectations in the WAB that the
              // EntryID and DisplayName indexes should have a one to one correspondence,
              // we cant really have an entry in the EntryID index and not have it in
              // the display name index. Hence an error in reading the record is serious
              // and we should either
              // - remove the corresponding entry from the EID index; or
              // - fail and restore from backup;
  
              // For the time being we will do (a)
              hr = MAPI_E_CORRUPT_DATA;
              goto out;
          }
  
          //reset hr
          hr = E_FAIL;
  
          for(j=0;j<ulcPropCount;j++)
          {
              if (lpPropArray[j].ulPropTag == PR_DISPLAY_NAME)
              {
                  lpszDisplayName = lpPropArray[j].Value.LPSZ;
                  break;
              }
          }
  
          if(!lpszDisplayName)
          {
              //we should remove this index from the EID index since this record
              //seems to have some errors <TBD>
              hr = MAPI_E_CORRUPT_DATA;
              goto out;
          }
          else
          {
              // We have a display name so create an index and write it to file ..
  
              ULONG nLen = TruncatePos(lpszDisplayName, MAX_INDEX_STRING-1);
              CopyMemory(MPSWabIndexString.szIndex,lpszDisplayName,sizeof(TCHAR)*nLen);
              MPSWabIndexString.szIndex[nLen]='\0';
  
              MPSWabIndexString.dwEntryID = dwEntryID;
  
              //
              // We are cerating a shadow index in memory in the lpIndexEID block
              // We then write this index into the file ..
              //
  
              //
              // Find the position in the index where this entry will go
              //
              bEID = BinSearchStr(lpIndexString,
                           MPSWabIndexString.szIndex,
                           i,
                           &nIndexPos);
  
              if(nIndexPos != i)
              {
                  CopyMemory( lpTmp,
                              &(lpIndexString[nIndexPos]),
                              sizeof(MPSWab_INDEX_ENTRY_DATA_STRING) * (i - nIndexPos));
              }
  
              CopyMemory( &(lpIndexString[nIndexPos]),
                          &(MPSWabIndexString),
                          sizeof(MPSWab_INDEX_ENTRY_DATA_STRING));
  
              if(nIndexPos != i)
              {
                  CopyMemory( &(lpIndexString[nIndexPos+1]),
                              lpTmp,
                              sizeof(MPSWab_INDEX_ENTRY_DATA_STRING) * (i - nIndexPos));
              }
  
              if(!WriteDataToWABFile( hMPSWabFile,
                                      MPSWabFileHeader.IndexData[indexDisplayName].ulOffset,
                                      (LPVOID) lpIndexString,
                                      sizeof(MPSWab_INDEX_ENTRY_DATA_STRING)*(i+1)))
                  goto out;
  
              NewMPSWabFileHeader.IndexData[indexDisplayName].UtilizedBlockSize = sizeof(MPSWab_INDEX_ENTRY_DATA_STRING)*(i+1);
              NewMPSWabFileHeader.IndexData[indexDisplayName].ulcNumEntries = (i+1);
  
              //
              // Write this NewMPSWabFileHeader to the file
              //
              if(!WriteDataToWABFile( hMPSWabFile,
                                      0,
                                      (LPVOID) &NewMPSWabFileHeader,
                                      sizeof(MPSWab_FILE_HEADER)))
                  goto out;
  
          }
  
          LocalFreePropArray(NULL, ulcPropCount,&lpPropArray);
  
          lpPropArray = NULL;
          ulcPropCount = 0;
  
      } //for loop
  
      // check that we have the correct number of entries in the both indexes
      //
      if (NewMPSWabFileHeader.IndexData[indexDisplayName].ulcNumEntries != NewMPSWabFileHeader.ulcNumEntries)
      {
          // If the 2 indexes dont contain the same number of elements, something failed above
          // Big problem ... cant recover
          hr = MAPI_E_CORRUPT_DATA;
          goto out;
      }
  
      //
      // Clear out the error tag from the File Header so we dont keep falling back
      // into this function ...
      //
  
      NewMPSWabFileHeader.ulFlags = WAB_CLEAR;
  
      //
      // Write this NewMPSWabFileHeader to the file
      //
      if(!WriteDataToWABFile( hMPSWabFile,
                              0,
                              (LPVOID) &NewMPSWabFileHeader,
                              sizeof(MPSWab_FILE_HEADER)))
          goto out;
  
      hr = hrSuccess;
  
  out:
      LocalFreeAndNull(&lpTmp);
  
      LocalFreeAndNull(&lpIndexEID);
  
      LocalFreeAndNull(&lpIndexString);
  
      LocalFreePropArray(NULL, ulcPropCount,&lpPropArray);
  
      // fix the return error code
      switch(hr)
      {
      case MAPI_E_NOT_ENOUGH_MEMORY:
      case MAPI_E_DISK_ERROR:
      case S_OK:
          break;
      default:
          hr = MAPI_E_CORRUPT_DATA;
          break;
      }
  
      DebugTrace(TEXT("---DoDetailedWABIntegrityCheck Exit: %x\n"),hr);
  
      SetCursor(hOldCur);
  
      return hr;
  }
  
  
  //$$////////////////////////////////////////////////////////////////////////////////
  //
  // bIsValidRecord - This function looks at the Record Header components to determine if
  // the record is valid or not
  //
  // It follows some very simple rules that can detect record header corruptions
  //
  // dwNextEntryID value will not be used if it is 0xFFFFFFFF
  ////////////////////////////////////////////////////////////////////////////////////////
  BOOL bIsValidRecord(MPSWab_RECORD_HEADER rh,
                      DWORD dwNextEntryID,
                      ULONG ulRecordOffset,
                      ULONG ulFileSize)
  {
      BOOL bRet = FALSE;
  
      // is this tagged as an invalid record (or something else)
      if ((rh.bValidRecord == FALSE) && (rh.bValidRecord != TRUE))
          goto out;
  
      // is this entry id value acceptable and correct
      if(dwNextEntryID != 0xFFFFFFFF)
      {
          if (rh.dwEntryID > dwNextEntryID)
              goto out;
      }
  
      // are the offsets in the header correct
      if (rh.ulPropTagArraySize != rh.ulcPropCount * sizeof(ULONG))
          goto out;
  
      if (rh.ulRecordDataOffset != rh.ulPropTagArraySize)
          goto out;
  
      if (rh.ulPropTagArrayOffset != 32) /***TBD - this is dependent on the struct elements***/
          goto out;
  
      if (ulRecordOffset + rh.ulRecordDataOffset + rh.ulRecordDataSize > ulFileSize)
          goto out;
  
      bRet = TRUE;
  
  out:
  
      if(!bRet)
          DebugTrace(TEXT("\n@@@@@@@@@@\n@@@Invalid Record Detected\n@@@@@@@@@@\n"));
  
      return bRet;
  }
  
  
  //$$////////////////////////////////////////////////////////////////////////
  //
  // TagWABFileError -
  //
  // If an error is detected while reading a files contents, we can tag the error
  // in the file so that the next access will attempt to fix it
  //
  ////////////////////////////////////////////////////////////////////////////
  BOOL TagWABFileError( 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_ERROR_DETECTED;
  
      // update the file header
      if(!WriteDataToWABFile( hMPSWabFile,
                              0,
                              (LPVOID) lpMPSWabFileHeader,
                              sizeof(MPSWab_FILE_HEADER)))
          goto out;
  
  
      bRet = TRUE;
  
  out:
      return bRet;
  }
  
  /*
  -
  -   SetNextEntryID - sets the next entryid to use in a file. Called durimg migration
  *
  */
  void SetNextEntryID(HANDLE hPropertyStoreTemp, DWORD dwEID)
  {
      MPSWab_FILE_HEADER WABHeader = {0};
      HANDLE hWABFile = NULL;
      LPMPSWab_FILE_INFO lpMPSWabFI = hPropertyStoreTemp;
  
      // First read the header from the existing file
      if(!HR_FAILED(OpenWABFile(lpMPSWabFI->lpszMPSWabFileName, NULL, &hWABFile)))
      {
          if(!ReadDataFromWABFile(hWABFile, 0, (LPVOID) &WABHeader, sizeof(MPSWab_FILE_HEADER)))
              goto out;
  
          WABHeader.dwNextEntryID = dwEID;
  
          if(!WriteDataToWABFile(hWABFile, 0, (LPVOID) &WABHeader, sizeof(MPSWab_FILE_HEADER)))
              goto out;
      }
  out:
      if ((hWABFile != NULL) && (hWABFile != INVALID_HANDLE_VALUE))
          CloseHandle(hWABFile);
  
      return;
  }
  
  
  enum WABVersion
  {
      W05 =0,
      W2,
      W3,
      W4,
  };
  
  
  //$$////////////////////////////////////////////////////////////////////////////
  //
  //  HrMigrateFromOldWABtoNew - Migrates older versions of the wab into current ones
  //
  //  IN hMPSWabFile
  //  IN lpMPSWabFileInfo
  //  hWnd .. used for displaying a "Please wait" dialog
  //  returns:
  //      E_FAIL or S_OK
  //      MAPI_E_CORRUPT_DATA if GUID is unrecognizable
  //
  ////////////////////////////////////////////////////////////////////////////////
  HRESULT HrMigrateFromOldWABtoNew(HWND hWnd, HANDLE hMPSWabFile, LPMPSWab_FILE_INFO lpMPSWabFileInfo, GUID WabGUID)
  {
      HRESULT hr = E_FAIL;
      int WABVersion;
      HANDLE hTempFile = NULL;
      MPSWab_FILE_HEADER NewFileHeader = {0};
      ULONG ulcMaxEntries = 0;
      MPSWab_FILE_HEADER_W2 MPSWabFileHeaderW2 = {0};
      MPSWab_FILE_HEADER MPSWabFileHeader = {0};
      TCHAR szFileName[MAX_PATH];
      ULONG ulAdditionalRecordOffset=0;
      LPVOID lpv = NULL;
      LPMPSWab_INDEX_ENTRY_DATA_ENTRYID lpeid = NULL;
      ULONG i = 0;
      DWORD dwOldEID = 0, dwNextEID = 0;
  
      HCURSOR hOldC = SetCursor(LoadCursor(NULL, IDC_WAIT));
  
      if (IsEqualGUID(&WabGUID,&MPSWab_OldBeta1_GUID))
      {
          WABVersion = W05;
      }
      else if (IsEqualGUID(&WabGUID,&MPSWab_W2_GUID))
      {
          WABVersion = W2;
      }
      else if (IsEqualGUID(&WabGUID,&MPSWab_GUID_V4))
      {
          WABVersion = W4;
      }
  
      // For WABVersion 1 and 2, we will read in the file header and 
      // save it to the new file. We will then read in the records one by 
      // one and write them to the file through the interface retaining the
      // old entryid. Version 1 and 2 did not have named prop support and also
      // had lesser numbers of indices. The individual record layour was the same as before
  
      // WABVersion 4 is the conversion from the ANSI store to the Unicode store ..
      // We can copy the file header and the named prop info as it is but we'll need
      // to read the records one by one, convert them to Unicode and then write it to the
      // new file so that all the indexes etc are rebuild correctly ...
      //
  
      // As long as all the entryids are retained, the relationship between the address
      // book data elements is the same.
  
  
      // Get a temp file name
      szFileName[0]='\0';
      GetWABTempFileName(szFileName);
  
      if(WABVersion <= W2)
      {
          // First read the header from the existing file
          if(!ReadDataFromWABFile(hMPSWabFile,
                                  0,
                                  (LPVOID) &MPSWabFileHeaderW2,
                                  sizeof(MPSWab_FILE_HEADER_W2)))
              goto out;
  
          // Create a new Temp WAB File
          if (!CreateMPSWabFile(  IN  &NewFileHeader,
                                  IN  szFileName,
                                  IN  MPSWabFileHeaderW2.ulcMaxNumEntries,
                                  IN  NAMEDPROP_STORE_SIZE))
          {
              DebugTrace(TEXT("Error creating new file\n"));
              goto out;
          }
  
          //Update header information
          NewFileHeader.ulModificationCount = MPSWabFileHeaderW2.ulModificationCount;
          dwNextEID = NewFileHeader.dwNextEntryID = MPSWabFileHeaderW2.dwNextEntryID;
          NewFileHeader.ulcNumEntries = MPSWabFileHeaderW2.ulcNumEntries;
          NewFileHeader.ulFlags = MPSWabFileHeaderW2.ulFlags;
          NewFileHeader.ulReserved1 = MPSWabFileHeaderW2.ulReserved;
      }
      else
      {
          // First read the header from the existing file
          if(!ReadDataFromWABFile(hMPSWabFile,
                                  0,
                                  (LPVOID) &MPSWabFileHeader,
                                  sizeof(MPSWab_FILE_HEADER)))
              goto out;
  
          // Create a new Temp WAB File
          if (!CreateMPSWabFile(  IN  &NewFileHeader,
                                  IN  szFileName,
                                  IN  MPSWabFileHeader.ulcMaxNumEntries,
                                  IN  MPSWabFileHeader.NamedPropData.AllocatedBlockSize))
          {
              DebugTrace(TEXT("Error creating new file\n"));
              goto out;
          }
          //Update header information
          dwOldEID = dwNextEID = NewFileHeader.dwNextEntryID = MPSWabFileHeader.dwNextEntryID;
      }
  
      {
          // Update the file header info from current file to the new file
          // now we open the new temp file and get a handle to it
          hTempFile = CreateFile( szFileName,
                                  GENERIC_READ | GENERIC_WRITE,
                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                                  (LPSECURITY_ATTRIBUTES) NULL,
                                  OPEN_EXISTING,
                                  FILE_FLAG_RANDOM_ACCESS,
                                  (HANDLE) NULL);
          if (hTempFile == INVALID_HANDLE_VALUE)
          {
              DebugTrace(TEXT("Could not open Temp file.\nExiting ...\n"));
              goto out;
          }
  
          if(WABVersion == W4)
          {
              DWORD dwNP = MPSWabFileHeader.NamedPropData.AllocatedBlockSize;
              LPBYTE  lpNP = LocalAlloc(LMEM_ZEROINIT, dwNP);
              LPGUID_NAMED_PROPS lpgnp = NULL;
  
              if(lpNP)
              {
                  if(!ReadDataFromWABFile(hMPSWabFile,
                                          MPSWabFileHeader.NamedPropData.ulOffset,
                                          (LPVOID) lpNP,
                                          dwNP))
                      goto out;
  
                  if(GetNamedPropsFromBuffer(lpNP, MPSWabFileHeader.NamedPropData.ulcNumEntries,
                                             TRUE, &lpgnp))
                  {
                      LocalFreeAndNull(&lpNP);
                      if(SetNamedPropsToBuffer(   MPSWabFileHeader.NamedPropData.ulcNumEntries,
                                                  lpgnp, &dwNP, &lpNP))
                      {
                          if(!WriteDataToWABFile(hTempFile, 
                                                  NewFileHeader.NamedPropData.ulOffset,
                                                  (LPVOID) lpNP,
                                                  dwNP))
                              goto out;
      
                          LocalFreeAndNull(&lpNP);
                      }
  
                      NewFileHeader.NamedPropData.UtilizedBlockSize = MPSWabFileHeader.NamedPropData.UtilizedBlockSize;
                      NewFileHeader.NamedPropData.ulcNumEntries = MPSWabFileHeader.NamedPropData.ulcNumEntries;
  
                      if(lpgnp)
                          FreeGuidnamedprops(MPSWabFileHeader.NamedPropData.ulcNumEntries, lpgnp);
                  }
  
              }
  
          }
  
          //Save this fileheader information
          if(!WriteDataToWABFile(hTempFile, 0, (LPVOID) &NewFileHeader, sizeof(MPSWab_FILE_HEADER)))
              goto out;
  
          CloseHandle(hTempFile);
          hTempFile = NULL;
      }
  
      {
          HANDLE hPropertyStoreTemp = NULL;
          ULONG ulOldRecordOffset = 0;
          ULONG ulWABFileSize = GetFileSize(hMPSWabFile,NULL);
          LPSPropValue lpPropArray = NULL;
          ULONG ulcValues = 0;
  
          hr = OpenPropertyStore( szFileName,
                                  AB_OPEN_EXISTING | AB_DONT_RESTORE,
                                  NULL,
                                  &hPropertyStoreTemp);
          if(HR_FAILED(hr))
          {
              DebugTrace(TEXT("Could not open Temp PropStore\n"));
              goto endW05;
          }
  
          // Get the start of the record data from this file
          if(WABVersion <= W2)
          {
              ulOldRecordOffset = MPSWabFileHeaderW2.IndexData[indexFirstName].ulOffset +
                                  MPSWabFileHeaderW2.IndexData[indexFirstName].AllocatedBlockSize;
          }
          else
          {
              ulOldRecordOffset = MPSWabFileHeader.IndexData[indexAlias].ulOffset +
                                  MPSWabFileHeader.IndexData[indexAlias].AllocatedBlockSize;
          }
  
          // walk the file record by record
          while (ulOldRecordOffset < ulWABFileSize)
          {
              ULONG ulRecordSize = 0;
              ULONG ulObjType = 0;
  
              //Read the record prop array from the old WAB file
              hr = HrGetPropArrayFromFileRecord(hMPSWabFile,
                                                ulOldRecordOffset,
                                                NULL,
                                                &ulObjType,
                                                &ulRecordSize,
                                                &ulcValues,
                                                &lpPropArray);
  
              if(ulRecordSize == 0)
              {
                  // If this happens we will get caught in a loop
                  // Better to exit.
                  DebugTrace(TEXT("Zero-lengthrecord found\n"));
                  goto endW05;
              }
  
              if(!HR_FAILED(hr))
              {
                  LPSBinary lpsbEID = NULL;
                  ULONG i = 0, iEID = 0;
                  DWORD dwEID = 0;
  
                  // The above PropArray has a PR_ENTRY_ID that has a value
                  // from the old store. We want to retain this entryid value
                  for(i=0;i<ulcValues;i++)
                  {
                      if(lpPropArray[i].ulPropTag == PR_ENTRYID)
                      {
                          lpsbEID = &lpPropArray[i].Value.bin;
                          iEID = i;
                          break;
                      }
                  }
  
                  // However, when we save this entry to the new store, the new
                  // store does not have an index and so the entryid will be rejected and
                  // a new one assigned .. so to trick the WAB into reusing the entryid, we will
                  // just set the entryid in the file header as the next one to use ..
                  {
                      AssertSz(lpsbEID->cb == SIZEOF_WAB_ENTRYID, TEXT("Entryid has unknown size!"));
  
                      CopyMemory(&dwEID, lpsbEID->lpb, sizeof(DWORD));
  
                      if(dwNextEID <= dwEID)
                          dwNextEID = dwEID + 1;
  
                      SetNextEntryID(hPropertyStoreTemp, dwEID);
  
                      //lpPropArray[iEID].ulPropTag = PR_NULL;
                      //lpsbEID->cb = 0;
                      //LocalFreeAndNull(&lpsbEID->lpb);
                      //lpsbEID = NULL;
                  }
  
                  // Convert any A props read in into W props
                  ConvertAPropsToWCLocalAlloc(lpPropArray, ulcValues);
  
                  // We have a valid record (otherwise ignore it)
                  hr = WriteRecord(IN hPropertyStoreTemp,
  									NULL,
                                      &lpsbEID,
                                      0,
                                      ulObjType,
                                      ulcValues,
                                      lpPropArray);
                  if(HR_FAILED(hr))
                  {
                      DebugTrace(TEXT("WriteRecord failed\n"));
                      goto endW05;
                  }
  
                  //if(lpsbEID)
                  //    FreeEntryIDs(NULL, 1, lpsbEID);
              }
  
              ulOldRecordOffset += ulRecordSize;
  
              LocalFreePropArray(NULL, ulcValues, &lpPropArray);
  
          }
  
          if(hr == MAPI_E_INVALID_OBJECT)
              hr = S_OK;
  
          // Just in case the Next Entryid value has changed (for whatever reason)(though this shouldnt happen)
          // update the value in the file .. 
          if(dwOldEID != dwNextEID)
              SetNextEntryID(hPropertyStoreTemp, dwNextEID);
  
  endW05:
          LocalFreePropArray(NULL, ulcValues, &lpPropArray);
  
          if(hPropertyStoreTemp)
              ClosePropertyStore(hPropertyStoreTemp, AB_DONT_BACKUP);
  
          if(!HR_FAILED(hr))
          {
              hr = E_FAIL;
  
              hTempFile = CreateFile( szFileName,
                                      GENERIC_READ | GENERIC_WRITE,
                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
                                      (LPSECURITY_ATTRIBUTES) NULL,
                                      OPEN_EXISTING,
                                      FILE_FLAG_RANDOM_ACCESS,
                                      (HANDLE) NULL);
              if (hTempFile == INVALID_HANDLE_VALUE)
              {
                  DebugTrace(TEXT("Could not open Temp file.\nExiting ...\n"));
                  goto out;
              }
  
              if(!CopySrcFileToDestFile(hTempFile, 0, hMPSWabFile, 0))
              {
                  DebugTrace(TEXT("Could not copy file\n"));
                  goto out;
              }
  
  
              hr = S_OK;
          }
      }
  
  out:
      LocalFreeAndNull(&lpv);
  
      LocalFreeAndNull(&lpeid);
  
      if(hTempFile)
          IF_WIN32(CloseHandle(hTempFile);) IF_WIN16(CloseFile(hTempFile);)
  
      if(lstrlen(szFileName))
          DeleteFile(szFileName);
  
      if(hOldC)
          SetCursor(hOldC);
  
      return hr;
  }
  
  //$$////////////////////////////////////////////////////////////////////////////
  //
  //  HrVerifyWABVersionAndUpdate - Looks at the WAB File version and migrates
  //              older versions into newer versions
  //
  //  IN hMPSWabFile
  //  IN lpMPSWabFileInfo
  //      hWnd - used for displaying some kind of dialog if necessary
  //
  //  BUG 16681:
  //  In randomly occuring cases, the wab file seems to get totally wiped and turned
  //      into 0's ... In such events, we will rename the file and try again .. we
  //      indicate this condition to the OpenPropertyStore function by returning
  //      MAPI_E_VERSION ..
  //
  //  returns:
  //      E_FAIL or S_OK
  //      MAPI_E_CORRUPT_DATA if GUID is unrecognizable
  //
  ////////////////////////////////////////////////////////////////////////////////
  HRESULT HrVerifyWABVersionAndUpdate(HWND hWnd,
                                      HANDLE hMPSWabFile,
                                      LPMPSWab_FILE_INFO lpMPSWabFileInfo)
  {
      GUID TmpGuid = {0};
      HRESULT hr = E_FAIL;
      DWORD dwNumofBytes = 0;
  
  
      // First read the GUID from the file
      //
      if(!ReadDataFromWABFile(hMPSWabFile,
                              0,
                              (LPVOID) &TmpGuid,
                              (DWORD) sizeof(GUID)))
         goto out;
  
  
      //
      // Check if this is a Microsoft Property Store by looking for MPSWab GUID in header
      // (If this was an old file it should have been updated above)(If not, we should
      // avoid an error and go ahead and open it)
      // However if the guids dont match, we cant tell if this is a WAB file or not
      // because it could also be a valid corrupt wab file ... so when the guids dont
      // match, we will assume that the file is a corrupt wab file.
      //
      if ( (!IsEqualGUID(&TmpGuid,&MPSWab_GUID)) &&
           (!IsEqualGUID(&TmpGuid,&MPSWab_GUID_V4)) &&
           (!IsEqualGUID(&TmpGuid,&MPSWab_W2_GUID)) &&
           (!IsEqualGUID(&TmpGuid,&MPSWab_OldBeta1_GUID)) )
      {
          DebugTrace(TEXT("%s is not a Microsoft Property Store File. GUIDS don't match\n"),lpMPSWabFileInfo->lpszMPSWabFileName);
          hr = MAPI_E_INVALID_OBJECT;
  
          //Bug 16681:
          //Check the special condition where everything is all zero's
          {
              if (    (TmpGuid.Data1 == 0) &&
                      (TmpGuid.Data2 == 0) &&
                      (TmpGuid.Data3 == 0) &&
                      (lstrlen((LPTSTR)TmpGuid.Data4) == 0) )
              {
                  hr = MAPI_E_VERSION;
              }
          }
  
          goto out;
      }
  
  
      //
      // If this is an older version of the file, update it to a more current
      // store format.
      //
      if (    (IsEqualGUID(&TmpGuid,&MPSWab_GUID_V4)) ||
              (IsEqualGUID(&TmpGuid,&MPSWab_OldBeta1_GUID)) ||
              (IsEqualGUID(&TmpGuid,&MPSWab_W2_GUID))   )
      {
          // We will basically scavenge the old file for records out of the
          // file (ignoring the indexes so we can avoid all errors)
          // and put them in a new prop store file which we will then
          // populate with the old records and finally use to replace the
          // current file - very similar to how CompressFile works
          DebugTrace(TEXT("Old WAB File Found. Migrating to new ...\n"));
          hr = HrMigrateFromOldWABtoNew(  hWnd, hMPSWabFile,
                                          lpMPSWabFileInfo,
                                          TmpGuid);
          if(HR_FAILED(hr))
          {
              DebugTrace(TEXT("MPSWabUpdateAndVerifyOldWAB: %x\n"));
              goto out;
          }
      }
  
  
      //reset hr
      hr = E_FAIL;
  
      lpMPSWabFileInfo->nCurrentlyLoadedStrIndexType = indexDisplayName;
  
      // Reload whatever new info we added as a result of the above.
      if(!ReloadMPSWabFileInfo(
                      lpMPSWabFileInfo,
                       hMPSWabFile))
      {
          DebugTrace(TEXT("Reading file info failed.\n"));
          goto out;
      }
  
  
      hr = S_OK;
  
  out:
      return hr;
  }
  
  
  
  //$$////////////////////////////////////////////////////////////////////////////////
  //
  // HrGetBufferFromPropArray - translates a prop array into a flat buffer.
  //          The format of the data in the buffer is the same as
  //          the format of data in the .wab file
  //
  //
  //  Params: 
  //          ulcPropCount- # of props in array
  //          lpPropArray - prop array
  //          lpcbBuf       - size of returned buffer
  //          lppBuf       - returned buffer
  //
  ////////////////////////////////////////////////////////////////////////////////////
  HRESULT HrGetBufferFromPropArray(   ULONG ulcPropCount, 
                                      LPSPropValue lpPropArray,
                                      ULONG * lpcbBuf,
                                      LPBYTE * lppBuf)
  {
      HRESULT hr = E_FAIL;
      LPULONG lpulrgPropDataSize = NULL;
      ULONG ulRecordDataSize = 0;
      LPBYTE lp = NULL;
      LPBYTE szBuf = NULL;
      ULONG   i=0,j=0,k=0;
  
      if(!lpcbBuf || !lppBuf)
          goto out;
  
      *lpcbBuf = 0;
      *lppBuf = NULL;
  
  
  //    _DebugProperties(lpPropArray, ulcPropCount, TEXT("GetBufferFromPropArray"));
      //
      // We will go through the given data and determine how much space we need for each
      // property. lpulrgPropDataSize is an array of ULONGs in which we store the calculated sizes
      // temporarily.
      //
      lpulrgPropDataSize = LocalAlloc(LMEM_ZEROINIT, ulcPropCount * sizeof(ULONG));
  
      if (!lpulrgPropDataSize)
      {
          DebugTrace(TEXT("Error allocating memory\n"));
          hr = MAPI_E_NOT_ENOUGH_MEMORY;
          goto out;
      }
  
      //
      // Get an estimate of how big the data portion of this record will be ...
      // We need this estimate to allocate a block of memory into which we will
      // write the data and then blt the block into the file
      //
      for(i=0;i<ulcPropCount;i++)
      {
          // This is the eventual data format:
          //
          //  If (SingleValued)
          //      <PropTag><DataSize><Data>
          //  else
          //      <MultiPropTag><cValues><DataSize><data>
          //  unless PropType is MV_BINARY or MV_TSTRING in which case we need a
          //  more flexible data storage
          //      <MultiPropTag><cValues><DataSize>
          //                                  <cb/strlen><Data>
          //                                  <cb/strlen><Data> ...
          //
  
          ulRecordDataSize += sizeof(ULONG);   // holds <PropTag>
          if ((lpPropArray[i].ulPropTag & MV_FLAG))
          {
              //
              // multi-valued
              //
              lpulrgPropDataSize[i] = SizeOfMultiPropData(lpPropArray[i]); //Data
              ulRecordDataSize += sizeof(ULONG); // holds <CValues>
          }
          else
          {
              //
              // single-valued
              //
              lpulrgPropDataSize[i] = SizeOfSinglePropData(lpPropArray[i]); //Data
          }
          ulRecordDataSize += sizeof(ULONG)   // holds <DataSize>
                              + lpulrgPropDataSize[i]; //holds <Data>
      }
  
  
      lp = LocalAlloc(LMEM_ZEROINIT, ulRecordDataSize);
  
      if (!lp)
      {
          DebugTrace(TEXT("Error allocating memory\n"));
          hr = MAPI_E_NOT_ENOUGH_MEMORY;
          goto out;
      }
  
      szBuf = lp;
  
      for (i = 0; i<ulcPropCount; i++)
      {
          //
          // copy the prop tag
          //
          CopyMemory(szBuf,&lpPropArray[i].ulPropTag, sizeof(ULONG));
          szBuf += sizeof(ULONG);
  
          //
          // difference between handling of multivalued and singlevalued
          //
          if (!(lpPropArray[i].ulPropTag & MV_FLAG))
          {
              // Single Valued
  
              //
              // Record the size of data
              //
              CopyMemory(szBuf,&lpulrgPropDataSize[i], sizeof(ULONG));
              szBuf += sizeof(ULONG);
  
              ////DebugTrace(TEXT("%x: "),lpPropArray[i].ulPropTag);
  
              switch(PROP_TYPE(lpPropArray[i].ulPropTag))
              {
              case(PT_STRING8):
                  //
                  // Record the data ...
                  //
                  CopyMemory(szBuf,lpPropArray[i].Value.lpszA, lpulrgPropDataSize[i]);
                  ////DebugTrace(TEXT("%s\n"),lpPropArray[i].Value.LPSZ);
                  break;
  
              case(PT_UNICODE):
                  //
                  // Record the data ...
                  //
                  CopyMemory(szBuf,lpPropArray[i].Value.lpszW, lpulrgPropDataSize[i]);
                  ////DebugTrace(TEXT("%s\n"),lpPropArray[i].Value.LPSZ);
                  break;
  
              case(PT_CLSID):
                  //
                  // Record the data ...
                  //
                  CopyMemory(szBuf,lpPropArray[i].Value.lpguid, lpulrgPropDataSize[i]);
                  ////DebugTrace(TEXT("%x-%x-%x-%x\n"),lpPropArray[i].Value.lpguid->Data1,lpPropArray[i].Value.lpguid->Data2,lpPropArray[i].Value.lpguid->Data3,lpPropArray[i].Value.lpguid->Data4);
                  break;
  
              case(PT_BINARY):
                  //
                  // Record the data ...
                  //
                  CopyMemory(szBuf,lpPropArray[i].Value.bin.lpb, lpulrgPropDataSize[i]);
                  break;
  
              case(PT_SHORT):
                  //Record the data ...
                  CopyMemory(szBuf,&lpPropArray[i].Value.i, lpulrgPropDataSize[i]);
                  ////DebugTrace(TEXT("%d\n"),lpPropArray[i].Value.i);
                  break;
  
              case(PT_LONG):
              case(PT_R4):
              case(PT_DOUBLE):
              case(PT_BOOLEAN):
              case(PT_APPTIME):
              case(PT_CURRENCY):
                  //Record the data ...
                  CopyMemory(szBuf,&lpPropArray[i].Value.i, lpulrgPropDataSize[i]);
                  ////DebugTrace(TEXT("%d\n"),lpPropArray[i].Value.l);
                  break;
  
              case(PT_SYSTIME):
                  //Record the data ...
                  CopyMemory(szBuf,&lpPropArray[i].Value.ft, lpulrgPropDataSize[i]);
                  ////DebugTrace(TEXT("%d,%d\n"),lpPropArray[i].Value.ft.dwLowDateTime,lpPropArray[i].Value.ft.dwHighDateTime);
                  break;
  
              default:
                  DebugTrace(TEXT("Unknown PropTag !!\n"));
                  break;
  
  
              }
              szBuf += lpulrgPropDataSize[i];
  
          }
          else
          {
              //multivalued
  
              //copy the # of multi-values
              CopyMemory(szBuf,&lpPropArray[i].Value.MVi.cValues, sizeof(ULONG));
              szBuf += sizeof(ULONG);
  
              //Record the size of data
              CopyMemory(szBuf,&lpulrgPropDataSize[i], sizeof(ULONG));
              szBuf += sizeof(ULONG);
  
              ////DebugTrace(TEXT("%x: MV_PROP\n"),lpPropArray[i].ulPropTag);
  
  
              switch(PROP_TYPE(lpPropArray[i].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):
                  CopyMemory(szBuf,lpPropArray[i].Value.MVft.lpft, lpulrgPropDataSize[i]);
                  szBuf += lpulrgPropDataSize[i];
                  break;
  
              case(PT_MV_I8):
                  CopyMemory(szBuf,lpPropArray[i].Value.MVli.lpli, lpulrgPropDataSize[i]);
                  szBuf += lpulrgPropDataSize[i];
                  break;
  
              case(PT_MV_BINARY):
                  for (j=0;j<lpPropArray[i].Value.MVbin.cValues;j++)
                  {
                      CopyMemory(szBuf,&lpPropArray[i].Value.MVbin.lpbin[j].cb, sizeof(ULONG));
                      szBuf += sizeof(ULONG);
                      CopyMemory(szBuf,lpPropArray[i].Value.MVbin.lpbin[j].lpb, lpPropArray[i].Value.MVbin.lpbin[j].cb);
                      szBuf += lpPropArray[i].Value.MVbin.lpbin[j].cb;
                  }
                  break;
  
              case(PT_MV_STRING8):
                  for (j=0;j<lpPropArray[i].Value.MVszA.cValues;j++)
                  {
                      ULONG nLen;
                      nLen = lstrlenA(lpPropArray[i].Value.MVszA.lppszA[j])+1;
                      CopyMemory(szBuf,&nLen, sizeof(ULONG));
                      szBuf += sizeof(ULONG);
                      CopyMemory(szBuf,lpPropArray[i].Value.MVszA.lppszA[j], nLen);
                      szBuf += nLen;
                  }
                  break;
  
              case(PT_MV_UNICODE):
                  for (j=0;j<lpPropArray[i].Value.MVszW.cValues;j++)
                  {
                      ULONG nLen;
                      nLen = sizeof(TCHAR)*(lstrlenW(lpPropArray[i].Value.MVszW.lppszW[j])+1);
                      CopyMemory(szBuf,&nLen, sizeof(ULONG));
                      szBuf += sizeof(ULONG);
                      CopyMemory(szBuf,lpPropArray[i].Value.MVszW.lppszW[j], nLen);
                      szBuf += nLen;
                  }
                  break;
  
              case(PT_MV_CLSID):
                  CopyMemory(szBuf,lpPropArray[i].Value.MVguid.lpguid, lpulrgPropDataSize[i]);
                  szBuf += lpulrgPropDataSize[i];
                  break;
  
              } //switch
          } //if
      } //for
  
      *lpcbBuf = ulRecordDataSize;
      *lppBuf = lp;
      
      hr = S_OK;
  
  out:
      
      LocalFreeAndNull(&lpulrgPropDataSize);
      
      return hr;
  }
  
  //$$////////////////////////////////////////////////////////////////////////////////
  //
  // HrGetPropArrayFromBuffer - translates a buffer (from file or memory) into 
  //          a prop array. The format of the data in the buffer is the same as
  //          the format of data in the .wab file
  //
  //
  //  Params: char * szBuf- buffer to interpret 
  //          cbBuf       - sizeof buffer
  //          ulcPropCount- # of props in buffer
  //          lppPropArray - returned prop array
  //
  //          ulcNumExtraProps - an extra number of props to add to the PropArray
  //                      during allocation. These blank props are later used for
  //                      extending the prop array easily ..
  //
  ////////////////////////////////////////////////////////////////////////////////////
  HRESULT HrGetPropArrayFromBuffer(   LPBYTE szBuf, 
                                      ULONG cbBuf, 
                                      ULONG ulcPropCount,
                                      ULONG ulcNumExtraProps,
                                      LPSPropValue * lppPropArray)
  {
      HRESULT hr = S_OK;
      LPBYTE lp = NULL;
      ULONG i = 0,j=0, k=0;
      DWORD cbBufLeft = cbBuf;
  
      if(!lppPropArray)
      {
          hr = MAPI_E_INVALID_PARAMETER;
          goto out;
      }
  
      *lppPropArray = NULL;
  
      *lppPropArray = LocalAlloc(LMEM_ZEROINIT, (ulcPropCount+ulcNumExtraProps) * sizeof(SPropValue));
  
      if (!(*lppPropArray))
      {
          DebugTrace(TEXT("Error allocating memory\n"));
          hr = MAPI_E_NOT_ENOUGH_MEMORY;
          goto out;
      }
  
      for(i=0;i<(ulcPropCount+ulcNumExtraProps);i++)
          (*lppPropArray)[i].ulPropTag = PR_NULL;
  
      lp = szBuf;
  
      for(i=0;i<ulcPropCount;i++)
      {
          ULONG ulDataSize = 0;
          ULONG ulcValues = 0;
  
          // Copy PropTag
          CopyMemory(&((*lppPropArray)[i].ulPropTag),lp,sizeof(ULONG));
          lp+=sizeof(ULONG);
  
          AssertSz((*lppPropArray)[i].ulPropTag, TEXT("Null PropertyTag"));
  
          if(ulDataSize > cbBuf)
          {
              hr = MAPI_E_CORRUPT_DATA;
              goto out;
          }
  
          if (((*lppPropArray)[i].ulPropTag & MV_FLAG))
          {
              //multi-valued
              //DebugTrace(TEXT("MV_PROP\n"));
  
              //Copy cValues
              CopyMemory(&ulcValues,lp,sizeof(ULONG));
              lp+=sizeof(ULONG);
  
              //Copy DataSize
              CopyMemory(&ulDataSize,lp,sizeof(ULONG));
              lp+=sizeof(ULONG);
  
              switch(PROP_TYPE((*lppPropArray)[i].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):
                  (*lppPropArray)[i].Value.MVi.lpi = LocalAlloc(LMEM_ZEROINIT,ulDataSize);
                  if (!((*lppPropArray)[i].Value.MVi.lpi))
                  {
                      DebugTrace(TEXT("Error allocating memory\n"));
                      hr = MAPI_E_NOT_ENOUGH_MEMORY;
                      goto out;
                  }
                  (*lppPropArray)[i].Value.MVi.cValues = ulcValues;
                  CopyMemory((*lppPropArray)[i].Value.MVi.lpi, lp, ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_MV_BINARY):
                  (*lppPropArray)[i].Value.MVbin.lpbin = LocalAlloc(LMEM_ZEROINIT, ulcValues * sizeof(SBinary));
                  if (!((*lppPropArray)[i].Value.MVbin.lpbin))
                  {
                      DebugTrace(TEXT("Error allocating memory\n"));
                      hr = MAPI_E_NOT_ENOUGH_MEMORY;
                      goto out;
                  }
                  (*lppPropArray)[i].Value.MVbin.cValues = ulcValues;
                  for (j=0;j<ulcValues;j++)
                  {
                      ULONG nLen;
                      CopyMemory(&nLen, lp, sizeof(ULONG));
                      lp += sizeof(ULONG);
                      (*lppPropArray)[i].Value.MVbin.lpbin[j].cb = nLen;
                      (*lppPropArray)[i].Value.MVbin.lpbin[j].lpb = LocalAlloc(LMEM_ZEROINIT, nLen);
                      if (!((*lppPropArray)[i].Value.MVbin.lpbin[j].lpb))
                      {
                          DebugTrace(TEXT("Error allocating memory\n"));
                          hr = MAPI_E_NOT_ENOUGH_MEMORY;
                          goto out;
                      }
                      CopyMemory((*lppPropArray)[i].Value.MVbin.lpbin[j].lpb, lp, nLen);
                      lp += nLen;
                  }
                  // hack: we want to upgrade the old WAB_FOLDER_PARENT_OLDPROP props to a new WAB_FOLDER_PARENT
                  // This is the best place to do it and the check only happens on contacts with MV_BINARY props
                  if((*lppPropArray)[i].ulPropTag == PR_WAB_FOLDER_PARENT_OLDPROP && PR_WAB_FOLDER_PARENT)
                      (*lppPropArray)[i].ulPropTag = PR_WAB_FOLDER_PARENT;
                  break;
  
              case(PT_MV_STRING8):
                  (*lppPropArray)[i].Value.MVszA.lppszA = LocalAlloc(LMEM_ZEROINIT, ulcValues * sizeof(LPSTR));
                  if (!((*lppPropArray)[i].Value.MVszA.lppszA))
                  {
                      DebugTrace(TEXT("Error allocating memory\n"));
                      hr = MAPI_E_NOT_ENOUGH_MEMORY;
                      goto out;
                  }
                  for (j=0;j<ulcValues;j++)
                  {
                      ULONG nLen;
                      CopyMemory(&nLen, lp, sizeof(ULONG));
                      lp += sizeof(ULONG);
                      (*lppPropArray)[i].Value.MVszA.lppszA[j] = LocalAlloc(LMEM_ZEROINIT, nLen);
                      if (!((*lppPropArray)[i].Value.MVszA.lppszA[j]))
                      {
                          DebugTrace(TEXT("Error allocating memory\n"));
                          hr = MAPI_E_NOT_ENOUGH_MEMORY;
                          goto out;
                      }
                      CopyMemory((*lppPropArray)[i].Value.MVszA.lppszA[j], lp, nLen);
                      lp += nLen;
                  }
                  (*lppPropArray)[i].Value.MVszA.cValues = ulcValues;
                  break;
  
              case(PT_MV_UNICODE):
                  (*lppPropArray)[i].Value.MVszW.lppszW = LocalAlloc(LMEM_ZEROINIT, ulcValues * sizeof(LPTSTR));
                  if (!((*lppPropArray)[i].Value.MVszW.lppszW))
                  {
                      DebugTrace(TEXT("Error allocating memory\n"));
                      hr = MAPI_E_NOT_ENOUGH_MEMORY;
                      goto out;
                  }
                  for (j=0;j<ulcValues;j++)
                  {
                      ULONG nLen;
                      CopyMemory(&nLen, lp, sizeof(ULONG));
                      lp += sizeof(ULONG);
                      (*lppPropArray)[i].Value.MVszW.lppszW[j] = LocalAlloc(LMEM_ZEROINIT, nLen);
                      if (!((*lppPropArray)[i].Value.MVszW.lppszW[j]))
                      {
                          DebugTrace(TEXT("Error allocating memory\n"));
                          hr = MAPI_E_NOT_ENOUGH_MEMORY;
                          goto out;
                      }
                      CopyMemory((*lppPropArray)[i].Value.MVszW.lppszW[j], lp, nLen);
                      lp += nLen;
                  }
                  (*lppPropArray)[i].Value.MVszW.cValues = ulcValues;
                  break;
  
              default:
                  DebugTrace(TEXT("Unknown Prop Type\n"));
                  break;
              }
          }
          else
          {
              //Single Valued
  
              CopyMemory(&ulDataSize,lp,sizeof(ULONG));
              lp+=sizeof(ULONG);
  
              if(ulDataSize > cbBuf)
              {
                  hr = MAPI_E_CORRUPT_DATA;
                  goto out;
              }
  
              switch(PROP_TYPE((*lppPropArray)[i].ulPropTag))
              {
              case(PT_CLSID):
                  (*lppPropArray)[i].Value.lpguid = (LPGUID) LocalAlloc(LMEM_ZEROINIT,ulDataSize);
                  if (!((*lppPropArray)[i].Value.lpguid))
                  {
                      DebugTrace(TEXT("Error allocating memory\n"));
                      hr = MAPI_E_NOT_ENOUGH_MEMORY;
                      goto out;
                  }
                  CopyMemory((*lppPropArray)[i].Value.lpguid, lp, ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_STRING8):
                  (*lppPropArray)[i].Value.lpszA = (LPSTR) LocalAlloc(LMEM_ZEROINIT,ulDataSize);
                  if (!((*lppPropArray)[i].Value.lpszA))
                  {
                      DebugTrace(TEXT("Error allocating memory\n"));
                      hr = MAPI_E_NOT_ENOUGH_MEMORY;
                      goto out;
                  }
                  CopyMemory((*lppPropArray)[i].Value.lpszA, lp, ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_UNICODE):
                  (*lppPropArray)[i].Value.lpszW = (LPTSTR) LocalAlloc(LMEM_ZEROINIT,ulDataSize);
                  if (!((*lppPropArray)[i].Value.lpszW))
                  {
                      DebugTrace(TEXT("Error allocating memory\n"));
                      hr = MAPI_E_NOT_ENOUGH_MEMORY;
                      goto out;
                  }
                  CopyMemory((*lppPropArray)[i].Value.lpszW, lp, ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_BINARY):
                  (*lppPropArray)[i].Value.bin.lpb = LocalAlloc(LMEM_ZEROINIT,ulDataSize);
                  if (!((*lppPropArray)[i].Value.bin.lpb))
                  {
                      DebugTrace(TEXT("Error allocating memory\n"));
                      hr = MAPI_E_NOT_ENOUGH_MEMORY;
                      goto out;
                  }
                  (*lppPropArray)[i].Value.bin.cb = ulDataSize;
                  CopyMemory((*lppPropArray)[i].Value.bin.lpb, lp, ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_SHORT):
                  CopyMemory(&((*lppPropArray)[i].Value.i),lp,ulDataSize);
                  lp += ulDataSize;
                  break;
  
  
              case(PT_LONG):
                  CopyMemory(&((*lppPropArray)[i].Value.l),lp,ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_R4):
                  CopyMemory(&((*lppPropArray)[i].Value.flt),lp,ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_DOUBLE):
              case(PT_APPTIME):
                  CopyMemory(&((*lppPropArray)[i].Value.dbl),lp,ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_BOOLEAN):
                  CopyMemory(&((*lppPropArray)[i].Value.b),lp,ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_SYSTIME):
                  CopyMemory(&((*lppPropArray)[i].Value.ft),lp,ulDataSize);
                  lp += ulDataSize;
                  break;
  
              case(PT_CURRENCY):
                  CopyMemory(&((*lppPropArray)[i].Value.cur),lp,ulDataSize);
                  lp += ulDataSize;
                  break;
  
              default:
                  DebugTrace(TEXT("Unknown Prop Type\n"));
                  CopyMemory(&((*lppPropArray)[i].Value.i),lp,ulDataSize);
                  lp += ulDataSize;
                  break;
              }
  
          }
      }
  
  
      hr = S_OK;
  
  out:
  
      if (FAILED(hr))
      {
          if ((*lppPropArray) && (ulcPropCount > 0))
          {
              LocalFreePropArray(NULL, ulcPropCount, lppPropArray);
              *lppPropArray = NULL;
          }
      }
  
  //    _DebugProperties(*lppPropArray, ulcPropCount, TEXT("GetPropArrayFromBuffer"));
  
      return hr;
  
  }
  
  
  //$$////////////////////////////////////////////////////////////////////////////////
  //
  // HrGetPropArrayFromFileRecord - goes into a file, reads the record at the
  //      given offset, and turns the record data into a lpPropArray
  //
  // IN
  // hFile - the WAB file to read from
  // ulOffset - the record offset within the file
  //
  // OUT
  // ulcValues - # of props in the prop array
  // lpPropArray - the prop array from the record
  //
  // Returns
  //      E_FAIL, S_OK,
  //      MAPI_E_INVALID_OBJECT
  //      MAPI_E_CORRUPT_DATA
  //      MAPI_E_NOT_ENOUGH_MEMORY
  //
  ////////////////////////////////////////////////////////////////////////////////////
  HRESULT HrGetPropArrayFromFileRecord(HANDLE hMPSWabFile,
                                       ULONG ulRecordOffset,
                                       BOOL * lpbErrorDetected,
                                       ULONG * lpulObjType,
                                       ULONG * lpulRecordSize,
                                       ULONG * lpulcPropCount,
                                       LPSPropValue * lppPropArray)
  {
      HRESULT hr = S_OK;
      MPSWab_RECORD_HEADER MPSWabRecordHeader = {0};
      DWORD dwNumofBytes = 0;
      LPBYTE szBuf = NULL;
      LPBYTE lp = NULL;
      ULONG i = 0,j=0, k=0;
      ULONG nIndexPos = 0;
      BOOL bErrorDetected = FALSE;
  
      if(!ReadDataFromWABFile(hMPSWabFile,
                              ulRecordOffset,
                              (LPVOID) &MPSWabRecordHeader,
                              (DWORD) sizeof(MPSWab_RECORD_HEADER)))
         goto out;
  
  
      // Its important that we get the record size first because a
      // calling client may depend on the size to move to the
      // next record if they want to skip invalid ones.
      if(lpulRecordSize)
      {
          *lpulRecordSize = sizeof(MPSWab_RECORD_HEADER) +
                              MPSWabRecordHeader.ulPropTagArraySize +
                              MPSWabRecordHeader.ulRecordDataSize;
      }
  
      if(lpulObjType)
      {
          *lpulObjType = MPSWabRecordHeader.ulObjType;
      }
  
      if(!bIsValidRecord( MPSWabRecordHeader,
                          0xFFFFFFFF,
                          ulRecordOffset,
                          GetFileSize(hMPSWabFile,NULL)))
      {
          //this should never happen but who knows
          DebugTrace(TEXT("Error: Obtained an invalid record ...\n"));
          hr = MAPI_E_INVALID_OBJECT;
          goto out;
      }
  
      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;
      }
  
      if(HR_FAILED(hr = HrGetPropArrayFromBuffer( szBuf, 
                                                  MPSWabRecordHeader.ulRecordDataSize, 
                                                  MPSWabRecordHeader.ulcPropCount, 0,
                                                  lppPropArray) ))
      {
          goto out;
      }
  
      *lpulcPropCount = MPSWabRecordHeader.ulcPropCount;
  
      hr = S_OK;
  
  out:
  
      // if this is a container, make sure its object type is correct
      if(!HR_FAILED(hr) && MPSWabRecordHeader.ulObjType == RECORD_CONTAINER)
          SetContainerObjectType(*lpulcPropCount, *lppPropArray, FALSE);
  
      if(hr == MAPI_E_CORRUPT_DATA)
          bErrorDetected = TRUE;
  
      if (lpbErrorDetected)
          *lpbErrorDetected = bErrorDetected;
  
      LocalFreeAndNull(&szBuf);
  
      return hr;
  }
  
  
  //$$////////////////////////////////////////////////////////////////////////////
  //
  // Space saver function for writing data to the file
  //
  ////////////////////////////////////////////////////////////////////////////////
  BOOL WriteDataToWABFile(HANDLE hMPSWabFile,
                             ULONG ulOffset,
                             LPVOID lpData,
                             ULONG ulDataSize)
  {
      DWORD dwNumofBytes = 0;
  
      if (0xFFFFFFFF != SetFilePointer (  hMPSWabFile,
                                          ulOffset,
                                          NULL,
                                          FILE_BEGIN))
      {
           return WriteFile(  hMPSWabFile,
                          lpData,
                          (DWORD) ulDataSize,
                          &dwNumofBytes,
                          NULL);
      }
  
      return FALSE;
  }
  
  //$$////////////////////////////////////////////////////////////////////////////////
  //
  // Space saver function for reading data from the file
  //
  ////////////////////////////////////////////////////////////////////////////////////
  BOOL ReadDataFromWABFile(HANDLE hMPSWabFile,
                             ULONG ulOffset,
                             LPVOID lpData,
                             ULONG ulDataSize)
  {
      DWORD dwNumofBytes = 0;
  
      if (0xFFFFFFFF != SetFilePointer (  hMPSWabFile,
                                          ulOffset,
                                          NULL,
                                          FILE_BEGIN))
      {
          return ReadFile(hMPSWabFile,
                          lpData,
                          (DWORD) ulDataSize,
                          &dwNumofBytes,
                          NULL);
      }
  
      return FALSE;
  }
  
  
  //$$****************************************************************************
  //
  // FreeGuidnamedprops - frees a GUID_NAMED_PROPS array
  //
  // ulcGUIDCount - number of elements in the lpgnp array
  // lpgnp - arry of GUID_NAMED_PROPS
  //****************************************************************************//
  void FreeGuidnamedprops(ULONG ulcGUIDCount,
                          LPGUID_NAMED_PROPS lpgnp)
  {
      ULONG i=0,j=0;
  
      if(lpgnp)
      {
  //        for(i=ulcGUIDCount;i>0;--i)
          for (i = 0; i < ulcGUIDCount; i++)
          {
              if(lpgnp[i].lpnm)
              {
  //                for(j=lpgnp[i].cValues;j>0;--j)
                  for (j = 0; j < lpgnp[i].cValues; j++)
                  {
                      LocalFreeAndNull(&lpgnp[i].lpnm[j].lpsz);
                  }
                  LocalFreeAndNull(&lpgnp[i].lpnm);
              }
              LocalFreeAndNull(&lpgnp[i].lpGUID);
          }
          LocalFreeAndNull(&lpgnp);
      }
      return;
  }
  
  
  //$$////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // OpenWABFile - Opens the WAB file. If file is missing, restores from backup. If backup is missing
  //                  creates a new file
  //
  //  hWndParent - parent for opening message boxes - no message boxesif null
  //  lphMPSWabFile - returned file pointer
  //
  ////////////////////////////////////////////////////////////////////////////////////////////////////
  HRESULT OpenWABFile(LPTSTR lpszFileName, HWND hWndParent, HANDLE * lphMPSWabFile)
  {
      HANDLE hMPSWabFile = NULL;
      TCHAR szBackupFileName[MAX_PATH];
      WIN32_FIND_DATA wfd = {0};
      HANDLE hff = NULL;
      HRESULT hr = E_FAIL;
      DWORD dwErr = 0;
  
      hMPSWabFile = CreateFile(   lpszFileName,
                                  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)
      {
          hr = S_OK;
          goto out;
      }
  
      // Something happened find out what..
      dwErr = GetLastError();
  
      if(dwErr == ERROR_ACCESS_DENIED)
      {
          hr = MAPI_E_NO_ACCESS;
          goto out;
      }
  
      if(dwErr != ERROR_FILE_NOT_FOUND)
      {
          hr = E_FAIL;
          goto out;
      }
  
      // Get the backup file name
      szBackupFileName[0]='\0';
      GetWABBackupFileName(lpszFileName, szBackupFileName, ARRAYSIZE(szBackupFileName));
  
      hff = FindFirstFile(szBackupFileName,&wfd);
  
      if(hff != INVALID_HANDLE_VALUE)
      {
          // we found the backup file
          // copy it into the wab file name
          CopyFile(szBackupFileName, lpszFileName, FALSE);
  
          hMPSWabFile = CreateFile(   lpszFileName,
                                      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)
          {
              hr = S_OK;
              goto out;
          }
  
      }
  
      // if we're still here .. nothing worked .. so create a new file
      {
          MPSWab_FILE_HEADER MPSWabFileHeader;
  
          if(CreateMPSWabFile(IN &MPSWabFileHeader,
                              lpszFileName,
                              MAX_INITIAL_INDEX_ENTRIES,
                              NAMEDPROP_STORE_SIZE))
          {
              hMPSWabFile = CreateFile(   lpszFileName,
                                          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)
                  hr = S_OK;
          }
      }
  
  
  out:
      *lphMPSWabFile = hMPSWabFile;
  
      if(hff)
          FindClose(hff);
  
      return hr;
  }
  
  //$$/////////////////////////////////////////////////////////////////////////////////
  //
  //
  // nCountSubStrings - counts space-delimted substrings in a given string
  //
  //
  /////////////////////////////////////////////////////////////////////////////////////
  int nCountSubStrings(LPTSTR lpszSearchStr)
  {
      int nSubStr = 0;
      LPTSTR lpTemp = lpszSearchStr;
      LPTSTR lpStart = lpszSearchStr;
  
      if (!lpszSearchStr)
          goto out;
  
      if (!lstrlen(lpszSearchStr))
          goto out;
  
      TrimSpaces(lpszSearchStr);
  
      // Count the spaces
      while(*lpTemp)
      {
          if (IsSpace(lpTemp) &&
            ! IsSpace(CharNext(lpTemp))) {
              nSubStr++;
          }
          lpTemp = CharNext(lpTemp);
      }
  
      // Number of substrings is 1 more than number of spaces
      nSubStr++;
  
  out:
      return nSubStr;
  }
  
  #define DONTFIND 0
  #define DOFIND   1
  #define NOTFOUND 0
  #define FOUND    1
  
  extern BOOL SubstringSearchEx(LPTSTR pszTarget, LPTSTR pszSearch, LCID lcid);
  extern int my_atoi(LPTSTR lpsz);
  //$$////////////////////////////////////////////////////////////////////////////////
  //
  // HrDoLocalWABSearch
  //
  //
  //
  //
  //
  ////////////////////////////////////////////////////////////////////////////////////
  HRESULT HrDoLocalWABSearch( IN  HANDLE hPropertyStore,
                              IN  LPSBinary lpsbCont, //container entryid for Outlook stores
                              IN  LDAP_SEARCH_PARAMS LDAPsp,
                              OUT LPULONG lpulFoundCount,
                              OUT LPSBinary * lprgsbEntryIDs )
  {
  
      int bFindName = DONTFIND;
      int bFindEmail = DONTFIND;
      int bFindAddress = DONTFIND;
      int bFindPhone = DONTFIND;
      int bFindOther = DONTFIND;
  
      LCID lcid = 0;
      int nUseLCID = 0;
      TCHAR szUseLCID[2];
  
      int bFoundName = NOTFOUND;
      int bFoundEmail = NOTFOUND;
      int bFoundAddress = NOTFOUND;
      int bFoundPhone = NOTFOUND;
      int bFoundOther = NOTFOUND;
  
      ULONG nSubStr[ldspMAX];
      LPTSTR * lppszSubStr[ldspMAX];
  
      HRESULT hr = E_FAIL;
      SPropertyRestriction PropRes;
      ULONG   ulPropCount = 0;
      ULONG   ulEIDCount = 0;
      LPSBinary rgsbEntryIDs = NULL;
  
      LPSPropValue lpPropArray = NULL;
      ULONG ulcPropCount = 0;
  
      ULONG i,j,k;
      ULONG ulFoundIndex = 0;
      BOOL bFileLocked = FALSE;
      BOOL bFound = FALSE;
  
      LPMPSWab_FILE_INFO lpMPSWabFileInfo = (LPMPSWab_FILE_INFO) hPropertyStore;
      HANDLE hMPSWabFile = NULL;
  
      LPPTGDATA lpPTGData=GetThreadStoragePointer();
      ULONG cchSize;
  
      if(!lpulFoundCount || !lprgsbEntryIDs)
          goto out;
  
      *lpulFoundCount = 0;
      *lprgsbEntryIDs = NULL;
  
      LoadString(hinstMapiX, idsUseLCID, szUseLCID, CharSizeOf(szUseLCID));
      nUseLCID = my_atoi(szUseLCID);
      if(nUseLCID)
          lcid = GetUserDefaultLCID();
  
      if(lstrlen(LDAPsp.szData[ldspDisplayName]))
          bFindName = DOFIND;
      if(lstrlen(LDAPsp.szData[ldspEmail]))
          bFindEmail = DOFIND;
      if(lstrlen(LDAPsp.szData[ldspAddress]))
          bFindAddress = DOFIND;
      if(lstrlen(LDAPsp.szData[ldspPhone]))
          bFindPhone = DOFIND;
      if(lstrlen(LDAPsp.szData[ldspOther]))
          bFindOther = DOFIND;
  
      if (bFindName +bFindEmail +bFindPhone +bFindAddress +bFindOther == 0)
          goto out;
  
      for(i=0;i<ldspMAX;i++)
      {
  
          nSubStr[i] = (ULONG) nCountSubStrings(LDAPsp.szData[i]);
  
          if(!nSubStr[i])
          {
              lppszSubStr[i] = NULL;
              continue;
          }
  
          lppszSubStr[i] = LocalAlloc(LMEM_ZEROINIT, sizeof(LPTSTR) * nSubStr[i]);
          if(!lppszSubStr[i])
          {
              hr = MAPI_E_NOT_ENOUGH_MEMORY;
              goto out;
          }
  
          {
              // Fill in the substrings
              ULONG nIndex = 0;
              LPTSTR lpTemp = NULL;
              LPTSTR lpStart = NULL;
              TCHAR szBuf[MAX_UI_STR];
  
              StrCpyN(szBuf, LDAPsp.szData[i], ARRAYSIZE(szBuf));
              lpTemp = szBuf;
              lpStart = szBuf;
  
              // Bug 2558 - filter out commas from display name
              if(i == ldspDisplayName)
              {
                  while(lpTemp && *lpTemp)
                  {
                      if(*lpTemp == ',')
                          *lpTemp = ' ';
                      lpTemp++;
                  }
                  lpTemp = szBuf;
              }
  
              while(*lpTemp)
              {
                  if (IsSpace(lpTemp) &&
                    ! IsSpace(CharNext(lpTemp))) {
                      LPTSTR lpNextString = CharNext(lpTemp);
                      *lpTemp = '\0';
                      lpTemp = lpNextString;
  
                      cchSize = lstrlen(lpStart)+1;
                      lppszSubStr[i][nIndex] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
                      if(!lppszSubStr[i][nIndex])
                      {
                          hr = MAPI_E_NOT_ENOUGH_MEMORY;
                          goto out;
                      }
                      StrCpyN(lppszSubStr[i][nIndex], lpStart, cchSize);
                      lpStart = lpTemp;
                      nIndex++;
                  }
                  else
                      lpTemp = CharNext(lpTemp);
              }
  
              if(nIndex==nSubStr[i]-1)
              {
                  //we're off by one
                  cchSize = lstrlen(lpStart)+1;
                  lppszSubStr[i][nIndex] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
                  if(!lppszSubStr[i][nIndex])
                  {
                      hr = MAPI_E_NOT_ENOUGH_MEMORY;
                      goto out;
                  }
                  StrCpyN(lppszSubStr[i][nIndex], lpStart, cchSize);
              }
  
              for(j=0;j<nSubStr[i];j++)
                  TrimSpaces(lppszSubStr[i][j]);
  
          }
      } // for i ...
  
  
      if(!pt_bIsWABOpenExSession)
      {
          // Lock the file
          if(!LockFileAccess(lpMPSWabFileInfo))
          {
              DebugTrace(TEXT("LockFileAccess Failed\n"));
              hr = MAPI_E_NO_ACCESS;
              goto out;
          }
          else
          {
              bFileLocked = TRUE;
          }
      }
  
      // Get an index of all entries in the WAB
      PropRes.ulPropTag = PR_DISPLAY_NAME;
      PropRes.relop = RELOP_EQ;
      PropRes.lpProp = NULL;
  
  
      hr = FindRecords(   IN  hPropertyStore,
  						IN  lpsbCont,
                          IN  AB_MATCH_PROP_ONLY,
                          FALSE,
                          &PropRes,
                          &ulEIDCount,
                          &rgsbEntryIDs);
  
      ulFoundIndex = 0;
  
      if(!pt_bIsWABOpenExSession)
      {
          hr = OpenWABFile(lpMPSWabFileInfo->lpszMPSWabFileName, NULL, &hMPSWabFile);
  
          if (    (hMPSWabFile == INVALID_HANDLE_VALUE) ||
                  HR_FAILED(hr))
          {
              DebugTrace(TEXT("Could not open file.\nExiting ...\n"));
              goto out;
          }
      }
  
      for(i=0;i<ulEIDCount;i++)
      {
          if(!pt_bIsWABOpenExSession)
          {
              DWORD dwEID = 0;
              CopyMemory(&dwEID, rgsbEntryIDs[i].lpb, rgsbEntryIDs[i].cb);
              hr = ReadRecordWithoutLocking(
                              hMPSWabFile,
                              lpMPSWabFileInfo,
                              dwEID,
                              &ulcPropCount,
                              &lpPropArray);
          }
          else
          {
              hr = ReadRecord(hPropertyStore,
                              &rgsbEntryIDs[i],
                              0,
                              &ulcPropCount,
                              &lpPropArray);
          }
  
          if(HR_FAILED(hr))
              goto endloop;
  
          bFoundName = NOTFOUND;
          bFoundEmail = NOTFOUND;
          bFoundAddress = NOTFOUND;
          bFoundPhone = NOTFOUND;
          bFoundOther = NOTFOUND;
  
          for(j=0;j<ulcPropCount;j++)
          {
              switch(lpPropArray[j].ulPropTag)
              {
              case PR_DISPLAY_NAME:
              case PR_GIVEN_NAME:
              case PR_SURNAME:
              case PR_NICKNAME:
              case PR_MIDDLE_NAME:
              case PR_COMPANY_NAME:
                  if(bFindName == DONTFIND)
                      continue;
                  if(bFoundName == FOUND)
                      continue;
                  bFound = TRUE;
                  for(k=0;k<nSubStr[ldspDisplayName];k++)
                  {
                      if(!SubstringSearchEx(lpPropArray[j].Value.LPSZ, lppszSubStr[ldspDisplayName][k],lcid))
                      {
                          bFound = FALSE;
                          break;
                      }
                  }
                  if(bFound)
                  {
                      bFoundName = FOUND;
                      continue;
                  }
                  break;
  
              case PR_EMAIL_ADDRESS:
              case PR_ADDRTYPE:
                  if(bFindEmail == DONTFIND)
                      continue;
                  if(bFoundEmail == FOUND)
                      continue;
                  bFound = TRUE;
                  for(k=0;k<nSubStr[ldspEmail];k++)
                  {
                      if(!SubstringSearchEx(lpPropArray[j].Value.LPSZ, lppszSubStr[ldspEmail][k],lcid))
                      {
                          bFound = FALSE;
                          break;
                      }
                  }
                  if(bFound)
                  {
                      bFoundEmail = FOUND;
                      continue;
                  }
                  break;
  
              case PR_HOME_ADDRESS_STREET:
              case PR_HOME_ADDRESS_CITY:
              case PR_HOME_ADDRESS_POSTAL_CODE:
              case PR_HOME_ADDRESS_STATE_OR_PROVINCE:
              case PR_HOME_ADDRESS_COUNTRY:
              case PR_BUSINESS_ADDRESS_STREET:
              case PR_BUSINESS_ADDRESS_CITY:
              case PR_BUSINESS_ADDRESS_POSTAL_CODE:
              case PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE:
              case PR_BUSINESS_ADDRESS_COUNTRY:
                  if(bFindAddress == DONTFIND)
                      continue;
                  if(bFoundAddress == FOUND)
                      continue;
                  bFound = TRUE;
                  for(k=0;k<nSubStr[ldspAddress];k++)
                  {
                      if(!SubstringSearchEx(lpPropArray[j].Value.LPSZ, lppszSubStr[ldspAddress][k],lcid))
                      {
                          bFound = FALSE;
                          break;
                      }
                  }
                  if(bFound)
                  {
                      bFoundAddress = FOUND;
                      continue;
                  }
                  break;
  
              case PR_HOME_TELEPHONE_NUMBER:
              case PR_HOME_FAX_NUMBER:
              case PR_CELLULAR_TELEPHONE_NUMBER:
              case PR_BUSINESS_TELEPHONE_NUMBER:
              case PR_BUSINESS_FAX_NUMBER:
              case PR_PAGER_TELEPHONE_NUMBER:
                  if(bFindPhone == DONTFIND)
                      continue;
                  if(bFoundPhone == FOUND)
                      continue;
                  bFound = TRUE;
                  for(k=0;k<nSubStr[ldspPhone];k++)
                  {
                      if(!SubstringSearchEx(lpPropArray[j].Value.LPSZ, lppszSubStr[ldspPhone][k],lcid))
                      {
                          bFound = FALSE;
                          break;
                      }
                  }
                  if(bFound)
                  {
                      bFoundPhone = FOUND;
                      continue;
                  }
                  break;
              case PR_TITLE:
              case PR_DEPARTMENT_NAME:
              case PR_OFFICE_LOCATION:
              case PR_COMMENT:
              case PR_BUSINESS_HOME_PAGE:
              case PR_PERSONAL_HOME_PAGE:
                  if(bFindOther == DONTFIND)
                      continue;
                  if(bFoundOther == FOUND)
                      continue;
                  bFound = TRUE;
                  for(k=0;k<nSubStr[ldspOther];k++)
                  {
                      if(!SubstringSearchEx(lpPropArray[j].Value.LPSZ, lppszSubStr[ldspOther][k],lcid))
                      {
                          bFound = FALSE;
                          break;
                      }
                  }
                  if(bFound)
                  {
                      bFoundOther = FOUND;
                      continue;
                  }
                  break;
              } //switch
          }// for j
  
          if ((bFindName +bFindEmail +bFindPhone +bFindAddress +bFindOther) !=
              (bFoundName+bFoundEmail+bFoundPhone+bFoundAddress+bFoundOther))
              goto endloop;
  
          // doublecheck that we didnt get a container entry here
          for(j=0;j<ulcPropCount;j++)
          {
              if( lpPropArray[j].ulPropTag == PR_OBJECT_TYPE)
              {
                  if(lpPropArray[j].Value.l == MAPI_ABCONT)
                      goto endloop;
                  break;
              }
          }
  
          // match!
          CopyMemory(rgsbEntryIDs[ulFoundIndex].lpb, rgsbEntryIDs[i].lpb, rgsbEntryIDs[i].cb);
          ulFoundIndex++;
  
  endloop:
          if(ulcPropCount && lpPropArray)
          {
              ReadRecordFreePropArray(hPropertyStore, ulcPropCount, &lpPropArray);
              lpPropArray = NULL;
          }
      } // for i
  
  
      if(ulFoundIndex)
      {
          *lpulFoundCount = ulFoundIndex;
          *lprgsbEntryIDs = rgsbEntryIDs;
      }
  
      hr = S_OK;
  
  out:
  
      ReadRecordFreePropArray(hPropertyStore, ulcPropCount, &lpPropArray);
  
      for(i=0;i<ldspMAX;i++)
      {
          if(lppszSubStr[i])
          {
              for(j=0;j<nSubStr[i];j++)
                  LocalFree(lppszSubStr[i][j]);
              LocalFree(lppszSubStr[i]);
          }
      }
  
      if(!*lpulFoundCount || !*lprgsbEntryIDs)
      {
          if(rgsbEntryIDs)
              FreeEntryIDs(hPropertyStore, ulEIDCount, rgsbEntryIDs);
      }
      else if(ulFoundIndex && (ulFoundIndex < ulEIDCount) && !pt_bIsWABOpenExSession)
      {
          // We will leak anything we are not using here so clear up before exiting
          // Do this only if this is a WAB session because then that memory was LocalAlloced
          // and can be partially freed up here
          for(i=ulFoundIndex;i<ulEIDCount;i++)
          {
              if(rgsbEntryIDs[i].lpb)
                  LocalFree(rgsbEntryIDs[i].lpb);
          }
      }
  
      if(!pt_bIsWABOpenExSession)
      {
          if(hMPSWabFile)
          IF_WIN32(CloseHandle(hMPSWabFile);) IF_WIN16(CloseFile(hMPSWabFile);)
      }
  
      if(!pt_bIsWABOpenExSession && bFileLocked)
          UnLockFileAccess(lpMPSWabFileInfo);
  
      return hr;
  }
  
  
  
  //$$
  //
  // Determines if the disk on which WAB resides has free space or not..
  // Free space is defined as space being equal too or more than the size
  // of the current WAB file. If the available space is less than the size
  // of the WAB file, this function will return false ...
  //
  BOOL WABHasFreeDiskSpace(LPTSTR lpszName, HANDLE hFile)
  {
      TCHAR szBuf[MAX_PATH];
      DWORD dwWABSize=0;
      DWORDLONG dwDiskFreeSpace = 0;
      DWORD SectorsPerCluster=0;
      DWORD BytesPerSector=0;
      DWORD NumberOfFreeClusters=0;
      DWORD TotalNumberOfClusters=0;
      BOOL bRet = TRUE;
  
      szBuf[0]='\0';
      StrCpyN(szBuf, lpszName, ARRAYSIZE(szBuf));
      TrimSpaces(szBuf);
      if(lstrlen(szBuf))
      {
          dwWABSize = GetFileSize(hFile, NULL);
  
          {
              LPTSTR lpszFirst = szBuf;
              LPTSTR lpszSecond = CharNext(szBuf);
              LPTSTR lpRoot = NULL;
              LPTSTR lpTemp;
              ULONG ulCount = 0;
  
              if(*lpszFirst == '\\' && *lpszSecond == '\\')
              {
                  // This looks like a network share ..
                  // There doesnt seem to be any way to determine disk space
                  // on a network share .. so we will try to copy the wab
                  // file into a tmp file and delete the tmp file. If this operation
                  // succeeds we have plenty of disk space. If it fails we dont have any
                  // space
                  TCHAR szTmp[MAX_PATH];
                  StrCpyN(szTmp, szBuf, ARRAYSIZE(szTmp));
  
                  // our temp file name is the wab file name with a - instead of the last char
                  lpTemp = szTmp;
                  while(*lpTemp)
                      lpTemp = CharNext(lpTemp);
                  lpTemp = CharPrev(szTmp, lpTemp);
                  if(*lpTemp != '-')
                      *lpTemp = '-';
                  else
                      *lpTemp = '_';
  
                  if(!CopyFile(szBuf, szTmp, FALSE))
                  {
                      bRet = FALSE;
                  }
                  else
                      DeleteFile(szTmp);
                  /***
                  lpTemp = CharNext(lpszSecond);
                  while(*lpTemp)
                  {
                      if(*lpTemp == '\\')
                      {
                          ulCount++;
                          if (ulCount == 1)
                          {
                              //lpTemp=CharNext(lpTemp);
                              *lpTemp = '\0';
                              break;
                          }
                      }
                      lpTemp = CharNext(lpTemp);
                  }
                  ***/
              }
              else
              {
                  if(*lpszSecond == ':')
                  {
                      lpTemp = CharNext(lpszSecond);
                      if(*lpTemp != '\\')
                          *lpTemp = '\0';
                      else
                      {
                          lpTemp = CharNext(lpTemp);
                          *lpTemp = '\0';
                      }
                  }
                  else
                  {
                      *lpszFirst = '\0';
                  }
                  if(lstrlen(szBuf))
                      lpRoot = szBuf;
                  if( GetDiskFreeSpace(lpRoot,
                                      &SectorsPerCluster,	// address of sectors per cluster
                                      &BytesPerSector,	// address of bytes per sector
                                      &NumberOfFreeClusters,	// address of number of free clusters
                                      &TotalNumberOfClusters 	// address of total number of clusters
                                      ) )
                  {
                      dwDiskFreeSpace = BytesPerSector * SectorsPerCluster * NumberOfFreeClusters;
  
                      if(dwDiskFreeSpace < ((DWORDLONG) dwWABSize) )
                          bRet = FALSE;
                  }
                  else
                  {
                      DebugTrace(TEXT("GetDiskFreeSpace failed: %d\n"),GetLastError());
                  }
              }
          }
  
      }
  
      return bRet;
  }
  
  