windows-nt/Source/XPSP1/NT/shell/ext/brfcase/filesync/syncui/ibrfstg.c

2238 lines
65 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1993-1994
//
// File: ibrfstg.c
//
// This files contains the IBriefcaseStg interface.
//
// History:
// 02-02-94 ScottH Converted from iface.c
//
//---------------------------------------------------------------------------
#include "brfprv.h" // common headers
#undef LODWORD // (because they are redefined by configmg.h)
#undef HIDWORD
#include <brfcasep.h>
#include "recact.h"
#include "res.h"
#include <help.h>
// FEATURE - BobDay - We need some mechanism of determining dock state
//---------------------------------------------------------------------------
// BriefStg Class
//---------------------------------------------------------------------------
// An IBriefcaseStg interface instance is created for each
// folder the caller (the Shell) binds to, where the folder
// is known to be inside a briefcase storage. A briefcase
// storage is the overall storage area (the database) that
// starts at a given folder (called the "briefcase root")
// and extends onwards and below in the file-system.
//
// Internally, the briefcase storage holds the path to the
// folder that this instance is bound to, and it holds a
// cached briefcase structure (CBS), which itself holds a
// reference to the briefcase root.
//
typedef struct _BriefStg
{
IBriefcaseStg bs;
UINT cRef; // reference count
CBS * pcbs; // cached briefcase info
TCHAR szFolder[MAX_PATH]; // canonical path
HBRFCASEITER hbrfcaseiter; // handle to iterate briefcases
DWORD dwFlags; // BSTG_* flags
} BriefStg, * PBRIEFSTG;
// Flags for BriefStg
#define BSTG_SYNCFOLDER 0x00000001 // This folder has a sync copy
//---------------------------------------------------------------------------
// Supporting private code
//---------------------------------------------------------------------------
#ifdef DEBUG
/*----------------------------------------------------------
Purpose: Dump all the cache tables
Returns: --
Cond: --
*/
void PUBLIC DumpTables()
{
Atom_DumpAll();
CBS_DumpAll();
CRL_DumpAll();
}
#endif
/*----------------------------------------------------------
Purpose: Initialize the cache tables
Returns: --
Cond: --
*/
BOOL PRIVATE InitCacheTables()
{
ASSERT(Sync_IsEngineLoaded());
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Initialize cache tables")); )
if (!CBS_Init())
goto Init_Fail;
if (!CRL_Init())
goto Init_Fail;
return TRUE;
Init_Fail:
CRL_Term();
CBS_Term(NULL);
return FALSE;
}
/*----------------------------------------------------------
Purpose: Terminate the cache tables
Returns: --
Cond: --
*/
void PUBLIC TermCacheTables(void)
{
ASSERT(Sync_IsEngineLoaded());
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Terminate cache tables")); )
CRL_Term();
CBS_Term(NULL);
}
/*----------------------------------------------------------
Purpose: Returns TRUE if the path (a folder) has a sync copy.
Returns: see above
Cond: --
*/
BOOL PRIVATE HasFolderSyncCopy(
HBRFCASE hbrf,
LPCTSTR pszPath)
{
ASSERT(pszPath);
ASSERT(PathIsDirectory(pszPath));
return (S_OK == Sync_IsTwin(hbrf, pszPath, SF_ISFOLDER) ||
IsSubfolderTwin(hbrf, pszPath));
}
/*----------------------------------------------------------
Purpose: Open a folder that belongs to a briefcase storage.
The pszPath parameter is a folder, which is not necessarily
the briefcase root.
Returns: NOERROR on success
Cond: --
*/
HRESULT PRIVATE OpenBriefcaseStorage(
LPCTSTR pszPath,
CBS ** ppcbs,
HWND hwndOwner)
{
HRESULT hres;
UINT uLocality;
int atomBrf;
TCHAR szBrfPath[MAX_PATH];
TCHAR szBrfCanon[MAX_PATH];
ASSERT(pszPath);
ASSERT(ppcbs);
DBG_ENTER_SZ(TEXT("OpenBriefcaseStorage"), pszPath);
DEBUG_CODE( DEBUG_BREAK(BF_ONOPEN); )
// Get the root folder of the briefcase storage
// Get strictly up to the briefcase portion of path
//
uLocality = PathGetLocality(pszPath, szBrfPath);
if (PL_FALSE == uLocality)
{
// The only time we get here is if the caller had a legitimate
// reason to believe this folder was a briefcase storage,
// but no database exists (yet). Just continue on as normal,
// the database will get created later.
BrfPathCanonicalize(pszPath, szBrfCanon);
}
else
{
BrfPathCanonicalize(szBrfPath, szBrfCanon);
}
// Add this path to the atom list and add it to the
// cached briefcase structure table.
// (Reference count decrement happens in CloseBriefcase)
//
atomBrf = Atom_Add(szBrfCanon);
if (atomBrf != ATOM_ERR)
{
hres = CBS_Add(ppcbs, atomBrf, hwndOwner);
}
else
{
*ppcbs = NULL;
hres = ResultFromScode(E_OUTOFMEMORY);
}
DEBUG_CODE( DumpTables(); )
DBG_EXIT_HRES(TEXT("OpenBriefcaseStorage"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: Close a briefcase.
Returns: NOERROR on success
Cond: --
*/
HRESULT PRIVATE CloseBriefcaseStorage(
LPCTSTR pszPath)
{
int atomBrf;
TCHAR szBrfPath[MAX_PATH];
TCHAR szBrfCanon[MAX_PATH];
UINT uLocality;
ASSERT(pszPath);
ASSERT(*pszPath); // Should not be an emptry string
DBG_ENTER_SZ(TEXT("CloseBriefcaseStorage"), pszPath);
DEBUG_CODE( DEBUG_BREAK(BF_ONCLOSE); )
DEBUG_CODE( DumpTables(); )
// Save the briefcase and remove it from the cache
//
// Get the root folder of the briefcase storage
// Get strictly up to the briefcase portion of path
//
uLocality = PathGetLocality(pszPath, szBrfPath);
if (PL_FALSE == uLocality)
{
// The only time we get here is for a briefcase storage that
// has no database yet. Just continue on as normal,
// the database will get created very soon now.
BrfPathCanonicalize(pszPath, szBrfCanon);
}
else
{
BrfPathCanonicalize(szBrfPath, szBrfCanon);
}
atomBrf = Atom_Find(szBrfCanon);
ASSERT(atomBrf != ATOM_ERR);
CBS_Delete(atomBrf, NULL);
Atom_Delete(atomBrf); // for the Add in OpenBriefcaseStorage
DBG_EXIT_HRES(TEXT("CloseBriefcaseStorage"), NOERROR);
return NOERROR;
}
// Confirm button flags
#define CBF_YES 0x0001
#define CBF_NO 0x0002
#define CBF_TOALL 0x0004
#define CBF_CANCEL 0x0008
/*----------------------------------------------------------
Purpose: Checks to see if the given file/folder already exists
in the given directory. Prompts the user to confirm
replacing if this is true.
Returns: TRUE if path exists
confirm flag settings
Cond: --
*/
BOOL PRIVATE DoesPathAlreadyExist(
CBS * pcbs,
LPCTSTR pszPathOld,
LPCTSTR pszPathNew,
LPUINT puConfirmFlags, // CBF_*
UINT uFlags, // SF_ISFOLDER or SF_ISFILE
HWND hwndOwner,
BOOL bMultiDrop)
{
BOOL bRet;
BOOL bIsTwin;
ASSERT(puConfirmFlags);
// Retain settings of *puConfirmFlags coming in
bIsTwin = (S_OK == Sync_IsTwin(pcbs->hbrf, pszPathOld, uFlags));
if (bIsTwin)
uFlags |= SF_ISTWIN;
else
uFlags |= SF_ISNOTTWIN;
bRet = (FALSE != PathExists(pszPathOld));
// Does the path already exist?
if (!bRet)
{
// No; remove it from the database if it is in there so we
// don't add duplicates.
Sync_Split(pcbs->hbrf, pszPathOld, 1, hwndOwner, uFlags | SF_QUIET | SF_NOCONFIRM);
}
else
{
// Yes; has a "to all" previously been specified by the user?
if (IsFlagSet(*puConfirmFlags, CBF_TOALL))
{
// Yes; keep flags as they are
// (CBF_YES and CBF_NO flags are mutually exclusive)
ASSERT(IsFlagSet(*puConfirmFlags, CBF_YES) &&
IsFlagClear(*puConfirmFlags, CBF_NO | CBF_CANCEL) ||
IsFlagSet(*puConfirmFlags, CBF_NO) &&
IsFlagClear(*puConfirmFlags, CBF_YES | CBF_CANCEL));
}
else
{
// No; prompt the user
UINT uFlagsCRF = bMultiDrop ? CRF_MULTI : CRF_DEFAULT;
int id = ConfirmReplace_DoModal(hwndOwner, pszPathOld, pszPathNew, uFlagsCRF);
*puConfirmFlags = 0;
if (GetKeyState(VK_SHIFT) < 0)
SetFlag(*puConfirmFlags, CBF_TOALL);
if (IDYES == id)
SetFlag(*puConfirmFlags, CBF_YES);
else if (IDNO == id)
SetFlag(*puConfirmFlags, CBF_NO);
else if (IDC_YESTOALL == id)
SetFlag(*puConfirmFlags, CBF_YES | CBF_TOALL);
else
{
ASSERT(IDCANCEL == id);
SetFlag(*puConfirmFlags, CBF_CANCEL);
}
}
// Has the user chosen to replace the file?
if (IsFlagSet(*puConfirmFlags, CBF_YES))
{
// Yes; is this an existing twin?
if (bIsTwin)
{
// Yes; delete it from the database before we continue
Sync_Split(pcbs->hbrf, pszPathOld, 1, hwndOwner, SF_QUIET | SF_NOCONFIRM);
}
// Some merge-handlers need the unwanted file to be deleted
// first because they cannot tell the difference between
// a newly added file (that is replacing an existing file)
// and a one-way merge.
if (!PathIsDirectory(pszPathOld))
DeleteFile(pszPathOld);
}
}
return bRet;
}
/*----------------------------------------------------------
Purpose: Add the folder twin to the database, using the default
*.* wildcard settings.
Returns: standard result
Cond: --
*/
HRESULT PRIVATE AddDefaultFolderTwin(
HWND hwndOwner,
HBRFCASE hbrf,
HDPA hdpa, // Return: twin handle in array
LPCTSTR pszPathFrom, // Source path
LPCTSTR pszPathTo) // Target path
{
HRESULT hres;
int iTwin;
// First make sure we can add another handle to hdpa (set to zero for now)
if (DPA_ERR == (iTwin = DPA_InsertPtr(hdpa, DPA_APPEND, (LPVOID)NULL)))
{
hres = E_OUTOFMEMORY;
}
else
{
NEWFOLDERTWIN nft;
TWINRESULT tr;
HFOLDERTWIN hft;
RETRY_BEGIN(FALSE)
{
ZeroInit(&nft, NEWFOLDERTWIN);
nft.ulSize = sizeof(nft);
nft.pcszFolder1 = pszPathFrom;
nft.pcszFolder2 = pszPathTo;
nft.pcszName = c_szAllFiles;
nft.dwAttributes = OBJECT_TWIN_ATTRIBUTES;
nft.dwFlags = NFT_FL_SUBTREE;
// Add the twin
tr = Sync_AddFolder(hbrf, &nft, &hft);
hres = HRESULT_FROM_TR(tr);
if (FAILED(hres))
{
DWORD dwError = GetLastError();
int id;
extern SETbl const c_rgseInfo[4];
// Unavailable disk?
if (ERROR_INVALID_DATA == dwError || ERROR_ACCESS_DENIED == dwError)
{
// Yes
hres = E_TR_UNAVAILABLE_VOLUME;
}
id = SEMsgBox(hwndOwner, IDS_CAP_INFO, hres, c_rgseInfo, ARRAYSIZE(c_rgseInfo));
if (IDRETRY == id)
{
// Try the operation again
RETRY_SET();
}
}
}
RETRY_END()
if (FAILED(hres))
{
DPA_DeletePtr(hdpa, iTwin);
}
else
{
// Success
ASSERT(DPA_ERR != iTwin);
ASSERT(NULL != hft);
DPA_SetPtr(hdpa, iTwin, hft);
}
}
return hres;
}
/*----------------------------------------------------------
Purpose: Create a twin relationship between a folder and
another folder.
Returns: standard hresult
handles to created twins in hdpa
confirm flag settings
Cond: --
*/
HRESULT PRIVATE CreateTwinOfFolder(
CBS * pcbs,
LPTSTR pszPath, // Dragged folder path
LPCTSTR pszDir, // Location to place twin
HDPA hdpaTwin, // array of twin handles
UINT uFlags, // AOF_*
PUINT puConfirmFlags, // CBF_*
HWND hwndOwner,
BOOL bMultiDrop) // TRUE: more than 1 file/folder was dropped
{
HRESULT hres;
TCHAR szPathB[MAX_PATH];
LPTSTR pszFile;
ASSERT(pszPath);
ASSERT(pszDir);
pszFile = PathFindFileName(pszPath);
// Will the path name be too long?
if (PathsTooLong(pszDir, pszFile))
{
// Yes; bail
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_ADDFOLDER_TOOLONG),
MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_ERROR, pszFile);
hres = E_FAIL;
}
// Did the user drag another briefcase root into this briefcase?
else if (PathIsBriefcase(pszPath))
{
// Yes; we don't allow nested briefcases! Tell the user.
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_CANTADDBRIEFCASE),
MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_WARNING);
hres = E_FAIL;
}
else
{
// No; check for an existing folder in the target folder.
BOOL bExists;
PathCombine(szPathB, pszDir, pszFile);
bExists = DoesPathAlreadyExist(pcbs, szPathB, pszPath, puConfirmFlags, SF_ISFOLDER, hwndOwner, bMultiDrop);
if (!bExists || IsFlagSet(*puConfirmFlags, CBF_YES))
{
ASSERT(IsFlagClear(*puConfirmFlags, CBF_NO) &&
IsFlagClear(*puConfirmFlags, CBF_CANCEL));
// Show 'Add Folder' dialog?
if (IsFlagSet(uFlags, AOF_FILTERPROMPT))
{
// Yes
hres = Info_DoModal(hwndOwner, pszPath, szPathB, hdpaTwin,
pcbs);
}
else
{
// No; just default to *.*
hres = AddDefaultFolderTwin(hwndOwner, pcbs->hbrf, hdpaTwin,
pszPath, szPathB);
}
}
else if (IsFlagSet(*puConfirmFlags, CBF_NO))
{
// The user said NO
ASSERT(IsFlagClear(*puConfirmFlags, CBF_YES) &&
IsFlagClear(*puConfirmFlags, CBF_CANCEL));
hres = NOERROR;
}
else
{
ASSERT(IsFlagSet(*puConfirmFlags, CBF_CANCEL));
ASSERT(IsFlagClear(*puConfirmFlags, CBF_YES) &&
IsFlagClear(*puConfirmFlags, CBF_NO));
hres = E_ABORT;
}
}
return hres;
}
/*----------------------------------------------------------
Purpose: Create a twin of a file.
Returns: standard result
twin handle in hdpa
Cond: --
*/
HRESULT PRIVATE CreateTwinOfFile(
CBS * pcbs,
LPCTSTR pszPath, // ptr to path to twin
LPCTSTR pszTargetDir, // ptr to dest dir
HDPA hdpa, // Return: twin handle in array
UINT uFlags, // AOF_*
PUINT puConfirmFlags, // CBF_*
HWND hwndOwner,
BOOL bMultiDrop) // TRUE: more than 1 file/folder was dropped
{
HRESULT hres;
int iTwin;
TCHAR szPath[MAX_PATH];
LPCTSTR pszFile;
HTWINFAMILY htfam = NULL;
ASSERT(pszPath);
ASSERT(pszTargetDir);
pszFile = PathFindFileName(pszPath);
// Will the path name be too long?
if (PathsTooLong(pszTargetDir, pszFile))
{
// Yes; bail
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_ADDFILE_TOOLONG),
MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_ERROR, pszFile);
iTwin = DPA_ERR;
hres = E_FAIL;
}
// First make sure we can add another handle to hdpa (set to zero for now)
else if (DPA_ERR == (iTwin = DPA_InsertPtr(hdpa, DPA_APPEND, (LPVOID)NULL)))
{
hres = E_OUTOFMEMORY;
}
else
{
BOOL bExists;
// Confirm the replace if a file with the same name already exists.
//
PathCombine(szPath, pszTargetDir, pszFile);
bExists = DoesPathAlreadyExist(pcbs, szPath, pszPath, puConfirmFlags, SF_ISFILE, hwndOwner, bMultiDrop);
if (!bExists ||
IsFlagSet(*puConfirmFlags, CBF_YES))
{
NEWOBJECTTWIN not;
TWINRESULT tr;
DECLAREHOURGLASS;
ASSERT(IsFlagClear(*puConfirmFlags, CBF_NO) &&
IsFlagClear(*puConfirmFlags, CBF_CANCEL));
lstrcpy(szPath, pszPath);
PathRemoveFileSpec(szPath);
// User has either opted to continue adding this object to the
// database, or it does not exist in the destination folder.
RETRY_BEGIN(FALSE)
{
ZeroInit(&not, NEWOBJECTTWIN);
not.ulSize = sizeof(NEWOBJECTTWIN);
not.pcszFolder1 = szPath;
not.pcszFolder2 = pszTargetDir;
not.pcszName = pszFile;
SetHourglass();
Sync_Dump(&not, NEWOBJECTTWIN);
tr = Sync_AddObject(pcbs->hbrf, &not, &htfam);
ResetHourglass();
hres = HRESULT_FROM_TR(tr);
if (FAILED(hres))
{
DWORD dwError = GetLastError();
// Unavailable disk?
if (ERROR_INVALID_DATA == dwError || ERROR_ACCESS_DENIED == dwError)
{
// Yes; ask user to retry/cancel
int id = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_ADDFILE_UNAVAIL_VOL),
MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_RETRYCANCEL | MB_ICONWARNING);
// Set specific error value
hres = E_TR_UNAVAILABLE_VOLUME;
if (IDRETRY == id)
{
RETRY_SET(); // Try again
}
}
}
}
RETRY_END()
}
else if (IsFlagSet(*puConfirmFlags, CBF_NO))
{
// The user said NO
ASSERT(IsFlagClear(*puConfirmFlags, CBF_YES) &&
IsFlagClear(*puConfirmFlags, CBF_CANCEL));
DPA_DeletePtr(hdpa, iTwin);
hres = NOERROR;
}
else
{
ASSERT(IsFlagSet(*puConfirmFlags, CBF_CANCEL));
ASSERT(IsFlagClear(*puConfirmFlags, CBF_YES) &&
IsFlagClear(*puConfirmFlags, CBF_NO));
hres = E_ABORT;
}
}
if (FAILED(hres))
{
if (DPA_ERR != iTwin)
{
DPA_DeletePtr(hdpa, iTwin);
}
}
else
{
// Success
ASSERT(DPA_ERR != iTwin);
if (htfam)
DPA_SetPtr(hdpa, iTwin, htfam);
}
return hres;
}
/*----------------------------------------------------------
Purpose: Deletes the new twins
Returns: --
Cond: --
*/
void PRIVATE DeleteNewTwins(
CBS * pcbs,
HDPA hdpa)
{
int iItem;
int cItems;
ASSERT(pcbs);
ASSERT(hdpa);
cItems = DPA_GetPtrCount(hdpa);
for (iItem = 0; iItem < cItems; iItem++)
{
HTWIN htwin = DPA_FastGetPtr(hdpa, iItem);
if (htwin)
Sync_DeleteTwin(htwin);
}
}
/*----------------------------------------------------------
Purpose: Releases the twin handles
Returns: --
Cond: --
*/
void PRIVATE ReleaseNewTwins(
HDPA hdpa)
{
int i;
int cItems;
ASSERT(hdpa);
cItems = DPA_GetPtrCount(hdpa);
for (i = 0; i < cItems; i++)
{
HTWIN htwin = DPA_FastGetPtr(hdpa, i);
if (htwin)
Sync_ReleaseTwin(htwin);
}
}
/*----------------------------------------------------------
Purpose: Returns the count of nodes that do not have FS_COND_UNAVAILABLE.
Returns: see above
Cond: --
*/
UINT PRIVATE CountAvailableNodes(
PRECITEM pri)
{
UINT ucNodes = 0;
PRECNODE prn;
for (prn = pri->prnFirst; prn; prn = prn->prnNext)
{
if (FS_COND_UNAVAILABLE != prn->fsCurrent.fscond)
{
ucNodes++;
}
}
return ucNodes;
}
/*----------------------------------------------------------
Purpose: Returns the count of nodes that require some sort of
action.
Returns: see above
Cond: --
*/
UINT PRIVATE CountActionItem(
PRECLIST prl)
{
UINT uc = 0;
PRECITEM pri;
for (pri = prl->priFirst; pri; pri = pri->priNext)
{
if (RIA_NOTHING != pri->riaction)
{
uc++;
}
}
return uc;
}
/*----------------------------------------------------------
Purpose: Update the twins in the list
Returns:
Cond: --
*/
HRESULT PRIVATE MassageReclist(
CBS * pcbs,
PRECLIST prl,
LPCTSTR pszInsideDir,
BOOL bCopyIn,
HWND hwndOwner)
{
HRESULT hres = NOERROR;
PRECITEM pri;
BOOL bWarnUser = TRUE;
PRECNODE prnInside;
PRECNODE prnOutside;
// Make sure the direction of the reconciliation coincides
// with the direction of the user's action.
for (pri = prl->priFirst; pri; pri = pri->priNext)
{
if (RIA_NOTHING != pri->riaction)
{
UINT cAvailableNodes = CountAvailableNodes(pri);
// Is this a wierd multi-edged case (not including
// Sneakernet)?
if (2 < cAvailableNodes)
{
// Should never get here, but better safe than sorry
ASSERT(0);
}
else
{
// No; get the pair of nodes that we just added to the
// database.
hres = Sync_GetNodePair(pri, Atom_GetName(pcbs->atomBrf),
pszInsideDir, &prnInside, &prnOutside);
if (SUCCEEDED(hres))
{
ASSERT(prnInside);
ASSERT(prnOutside);
if (bCopyIn)
{
switch (prnOutside->rnstate)
{
case RNS_UNAVAILABLE:
case RNS_DOES_NOT_EXIST:
case RNS_DELETED:
break; // leave alone
default:
// Force the update to be a copy into the briefcase.
pri->riaction = RIA_COPY;
prnInside->rnaction = RNA_COPY_TO_ME;
prnOutside->rnaction = RNA_COPY_FROM_ME;
TRACE_MSG(TF_GENERAL, TEXT("Massaging reclist"));
break;
}
}
else
{
switch (prnInside->rnstate)
{
case RNS_UNAVAILABLE:
case RNS_DOES_NOT_EXIST:
case RNS_DELETED:
break; // leave alone
default:
// Force the update to be a copy out of the briefcase.
pri->riaction = RIA_COPY;
prnInside->rnaction = RNA_COPY_FROM_ME;
prnOutside->rnaction = RNA_COPY_TO_ME;
TRACE_MSG(TF_GENERAL, TEXT("Massaging reclist"));
break;
}
}
}
else
break; // Error
}
}
}
return hres;
}
/*----------------------------------------------------------
Purpose: Check for more than 2 available nodes in each recitem.
Remove the associated twin if we find such a case,
to prevent multiple sync copies.
Returns: S_OK if everything looks ok
S_FALSE if there were multiple sync copies introduced
Cond: --
*/
HRESULT PRIVATE VerifyTwins(
CBS * pcbs,
PRECLIST prl,
LPCTSTR pszTargetDir,
HWND hwndOwner)
{
HRESULT hres = NOERROR;
PRECITEM pri;
BOOL bWarnUser = TRUE;
BOOL bWarnUserFolder = TRUE;
TCHAR szPath[MAX_PATH];
// Look thru the reclist and pick out recitems that have more than
// 2 recnodes that are currently available.
// Scenarios when this can happen:
//
// 1) Foo.txt --> BC
// Foo.txt --> BC\Orphan Folder
//
// Expected result: delete BC\Orphan Folder\Foo.txt twin
//
// 2) Foo.txt --> BC\Orphan Folder
// Orphan Folder --> BC
//
// Expected result: delete BC\Orphan Folder twin
//
// 3) Foo.txt --> BC\Orphan Folder
// Foo.txt --> BC
//
// Expected result: delete BC\Foo.txt twin
//
for (pri = prl->priFirst; pri; pri = pri->priNext)
{
UINT cAvailableNodes = CountAvailableNodes(pri);
PRECNODE prn;
// Are there more than 2 available nodes?
if (2 < cAvailableNodes && *pri->pcszName)
{
BOOL bLookForFolders = TRUE;
// FIRST: Look for object twins that are not in folder twins.
for (prn = pri->prnFirst; prn; prn = prn->prnNext)
{
// Is this file here because the file was dragged in?
if (IsSzEqual(pszTargetDir, prn->pcszFolder))
{
// Yes; warn the user
if (bWarnUser)
{
MsgBox(hwndOwner,
MAKEINTRESOURCE(IDS_ERR_ADDFILE_TOOMANY),
MAKEINTRESOURCE(IDS_CAP_ADD),
NULL, MB_WARNING, pri->pcszName);
if (0 > GetKeyState(VK_SHIFT))
{
bWarnUser = FALSE;
}
}
// Try to remove the object twin
PathCombine(szPath, prn->pcszFolder, pri->pcszName);
hres = Sync_Split(pcbs->hbrf, szPath, 1, hwndOwner,
SF_QUIET | SF_NOCONFIRM);
TRACE_MSG(TF_GENERAL, TEXT("Deleted object twin for %s"), szPath);
ASSERT(FAILED(hres) || S_OK == hres);
bLookForFolders = FALSE;
break;
}
}
if (bLookForFolders)
{
// SECOND: Look for object twins that exist because of folder
// twins.
for (prn = pri->prnFirst; prn; prn = prn->prnNext)
{
lstrcpy(szPath, prn->pcszFolder);
PathRemoveFileSpec(szPath);
// Is this file here because it is in a folder that was
// dragged in?
if (IsSzEqual(pszTargetDir, szPath))
{
// Yes; warn the user
if (bWarnUserFolder && bWarnUser)
{
MsgBox(hwndOwner,
MAKEINTRESOURCE(IDS_ERR_ADDFOLDER_TOOMANY),
MAKEINTRESOURCE(IDS_CAP_ADD),
NULL, MB_WARNING, PathFindFileName(prn->pcszFolder));
// Hack: to prevent showing this messagebox for
// every file in this folder, set this flag
bWarnUserFolder = FALSE;
if (0 > GetKeyState(VK_SHIFT))
{
bWarnUser = FALSE;
}
}
// Remove the folder twin
hres = Sync_Split(pcbs->hbrf, prn->pcszFolder, 1, hwndOwner,
SF_ISFOLDER | SF_QUIET | SF_NOCONFIRM);
TRACE_MSG(TF_GENERAL, TEXT("Deleted folder twin for %s"), prn->pcszFolder);
ASSERT(FAILED(hres) || !bWarnUserFolder || S_OK == hres);
break;
}
}
}
hres = S_FALSE;
}
}
return hres;
}
#define STATE_VERIFY 0
#define STATE_UPDATE 1
#define STATE_STOP 2
/*----------------------------------------------------------
Purpose: This function updates the new files. Unlike the general
update function, this strictly updates file pairs. All
other incidental nodes are set to RNA_NOTHING.
In addition, to be safe, we force the update to always
perform a copy into the briefcase.
This function releases the twin handles when it is finished.
Returns: standard result
Cond: --
*/
HRESULT PRIVATE UpdateNewTwins(
CBS * pcbs,
LPCTSTR pszInsideDir,
LPCTSTR pszTargetDir,
BOOL bCopyIn,
HDPA hdpa,
HWND hwndOwner)
{
HRESULT hres = E_FAIL;
int iItem;
int cItems;
ASSERT(pcbs);
ASSERT(hdpa);
cItems = DPA_GetPtrCount(hdpa);
if (cItems > 0)
{
HTWINLIST htl;
PRECLIST prl;
TWINRESULT tr;
tr = Sync_CreateTwinList(pcbs->hbrf, &htl);
if (TR_SUCCESS != tr)
{
hres = HRESULT_FROM_TR(tr);
}
else
{
HWND hwndProgress;
UINT nState = STATE_VERIFY;
DEBUG_CODE( UINT nCount = 0; )
// State progression is simple:
// STATE_VERIFY --> STATE_UPDATE --> STATE_STOP
// Any questions?
hwndProgress = UpdBar_Show(hwndOwner, UB_CHECKING, DELAY_UPDBAR);
for (iItem = 0; iItem < cItems; iItem++)
{
HTWIN htwin = DPA_FastGetPtr(hdpa, iItem);
if (htwin)
Sync_AddToTwinList(htl, htwin);
}
do
{
ASSERT(STATE_VERIFY == nState || STATE_UPDATE == nState);
ASSERT(2 > nCount++); // Sanity check for infinite loop
// Create the reclist
hres = Sync_CreateRecListEx(htl, UpdBar_GetAbortEvt(hwndProgress), &prl);
DEBUG_CODE( Sync_DumpRecList(GET_TR(hres), prl, TEXT("Adding new twins")); )
if (SUCCEEDED(hres))
{
ASSERT(prl);
switch (nState)
{
case STATE_VERIFY:
hres = VerifyTwins(pcbs, prl, pszTargetDir, hwndOwner);
if (S_FALSE == hres)
nState = STATE_UPDATE;
else if (S_OK == hres)
goto Update;
else
nState = STATE_STOP;
break;
case STATE_UPDATE:
// After recreating the reclist, is there anything
// that needs updating?
if (0 < CountActionItems(prl))
{
// Yes
Update:
UpdBar_SetAvi(hwndProgress, UB_UPDATEAVI);
hres = MassageReclist(pcbs, prl, pszInsideDir, bCopyIn, hwndOwner);
if (SUCCEEDED(hres))
{
// Update these files
hres = Sync_ReconcileRecList(prl, Atom_GetName(pcbs->atomBrf),
hwndProgress, RF_ONADD);
}
}
nState = STATE_STOP;
break;
default:
ASSERT(0);
break;
}
Sync_DestroyRecList(prl);
}
} while (SUCCEEDED(hres) && STATE_UPDATE == nState);
Sync_DestroyTwinList(htl);
UpdBar_Kill(hwndProgress);
}
}
return hres;
}
// FEATURE - BobDay - WinNT docking state determination code goes here.
/*----------------------------------------------------------
Purpose: Return TRUE if the machine is docked
Returns: See above.
Cond: --
*/
BOOL PRIVATE IsMachineDocked(void)
{
return TRUE;
}
//---------------------------------------------------------------------------
// IBriefcaseStg member functions
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: IBriefcaseStg::Release
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefStg_Release(
LPBRIEFCASESTG pstg)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
DBG_ENTER(TEXT("BriefStg_Release"));
if (--this->cRef)
{
DBG_EXIT_UL(TEXT("BriefStg_Release"), this->cRef);
return this->cRef; // Return decremented reference count
}
if (this->pcbs)
{
// Release this briefcase storage instance
CloseBriefcaseStorage(this->szFolder);
}
if (this->hbrfcaseiter)
{
Sync_FindClose(this->hbrfcaseiter);
}
GFree(this);
ENTEREXCLUSIVE();
{
DecBriefSemaphore();
if (IsLastBriefSemaphore())
{
CommitIniFile();
DEBUG_CODE( DumpTables(); )
TermCacheTables();
}
}
LEAVEEXCLUSIVE();
DBG_EXIT_UL(TEXT("BriefStg_Release"), 0);
return 0;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::AddRef
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefStg_AddRef(
LPBRIEFCASESTG pstg)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
UINT cRef;
DBG_ENTER(TEXT("BriefStg_AddRef"));
cRef = ++this->cRef;
DBG_EXIT_UL(TEXT("BriefStg_AddRef"), cRef);
return cRef;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::QueryInterface
Returns: standard
Cond: --
*/
STDMETHODIMP BriefStg_QueryInterface(
LPBRIEFCASESTG pstg,
REFIID riid,
LPVOID * ppvOut)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres;
DBG_ENTER_RIID(TEXT("BriefStg_QueryInterface"), riid);
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IBriefcaseStg))
{
// We use the bs field as our IUnknown as well
*ppvOut = &this->bs;
this->cRef++;
hres = NOERROR;
}
else
{
*ppvOut = NULL;
hres = ResultFromScode(E_NOINTERFACE);
}
DBG_EXIT_HRES(TEXT("BriefStg_QueryInterface"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::Initialize
Called to initialize a briefcase storage instance.
The pszFolder indicates the folder we are binding to,
which is in the briefcase storage (somewhere).
Returns: standard
Cond: --
*/
STDMETHODIMP BriefStg_Initialize(
LPBRIEFCASESTG pstg,
LPCTSTR pszPath,
HWND hwndOwner)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres = ResultFromScode(E_FAIL);
DBG_ENTER_SZ(TEXT("BriefStg_Initialize"), pszPath);
ASSERT(pszPath);
// Only initialize once per interface instance
//
if (pszPath && NULL == this->pcbs)
{
BOOL bCancel = FALSE;
RETRY_BEGIN(FALSE)
{
// Unavailable disk?
if (!PathExists(pszPath))
{
// Yes; ask user to retry/cancel
int id = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_OPEN_UNAVAIL_VOL),
MAKEINTRESOURCE(IDS_CAP_OPEN), NULL, MB_RETRYCANCEL | MB_ICONWARNING);
if (IDRETRY == id)
RETRY_SET(); // Try again
else
bCancel = TRUE;
}
}
RETRY_END()
if (!bCancel)
{
BrfPathCanonicalize(pszPath, this->szFolder);
if (PathExists(this->szFolder) && !PathIsDirectory(this->szFolder))
{
// (Store this as a path to a folder)
PathRemoveFileSpec(this->szFolder);
}
// Open the briefcase storage for this folder
//
hres = OpenBriefcaseStorage(this->szFolder, &this->pcbs, hwndOwner);
if (SUCCEEDED(hres))
{
// Is this folder a sync folder?
if (HasFolderSyncCopy(this->pcbs->hbrf, this->szFolder))
{
// Yes
SetFlag(this->dwFlags, BSTG_SYNCFOLDER);
}
else
{
// No (or error, in which case we default to no)
ClearFlag(this->dwFlags, BSTG_SYNCFOLDER);
}
}
}
}
hres = MapToOfficialHresult(hres);
DBG_EXIT_HRES(TEXT("BriefStg_Initialize"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: Add an object or objects to the briefcase storage.
This function does the real work for BriefStg_AddObject.
Returns: standard result
NOERROR if the object(s) were added
S_FALSE if the object(s) should be handled by the caller
Cond: --
*/
HRESULT PRIVATE BriefStg_AddObjectPrivate(
LPBRIEFCASESTG pstg,
LPDATAOBJECT pdtobj,
LPCTSTR pszFolderEx, // optional (may be NULL)
UINT uFlags, // One of AOF_*
HWND hwndOwner)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres;
LPTSTR pszList;
LPTSTR psz;
UINT i;
UINT cFiles;
TCHAR szCanon[MAX_PATH];
HDPA hdpa;
LPCTSTR pszTarget;
BOOL bMultiFiles;
static SETbl const c_rgseAdd[] = {
{ E_OUTOFMEMORY, IDS_OOM_ADD, MB_ERROR },
{ E_TR_OUT_OF_MEMORY, IDS_OOM_ADD, MB_ERROR },
};
ASSERT(pdtobj);
// Verify that the folder of this briefcase storage is actually inside
// a briefcase. (szCanon is used as a dummy here.)
ASSERT( !PathExists(this->szFolder) || PL_FALSE != PathGetLocality(this->szFolder, szCanon) );
// Get list of files to add
hres = DataObj_QueryFileList(pdtobj, &pszList, &cFiles);
if (SUCCEEDED(hres))
{
// Grab the mutex to delay any further calculation in any
// Briefcase views' secondary threads until we're done
// processing here.
Delay_Own();
// Does the caller want to create sync copies of objects that are
// already in the briefcase to some other folder? (Sneakernet)
if (NULL != pszFolderEx)
{
// Yes
pszTarget = pszFolderEx;
}
else
{
// No
pszTarget = this->szFolder;
// Are the entities already in this briefcase?
//
// Based on the success return value of DataObj_QueryFileList,
// we can tell if the entities are already within a briefcase.
// Because of the nature of the shell, we assume the file
// list contains entities which all exist in the same folder,
// so we consider it an "all or nothing" sort of indicator.
// If the entities are indeed in a briefcase, we compare the
// roots of the source and destination briefcases, and BLOCK
// the addition if they are the same.
//
if (S_OK == hres)
{
// They are in *a* briefcase. Which one?
DataObj_QueryBriefPath(pdtobj, szCanon);
if (IsSzEqual(szCanon, Atom_GetName(this->pcbs->atomBrf)))
{
// This same one! Don't do anything.
// display message box
hres = ResultFromScode(E_FAIL);
goto Error1;
}
}
}
bMultiFiles = (1 < cFiles);
// Create the temporary DPA list
if (NULL == (hdpa = DPA_Create(cFiles)))
{
hres = ResultFromScode(E_OUTOFMEMORY);
}
else
{
UINT uConfirmFlags = 0;
// Add all the objects to the briefcase storage
for (i = 0, psz = pszList; i < cFiles; i++)
{
// Get file/folder name that was dropped
BrfPathCanonicalize(psz, szCanon);
if (PathIsDirectory(szCanon))
{
hres = CreateTwinOfFolder(this->pcbs, szCanon, pszTarget,
hdpa, uFlags, &uConfirmFlags,
hwndOwner, bMultiFiles);
}
else
{
hres = CreateTwinOfFile(this->pcbs, szCanon, pszTarget,
hdpa, uFlags, &uConfirmFlags,
hwndOwner, bMultiFiles);
}
if (FAILED(hres))
{
// An error occurred while attempting to add a twin
break;
}
DataObj_NextFile(psz); // Set psz to next file in list
}
if (FAILED(hres))
{
// Delete the twins that were added.
DeleteNewTwins(this->pcbs, hdpa);
}
else
{
// Update these new twins
hres = UpdateNewTwins(this->pcbs, this->szFolder, pszTarget, (NULL == pszFolderEx), hdpa, hwndOwner);
}
ReleaseNewTwins(hdpa);
DPA_Destroy(hdpa);
}
Error1:
DataObj_FreeList(pszList);
Delay_Release();
}
if (FAILED(hres))
{
SEMsgBox(hwndOwner, IDS_CAP_ADD, hres, c_rgseAdd, ARRAYSIZE(c_rgseAdd));
}
return hres;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::AddObject
Add an object to the briefcase storage.
Returns: standard hresult
Cond: --
*/
STDMETHODIMP BriefStg_AddObject(
LPBRIEFCASESTG pstg,
LPDATAOBJECT pdtobj,
LPCTSTR pszFolderEx, // optional
UINT uFlags,
HWND hwndOwner)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres = NOERROR;
LPCTSTR pszFolder;
UINT ids;
DEBUG_CODE( TCHAR szDbg[MAX_PATH]; )
DBG_ENTER_DTOBJ(TEXT("BriefStg_AddObject"), pdtobj, szDbg);
ASSERT(pdtobj);
ASSERT(this->pcbs);
// Is this sneakernet?
// Is this folder a sync folder?
if (pszFolderEx)
{
// Yes; is the source a sync folder already?
if (HasFolderSyncCopy(this->pcbs->hbrf, pszFolderEx))
{
// Yes; don't allow other sync copies into (or out of) it
ids = IDS_ERR_ADD_SYNCFOLDER;
pszFolder = PathFindFileName(pszFolderEx);
hres = E_FAIL;
}
// Is the source folder a sync folder already?
else if (IsFlagSet(this->dwFlags, BSTG_SYNCFOLDER))
{
// Yes; don't allow other sync copies into (or out of) it
ids = IDS_ERR_ADD_SYNCFOLDER_SRC;
pszFolder = PathFindFileName(this->szFolder);
hres = E_FAIL;
}
}
else if (IsFlagSet(this->dwFlags, BSTG_SYNCFOLDER))
{
// Yes; don't allow other sync copies into (or out of) it
ids = IDS_ERR_ADD_SYNCFOLDER;
pszFolder = PathFindFileName(this->szFolder);
hres = E_FAIL;
}
if (SUCCEEDED(hres))
{
hres = BriefStg_AddObjectPrivate(pstg, pdtobj, pszFolderEx, uFlags, hwndOwner);
}
else
{
MsgBox(hwndOwner,
MAKEINTRESOURCE(ids),
MAKEINTRESOURCE(IDS_CAP_ADD),
NULL,
MB_WARNING,
pszFolder);
}
DEBUG_CODE( DumpTables(); )
hres = MapToOfficialHresult(hres);
DBG_EXIT_HRES(TEXT("BriefStg_AddObject"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: Removes an object or objects from the briefcase storage.
Returns: standard hresult
Cond: --
*/
HRESULT PRIVATE ReleaseObject(
CBS * pcbs,
LPDATAOBJECT pdtobj,
HWND hwndOwner)
{
HRESULT hres;
LPTSTR pszList;
UINT cFiles;
ASSERT(pdtobj);
hres = DataObj_QueryFileList(pdtobj, &pszList, &cFiles);
if (SUCCEEDED(hres))
{
RETRY_BEGIN(FALSE)
{
hres = Sync_Split(pcbs->hbrf, pszList, cFiles, hwndOwner, 0);
// Unavailable disk?
if (E_TR_UNAVAILABLE_VOLUME == hres)
{
// Yes; ask user to retry/cancel
int id = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_UNAVAIL_VOL),
MAKEINTRESOURCE(IDS_CAP_Split), NULL, MB_RETRYCANCEL | MB_ICONWARNING);
if (IDRETRY == id)
RETRY_SET(); // Try again
}
}
RETRY_END()
DataObj_FreeList(pszList);
}
return hres;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::ReleaseObject
Release an object from the briefcase storage.
Returns: standard hresult
Cond: --
*/
STDMETHODIMP BriefStg_ReleaseObject(
LPBRIEFCASESTG pstg,
LPDATAOBJECT pdtobj,
HWND hwndOwner)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres;
DEBUG_CODE( TCHAR szDbg[MAX_PATH]; )
DBG_ENTER_DTOBJ(TEXT("BriefStg_ReleaseObject"), pdtobj, szDbg);
ASSERT(pdtobj);
ASSERT(this->pcbs);
hres = ReleaseObject(this->pcbs, pdtobj, hwndOwner);
DEBUG_CODE( DumpTables(); )
hres = MapToOfficialHresult(hres);
DBG_EXIT_HRES(TEXT("BriefStg_ReleaseObject"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::UpdateObject
Update an object in the briefcase storage.
Returns: standard hresult
Cond: --
*/
STDMETHODIMP BriefStg_UpdateObject(
LPBRIEFCASESTG pstg,
LPDATAOBJECT pdtobj,
HWND hwndOwner)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres;
TCHAR szPath[MAX_PATH];
DEBUG_CODE( TCHAR szDbg[MAX_PATH]; )
DBG_ENTER_DTOBJ(TEXT("BriefStg_UpdateObject"), pdtobj, szDbg);
ASSERT(pdtobj);
ASSERT(this->pcbs);
// Determine whether this is an Update Selection or Update All.
hres = DataObj_QueryPath(pdtobj, szPath);
if (SUCCEEDED(hres))
{
// Is this a briefcase root?
if (PathIsBriefcase(szPath))
{
// Yes; do an Update All
hres = Upd_DoModal(hwndOwner, this->pcbs, NULL, 0, UF_ALL);
}
else
{
// No; do an Update Selection
LPTSTR pszList;
UINT cFiles;
hres = DataObj_QueryFileList(pdtobj, &pszList, &cFiles);
if (SUCCEEDED(hres))
{
hres = Upd_DoModal(hwndOwner, this->pcbs, pszList, cFiles, UF_SELECTION);
DataObj_FreeList(pszList);
}
}
}
DEBUG_CODE( DumpTables(); )
hres = MapToOfficialHresult(hres);
DBG_EXIT_HRES(TEXT("BriefStg_UpdateObject"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: Update a briefcase based on events
Returns: standard hresult
Cond: --
*/
HRESULT PRIVATE BriefStg_UpdateOnEvent(
LPBRIEFCASESTG pstg,
UINT uEvent,
HWND hwndOwner)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres = NOERROR;
DBG_ENTER(TEXT("BriefStg_UpdateOnEvent"));
switch (uEvent)
{
case UOE_CONFIGCHANGED:
case UOE_QUERYCHANGECONFIG:
// Is the machine docked?
if (IsMachineDocked())
{
// Yes; does the user want to update?
TCHAR sz[MAX_PATH];
int ids = (UOE_CONFIGCHANGED == uEvent) ? IDS_MSG_UpdateOnDock : IDS_MSG_UpdateBeforeUndock;
LPCTSTR pszBrf = Atom_GetName(this->pcbs->atomBrf);
int id = MsgBox(hwndOwner,
MAKEINTRESOURCE(ids),
MAKEINTRESOURCE(IDS_CAP_UPDATE),
LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_UPDATE_DOCK)),
MB_QUESTION,
PathGetDisplayName(pszBrf, sz));
if (IDYES == id)
{
// Yes; do an Update All
hres = Upd_DoModal(hwndOwner, this->pcbs, NULL, 0, UF_ALL);
}
}
break;
default:
hres = ResultFromScode(E_INVALIDARG);
break;
}
DEBUG_CODE( DumpTables(); )
hres = MapToOfficialHresult(hres);
DBG_EXIT_HRES(TEXT("BriefStg_UpdateOnEvent"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::Notify
Marks the path dirty in the briefcase storage cache.
(The path may not exist in the cache, in which case this
function does nothing.)
Returns: S_OK to force a refresh
S_FALSE to not force a refresh
Cond: --
*/
STDMETHODIMP BriefStg_Notify(
LPBRIEFCASESTG pstg,
LPCTSTR pszPath, // may be NULL
LONG lEvent, // one of NOE_ flags
UINT * puFlags, // returned NF_ flags
HWND hwndOwner)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres = ResultFromScode(E_OUTOFMEMORY);
TCHAR szCanon[MAX_PATH];
int atom;
DBG_ENTER_SZ(TEXT("BriefStg_Notify"), pszPath);
ASSERT(this->pcbs);
ASSERT(puFlags);
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Received event %lx for %s"), lEvent, Dbg_SafeStr(pszPath)); )
*puFlags = 0;
// Dirty the entire cache?
if (NOE_DIRTYALL == lEvent)
{
// Yes
TRACE_MSG(TF_GENERAL, TEXT("Marking everything"));
CRL_DirtyAll(this->pcbs->atomBrf);
Sync_ClearBriefcaseCache(this->pcbs->hbrf);
hres = NOERROR;
}
else if (pszPath && 0 < lEvent)
{
// No
BrfPathCanonicalize(pszPath, szCanon);
atom = Atom_Add(szCanon);
if (ATOM_ERR != atom)
{
int atomCab = Atom_Add(this->szFolder);
if (ATOM_ERR != atomCab)
{
// There are two actions we must determine: what gets marked dirty?
// and does this specific window get forcibly refreshed?
BOOL bRefresh;
BOOL bMarked;
bMarked = CRL_Dirty(atom, atomCab, lEvent, &bRefresh);
hres = NOERROR;
if (bMarked)
{
SetFlag(*puFlags, NF_ITEMMARKED);
}
#ifdef DEBUG
if (bMarked && bRefresh)
{
TRACE_MSG(TF_GENERAL, TEXT("Marked and forcing refresh of window on %s"), (LPTSTR)this->szFolder);
}
else if (bMarked)
{
TRACE_MSG(TF_GENERAL, TEXT("Marked"));
}
#endif
Atom_Delete(atomCab);
}
Atom_Delete(atom);
}
}
DBG_EXIT_HRES(TEXT("BriefStg_Notify"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: Gets special info (status and origin) of a path.
Returns: --
Cond: --
*/
HRESULT PRIVATE BriefStg_GetSpecialInfoOf(
PBRIEFSTG this,
LPCTSTR pszName,
UINT uFlag,
LPTSTR pszBuf,
int cchBuf)
{
HRESULT hres = E_OUTOFMEMORY;
TCHAR szPath[MAX_PATH];
TCHAR szCanon[MAX_PATH];
int atom;
ASSERT(this);
ASSERT(pszName);
ASSERT(pszBuf);
ASSERT(this->pcbs);
*pszBuf = TEXT('\0');
// Would the path be too long if combined?
if (PathsTooLong(this->szFolder, pszName))
{
// Yes
hres = E_FAIL;
}
else
{
PathCombine(szPath, this->szFolder, pszName);
BrfPathCanonicalize(szPath, szCanon);
atom = Atom_Add(szCanon);
if (ATOM_ERR != atom)
{
CRL * pcrl;
// The first CRL_Get call will get the reclist from the cache
// or get a fresh reclist if the dirty bit is set. If the cache
// item doesn't exist, add it. We add orphans to the cache too
// but they have no reclist.
// Does the cached item already exist?
hres = CRL_Get(atom, &pcrl);
if (FAILED(hres))
{
// No; add it
hres = CRL_Add(this->pcbs, atom);
if (SUCCEEDED(hres))
{
// Do another 'get' to offset the CRL_Delete at the end of
// this function. This will leave this new reclist in the
// cache upon exit. (We don't want to create a new reclist
// everytime this functin is called.) It will all get
// cleaned up when the CBS is freed.
//
hres = CRL_Get(atom, &pcrl);
}
}
ASSERT(FAILED(hres) || pcrl);
// Do we have a cache reclist entry to work with?
if (pcrl)
{
// Yes
if (GEI_ORIGIN == uFlag)
{
lstrcpyn(pszBuf, Atom_GetName(pcrl->atomOutside), cchBuf);
PathRemoveFileSpec(pszBuf);
}
else
{
ASSERT(GEI_STATUS == uFlag);
SzFromIDS(pcrl->idsStatus, pszBuf, cchBuf);
}
CRL_Delete(atom); // Decrement count
}
Atom_Delete(atom);
}
}
return hres;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::GetExtraInfo
Returns: standard hresult
Cond: --
*/
STDMETHODIMP BriefStg_GetExtraInfo(
LPBRIEFCASESTG pstg,
LPCTSTR pszName,
UINT uInfo,
WPARAM wParam,
LPARAM lParam)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres;
DBG_ENTER_SZ(TEXT("BriefStg_GetExtraInfo"), pszName);
ASSERT(this->pcbs);
switch (uInfo)
{
case GEI_ORIGIN:
case GEI_STATUS: {
LPTSTR pszBuf = (LPTSTR)lParam;
int cchBuf = (int)wParam;
ASSERT(pszName);
ASSERT(pszBuf);
hres = BriefStg_GetSpecialInfoOf(this, pszName, uInfo, pszBuf, cchBuf);
}
break;
case GEI_DELAYHANDLE: {
HANDLE * phMutex = (HANDLE *)lParam;
ASSERT(phMutex);
*phMutex = g_hMutexDelay;
hres = NOERROR;
}
break;
case GEI_ROOT: {
LPTSTR pszBuf = (LPTSTR)lParam;
int cchBuf = (int)wParam;
ASSERT(pszBuf);
lstrcpyn(pszBuf, Atom_GetName(this->pcbs->atomBrf), cchBuf);
#ifdef DEBUG
if (IsFlagSet(g_uDumpFlags, DF_PATHS))
{
TRACE_MSG(TF_ALWAYS, TEXT("Root is \"%s\""), pszBuf);
}
#endif
hres = NOERROR;
}
break;
case GEI_DATABASENAME: {
LPTSTR pszBuf = (LPTSTR)lParam;
int cchBuf = (int)wParam;
LPCTSTR pszDBName;
ASSERT(pszBuf);
if (IsFlagSet(this->pcbs->uFlags, CBSF_LFNDRIVE))
pszDBName = g_szDBName;
else
pszDBName = g_szDBNameShort;
lstrcpyn(pszBuf, pszDBName, cchBuf);
hres = NOERROR;
}
break;
default:
hres = E_INVALIDARG;
break;
}
DBG_EXIT_HRES(TEXT("BriefStg_GetExtraInfo"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::FindFirst
Returns the location of the root of the first briefcase storage
in the system.
Returns: S_OK if a briefcase was found
S_FALSE to end enumeration
Cond: --
*/
STDMETHODIMP BriefStg_FindFirst(
LPBRIEFCASESTG pstg,
LPTSTR pszName,
int cchMaxName)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres;
TWINRESULT tr;
BRFCASEINFO bcinfo;
DBG_ENTER(TEXT("BriefStg_FindFirst"));
ASSERT(pszName);
bcinfo.ulSize = sizeof(bcinfo);
tr = Sync_FindFirst(&this->hbrfcaseiter, &bcinfo);
switch (tr)
{
case TR_OUT_OF_MEMORY:
hres = ResultFromScode(E_OUTOFMEMORY);
break;
case TR_SUCCESS:
hres = ResultFromScode(S_OK);
lstrcpyn(pszName, bcinfo.rgchDatabasePath, cchMaxName);
break;
case TR_NO_MORE:
hres = ResultFromScode(S_FALSE);
break;
default:
hres = ResultFromScode(E_FAIL);
break;
}
DBG_EXIT_HRES(TEXT("BriefStg_FindFirst"), hres);
return hres;
}
/*----------------------------------------------------------
Purpose: IBriefcaseStg::FindNext
Returns the location of the root of the next briefcase storage
in the system.
Returns: S_OK if a briefcase was found
S_FALSE to end enumeration
Cond: --
*/
STDMETHODIMP BriefStg_FindNext(
LPBRIEFCASESTG pstg,
LPTSTR pszName,
int cchMaxName)
{
PBRIEFSTG this = IToClass(BriefStg, bs, pstg);
HRESULT hres;
TWINRESULT tr;
BRFCASEINFO bcinfo;
DBG_ENTER(TEXT("BriefStg_FindNext"));
ASSERT(pszName);
bcinfo.ulSize = sizeof(bcinfo);
tr = Sync_FindNext(this->hbrfcaseiter, &bcinfo);
switch (tr)
{
case TR_OUT_OF_MEMORY:
hres = ResultFromScode(E_OUTOFMEMORY);
break;
case TR_SUCCESS:
hres = ResultFromScode(S_OK);
lstrcpyn(pszName, bcinfo.rgchDatabasePath, cchMaxName);
break;
case TR_NO_MORE:
hres = ResultFromScode(S_FALSE);
break;
default:
hres = ResultFromScode(E_FAIL);
break;
}
DBG_EXIT_HRES(TEXT("BriefStg_FindNext"), hres);
return hres;
}
//---------------------------------------------------------------------------
// BriefStg class : Vtables
//---------------------------------------------------------------------------
IBriefcaseStgVtbl c_BriefStg_BSVtbl =
{
BriefStg_QueryInterface,
BriefStg_AddRef,
BriefStg_Release,
BriefStg_Initialize,
BriefStg_AddObject,
BriefStg_ReleaseObject,
BriefStg_UpdateObject,
BriefStg_UpdateOnEvent,
BriefStg_GetExtraInfo,
BriefStg_Notify,
BriefStg_FindFirst,
BriefStg_FindNext,
};
/*----------------------------------------------------------
Purpose: This function is called back from within
IClassFactory::CreateInstance() of the default class
factory object, which is created by SHCreateClassObject.
Returns: standard
Cond: --
*/
HRESULT CALLBACK BriefStg_CreateInstance(
LPUNKNOWN punkOuter, // Should be NULL for us
REFIID riid,
LPVOID * ppvOut)
{
HRESULT hres = E_FAIL;
PBRIEFSTG this;
DBG_ENTER_RIID(TEXT("BriefStg_CreateInstance"), riid);
// Briefcase storage does not support aggregation.
//
if (punkOuter)
{
hres = CLASS_E_NOAGGREGATION;
*ppvOut = NULL;
goto Leave;
}
this = GAlloc(sizeof(*this));
if (!this)
{
hres = E_OUTOFMEMORY;
*ppvOut = NULL;
goto Leave;
}
this->bs.lpVtbl = &c_BriefStg_BSVtbl;
this->cRef = 1;
this->pcbs = NULL;
this->dwFlags = 0;
// Load the engine if it hasn't already been loaded
// (this only returns FALSE if something went wrong)
if (Sync_QueryVTable())
{
ENTEREXCLUSIVE();
{
// The decrement is in BriefStg_Release()
IncBriefSemaphore();
if (IsFirstBriefSemaphore())
{
ProcessIniFile(); // Load settings first
// Initialize cache
if (InitCacheTables())
hres = NOERROR;
else
hres = E_OUTOFMEMORY;
}
else
{
hres = NOERROR;
}
}
LEAVEEXCLUSIVE();
}
if (SUCCEEDED(hres))
{
// Note that the Release member will free the object, if
// QueryInterface failed.
//
hres = this->bs.lpVtbl->QueryInterface(&this->bs, riid, ppvOut);
this->bs.lpVtbl->Release(&this->bs);
}
else
{
*ppvOut = NULL;
}
Leave:
DBG_EXIT_HRES(TEXT("BriefStg_CreateInstance"), hres);
return hres; // S_OK or E_NOINTERFACE
}