|
|
//---------------------------------------------------------------------------
//
// 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, ARRAYSIZE(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, ARRAYSIZE(szBrfCanon)); } else { BrfPathCanonicalize(szBrfPath, szBrfCanon, ARRAYSIZE(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, ARRAYSIZE(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, ARRAYSIZE(szBrfCanon)); } else { BrfPathCanonicalize(szBrfPath, szBrfCanon, ARRAYSIZE(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));
lstrcpyn(szPath, pszPath, ARRAYSIZE(szPath)); 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(¬, NEWOBJECTTWIN); not.ulSize = sizeof(NEWOBJECTTWIN); not.pcszFolder1 = szPath; not.pcszFolder2 = pszTargetDir; not.pcszName = pszFile;
SetHourglass(); Sync_Dump(¬, NEWOBJECTTWIN); tr = Sync_AddObject(pcbs->hbrf, ¬, &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) { lstrcpyn(szPath, prn->pcszFolder, ARRAYSIZE(szPath)); 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, ARRAYSIZE(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, ARRAYSIZE(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, ARRAYSIZE(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, ARRAYSIZE(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, ARRAYSIZE(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, ARRAYSIZE(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, ARRAYSIZE(szDbg));
ASSERT(pdtobj); ASSERT(this->pcbs);
// Determine whether this is an Update Selection or Update All.
hres = DataObj_QueryPath(pdtobj, szPath, ARRAYSIZE(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, ARRAYSIZE(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, ARRAYSIZE(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, ARRAYSIZE(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
}
|