2238 lines
65 KiB
C
2238 lines
65 KiB
C
//---------------------------------------------------------------------------
|
|
//
|
|
// 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(¬, 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)
|
|
{
|
|
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
|
|
}
|