update.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 139k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //+-------------------------------------------------------------------------
  2. //
  3. //  Microsoft Windows
  4. //
  5. //  Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. //  File:       update.cpp
  8. //
  9. //  Authors;
  10. //    Jeff Saathoff (jeffreys)
  11. //
  12. //  Notes;
  13. //    SyncMgr integration
  14. //--------------------------------------------------------------------------
  15. #include "pch.h"
  16. #include "msgbox.h"     // CscWin32Message
  17. #include "folder.h"
  18. #include <openfile.h>   // OpenOfflineFile
  19. #include "cscst.h"      // PostToSystray
  20. #include "uihooks.h"    // Self-host notifications
  21. #include "fopendlg.h"   // OpenFilesWarningDialog
  22. #include "statdlg.h"    // ReconnectServers
  23. #include "security.h"
  24. #define RAS_CONNECT_DELAY       (10 * 1000)
  25. // Maximum length of username
  26. #define MAX_USERNAME_CHARS      64
  27. // SYNCTHREADDATA.dwSyncStatus flags
  28. #define SDS_SYNC_OUT                0x00000001  // CSCMergeShare
  29. #define SDS_SYNC_IN_QUICK           0x00000002  // CSCFillSparseFiles(FALSE)
  30. #define SDS_SYNC_IN_FULL            0x00000004  // CSCFillSparseFiles(TRUE)
  31. #define SDS_SYNC_FORCE_INWARD       0x00000008
  32. #define SDS_SYNC_RAS_CONNECTED      0x00000010
  33. #define SDS_SYNC_RESTART_MERGE      0x00000020
  34. #define SDS_SYNC_DELETE_DELETE      0x00000040
  35. #define SDS_SYNC_DELETE_RESTORE     0x00000080
  36. #define SDS_SYNC_AUTOCACHE          0x00000100
  37. #define SDS_SYNC_CONFLICT_KEEPLOCAL 0x00000200
  38. #define SDS_SYNC_CONFLICT_KEEPNET   0x00000400
  39. #define SDS_SYNC_CONFLICT_KEEPBOTH  0x00000800
  40. #define SDS_SYNC_STARTED            0x00010000
  41. #define SDS_SYNC_ERROR              0x00020000
  42. #define SDS_SYNC_CANCELLED          0x00040000
  43. #define SDS_SYNC_FILE_SKIPPED       0x00080000
  44. #define SDS_SYNC_DELETE_CONFLICT_MASK   (SDS_SYNC_DELETE_DELETE | SDS_SYNC_DELETE_RESTORE)
  45. #define SDS_SYNC_FILE_CONFLICT_MASK     (SDS_SYNC_CONFLICT_KEEPLOCAL | SDS_SYNC_CONFLICT_KEEPNET | SDS_SYNC_CONFLICT_KEEPBOTH)
  46. // Sync Flags used internally by CCscUpdate
  47. #define CSC_SYNC_OUT                0x00000001L
  48. #define CSC_SYNC_IN_QUICK           0x00000002L
  49. #define CSC_SYNC_IN_FULL            0x00000004L
  50. #define CSC_SYNC_SETTINGS           0x00000008L
  51. #define CSC_SYNC_MAYBOTHERUSER      0x00000010L
  52. #define CSC_SYNC_NOTIFY_SYSTRAY     0x00000020L
  53. #define CSC_SYNC_LOGOFF             0x00000040L
  54. #define CSC_SYNC_LOGON              0x00000080L
  55. #define CSC_SYNC_IDLE               0x00000100L
  56. #define CSC_SYNC_NONET              0x00000200L
  57. #define CSC_SYNC_PINFILES           0x00000400L
  58. #define CSC_SYNC_PIN_RECURSE        0x00000800L
  59. #define CSC_SYNC_OFWARNINGDONE      0x00001000L
  60. #define CSC_SYNC_CANCELLED          0x00002000L
  61. #define CSC_SYNC_SHOWUI_ALWAYS      0x00004000L
  62. #define CSC_SYNC_IGNORE_ACCESS      0x00008000L
  63. #define CSC_SYNC_SKIP_EFS           0x00010000L
  64. #define CSC_SYNC_EFS_WARNING_SHOWN  0x00020000L
  65. #define CSC_SYNC_RECONNECT          0x00040000L
  66. #define CSC_LOCALLY_MODIFIED    (FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED         
  67.                                     | FLAG_CSC_COPY_STATUS_LOCALLY_DELETED          
  68.                                     | FLAG_CSC_COPY_STATUS_LOCALLY_CREATED)
  69. HICON g_hCscIcon = NULL;
  70. // Used for marshalling data into the SyncMgr process
  71. typedef struct _CSC_UPDATE_DATA
  72. {
  73.     DWORD dwUpdateFlags;
  74.     DWORD dwFileBufferOffset;
  75. } CSC_UPDATE_DATA, *PCSC_UPDATE_DATA;
  76. LPTSTR GetErrorText(DWORD dwErr)
  77. {
  78.     UINT idString = (UINT)-1;
  79.     LPTSTR pszError = NULL;
  80.     switch (dwErr)
  81.     {
  82.     case ERROR_INVALID_NAME:
  83.         // "Files of this type cannot be made available offline."
  84.         idString = IDS_CACHING_DISALLOWED;
  85.         break;
  86.     }
  87.     if ((UINT)-1 != idString)
  88.     {
  89.         LoadStringAlloc(&pszError, g_hInstance, idString);
  90.     }
  91.     else if (NOERROR != dwErr)
  92.     {
  93.         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  94.                       NULL,
  95.                       dwErr,
  96.                       0,
  97.                       (LPTSTR)&pszError,
  98.                       1,
  99.                       NULL);
  100.     }
  101.     return pszError;
  102. }
  103. //*************************************************************
  104. //
  105. //  CscRegisterHandler
  106. //
  107. //  Purpose:    Register/unregister CSC Update handler with SyncMgr
  108. //
  109. //  Parameters: bRegister - TRUE to register, FALSE to unregister
  110. //              punkSyncMgr - (optional) instance of SyncMgr to use
  111. //
  112. //  Return:     HRESULT
  113. //
  114. //*************************************************************
  115. HRESULT
  116. CscRegisterHandler(BOOL bRegister, LPUNKNOWN punkSyncMgr)
  117. {
  118.     HRESULT hr;
  119.     HRESULT hrComInit = E_FAIL;
  120.     ISyncMgrRegister *pSyncRegister = NULL;
  121.     const DWORD dwRegFlags = SYNCMGRREGISTERFLAG_CONNECT | SYNCMGRREGISTERFLAG_PENDINGDISCONNECT;
  122.     static BOOL s_bSyncHandlerRegistered = FALSE;
  123.     TraceEnter(TRACE_UPDATE, "CscRegisterHandler");
  124.     if (bRegister && s_bSyncHandlerRegistered)
  125.         TraceLeaveResult(S_OK); // already registered
  126.     // Note: if bRegister and s_bSyncHandlerRegistered are both FALSE, unregister anyway.
  127.     if (punkSyncMgr)
  128.     {
  129.         hr = punkSyncMgr->QueryInterface(IID_ISyncMgrRegister, (LPVOID*)&pSyncRegister);
  130.     }
  131.     else
  132.     {
  133.         hrComInit = CoInitialize(NULL);
  134.         hr = CoCreateInstance(CLSID_SyncMgr,
  135.                               NULL,
  136.                               CLSCTX_SERVER,
  137.                               IID_ISyncMgrRegister,
  138.                               (LPVOID*)&pSyncRegister);
  139.     }
  140.     FailGracefully(hr, "Unable to get ISyncMgrRegister interface");
  141.     if (bRegister)
  142.         hr = pSyncRegister->RegisterSyncMgrHandler(CLSID_CscUpdateHandler, NULL, dwRegFlags);
  143.     else
  144.         hr = pSyncRegister->UnregisterSyncMgrHandler(CLSID_CscUpdateHandler, dwRegFlags);
  145.     if (SUCCEEDED(hr))
  146.         s_bSyncHandlerRegistered = bRegister;
  147. exit_gracefully:
  148.     DoRelease(pSyncRegister);
  149.     if (SUCCEEDED(hrComInit))
  150.         CoUninitialize();
  151.     TraceLeaveResult(hr);
  152. }
  153. //*************************************************************
  154. //
  155. //  CscUpdateCache
  156. //
  157. //  Purpose:    Invoke SyncMgr to update the CSC cache
  158. //
  159. //  Parameters: pNamelist - list of files passed to the CSC SyncMgr handler
  160. //
  161. //
  162. //  Return:     HRESULT
  163. //
  164. //*************************************************************
  165. HRESULT
  166. CscUpdateCache(DWORD dwUpdateFlags, CscFilenameList *pfnl)
  167. {
  168.     HRESULT hr;
  169.     HRESULT hrComInit = E_FAIL;
  170.     ISyncMgrSynchronizeInvoke *pSyncInvoke = NULL;
  171.     DWORD dwSyncMgrFlags = 0;
  172.     ULONG cbDataLength = sizeof(CSC_UPDATE_DATA);
  173.     PCSC_UPDATE_DATA pUpdateData = NULL;
  174.     PCSC_NAMELIST_HDR pNamelist = NULL;
  175.     TraceEnter(TRACE_UPDATE, "CscUpdateCache");
  176.     hrComInit = CoInitialize(NULL);
  177.     hr = CoCreateInstance(CLSID_SyncMgr,
  178.                           NULL,
  179.                           CLSCTX_SERVER,
  180.                           IID_ISyncMgrSynchronizeInvoke,
  181.                           (LPVOID*)&pSyncInvoke);
  182.     FailGracefully(hr, "Unable to create SyncMgr object");
  183.     if (dwUpdateFlags & CSC_UPDATE_SELECTION)
  184.     {
  185.         if (NULL == pfnl || (0 == (CSC_UPDATE_SHOWUI_ALWAYS & dwUpdateFlags) && 0 == pfnl->GetShareCount()))
  186.             ExitGracefully(hr, E_INVALIDARG, "CSC_UPDATE_SELECTION with no selection");
  187.         pNamelist = pfnl->CreateListBuffer();
  188.         if (!pNamelist)
  189.             ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create namelist buffer");
  190.         cbDataLength += pNamelist->cbSize;
  191.     }
  192.     //
  193.     // Alloc a buffer for the cookie data
  194.     //
  195.     pUpdateData = (PCSC_UPDATE_DATA)LocalAlloc(LPTR, cbDataLength);
  196.     if (!pUpdateData)
  197.         ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");
  198.     pUpdateData->dwUpdateFlags = dwUpdateFlags;
  199.     if (pNamelist)
  200.     {
  201.         pUpdateData->dwFileBufferOffset = sizeof(CSC_UPDATE_DATA);
  202.         CopyMemory(ByteOffset(pUpdateData, pUpdateData->dwFileBufferOffset),
  203.                    pNamelist,
  204.                    pNamelist->cbSize);
  205.     }
  206.     if (dwUpdateFlags & CSC_UPDATE_STARTNOW)
  207.         dwSyncMgrFlags |= SYNCMGRINVOKE_STARTSYNC;
  208.     //
  209.     // Start SyncMgr
  210.     //
  211.     hr = pSyncInvoke->UpdateItems(dwSyncMgrFlags,
  212.                                   CLSID_CscUpdateHandler,
  213.                                   cbDataLength,
  214.                                   (LPBYTE)pUpdateData);
  215. exit_gracefully:
  216.     if (pNamelist)
  217.         CscFilenameList::FreeListBuffer(pNamelist);
  218.     if (pUpdateData)
  219.         LocalFree(pUpdateData);
  220.     DoRelease(pSyncInvoke);
  221.     if (SUCCEEDED(hrComInit))
  222.         CoUninitialize();
  223.     TraceLeaveResult(hr);
  224. }
  225. //*************************************************************
  226. //
  227. //  GetNewVersionName
  228. //
  229. //  Purpose:    Create unique names for copies of a file
  230. //
  231. //  Parameters: LPTSTR pszUNCPath - fully qualified UNC name of file
  232. //              LPTSTR pszShare - \servershare that file lives on
  233. //              LPTSTR pszDrive - drive mapping to use for net operations
  234. //              LPTSTR *ppszNewName - filename for new version returned here (must free)
  235. //
  236. //  Return:     Win32 error code
  237. //
  238. //*************************************************************
  239. DWORD
  240. GetNewVersionName(LPCTSTR pszUNCPath,
  241.                   LPCTSTR pszShare,
  242.                   LPCTSTR pszDrive,
  243.                   LPTSTR *ppszNewName)
  244. {
  245.     DWORD dwErr = NOERROR;
  246.     LPTSTR pszDriveLetterPath = NULL;
  247.     LPTSTR pszPath = NULL;
  248.     LPTSTR pszFile = NULL;
  249.     LPTSTR pszExt = NULL;
  250.     LPTSTR pszWildCardName = NULL;
  251.     TCHAR szUserName[MAX_USERNAME_CHARS];
  252.     ULONG nLength;
  253.     ULONG nMaxVersion = 0;
  254.     ULONG cOlderVersions = 0;
  255.     HANDLE hFind = INVALID_HANDLE_VALUE;
  256.     WIN32_FIND_DATA fd;
  257.     LPTSTR pszT;
  258.     TraceEnter(TRACE_UPDATE, "GetNewVersionName");
  259.     TraceAssert(pszUNCPath != NULL);
  260.     TraceAssert(ppszNewName != NULL);
  261.     *ppszNewName = NULL;
  262.     // 1. Split the path into components.
  263.     // 2. Build wildcard name "X:dirfoo (johndoe v*).txt"
  264.     // 3. Do a findfirst/findnext loop to get the min & max version #
  265.     //    and count the number of old versions.
  266.     // 4. Increment the max version # and build the new filename as:
  267.     //    "foo (johndoe v<max+1>).txt"
  268.     // Assume that the UNC name contains more than the share
  269.     TraceAssert(!StrCmpNI(pszUNCPath, pszShare, lstrlen(pszShare)));
  270.     TraceAssert(lstrlen(pszUNCPath) > lstrlen(pszShare));
  271.     // Copy the path (without \servershare)
  272.     if (!LocalAllocString(&pszPath, pszUNCPath + lstrlen(pszShare)))
  273.         ExitGracefully(dwErr, ERROR_OUTOFMEMORY, "LocalAllocString failed");
  274.     // Find the file part of the name
  275.     pszT = PathFindFileName(pszPath);
  276.     if (!pszT)
  277.         ExitGracefully(dwErr, ERROR_INVALID_PARAMETER, "Incomplete path");
  278.     // Copy the filename
  279.     if (!LocalAllocString(&pszFile, pszT))
  280.         ExitGracefully(dwErr, ERROR_OUTOFMEMORY, "LocalAllocString failed");
  281.     // Look for the file extension
  282.     pszT = PathFindExtension(pszFile);
  283.     if (pszT)
  284.     {
  285.         // Copy the extension and truncate the file root at this point
  286.         LocalAllocString(&pszExt, pszT);
  287.         *pszT = TEXT('');
  288.     }
  289.     // Truncate the path
  290.     PathRemoveFileSpec(pszPath);
  291.     // Get the user name
  292.     nLength = ARRAYSIZE(szUserName);
  293.     if (!GetUserName(szUserName, &nLength))
  294.         LoadString(g_hInstance, IDS_UNKNOWN_USER, szUserName, ARRAYSIZE(szUserName));
  295.     // Build the wildcard path "X:dirfoo (johndoe v*).txt"
  296.     nLength = FormatStringID(&pszWildCardName, g_hInstance, IDS_VERSION_FORMAT, pszFile, szUserName, c_szStar, pszExt);
  297.     if (!nLength)
  298.         ExitGracefully(dwErr, GetLastError(), "Unable to format string");
  299.     nLength += lstrlen(pszUNCPath) + lstrlen(szUserName);
  300.     pszDriveLetterPath = (LPTSTR)LocalAlloc(LPTR, MAX(nLength, ULONG(MAX_PATH)) * sizeof(TCHAR));
  301.     if (!pszDriveLetterPath)
  302.         ExitGracefully(dwErr, ERROR_OUTOFMEMORY, "LocalAlloc failed");
  303.     PathCombine(pszDriveLetterPath, pszDrive, pszPath);
  304.     PathAppend(pszDriveLetterPath, pszWildCardName);
  305.     nLength = (ULONG)(StrStr(pszWildCardName, c_szStar) - pszWildCardName); // remember where the '*' is
  306.     // Search for existing versions of the file with this username
  307.     hFind = FindFirstFile(pszDriveLetterPath, &fd);
  308.     if (hFind != INVALID_HANDLE_VALUE)
  309.     {
  310.         ULONG nVersion;
  311.         do
  312.         {
  313.             nVersion = StrToLong(&fd.cFileName[nLength]);
  314.             if (nVersion > nMaxVersion)
  315.             {
  316.                 nMaxVersion = nVersion;
  317.             }
  318.             cOlderVersions++;
  319.         }
  320.         while (FindNextFile(hFind, &fd));
  321.         FindClose(hFind);
  322.     }
  323.     // Build the new file name to return to the caller.
  324.     // This one is version nMaxVersion+1.
  325.     ULongToString(nMaxVersion+1, pszDriveLetterPath, lstrlen(pszDriveLetterPath));
  326.     nLength = FormatStringID(ppszNewName, g_hInstance, IDS_VERSION_FORMAT, pszFile, szUserName, pszDriveLetterPath, pszExt);
  327.     if (!nLength)
  328.         ExitGracefully(dwErr, GetLastError(), "Unable to format string");
  329. exit_gracefully:
  330.     LocalFreeString(&pszDriveLetterPath);
  331.     LocalFreeString(&pszPath);
  332.     LocalFreeString(&pszFile);
  333.     LocalFreeString(&pszExt);
  334.     LocalFreeString(&pszWildCardName);
  335.     if (NOERROR != dwErr)
  336.     {
  337.         LocalFreeString(ppszNewName);
  338.     }
  339.     TraceLeaveValue(dwErr);
  340. }
  341. //*************************************************************
  342. //
  343. //  ConflictDlgCallback
  344. //
  345. //  Purpose:    Display local or remote file from conflict dialog
  346. //
  347. //  Parameters: hWnd - conflict dialog handle (used as parent for UI)
  348. //              uMsg - one of RFCCM_*
  349. //              wParam - depends on uMsg (unused)
  350. //              lParam - pointer to context data (RFCDLGPARAM)
  351. //
  352. //
  353. //  Return:     TRUE on success, FALSE otherwise
  354. //
  355. //*************************************************************
  356. typedef struct _CONFLICT_DATA
  357. {
  358.     LPCTSTR pszShare;
  359.     LPCTSTR pszDrive;
  360. } CONFLICT_DATA;
  361. BOOL
  362. ConflictDlgCallback(HWND hWnd, UINT uMsg, WPARAM /*wParam*/, LPARAM lParam)
  363. {
  364.     RFCDLGPARAM *pdlgParam = (RFCDLGPARAM*)lParam;
  365.     CONFLICT_DATA cd = {0};
  366.     LPTSTR pszTmpName = NULL;
  367.     ULONG cchShare = 0;
  368.     LPTSTR szFile;
  369.     DWORD dwErr = NOERROR;
  370.     TraceEnter(TRACE_UPDATE, "ConflictDlgCallback");
  371.     if (NULL == pdlgParam)
  372.     {
  373.         TraceAssert(FALSE);
  374.         TraceLeaveValue(FALSE);
  375.     }
  376.     szFile = (LPTSTR)LocalAlloc(LMEM_FIXED,
  377.                                 MAX(StringByteSize(pdlgParam->pszLocation)
  378.                                     + StringByteSize(pdlgParam->pszFilename), MAX_PATH_BYTES));
  379.     if (!szFile)
  380.         TraceLeaveValue(FALSE);
  381.     if (pdlgParam->lCallerData)
  382.         cd = *(CONFLICT_DATA*)pdlgParam->lCallerData;
  383.     if (cd.pszShare)
  384.         cchShare = lstrlen(cd.pszShare);
  385.     switch (uMsg)
  386.     {
  387.     case RFCCM_VIEWLOCAL:
  388.         // Build UNC path and view what's in the cache
  389.         PathCombine(szFile, pdlgParam->pszLocation, pdlgParam->pszFilename);
  390.         dwErr = OpenOfflineFile(szFile);
  391.         break;
  392.     case RFCCM_VIEWNETWORK:
  393.         // Build drive letter (non-UNC) path and ShellExecute it
  394.         PathCombine(szFile, cd.pszDrive, pdlgParam->pszLocation + cchShare);
  395.         PathAppend(szFile, pdlgParam->pszFilename);
  396.         {
  397.             SHELLEXECUTEINFO si = {0};
  398.             si.cbSize           = sizeof(si);
  399.             si.fMask            = SEE_MASK_FLAG_NO_UI;
  400.             si.hwnd             = hWnd;
  401.             si.lpFile           = szFile;
  402.             si.nShow            = SW_NORMAL;
  403.             Trace((TEXT("ShellExecuting "%s""), szFile));
  404.             if (!ShellExecuteEx(&si))
  405.                 dwErr = GetLastError();
  406.         }
  407.         break;
  408.     }
  409.     if (NOERROR != dwErr)
  410.         CscWin32Message(hWnd, dwErr, CSCUI::SEV_ERROR);
  411.     LocalFree(szFile);
  412.     TraceLeaveValue(TRUE);
  413. }
  414. //*************************************************************
  415. //
  416. //  ShowConflictDialog
  417. //
  418. //  Purpose:    Invoke the conflict resolution dialog
  419. //
  420. //  Parameters: hWndParent - dialog parent window
  421. //              pszUNCPath - full UNC of file that conflicts
  422. //              pszNewName - filespec to use for new copy of file (e.g. "foo (johndoe v1).txt"
  423. //              pszShare - "\servershare"
  424. //              pszDrive - "X:" drive mapping of remote connection
  425. //              pfdLocal - Information about local file
  426. //              pfdRemote - Information about remote file
  427. //
  428. //
  429. //  Return:     HRESULT
  430. //
  431. //*************************************************************
  432. typedef int (WINAPI *PFNSYNCMGRRESOLVECONFLICT)(HWND hWndParent, RFCDLGPARAM *pdlgParam);
  433. TCHAR const c_szSyncMgrDll[]        = TEXT("mobsync.dll");
  434. #ifdef UNICODE
  435. CHAR  const c_szResolveConflict[]   = "SyncMgrResolveConflictW";
  436. #else
  437. CHAR  const c_szResolveConflict[]   = "SyncMgrResolveConflictA";
  438. #endif
  439. BOOL FileHasAssociation(LPCTSTR pszFile)
  440. {
  441.     HRESULT hr = HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  442.     if (pszFile)
  443.     {
  444.         pszFile = PathFindExtension(pszFile);
  445.         if (pszFile && *pszFile)
  446.         {
  447.             IQueryAssociations *pAssoc = NULL;
  448.             hr = AssocCreate(CLSID_QueryAssociations,
  449.                              IID_IQueryAssociations,
  450.                              (LPVOID*)&pAssoc);
  451.             if (SUCCEEDED(hr))
  452.             {
  453.                 hr = pAssoc->Init(ASSOCF_IGNOREBASECLASS, pszFile, NULL, NULL);
  454.                 pAssoc->Release();
  455.             }
  456.         }
  457.     }
  458.     return SUCCEEDED(hr);
  459. }
  460. int
  461. ShowConflictDialog(HWND hWndParent,
  462.                    LPCTSTR pszUNCPath,
  463.                    LPCTSTR pszNewName,
  464.                    LPCTSTR pszShare,
  465.                    LPCTSTR pszDrive,
  466.                    LPWIN32_FIND_DATA pfdLocal,
  467.                    LPWIN32_FIND_DATA pfdRemote)
  468. {
  469.     int nResult = 0;
  470.     TCHAR szUser[MAX_USERNAME_CHARS];
  471.     LPTSTR pszPath = NULL;
  472.     LPTSTR pszFile = NULL;
  473.     TCHAR szRemoteDate[MAX_PATH];
  474.     TCHAR szLocalDate[MAX_PATH];
  475.     ULONG nLength;
  476.     SYSTEMTIME st;
  477.     RFCDLGPARAM dp = {0};
  478.     CONFLICT_DATA cd;
  479.     BOOL bLocalIsDir = FALSE;
  480.     BOOL bRemoteIsDir = FALSE;
  481.     static PFNSYNCMGRRESOLVECONFLICT pfnResolveConflict = NULL;
  482.     TraceEnter(TRACE_UPDATE, "ShowConflictDialog");
  483.     TraceAssert(pszUNCPath);
  484.     if (NULL == pfnResolveConflict)
  485.     {
  486.         // The CSC Update handler is loaded by SyncMgr, so assume the SyncMgr
  487.         // dll is already loaded.  We don't want to link to the LIB to keep
  488.         // SyncMgr from loading every time our context menu or icon overlay
  489.         // handler is loaded (for example).
  490.         HMODULE hSyncMgrDll = GetModuleHandle(c_szSyncMgrDll);
  491.         if (NULL != hSyncMgrDll)
  492.             pfnResolveConflict = (PFNSYNCMGRRESOLVECONFLICT)GetProcAddress(hSyncMgrDll,
  493.                                                                            c_szResolveConflict);
  494.         if (NULL == pfnResolveConflict)
  495.             return 0;
  496.     }
  497.     TraceAssert(NULL != pfnResolveConflict);
  498.     szUser[0] = TEXT('');
  499.     nLength = ARRAYSIZE(szUser);
  500.     GetUserName(szUser, &nLength);
  501.     szRemoteDate[0] = TEXT('');
  502.     if (NULL != pfdRemote)
  503.     {
  504.         DWORD dwFlags = FDTF_DEFAULT;
  505.         SHFormatDateTime(&pfdRemote->ftLastWriteTime, &dwFlags, szRemoteDate, ARRAYSIZE(szRemoteDate));
  506.         if (pfdRemote->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  507.             bRemoteIsDir = TRUE;
  508.     }
  509.     szLocalDate[0] = TEXT('');
  510.     if (NULL != pfdLocal)
  511.     {
  512.         DWORD dwFlags = FDTF_DEFAULT;
  513.         SHFormatDateTime(&pfdLocal->ftLastWriteTime, &dwFlags, szLocalDate, ARRAYSIZE(szLocalDate));
  514.         if (pfdLocal->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  515.             bLocalIsDir = TRUE;
  516.     }
  517.     if (!LocalAllocString(&pszPath, pszUNCPath))
  518.         ExitGracefully(nResult, 0, "LocalAllocString failed");
  519.     pszFile = PathFindFileName(pszUNCPath);
  520.     PathRemoveFileSpec(pszPath);
  521.     dp.dwFlags              = RFCF_APPLY_ALL;
  522.     dp.pszFilename          = pszFile;
  523.     dp.pszLocation          = pszPath;
  524.     dp.pszNewName           = pszNewName;
  525.     dp.pszNetworkModifiedBy = NULL;
  526.     dp.pszLocalModifiedBy   = szUser;
  527.     dp.pszNetworkModifiedOn = szRemoteDate;
  528.     dp.pszLocalModifiedOn   = szLocalDate;
  529.     dp.pfnCallBack          = NULL;
  530.     dp.lCallerData          = 0;
  531.     // Only turn on the View buttons (set a callback) if we're
  532.     // dealing with files that have associations.
  533.     if (!(bLocalIsDir || bRemoteIsDir) && FileHasAssociation(pszFile))
  534.     {
  535.         // Save both the share name and drive letter for building paths to view files
  536.         cd.pszShare = pszShare;
  537.         cd.pszDrive = pszDrive;
  538.         dp.pfnCallBack      = ConflictDlgCallback;
  539.         dp.lCallerData      = (LPARAM)&cd;
  540.     }
  541.     nResult = (*pfnResolveConflict)(hWndParent, &dp);
  542. exit_gracefully:
  543.     LocalFreeString(&pszPath);
  544.     // No need to free pszFile
  545.     TraceLeaveValue(nResult);
  546. }
  547. ///////////////////////////////////////////////////////////////////////////////
  548. //                                                                           //
  549. // SyncMgr integration implementation                                        //
  550. //                                                                           //
  551. ///////////////////////////////////////////////////////////////////////////////
  552. CCscUpdate::CCscUpdate() : m_cRef(1), m_ShareLog(HKEY_CURRENT_USER, c_szCSCShareKey),
  553.   m_pSyncMgrCB(NULL), m_hSyncThreads(NULL),
  554.   m_pFileList(NULL), m_hSyncItems(NULL), m_hwndDlgParent(NULL),
  555.   m_hSyncInProgMutex(NULL), m_pConflictPinList(NULL),
  556.   m_pSilentFolderList(NULL), m_pSpecialFolderList(NULL)
  557. {
  558.     DllAddRef();
  559.     InitializeCriticalSection(&m_csThreadList);
  560.     if (!g_hCscIcon)
  561.         g_hCscIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_CSCUI_ICON));
  562.     m_hSyncMutex = CreateMutex(NULL, FALSE, c_szSyncMutex);
  563. }
  564. CCscUpdate::~CCscUpdate()
  565. {
  566.     TraceEnter(TRACE_UPDATE, "CCscUpdate::~CCscUpdate");
  567.     SyncCompleted();
  568.     TraceAssert(NULL == m_hSyncInProgMutex);
  569.     // We should never get here while a sync thread is still running
  570.     TraceAssert(NULL == m_hSyncThreads || 0 == DPA_GetPtrCount(m_hSyncThreads));
  571.     if (NULL != m_hSyncThreads)
  572.         DPA_Destroy(m_hSyncThreads);
  573.     DeleteCriticalSection(&m_csThreadList);
  574.     if (NULL != m_hSyncItems)
  575.         DSA_Destroy(m_hSyncItems);
  576.     DoRelease(m_pSyncMgrCB);
  577.     delete m_pFileList;
  578.     delete m_pConflictPinList;
  579.     delete m_pSilentFolderList;
  580.     delete m_pSpecialFolderList;
  581.     if (NULL != m_hSyncMutex)
  582.         CloseHandle(m_hSyncMutex);
  583.     DllRelease();
  584.     TraceLeaveVoid();
  585. }
  586. HRESULT WINAPI
  587. CCscUpdate::CreateInstance(REFIID riid, LPVOID *ppv)
  588. {
  589.     HRESULT hr;
  590.     CCscUpdate *pThis;
  591.     TraceEnter(TRACE_UPDATE, "CCscUpdate::CreateInstance");
  592.     TraceAssert(IsCSCEnabled());
  593.     pThis = new CCscUpdate;
  594.     if (pThis)
  595.     {
  596.         hr = pThis->QueryInterface(riid, ppv);
  597.         pThis->Release();
  598.     }
  599.     else
  600.         hr = E_OUTOFMEMORY;
  601.     TraceLeaveResult(hr);
  602. }
  603. ///////////////////////////////////////////////////////////////////////////////
  604. //                                                                           //
  605. // SyncMgr integration implementation (IUnknown)                             //
  606. //                                                                           //
  607. ///////////////////////////////////////////////////////////////////////////////
  608. STDMETHODIMP CCscUpdate::QueryInterface(REFIID riid, void **ppv)
  609. {
  610.     static const QITAB qit[] =
  611.     {
  612.         QITABENT(CCscUpdate, ISyncMgrSynchronize),
  613.         { 0 },
  614.     };
  615.     return QISearch(this, qit, riid, ppv);
  616. }
  617. STDMETHODIMP_(ULONG) CCscUpdate::AddRef()
  618. {
  619.     return InterlockedIncrement(&m_cRef);
  620. }
  621. STDMETHODIMP_(ULONG) CCscUpdate::Release()
  622. {
  623.     if (InterlockedDecrement(&m_cRef))
  624.         return m_cRef;
  625.     delete this;
  626.     return 0;
  627. }
  628. ///////////////////////////////////////////////////////////////////////////////
  629. //                                                                           //
  630. // Sync Manager integration implementation (ISyncMgrSynchronize)             //
  631. //                                                                           //
  632. ///////////////////////////////////////////////////////////////////////////////
  633. STDMETHODIMP
  634. CCscUpdate::Initialize(DWORD /*dwReserved*/,
  635.                        DWORD dwSyncFlags,
  636.                        DWORD cbCookie,
  637.                        const BYTE *pCookie)
  638. {
  639.     HRESULT hr = S_OK;
  640.     HKEY hkCSC;
  641.     BOOL bNoNet = TRUE;
  642.     TraceEnter(TRACE_UPDATE, "CCscUpdate::Initialize");
  643.     TraceAssert(IsCSCEnabled());
  644.     if (!(SYNCMGRFLAG_SETTINGS & dwSyncFlags) && ::IsSyncInProgress())
  645.     {
  646.         //
  647.         // We need to guard against running multiple syncs at the 
  648.         // same time.  User notification in the UI is handled where
  649.         // the UI code calls CscUpdate().  This is so that the UI 
  650.         // message contains the proper context with respect to what 
  651.         // the user is doing.
  652.         //
  653.         TraceLeaveResult(E_FAIL);
  654.     }
  655.     m_dwSyncFlags = 0;
  656.     delete m_pFileList;
  657.     m_pFileList = NULL;
  658.     delete m_pConflictPinList;
  659.     m_pConflictPinList = NULL;
  660.     // We used to get the tray status to check for NoNet, but
  661.     // there's a timing problem at logon (the tray window may not
  662.     // be created yet).  So ask RDR instead.  If this call fails,
  663.     // then RDR must be dead, so bNoNet defaults to TRUE.
  664.     CSCIsServerOffline(NULL, &bNoNet);
  665.     switch (dwSyncFlags & SYNCMGRFLAG_EVENTMASK)
  666.     {
  667.     case SYNCMGRFLAG_CONNECT:               // Logon
  668.         if (bNoNet)
  669.             ExitGracefully(hr, E_FAIL, "No Logon sync when no net");
  670.         m_dwSyncFlags = CSC_SYNC_OUT | CSC_SYNC_LOGON | CSC_SYNC_NOTIFY_SYSTRAY; // | CSC_SYNC_RECONNECT;
  671.         break;
  672.     case SYNCMGRFLAG_PENDINGDISCONNECT:     // Logoff
  673.         if (bNoNet)
  674.             ExitGracefully(hr, E_FAIL, "No Logoff sync when no net");
  675.         m_dwSyncFlags = CSC_SYNC_LOGOFF;
  676.         if (CConfig::eSyncFull == CConfig::GetSingleton().SyncAtLogoff())
  677.             m_dwSyncFlags |= CSC_SYNC_OUT | CSC_SYNC_IN_FULL;
  678.         else
  679.             m_dwSyncFlags |= CSC_SYNC_IN_QUICK;
  680.         break;
  681.     case SYNCMGRFLAG_INVOKE:                // CscUpdateCache
  682.         if (pCookie != NULL && cbCookie > 0)
  683.         {
  684.             PCSC_UPDATE_DATA pUpdateData = (PCSC_UPDATE_DATA)pCookie;
  685.             TraceAssert(cbCookie >= sizeof(CSC_UPDATE_DATA));
  686.             DWORD dwUpdateFlags = pUpdateData->dwUpdateFlags;
  687.             if (dwUpdateFlags & CSC_UPDATE_SELECTION)
  688.             {
  689.                 TraceAssert(cbCookie > sizeof(CSC_UPDATE_DATA));
  690.                 // Create the filelist from the selection provided
  691.                 m_pFileList = new CscFilenameList((PCSC_NAMELIST_HDR)ByteOffset(pUpdateData, pUpdateData->dwFileBufferOffset),
  692.                                                   true);
  693.                 if (!m_pFileList)
  694.                     ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create CscFilenameList object");
  695.                 if (!m_pFileList->IsValid())
  696.                     ExitGracefully(hr, E_FAIL, "Unable to initialize CscFilenameList object");
  697.                 if (CSC_UPDATE_SHOWUI_ALWAYS & dwUpdateFlags)
  698.                 {
  699.                     m_dwSyncFlags |= CSC_SYNC_SHOWUI_ALWAYS;
  700.                 }
  701.                 else if (0 == m_pFileList->GetShareCount())
  702.                     ExitGracefully(hr, E_UNEXPECTED, "CSC_UPDATE_SELECTION with no selection");
  703.             }
  704.             if (dwUpdateFlags & CSC_UPDATE_RECONNECT)
  705.             {
  706.                 m_dwSyncFlags |= CSC_SYNC_RECONNECT;
  707.             }
  708.             if (dwUpdateFlags & CSC_UPDATE_NOTIFY_DONE)
  709.             {
  710.                 //
  711.                 // Caller of CscUpdateCache want's systray notification
  712.                 // when sync is complete.
  713.                 //
  714.                 m_dwSyncFlags |= CSC_SYNC_NOTIFY_SYSTRAY;
  715.             }
  716.             if (dwUpdateFlags & CSC_UPDATE_FILL_ALL)
  717.                 m_dwSyncFlags |= CSC_SYNC_IN_FULL;
  718.             else if (dwUpdateFlags & CSC_UPDATE_FILL_QUICK)
  719.                 m_dwSyncFlags |= CSC_SYNC_IN_QUICK;
  720.             if (dwUpdateFlags & CSC_UPDATE_REINT)
  721.                 m_dwSyncFlags |= CSC_SYNC_OUT;
  722.             if (dwUpdateFlags & CSC_UPDATE_PIN_RECURSE)
  723.                 m_dwSyncFlags |= CSC_SYNC_PINFILES | CSC_SYNC_PIN_RECURSE | CSC_SYNC_IN_QUICK;
  724.             else if (dwUpdateFlags & CSC_UPDATE_PINFILES)
  725.                 m_dwSyncFlags |= CSC_SYNC_PINFILES | CSC_SYNC_IN_QUICK;
  726.             if (dwUpdateFlags & CSC_UPDATE_IGNORE_ACCESS)
  727.                 m_dwSyncFlags |= CSC_SYNC_IGNORE_ACCESS;
  728.         }
  729.         break;
  730.     case SYNCMGRFLAG_IDLE:                  // Auto-sync at idle time
  731.         if (bNoNet)
  732.             ExitGracefully(hr, E_FAIL, "No idle sync when no net");
  733.         m_dwSyncFlags = CSC_SYNC_OUT | CSC_SYNC_IN_QUICK | CSC_SYNC_IDLE | CSC_SYNC_NOTIFY_SYSTRAY;
  734.         break;
  735.     case SYNCMGRFLAG_MANUAL:                // Run "mobsync.exe"
  736.         m_dwSyncFlags = CSC_SYNC_OUT | CSC_SYNC_IN_FULL | CSC_SYNC_NOTIFY_SYSTRAY | CSC_SYNC_RECONNECT;
  737.         break;
  738.     case SYNCMGRFLAG_SCHEDULED:             // User scheduled sync
  739.         m_dwSyncFlags = CSC_SYNC_OUT | CSC_SYNC_IN_FULL | CSC_SYNC_NOTIFY_SYSTRAY;
  740.         break;
  741.     }
  742.     if (!(m_dwSyncFlags & CSC_SYNC_PINFILES))
  743.         m_dwSyncFlags |= CSC_SYNC_SKIP_EFS; // skip EFS if not pinning
  744.     if (dwSyncFlags & SYNCMGRFLAG_SETTINGS)
  745.         m_dwSyncFlags |= CSC_SYNC_SETTINGS;
  746.     if (!m_dwSyncFlags)
  747.         ExitGracefully(hr, E_UNEXPECTED, "Nothing to do");
  748.     if (dwSyncFlags & SYNCMGRFLAG_MAYBOTHERUSER)
  749.         m_dwSyncFlags |= CSC_SYNC_MAYBOTHERUSER;
  750.     if (bNoNet)
  751.         m_dwSyncFlags |= CSC_SYNC_NONET;
  752.     GetSilentFolderList();
  753. exit_gracefully:
  754.     TraceLeaveResult(hr);
  755. }
  756. STDMETHODIMP
  757. CCscUpdate::GetHandlerInfo(LPSYNCMGRHANDLERINFO *ppSyncMgrHandlerInfo)
  758. {
  759.     HRESULT hr = S_OK;
  760.     LPSYNCMGRHANDLERINFO pHandlerInfo;
  761.     TraceEnter(TRACE_UPDATE, "CCscUpdate::GetHandlerInfo");
  762.     if (NULL == ppSyncMgrHandlerInfo)
  763.         TraceLeaveResult(E_INVALIDARG);
  764.     *ppSyncMgrHandlerInfo = NULL;
  765.     pHandlerInfo = (LPSYNCMGRHANDLERINFO)CoTaskMemAlloc(sizeof(SYNCMGRHANDLERINFO));
  766.     if (NULL == pHandlerInfo)
  767.         ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");
  768.     pHandlerInfo->cbSize = sizeof(SYNCMGRHANDLERINFO);
  769.     pHandlerInfo->hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_CSCUI_ICON));
  770.     pHandlerInfo->SyncMgrHandlerFlags = SYNCMGRHANDLER_HASPROPERTIES | SYNCMGRHANDLER_MAYESTABLISHCONNECTION;
  771.     LoadStringW(g_hInstance,
  772.                 IDS_APPLICATION,
  773.                 pHandlerInfo->wszHandlerName,
  774.                 ARRAYSIZE(pHandlerInfo->wszHandlerName));
  775.     *ppSyncMgrHandlerInfo = pHandlerInfo;
  776. exit_gracefully:
  777.     TraceLeaveResult(hr);
  778. }
  779. STDMETHODIMP
  780. CCscUpdate::EnumSyncMgrItems(LPSYNCMGRENUMITEMS *ppenum)
  781. {
  782.     HRESULT hr;
  783.     PUPDATEENUM pNewEnum;
  784.     TraceEnter(TRACE_UPDATE, "CCscUpdate::EnumSyncMgrItems");
  785.     *ppenum = NULL;
  786.     pNewEnum = new CUpdateEnumerator(this);
  787.     if (pNewEnum)
  788.     {
  789.         hr = pNewEnum->QueryInterface(IID_ISyncMgrEnumItems, (LPVOID*)ppenum);
  790.         pNewEnum->Release();
  791.     }
  792.     else
  793.         hr = E_OUTOFMEMORY;
  794.     TraceLeaveResult(hr);
  795. }
  796. STDMETHODIMP
  797. CCscUpdate::GetItemObject(REFSYNCMGRITEMID /*rItemID*/, REFIID /*riid*/, LPVOID * /*ppv*/)
  798. {
  799.     return E_NOTIMPL;
  800. }
  801. STDMETHODIMP
  802. CCscUpdate::ShowProperties(HWND hWndParent, REFSYNCMGRITEMID rItemID)
  803. {
  804.     CSCEntry *pShareEntry;
  805.     LPCTSTR pszShareName = TEXT("");
  806.     pShareEntry = m_ShareLog.Get(rItemID);
  807.     // We don't enumerate shares to SyncMgr unless a share entry
  808.     // exists in the registry, so m_ShareLog.Get should never fail here.
  809.     if (pShareEntry)
  810.         pszShareName = pShareEntry->Name();
  811.     COfflineFilesFolder::Open();
  812.         // Notify SyncMgr that the ShowProperties is done.
  813.     if (NULL != m_pSyncMgrCB)
  814.         m_pSyncMgrCB->ShowPropertiesCompleted(S_OK);
  815.     return S_OK;
  816. }
  817. STDMETHODIMP
  818. CCscUpdate::SetProgressCallback(LPSYNCMGRSYNCHRONIZECALLBACK pCallback)
  819. {
  820.     TraceEnter(TRACE_UPDATE, "CCscUpdate::SetProgressCallback");
  821.     DoRelease(m_pSyncMgrCB);
  822.     m_pSyncMgrCB = pCallback;
  823.     if (m_pSyncMgrCB)
  824.         m_pSyncMgrCB->AddRef();
  825.     TraceLeaveResult(S_OK);
  826. }
  827. STDMETHODIMP
  828. CCscUpdate::PrepareForSync(ULONG cNumItems,
  829.                            SYNCMGRITEMID *pItemID,
  830.                            HWND /*hWndParent*/,
  831.                            DWORD /*dwReserved*/)
  832. {
  833.     HRESULT hr = S_OK;
  834.     TraceEnter(TRACE_UPDATE, "CCscUpdate::PrepareForSync");
  835.     TraceAssert(0 != cNumItems);
  836.     TraceAssert(NULL != pItemID);
  837.     //
  838.     // Copy the list of item ID's
  839.     //
  840.     if (NULL == m_hSyncItems)
  841.     {
  842.         m_hSyncItems = DSA_Create(sizeof(SYNCMGRITEMID), 4);
  843.         if (NULL == m_hSyncItems)
  844.             ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create DSA for SYNCMGRITEMID list");
  845.     }
  846.     else
  847.         DSA_DeleteAllItems(m_hSyncItems);
  848.     while (cNumItems--)
  849.         DSA_AppendItem(m_hSyncItems, pItemID++);
  850. exit_gracefully:
  851.     // ISyncMgrSynchronize::PrepareForSync is now an asynchronous call
  852.     // so we could create another thread to do the work and return from
  853.     // this call immediately.  However, since all we do is copy the list
  854.     // of Item IDs, let's do it here and call
  855.     // m_pSyncMgrCB->PrepareForSyncCompleted before returning.
  856.     if (NULL != m_pSyncMgrCB)
  857.         m_pSyncMgrCB->PrepareForSyncCompleted(hr);
  858.     TraceLeaveResult(hr);
  859. }
  860. STDMETHODIMP
  861. CCscUpdate::Synchronize(HWND hWndParent)
  862. {
  863.     HRESULT hr = E_FAIL;
  864.     ULONG cItems = 0;
  865.     BOOL bConnectionEstablished = FALSE;
  866.     TraceEnter(TRACE_UPDATE, "CCscUpdate::Synchronize");
  867.     if (NULL != m_hSyncItems)
  868.         cItems = DSA_GetItemCount(m_hSyncItems);
  869.     //
  870.     // Don't want systray UI updates while syncing.
  871.     // Whenever the systray UI is updated, the code checks first
  872.     // for this global mutex object.  If it's non-signaled, the
  873.     // systray knows there's a sync in progress and the UI isn't
  874.     // updated.
  875.     //
  876.     TraceAssert(NULL == m_hSyncInProgMutex);
  877.     m_hSyncInProgMutex = CreateMutex(NULL, TRUE, c_szSyncInProgMutex);
  878.     if (0 == cItems)
  879.     {
  880.         ExitGracefully(hr, E_UNEXPECTED, "Nothing to synchronize");
  881.     }
  882.     else if (1 == cItems)
  883.     {
  884.         SYNCMGRITEMID *pItemID = (SYNCMGRITEMID*)DSA_GetItemPtr(m_hSyncItems, 0);
  885.         if (NULL != pItemID && IsEqualGUID(GUID_CscNullSyncItem, *pItemID))
  886.         {
  887.             //
  888.             // A single item in the DSA and it's our "null sync" GUID.
  889.             // This means we really have nothing to sync but the invoker 
  890.             // of the sync wants to see some SyncMgr progress UI.  In 
  891.             // this scenario the update item enumerator already enumerated
  892.             // the "null sync" item.  Here we set this single item's progress
  893.             // UI info to 100% complete and skip any sync activity.
  894.             //
  895.             SYNCMGRPROGRESSITEM spi = {0};
  896.             spi.mask = SYNCMGRPROGRESSITEM_STATUSTYPE |
  897.                        SYNCMGRPROGRESSITEM_STATUSTEXT |
  898.                        SYNCMGRPROGRESSITEM_PROGVALUE | 
  899.                        SYNCMGRPROGRESSITEM_MAXVALUE;
  900.             spi.cbSize        = sizeof(spi);
  901.             spi.dwStatusType  = SYNCMGRSTATUS_SUCCEEDED;
  902.             spi.lpcStatusText = L" ";
  903.             spi.iProgValue    = 1;
  904.             spi.iMaxValue     = 1;
  905.             m_pSyncMgrCB->Progress(GUID_CscNullSyncItem, &spi);
  906.             m_pSyncMgrCB->SynchronizeCompleted(S_OK);
  907.             ExitGracefully(hr, NOERROR, "Nothing to sync.  Progress UI displayed");
  908.         }
  909.     }
  910.     m_hwndDlgParent = hWndParent;
  911.     // We can pin autocached files without a net (no sync required);
  912.     // otherwise we need to establish a RAS connection to do anything.
  913.     if ((m_dwSyncFlags & CSC_SYNC_NONET) && !(m_dwSyncFlags & CSC_SYNC_PINFILES))
  914.     {
  915.         hr = m_pSyncMgrCB->EstablishConnection(NULL, 0);
  916.         FailGracefully(hr, "Unable to establish RAS connection");
  917.         bConnectionEstablished = TRUE;
  918.     }
  919.     // For each share, kick off a thread to do the work
  920.     while (cItems > 0)
  921.     {
  922.         SYNCMGRITEMID *pItemID;
  923.         CSCEntry *pShareEntry;
  924.         --cItems;
  925.         pItemID = (SYNCMGRITEMID*)DSA_GetItemPtr(m_hSyncItems, cItems);
  926.         pShareEntry = m_ShareLog.Get(*pItemID);
  927.         // We don't enumerate shares to SyncMgr unless a share entry
  928.         // exists in the registry, so m_ShareLog.Get should never fail here.
  929.         if (NULL == pShareEntry)
  930.             ExitGracefully(hr, E_UNEXPECTED, "No share entry");
  931.         hr = SynchronizeShare(pItemID, pShareEntry->Name(), bConnectionEstablished);
  932.         DSA_DeleteItem(m_hSyncItems, cItems);
  933.         FailGracefully(hr, "Unable to create sync thread");
  934.     }
  935.     TraceAssert(0 == DSA_GetItemCount(m_hSyncItems));
  936. exit_gracefully:
  937.     if (FAILED(hr))
  938.         SetItemStatus(GUID_NULL, SYNCMGRSTATUS_STOPPED);
  939.     TraceLeaveResult(hr);
  940. }
  941. STDMETHODIMP
  942. CCscUpdate::SetItemStatus(REFSYNCMGRITEMID rItemID,
  943.                           DWORD dwSyncMgrStatus)
  944. {
  945.     HRESULT hr = E_FAIL;
  946.     ULONG cItems;
  947.     BOOL bAllItems;
  948.     TraceEnter(TRACE_UPDATE, "CCscUpdate::SetItemStatus");
  949.     if (SYNCMGRSTATUS_SKIPPED != dwSyncMgrStatus && SYNCMGRSTATUS_STOPPED != dwSyncMgrStatus)
  950.         TraceLeaveResult(E_NOTIMPL);
  951.     bAllItems = FALSE;
  952.     if (SYNCMGRSTATUS_STOPPED == dwSyncMgrStatus)
  953.     {
  954.         bAllItems = TRUE;
  955.         m_dwSyncFlags |= CSC_SYNC_CANCELLED;
  956.     }
  957.     // SetItemStatus can be called between PrepareForSync and Synchronize, in
  958.     // in which case the correct thing to do is remove the item from m_hSyncItems.
  959.     if (NULL != m_hSyncItems)
  960.     {
  961.         cItems = DSA_GetItemCount(m_hSyncItems);
  962.         while (cItems > 0)
  963.         {
  964.             SYNCMGRITEMID *pItemID;
  965.             --cItems;
  966.             pItemID = (SYNCMGRITEMID*)DSA_GetItemPtr(m_hSyncItems, cItems);
  967.             if (bAllItems || IsEqualGUID(rItemID, *pItemID))
  968.             {
  969.                 // Remove the item from the list of items to sync
  970.                 DSA_DeleteItem(m_hSyncItems, cItems);
  971.                 if (!bAllItems)
  972.                     ExitGracefully(hr, S_OK, "Skipping item");
  973.             }
  974.         }
  975.     }
  976.     // Lookup the thread for the item ID and set its status
  977.     // to cause it to terminate.
  978.     hr = SetSyncThreadStatus(SyncStop, bAllItems ? GUID_NULL : rItemID);
  979. exit_gracefully:
  980.     TraceLeaveResult(hr);
  981. }
  982. STDMETHODIMP
  983. CCscUpdate::ShowError(HWND /*hWndParent*/ , REFSYNCMGRERRORID /*ErrorID*/)
  984. {
  985.     return E_NOTIMPL;
  986. }
  987. HRESULT
  988. CCscUpdate::SynchronizeShare(SYNCMGRITEMID *pItemID, LPCTSTR pszShareName, BOOL bRasConnected)
  989. {
  990.     HRESULT hr = S_OK;
  991.     DWORD dwThreadID;
  992.     PSYNCTHREADDATA pThreadData;
  993.     ULONG cbShareName = 0;
  994.     TraceEnter(TRACE_UPDATE, "CCscUpdate::SynchronizeShare");
  995.     TraceAssert(NULL != pItemID);
  996.     TraceAssert(NULL != pszShareName);
  997.     TraceAssert(*pszShareName);
  998.     EnterCriticalSection(&m_csThreadList);
  999.     if (NULL == m_hSyncThreads)
  1000.         m_hSyncThreads = DPA_Create(4);
  1001.     LeaveCriticalSection(&m_csThreadList);
  1002.     if (NULL == m_hSyncThreads)
  1003.         ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create DPA for threads");
  1004.     cbShareName = StringByteSize(pszShareName);
  1005.     pThreadData = (PSYNCTHREADDATA)LocalAlloc(LPTR, sizeof(SYNCTHREADDATA) + cbShareName);
  1006.     if (!pThreadData)
  1007.         ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");
  1008.     pThreadData->pThis = this;
  1009.     pThreadData->ItemID = *pItemID;
  1010.     pThreadData->pszShareName = (LPTSTR)(pThreadData + 1);
  1011.     CopyMemory(pThreadData->pszShareName, pszShareName, cbShareName);
  1012.     //
  1013.     // If we established a RAS connection, then it will go away
  1014.     // right after the sync completes, so there's no point trying
  1015.     // to reconnect.  That is, only check CSC_SYNC_RECONNECT and
  1016.     // add the share to the reconnect list if we aren't doing RAS.
  1017.     //
  1018.     if (bRasConnected)
  1019.     {
  1020.         pThreadData->dwSyncStatus |= SDS_SYNC_RAS_CONNECTED;
  1021.     }
  1022.     else if (m_dwSyncFlags & CSC_SYNC_RECONNECT)
  1023.     {
  1024.         CscFilenameList::HSHARE hShare;
  1025.         m_ReconnectList.AddShare(pszShareName, &hShare);
  1026.     }
  1027.     pThreadData->hThread = CreateThread(NULL,
  1028.                                         0,
  1029.                                         _SyncThread,
  1030.                                         pThreadData,
  1031.                                         CREATE_SUSPENDED,
  1032.                                         &dwThreadID);
  1033.     if (NULL != pThreadData->hThread)
  1034.     {
  1035.         EnterCriticalSection(&m_csThreadList);
  1036.         DPA_AppendPtr(m_hSyncThreads, pThreadData);
  1037.         LeaveCriticalSection(&m_csThreadList);
  1038.         ResumeThread(pThreadData->hThread);
  1039.     }
  1040.     else
  1041.     {
  1042.         DWORD dwErr = GetLastError();
  1043.         LocalFree(pThreadData);
  1044.         LPTSTR pszErr = GetErrorText(GetLastError());
  1045.         LogError(*pItemID,
  1046.                  SYNCMGRLOGLEVEL_ERROR,
  1047.                  IDS_FILL_SPARSE_FILES_ERROR,
  1048.                  pszShareName,
  1049.                  pszErr);
  1050.         LocalFreeString(&pszErr);
  1051.         hr = HRESULT_FROM_WIN32(dwErr);
  1052.     }
  1053. exit_gracefully:
  1054.     TraceLeaveResult(hr);
  1055. }
  1056. void
  1057. CCscUpdate::SetLastSyncTime(LPCTSTR pszShareName)
  1058. {
  1059.     HKEY hKey = NULL;
  1060.     hKey = m_ShareLog.OpenKey(pszShareName, KEY_SET_VALUE);
  1061.     if (hKey)
  1062.     {
  1063.         FILETIME ft = {0};
  1064.         GetSystemTimeAsFileTime(&ft);
  1065.         RegSetValueEx(hKey, c_szLastSync, 0, REG_BINARY, (LPBYTE)&ft, sizeof(ft));
  1066.         RegCloseKey(hKey);
  1067.     }
  1068. }
  1069. DWORD
  1070. CCscUpdate::GetLastSyncTime(LPCTSTR pszShareName, LPFILETIME pft)
  1071. {
  1072.     DWORD dwResult = ERROR_PATH_NOT_FOUND;
  1073.     HKEY hKey = NULL;
  1074.     hKey = m_ShareLog.OpenKey(pszShareName, KEY_QUERY_VALUE);
  1075.     if (hKey)
  1076.     {
  1077.         DWORD dwSize = sizeof(*pft);
  1078.         dwResult = RegQueryValueEx(hKey, c_szLastSync, NULL, NULL, (LPBYTE)pft, &dwSize);
  1079.         RegCloseKey(hKey);
  1080.     }
  1081.     return dwResult;
  1082. }
  1083. void
  1084. CCscUpdate::SyncThreadCompleted(PSYNCTHREADDATA pSyncData)
  1085. {
  1086.     int iThread;
  1087.     TraceEnter(TRACE_UPDATE, "CCscUpdate::SyncThreadCompleted");
  1088.     TraceAssert(NULL != pSyncData);
  1089.     TraceAssert(NULL != m_hSyncThreads);
  1090.     EnterCriticalSection(&m_csThreadList);
  1091.     iThread = DPA_GetPtrIndex(m_hSyncThreads, pSyncData);
  1092.     TraceAssert(-1 != iThread);
  1093.     DPA_DeletePtr(m_hSyncThreads, iThread);
  1094.     CloseHandle(pSyncData->hThread);
  1095.     pSyncData->hThread = NULL;
  1096.     iThread = DPA_GetPtrCount(m_hSyncThreads);
  1097.     LeaveCriticalSection(&m_csThreadList);
  1098.     if (0 == iThread)
  1099.     {
  1100.         SyncCompleted();
  1101.     }
  1102.     TraceLeaveVoid();
  1103. }
  1104. void
  1105. CCscUpdate::SyncCompleted(void)
  1106. {
  1107.     if ((m_dwSyncFlags & CSC_SYNC_RECONNECT) &&
  1108.         !(m_dwSyncFlags & CSC_SYNC_CANCELLED))
  1109.     {
  1110.         m_dwSyncFlags &= ~CSC_SYNC_RECONNECT;
  1111.         ReconnectServers(&m_ReconnectList, FALSE, FALSE);
  1112.     }
  1113.     if (NULL != m_hSyncInProgMutex)
  1114.     {
  1115.         // We're not syncing so reset the global event
  1116.         ReleaseMutex(m_hSyncInProgMutex);
  1117.         CloseHandle(m_hSyncInProgMutex);
  1118.         m_hSyncInProgMutex = NULL;
  1119.     }
  1120.     if (m_dwSyncFlags & CSC_SYNC_NOTIFY_SYSTRAY)
  1121.     {
  1122.         // Notify systray that we're done
  1123.         PostToSystray(CSCWM_DONESYNCING, 0, 0);
  1124.         m_dwSyncFlags &= ~CSC_SYNC_NOTIFY_SYSTRAY;
  1125.     }
  1126.     // Notify SyncMgr that the sync is done
  1127.     if (NULL != m_pSyncMgrCB)
  1128.     {
  1129.         m_pSyncMgrCB->SynchronizeCompleted(S_OK);
  1130.     }
  1131. }
  1132. UINT
  1133. GetErrorFormat(DWORD dwErr, BOOL bMerging = FALSE)
  1134. {
  1135.     UINT idString = 0;
  1136.     // BUGBUG these are all just initial guesses.  Not sure
  1137.     // which error codes we'll get from CSC.
  1138.     switch (dwErr)
  1139.     {
  1140.     case ERROR_DISK_FULL:
  1141.         // "The server disk is full."
  1142.         // "The local disk is full."
  1143.         idString = bMerging ? IDS_SERVER_FULL_ERROR : IDS_LOCAL_DISK_FULL_ERROR;
  1144.         break;
  1145.     case ERROR_LOCK_VIOLATION:
  1146.     case ERROR_SHARING_VIOLATION:
  1147.     case ERROR_OPEN_FILES:
  1148.     case ERROR_ACTIVE_CONNECTIONS:
  1149.     case ERROR_DEVICE_IN_USE:
  1150.         // "'%1' is in use on %2"
  1151.         idString = IDS_FILE_OPEN_ERROR;
  1152.         break;
  1153.     case ERROR_BAD_NETPATH:
  1154.     case ERROR_DEV_NOT_EXIST:
  1155.     case ERROR_NETNAME_DELETED:
  1156.     case ERROR_BAD_NET_NAME:
  1157.     case ERROR_SHARING_PAUSED:
  1158.     case ERROR_REQ_NOT_ACCEP:
  1159.     case ERROR_REDIR_PAUSED:
  1160.     case ERROR_BAD_DEVICE:
  1161.     case ERROR_CONNECTION_UNAVAIL:
  1162.     case ERROR_NO_NET_OR_BAD_PATH:
  1163.     case ERROR_NO_NETWORK:
  1164.     case ERROR_CONNECTION_REFUSED:
  1165.     case ERROR_GRACEFUL_DISCONNECT:
  1166.     case ERROR_NETWORK_UNREACHABLE:
  1167.     case ERROR_HOST_UNREACHABLE:
  1168.     case ERROR_PROTOCOL_UNREACHABLE:
  1169.     case ERROR_PORT_UNREACHABLE:
  1170.     case ERROR_LOGON_FAILURE:
  1171.         // "Unable to connect to '%1.'  %2"
  1172.         idString = IDS_SHARE_CONNECT_ERROR;
  1173.         break;
  1174.     case ERROR_OPEN_FAILED:
  1175.     case ERROR_UNEXP_NET_ERR:
  1176.     case ERROR_NETWORK_BUSY:
  1177.     case ERROR_BAD_NET_RESP:
  1178.         // "Unable to access '%1' on %2.  %3"
  1179.         idString = IDS_NET_ERROR;
  1180.         break;
  1181.     case ERROR_ACCESS_DENIED:
  1182.     case ERROR_NETWORK_ACCESS_DENIED:
  1183.         // "Access to '%1' is denied on %2"
  1184.         idString = IDS_ACCESS_ERROR;
  1185.         break;
  1186.     case ERROR_BAD_FORMAT:
  1187.         // "The Offline Files cache is corrupt.  Restart the computer to correct the cache."
  1188.         idString = IDS_CACHE_CORRUPT;
  1189.         break;
  1190.     default:
  1191.         // "Error accessing '%1' on %2.  %3"
  1192.         idString = IDS_UNKNOWN_SYNC_ERROR;
  1193.         break;
  1194.     }
  1195.     return idString;
  1196. }
  1197. HRESULT
  1198. CCscUpdate::LogError(REFSYNCMGRITEMID rItemID,
  1199.                      LPCTSTR pszText,
  1200.                      DWORD dwLogLevel,
  1201.                      REFSYNCMGRERRORID ErrorID)
  1202. {
  1203.     HRESULT hr;
  1204.     SYNCMGRLOGERRORINFO slei;
  1205.     USES_CONVERSION;
  1206.     TraceEnter(TRACE_UPDATE, "CCscUpdate::LogError");
  1207.     if (NULL == m_pSyncMgrCB)
  1208.         TraceLeaveResult(E_UNEXPECTED);
  1209.     slei.cbSize = sizeof(slei);
  1210.     slei.mask   = SYNCMGRLOGERROR_ITEMID | SYNCMGRLOGERROR_ERRORID;
  1211.     slei.ItemID = rItemID;
  1212.     slei.ErrorID = ErrorID;
  1213.     // if we have a jumptext associated with this item then
  1214.     // set the enable jumptext flag
  1215.     if (ErrorID != GUID_NULL)
  1216.     {
  1217.         slei.mask |= SYNCMGRLOGERROR_ERRORFLAGS;
  1218.         slei.dwSyncMgrErrorFlags = SYNCMGRERRORFLAG_ENABLEJUMPTEXT;
  1219.     }
  1220.     Trace((pszText));
  1221.     hr = m_pSyncMgrCB->LogError(dwLogLevel, T2CW(pszText), &slei);
  1222.     TraceLeaveResult(hr);
  1223. }
  1224. DWORD
  1225. CCscUpdate::LogError(REFSYNCMGRITEMID rItemID,
  1226.                      DWORD dwLogLevel,
  1227.                      UINT nFormatID,
  1228.                      ...)
  1229. {
  1230.     LPTSTR pszError = NULL;
  1231.     va_list args;
  1232.     va_start(args, nFormatID);
  1233.     if (vFormatStringID(&pszError, g_hInstance, nFormatID, &args))
  1234.     {
  1235.         LogError(rItemID, pszError, dwLogLevel);
  1236.         LocalFree(pszError);
  1237.     }
  1238.     va_end(args);
  1239.     return 0;
  1240. }
  1241. DWORD
  1242. CCscUpdate::LogError(REFSYNCMGRITEMID rItemID,
  1243.                      UINT nFormatID,
  1244.                      LPCTSTR pszName,
  1245.                      DWORD dwErr,
  1246.                      DWORD dwLogLevel)
  1247. {
  1248.     //
  1249.     // Break the filename into "file" and "path" components
  1250.     //
  1251.     TCHAR szPath[MAX_PATH] = TEXT("\");
  1252.     LPCTSTR pszFile = NULL;
  1253.     if (pszName)
  1254.     {
  1255.         pszFile = PathFindFileName(pszName);
  1256.         lstrcpyn(szPath, pszName, min(ARRAYSIZE(szPath),(int)(pszFile-pszName)));
  1257.     }
  1258.     //
  1259.     // Get the system error text and format the error
  1260.     //
  1261.     LPTSTR pszErr = GetErrorText(dwErr);
  1262.     LogError(rItemID,
  1263.              dwLogLevel,
  1264.              nFormatID,
  1265.              pszFile,
  1266.              szPath,
  1267.              pszErr);
  1268.     LocalFreeString(&pszErr);
  1269.     return 0;
  1270. }
  1271. BOOL
  1272. MakeDriveLetterPath(LPCTSTR pszUNC,
  1273.                     LPCTSTR pszShare,
  1274.                     LPCTSTR pszDrive,
  1275.                     LPTSTR *ppszResult)
  1276. {
  1277.     BOOL bResult = FALSE;
  1278.     ULONG cchShare;
  1279.     if (!pszUNC || !pszShare || !ppszResult)
  1280.         return FALSE;
  1281.     *ppszResult = NULL;
  1282.     cchShare = lstrlen(pszShare);
  1283.     // If the path is on the share, use the drive letter instead
  1284.     if (pszDrive && *pszDrive &&
  1285.         CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
  1286.                                     NORM_IGNORECASE,
  1287.                                     pszUNC,
  1288.                                     cchShare,
  1289.                                     pszShare,
  1290.                                     cchShare))
  1291.     {
  1292.         *ppszResult = (LPTSTR)LocalAlloc(LPTR, MAX(StringByteSize(pszUNC), MAX_PATH_BYTES));
  1293.         if (*ppszResult)
  1294.         {
  1295.             PathCombine(*ppszResult, pszDrive, pszUNC + cchShare);
  1296.             bResult = TRUE;
  1297.         }
  1298.     }
  1299.     return bResult;
  1300. }
  1301. DWORD
  1302. CCscUpdate::CopyLocalFileWithDriveMapping(LPCTSTR pszSrc,
  1303.                                           LPCTSTR pszDst,
  1304.                                           LPCTSTR pszShare,
  1305.                                           LPCTSTR pszDrive,
  1306.                                           BOOL    bDirectory)
  1307. {
  1308.     DWORD dwErr = NOERROR;
  1309.     LPTSTR szDst = NULL;
  1310.     if (!pszSrc || !pszDst || !pszShare)
  1311.         return ERROR_INVALID_PARAMETER;
  1312.     // If the destination is on the share, use the drive letter instead
  1313.     if (MakeDriveLetterPath(pszDst, pszShare, pszDrive, &szDst))
  1314.         pszDst = szDst;
  1315.     if (bDirectory)
  1316.     {
  1317.         // We don't need to copy the directory contents here, just create
  1318.         // the tree structure on the server.
  1319.         if (!CreateDirectory(pszDst, NULL))
  1320.         {
  1321.             dwErr = GetLastError();
  1322.             if (ERROR_ALREADY_EXISTS == dwErr)
  1323.                 dwErr = NOERROR;
  1324.         }
  1325.     }
  1326.     else
  1327.     {
  1328.         LPTSTR pszTmpName = NULL;
  1329.         if (!CSCCopyReplica(pszSrc, &pszTmpName) ||
  1330.             !MoveFileEx(pszTmpName,
  1331.                         pszDst,
  1332.                         MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
  1333.         {
  1334.             dwErr = GetLastError();
  1335.         }
  1336.         if (NULL != pszTmpName)
  1337.         {
  1338.             DeleteFile(pszTmpName);
  1339.             LocalFree(pszTmpName);
  1340.         }
  1341.     }
  1342.     if (ERROR_PATH_NOT_FOUND == dwErr)
  1343.     {
  1344.         // The parent directory doesn't exist, create it now.
  1345.         TCHAR szParent[MAX_PATH];
  1346.         lstrcpyn(szParent, pszDst, ARRAYSIZE(szParent));
  1347.         PathRemoveFileSpec(szParent);
  1348.         dwErr = CopyLocalFileWithDriveMapping(pszSrc, szParent, pszShare, NULL, TRUE);
  1349.         // If that worked, retry the original operation.
  1350.         if (NOERROR == dwErr)
  1351.             dwErr = CopyLocalFileWithDriveMapping(pszSrc, pszDst, pszShare, NULL, bDirectory);
  1352.     }
  1353.     LocalFreeString(&szDst);
  1354.     return dwErr;
  1355. }
  1356. BOOL
  1357. HandleConflictLocally(PSYNCTHREADDATA   pSyncData,
  1358.                       LPCTSTR           pszPath,
  1359.                       DWORD             dwCscStatus,
  1360.                       DWORD             dwLocalAttr,
  1361.                       DWORD             dwRemoteAttr = 0)
  1362. {
  1363.     BOOL bResult = FALSE;
  1364.     LPTSTR szParent = NULL;
  1365.     // If it's super-hidden or not modified locally, we can always
  1366.     // handle the conflict locally.
  1367.     if (!(dwCscStatus & CSC_LOCALLY_MODIFIED) || IsHiddenSystem(dwLocalAttr) || IsHiddenSystem(dwRemoteAttr))
  1368.         return TRUE;
  1369.     // If we're dealing with 2 folders, the worst that happens is that the
  1370.     // underlying files/folders get merged.
  1371.     if ((FILE_ATTRIBUTE_DIRECTORY & dwLocalAttr) && (FILE_ATTRIBUTE_DIRECTORY & dwRemoteAttr))
  1372.         return TRUE;
  1373.     //
  1374.     // Next, check whether the parent path is super-hidden.
  1375.     //
  1376.     // For example, recycle bin makes super-hidden folders and puts
  1377.     // metadata files in them.
  1378.     //
  1379.     // Do this on the server, since CSC has exclusive access to the database
  1380.     // while merging, causing GetFileAttributes to fail with Access Denied.
  1381.     //
  1382.     if (MakeDriveLetterPath(pszPath, pSyncData->pszShareName, pSyncData->szDrive, &szParent))
  1383.     {
  1384.         do
  1385.         {
  1386.             PathRemoveFileSpec(szParent);
  1387.             dwRemoteAttr = GetFileAttributes(szParent);
  1388.             if ((DWORD)-1 == dwRemoteAttr)
  1389.             {
  1390.                 // Path doesn't exist, access denied, etc.
  1391.                 break;
  1392.             }
  1393.             if (IsHiddenSystem(dwRemoteAttr))
  1394.             {
  1395.                 bResult = TRUE;
  1396.                 break;
  1397.             }
  1398.         }
  1399.         while (!PathIsRoot(szParent));
  1400.     }
  1401.     LocalFreeString(&szParent);
  1402.     return bResult;
  1403. }
  1404. DWORD
  1405. CCscUpdate::HandleFileConflict(PSYNCTHREADDATA     pSyncData,
  1406.                                LPCTSTR             pszName,
  1407.                                DWORD               dwStatus,
  1408.                                DWORD               dwHintFlags,
  1409.                                LPWIN32_FIND_DATA   pFind32)
  1410. {
  1411.     DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  1412.     DWORD dwErr = NOERROR;
  1413.     int nErrorResolution = RFC_KEEPBOTH;
  1414.     LPTSTR pszNewName = NULL;
  1415.     LPTSTR szFullPath = NULL;
  1416.     BOOL bApplyToAll = FALSE;
  1417.     TraceEnter(TRACE_UPDATE, "CCscUpdate::HandleFileConflict");
  1418.     Trace((TEXT("File conflict: %s"), pszName));
  1419.     TraceAssert(pSyncData->dwSyncStatus & SDS_SYNC_OUT);
  1420.     szFullPath = (LPTSTR)LocalAlloc(LPTR, StringByteSize(pszName) + MAX_PATH*sizeof(TCHAR));
  1421.     if (!szFullPath)
  1422.     {
  1423.         dwErr = ERROR_OUTOFMEMORY;
  1424.         ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "LocalAlloc failed");
  1425.     }
  1426.     HANDLE hFind;
  1427.     WIN32_FIND_DATA fdRemote;
  1428.     PathCombine(szFullPath, pSyncData->szDrive, pszName + lstrlen(pSyncData->pszShareName));
  1429.     hFind = FindFirstFile(szFullPath, &fdRemote);
  1430.     // Does the net version still exist?
  1431.     if (hFind == INVALID_HANDLE_VALUE)
  1432.         ExitGracefully(dwResult, HandleDeleteConflict(pSyncData, pszName, dwStatus, dwHintFlags, pFind32), "Net file deleted");
  1433.     // Still exists, continue
  1434.     FindClose(hFind);
  1435.     // If only the attributes or file times were modified locally,
  1436.     // or if the file is hidden+system, keep the server copy and
  1437.     // don't bother the user.  (e.g. desktop.ini)
  1438.     if (HandleConflictLocally(pSyncData, pszName, dwStatus, pFind32->dwFileAttributes, fdRemote.dwFileAttributes))
  1439.     {
  1440.         ExitGracefully(dwResult, CSCPROC_RETURN_FORCE_INWARD, "Ignoring conflict");
  1441.     }
  1442.     else if (IsSilentFolder(pszName))
  1443.     {
  1444.         // It's in a per-user shell special folder. Last writer wins.
  1445.         if (CompareFileTime(&pFind32->ftLastWriteTime, &fdRemote.ftLastWriteTime) < 0)
  1446.         {
  1447.             ExitGracefully(dwResult, CSCPROC_RETURN_FORCE_INWARD, "Handling special folder conflict - server copy wins");
  1448.         }
  1449.         else
  1450.         {
  1451.             ExitGracefully(dwResult, CSCPROC_RETURN_FORCE_OUTWARD, "Handling special folder conflict - local copy wins");
  1452.         }
  1453.     }
  1454.     dwErr = GetNewVersionName(pszName,
  1455.                               pSyncData->pszShareName,
  1456.                               pSyncData->szDrive,
  1457.                               &pszNewName);
  1458.     if (NOERROR != dwErr)
  1459.     {
  1460.         ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "GetNewVersionName failed");
  1461.     }
  1462.     switch (SDS_SYNC_FILE_CONFLICT_MASK & pSyncData->dwSyncStatus)
  1463.     {
  1464.     case 0:
  1465.         if (CSC_SYNC_MAYBOTHERUSER & m_dwSyncFlags)
  1466.         {
  1467.             nErrorResolution = ShowConflictDialog(m_hwndDlgParent,
  1468.                                                   pszName,
  1469.                                                   pszNewName,
  1470.                                                   pSyncData->pszShareName,
  1471.                                                   pSyncData->szDrive,
  1472.                                                   pFind32,
  1473.                                                   &fdRemote);
  1474.             if (RFC_APPLY_TO_ALL & nErrorResolution)
  1475.             {
  1476.                 bApplyToAll = TRUE;
  1477.                 nErrorResolution &= ~RFC_APPLY_TO_ALL;
  1478.             }
  1479.         }
  1480.         break;
  1481.     case SDS_SYNC_CONFLICT_KEEPLOCAL:
  1482.         nErrorResolution = RFC_KEEPLOCAL;
  1483.         break;
  1484.     case SDS_SYNC_CONFLICT_KEEPNET:
  1485.         nErrorResolution = RFC_KEEPNETWORK;
  1486.         break;
  1487.     case SDS_SYNC_CONFLICT_KEEPBOTH:
  1488.         nErrorResolution = RFC_KEEPBOTH;
  1489.         break;
  1490.     }
  1491.     // Self-host notification callback
  1492.     CSCUI_NOTIFYHOOK((CSCH_UpdateConflict, TEXT("Update conflict: %1, resolution %2!d!"), pszName, nErrorResolution));
  1493.     switch (nErrorResolution)
  1494.     {
  1495.     default:
  1496.     case RFC_KEEPBOTH:
  1497.         if (bApplyToAll)
  1498.             pSyncData->dwSyncStatus |= SDS_SYNC_CONFLICT_KEEPBOTH;
  1499.         lstrcpy(szFullPath, pszName);
  1500.         PathRemoveFileSpec(szFullPath);
  1501.         if (FILE_ATTRIBUTE_DIRECTORY & pFind32->dwFileAttributes)
  1502.         {
  1503.             // Rename the local version in the cache and merge again.
  1504.             lstrcpyn(pFind32->cFileName, pszNewName, ARRAYSIZE(pFind32->cFileName));
  1505.             if (!CSCDoLocalRenameEx(pszName, szFullPath, pFind32, TRUE, TRUE))
  1506.             {
  1507.                 dwErr = GetLastError();
  1508.                 ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "CSCDoLocalRenameEx failed");
  1509.             }
  1510.             // Because CSCDoLocalRenameEx and CSCMergeShare are separate operations,
  1511.             // we have to abort the current merge operation and start over.
  1512.             // Otherwise, the current merge operation fails due to the "left
  1513.             // hand not knowing what the right hande is doing".
  1514.             Trace((TEXT("Restarting merge on: %s"), pSyncData->pszShareName));
  1515.             pSyncData->dwSyncStatus |= SDS_SYNC_RESTART_MERGE;
  1516.             dwResult = CSCPROC_RETURN_ABORT;
  1517.         }
  1518.         else
  1519.         {
  1520.             // Note that CSCDoLocalRenameEx would work for files also, but we
  1521.             // prefer to avoid restarting CSCMergeShare so do these ourselves.
  1522.             PathAppend(szFullPath, pszNewName);
  1523.             dwErr = CopyLocalFileWithDriveMapping(pszName,
  1524.                                                   szFullPath,
  1525.                                                   pSyncData->pszShareName,
  1526.                                                   pSyncData->szDrive);
  1527.             if (NOERROR != dwErr)
  1528.                 ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "CopyLocalFileWithDriveMapping failed");
  1529.             // If the original file was pinned, we want to pin the copy also.
  1530.             // Unfortunately, we can't reliably pin during a merge, so we have
  1531.             // to remember these in a list and pin them later.
  1532.             if (dwHintFlags & FLAG_CSC_HINT_PIN_USER)
  1533.             {
  1534.                 if (!m_pConflictPinList)
  1535.                     m_pConflictPinList = new CscFilenameList;
  1536.                 if (m_pConflictPinList)
  1537.                 {
  1538.                     m_pConflictPinList->AddFile(szFullPath,
  1539.                                                 !!(pFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
  1540.                 }
  1541.             }
  1542.             // Tell CSCMergeShare to copy the server copy to the cache
  1543.             // (with the old name).  This clears the dirty cache.
  1544.             dwResult = CSCPROC_RETURN_FORCE_INWARD;
  1545.         }
  1546.         break;
  1547.     case RFC_KEEPNETWORK:
  1548.         // Tell CSCMergeShare to copy the server copy to the cache
  1549.         dwResult = CSCPROC_RETURN_FORCE_INWARD;
  1550.         if (bApplyToAll)
  1551.             pSyncData->dwSyncStatus |= SDS_SYNC_CONFLICT_KEEPNET;
  1552.         break;
  1553.     case RFC_KEEPLOCAL:
  1554.         // Tell CSCMergeShare to push the local copy to the server
  1555.         dwResult = CSCPROC_RETURN_FORCE_OUTWARD;
  1556.         if (bApplyToAll)
  1557.             pSyncData->dwSyncStatus |= SDS_SYNC_CONFLICT_KEEPLOCAL;
  1558.         break;
  1559.     case RFC_CANCEL:
  1560.         TraceMsg("HandleFileConflict: Cancelling sync - user bailed");
  1561.         SetItemStatus(GUID_NULL, SYNCMGRSTATUS_STOPPED);
  1562.         dwResult = CSCPROC_RETURN_ABORT;
  1563.         break;
  1564.     }
  1565. exit_gracefully:
  1566.     if (CSCPROC_RETURN_FORCE_INWARD == dwResult)
  1567.     {
  1568.         // CSCMergeShare truncates (makes sparse) the
  1569.         // file if we return this.  We'd like to fill
  1570.         // it during this sync.
  1571.         pSyncData->cFilesToSync++;
  1572.         pSyncData->dwSyncStatus |= SDS_SYNC_FORCE_INWARD;
  1573.     }
  1574.     if (NOERROR != dwErr)
  1575.     {
  1576.         pszName += lstrlen(pSyncData->pszShareName);
  1577.         if (*pszName == TEXT('\'))
  1578.             pszName++;
  1579.         LogError(pSyncData->ItemID,
  1580.                  IDS_NAME_CONFLICT_ERROR,
  1581.                  pszName,
  1582.                  dwErr);
  1583.         pSyncData->dwSyncStatus |= SDS_SYNC_ERROR;
  1584.     }
  1585.     LocalFreeString(&szFullPath);
  1586.     LocalFreeString(&pszNewName);
  1587.     TraceLeaveResult(dwResult);
  1588. }
  1589. // Returns values for the Resolve Delete Conflict dialog
  1590. #define RDC_CANCEL      0x00
  1591. #define RDC_DELETE      0x01
  1592. #define RDC_RESTORE     0x02
  1593. #define RDC_APPLY_ALL   0x04
  1594. #define RDC_DELETE_ALL  (RDC_APPLY_ALL | RDC_DELETE)
  1595. #define RDC_RESTORE_ALL (RDC_APPLY_ALL | RDC_RESTORE)
  1596. TCHAR const c_szDeleteSelection[]   = TEXT("DeleteConflictSelection");
  1597. INT_PTR CALLBACK
  1598. DeleteConflictProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1599. {
  1600.     int nResult;
  1601.     switch (uMsg)
  1602.     {
  1603.     case WM_INITDIALOG:
  1604.         {
  1605.             TCHAR szShare[MAX_PATH];
  1606.             LPCTSTR pszPath = (LPCTSTR)lParam;
  1607.             LPTSTR pszT = NULL;
  1608.             szShare[0] = TEXT('');
  1609.             lstrcpyn(szShare, pszPath, ARRAYSIZE(szShare));
  1610.             PathStripToRoot(szShare);
  1611.             // Format the file name
  1612.             PathSetDlgItemPath(hDlg, IDC_FILENAME, pszPath);
  1613.             // Build the "Do this for all on <this share>" string
  1614.             FormatStringID(&pszT, g_hInstance, IDS_FMT_DELETE_APPLY_ALL, szShare);
  1615.             if (pszT)
  1616.             {
  1617.                 SetDlgItemText(hDlg, IDC_APPLY_TO_ALL, pszT);
  1618.                 LocalFreeString(&pszT);
  1619.             }
  1620.             // else default text is OK (no share name)
  1621.             // Select whatever the user chose last time, default is "restore"
  1622.             DWORD dwPrevSelection = RDC_RESTORE;
  1623.             DWORD dwType;
  1624.             DWORD cbData = sizeof(dwPrevSelection);
  1625.             SHGetValue(HKEY_CURRENT_USER,
  1626.                        c_szCSCKey,
  1627.                        c_szDeleteSelection,
  1628.                        &dwType,
  1629.                        &dwPrevSelection,
  1630.                        &cbData);
  1631.             dwPrevSelection = (RDC_DELETE == dwPrevSelection ? IDC_DELETE_LOCAL : IDC_KEEP_LOCAL);
  1632.             CheckRadioButton(hDlg, IDC_KEEP_LOCAL, IDC_DELETE_LOCAL, dwPrevSelection);
  1633.             // Get the file-type icon
  1634.             pszT = PathFindExtension(pszPath);
  1635.             if (pszT)
  1636.             {
  1637.                 SHFILEINFO sfi = {0};
  1638.                 SHGetFileInfo(pszT, 0, &sfi, sizeof(sfi), SHGFI_ICON);
  1639.                 if (sfi.hIcon)
  1640.                 {
  1641.                     SendDlgItemMessage(hDlg,
  1642.                                        IDC_DLGTYPEICON,
  1643.                                        STM_SETICON,
  1644.                                        (WPARAM)sfi.hIcon,
  1645.                                        0L);
  1646.                 }
  1647.             }
  1648.         }
  1649.         return TRUE;
  1650.     case WM_COMMAND:
  1651.         nResult = -1;
  1652.         switch (LOWORD(wParam))
  1653.         {
  1654.         case IDCANCEL:
  1655.             nResult = RDC_CANCEL;
  1656.             break;
  1657.         case IDOK:
  1658.             if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_DELETE_LOCAL))
  1659.                 nResult = RDC_DELETE;
  1660.             else
  1661.                 nResult = RDC_RESTORE;
  1662.             // Remember the selection for next time
  1663.             SHSetValue(HKEY_CURRENT_USER,
  1664.                        c_szCSCKey,
  1665.                        c_szDeleteSelection,
  1666.                        REG_DWORD,
  1667.                        &nResult,
  1668.                        sizeof(nResult));
  1669.             if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_APPLY_TO_ALL))
  1670.                 nResult |= RDC_APPLY_ALL;
  1671.             break;
  1672.         }
  1673.         if (-1 != nResult)
  1674.         {
  1675.             EndDialog(hDlg, nResult);
  1676.             return TRUE;
  1677.         }
  1678.         break;
  1679.     }
  1680.     return FALSE;
  1681. }
  1682. BOOL CALLBACK
  1683. ConflictPurgeCallback(LPCWSTR /*pszFile*/, LPARAM lParam)
  1684. {
  1685.     PSYNCTHREADDATA pSyncData = (PSYNCTHREADDATA)lParam;
  1686.     return !(SDS_SYNC_CANCELLED & pSyncData->dwSyncStatus);
  1687. }
  1688. DWORD
  1689. CCscUpdate::HandleDeleteConflict(PSYNCTHREADDATA    pSyncData,
  1690.                                  LPCTSTR            pszName,
  1691.                                  DWORD              dwStatus,
  1692.                                  DWORD              dwHintFlags,
  1693.                                  LPWIN32_FIND_DATA  pFind32)
  1694. {
  1695.     DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  1696.     int nErrorResolution = RDC_DELETE;  // default action
  1697.     BOOL bDirectory = (FILE_ATTRIBUTE_DIRECTORY & pFind32->dwFileAttributes);
  1698.     TraceEnter(TRACE_UPDATE, "CCscUpdate::HandleDeleteConflict");
  1699.     Trace((TEXT("Net file deleted: %s"), pszName));
  1700.     //
  1701.     // We already know that the net file was deleted, or HandleDeleteConflict
  1702.     // wouldn't be called.  If the local copy was also deleted, then there
  1703.     // isn't really a conflict and we can continue without prompting.
  1704.     //
  1705.     // If the file isn't pinned, handle the conflict silently if
  1706.     // only attributes changed or it's super-hidden.
  1707.     //
  1708.     // Finally, if the file lives in certain special folder locations,
  1709.     // such as AppData, handle the conflict silently.
  1710.     //
  1711.     // If we get past all that, ask the user what to do, but only bother
  1712.     // the user as a last resort.
  1713.     //
  1714.     if ( !(dwStatus & FLAG_CSC_COPY_STATUS_LOCALLY_DELETED)
  1715.          && ((dwHintFlags & (FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN)) ||
  1716.              !HandleConflictLocally(pSyncData, pszName, dwStatus, pFind32->dwFileAttributes))
  1717.          && !IsSilentFolder(pszName)
  1718.         )
  1719.     {
  1720.         // The file is either pinned or modified locally, so
  1721.         // default action is now "restore".
  1722.         nErrorResolution = RDC_RESTORE;