Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

410 lines
9.8 KiB

  1. #include "shellprv.h"
  2. class CLocalCopyHelper : public ILocalCopy
  3. , public IItemHandler
  4. {
  5. public:
  6. CLocalCopyHelper();
  7. // IUnknown methods
  8. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  9. STDMETHODIMP_(ULONG) AddRef () ;
  10. STDMETHODIMP_(ULONG) Release ();
  11. // ILocalCopy methods
  12. STDMETHODIMP Download(LCFLAGS flags, IBindCtx *pbc, LPWSTR *ppszOut);
  13. STDMETHODIMP Upload(LCFLAGS flags, IBindCtx *pbc);
  14. // IPersist
  15. STDMETHODIMP GetClassID(CLSID *pclsid) { *pclsid = CLSID_LocalCopyHelper; return S_OK;}
  16. // IItemHandler
  17. STDMETHODIMP SetItem(IShellItem *psi);
  18. STDMETHODIMP GetItem(IShellItem **ppsi);
  19. protected:
  20. ~CLocalCopyHelper();
  21. // private methods
  22. HRESULT _InitCacheEntry(void);
  23. HRESULT _SetCacheName(void);
  24. HRESULT _FinishLocal(BOOL fReadOnly);
  25. HRESULT _GetLocalStream(DWORD grfMode, IStream **ppstm, FILETIME *pft);
  26. HRESULT _GetRemoteStream(DWORD grfMode, IBindCtx *pbc, IStream **ppstm, FILETIME *pft);
  27. // members
  28. long _cRef;
  29. IShellItem *_psi;
  30. LPWSTR _pszName; // name retrieved from psi
  31. LPWSTR _pszCacheName; // name used to ID cache entry
  32. LPCWSTR _pszExt; // points into _pszName
  33. // caches of the MTIMEs for the streams
  34. FILETIME _ftRemoteGet;
  35. FILETIME _ftLocalGet;
  36. FILETIME _ftRemoteCommit;
  37. FILETIME _ftLocalCommit;
  38. BOOL _fIsLocalFile; // this is actually file system item (pszName is a FS path)
  39. BOOL _fMadeLocal; // we have already copied this item locally
  40. // put this at the end so we can see all the rest of the pointers easily in debug
  41. WCHAR _szLocalPath[MAX_PATH];
  42. };
  43. CLocalCopyHelper::CLocalCopyHelper() : _cRef(1)
  44. {
  45. }
  46. CLocalCopyHelper::~CLocalCopyHelper()
  47. {
  48. ATOMICRELEASE(_psi);
  49. if (_pszName)
  50. CoTaskMemFree(_pszName);
  51. if (_pszCacheName)
  52. LocalFree(_pszCacheName);
  53. }
  54. STDMETHODIMP CLocalCopyHelper::QueryInterface(REFIID riid, void **ppv)
  55. {
  56. static const QITAB qit[] =
  57. {
  58. QITABENT(CLocalCopyHelper, ILocalCopy),
  59. QITABENT(CLocalCopyHelper, IItemHandler),
  60. QITABENTMULTI(CLocalCopyHelper, IPersist, IItemHandler),
  61. { 0 },
  62. };
  63. return QISearch(this, qit, riid, ppv);
  64. }
  65. STDMETHODIMP_(ULONG) CLocalCopyHelper::AddRef()
  66. {
  67. return InterlockedIncrement(&_cRef);
  68. }
  69. STDMETHODIMP_(ULONG) CLocalCopyHelper::Release()
  70. {
  71. if (InterlockedDecrement(&_cRef))
  72. return _cRef;
  73. delete this;
  74. return 0;
  75. }
  76. STDMETHODIMP CLocalCopyHelper::SetItem(IShellItem *psi)
  77. {
  78. if (!_psi)
  79. {
  80. SFGAOF flags = SFGAO_STREAM;
  81. if (SUCCEEDED(psi->GetAttributes(flags, &flags))
  82. && (flags & SFGAO_STREAM))
  83. {
  84. HRESULT hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &_pszName);
  85. if (SUCCEEDED(hr))
  86. {
  87. _fIsLocalFile = TRUE;
  88. }
  89. else
  90. hr = psi->GetDisplayName(SIGDN_PARENTRELATIVEEDITING, &_pszName);
  91. if (SUCCEEDED(hr))
  92. {
  93. _psi = psi;
  94. _psi->AddRef();
  95. }
  96. return hr;
  97. }
  98. }
  99. return E_UNEXPECTED;
  100. }
  101. STDMETHODIMP CLocalCopyHelper::GetItem(IShellItem **ppsi)
  102. {
  103. *ppsi = _psi;
  104. if (_psi)
  105. {
  106. _psi->AddRef();
  107. return S_OK;
  108. }
  109. else
  110. return E_UNEXPECTED;
  111. }
  112. #define SZTEMPURL TEXTW("temp:")
  113. #define CCHTEMPURL SIZECHARS(SZTEMPURL) -1
  114. HRESULT CLocalCopyHelper::_SetCacheName(void)
  115. {
  116. ASSERT(!_pszCacheName);
  117. _pszCacheName = (LPWSTR) LocalAlloc(LPTR, CbFromCchW(lstrlenW(_pszName) + CCHTEMPURL + 1));
  118. if (_pszCacheName)
  119. {
  120. LPCWSTR pszName = _pszName;
  121. StrCpy(_pszCacheName, SZTEMPURL);
  122. StrCpy(_pszCacheName + CCHTEMPURL, _pszName);
  123. if (UrlIs(_pszName, URLIS_URL))
  124. {
  125. // need to push past all slashes
  126. pszName = StrRChr(pszName, NULL, TEXT('/'));
  127. }
  128. // the cache APIs need the extension without the dot
  129. if (pszName)
  130. {
  131. _pszExt = PathFindExtension(pszName);
  132. if (*_pszExt)
  133. _pszExt++;
  134. }
  135. return S_OK;
  136. }
  137. return E_OUTOFMEMORY;
  138. }
  139. void _GetMTime(IStream *pstm, FILETIME *pft)
  140. {
  141. // see if we can get an accurate Mod time
  142. STATSTG stat;
  143. if (S_OK == pstm->Stat(&stat, STATFLAG_NONAME))
  144. *pft = stat.mtime;
  145. else
  146. {
  147. GetSystemTimeAsFileTime(pft);
  148. }
  149. }
  150. HRESULT CLocalCopyHelper::_InitCacheEntry(void)
  151. {
  152. if (!_pszCacheName)
  153. {
  154. HRESULT hr = _SetCacheName();
  155. if (SUCCEEDED(hr) && !CreateUrlCacheEntryW(_pszCacheName, 0, _pszExt, _szLocalPath, 0))
  156. {
  157. hr = HRESULT_FROM_WIN32(GetLastError());
  158. LocalFree(_pszCacheName);
  159. _pszCacheName = NULL;
  160. }
  161. return hr;
  162. }
  163. return S_OK;
  164. }
  165. HRESULT CLocalCopyHelper::_GetLocalStream(DWORD grfMode, IStream **ppstm, FILETIME *pft)
  166. {
  167. HRESULT hr = _InitCacheEntry();
  168. if (SUCCEEDED(hr))
  169. {
  170. hr = SHCreateStreamOnFileW(_szLocalPath, grfMode, ppstm);
  171. if (SUCCEEDED(hr))
  172. _GetMTime(*ppstm, pft);
  173. }
  174. return hr;
  175. }
  176. HRESULT CLocalCopyHelper::_FinishLocal(BOOL fReadOnly)
  177. {
  178. HRESULT hr = S_OK;
  179. FILETIME ftExp = {0};
  180. if (CommitUrlCacheEntryW(_pszCacheName, _szLocalPath, ftExp, _ftLocalGet, STICKY_CACHE_ENTRY, NULL, 0, NULL, NULL))
  181. {
  182. // we could also check _GetRemoteStream(STGM_WRITE)
  183. // and if it fails we could fail this as well.
  184. if (fReadOnly)
  185. SetFileAttributesW(_szLocalPath, FILE_ATTRIBUTE_READONLY);
  186. }
  187. else
  188. hr = HRESULT_FROM_WIN32(GetLastError());
  189. return hr;
  190. }
  191. HRESULT CLocalCopyHelper::_GetRemoteStream(DWORD grfMode, IBindCtx *pbc, IStream **ppstm, FILETIME *pft)
  192. {
  193. HRESULT hr = E_OUTOFMEMORY;
  194. if (!pbc)
  195. CreateBindCtx(0, &pbc);
  196. else
  197. pbc->AddRef();
  198. if (pbc)
  199. {
  200. BIND_OPTS bo = {sizeof(bo)}; // Requires size filled in.
  201. if (SUCCEEDED(pbc->GetBindOptions(&bo)))
  202. {
  203. bo.grfMode = grfMode;
  204. pbc->SetBindOptions(&bo);
  205. }
  206. hr = _psi->BindToHandler(pbc, BHID_Storage, IID_PPV_ARG(IStream, ppstm));
  207. if (SUCCEEDED(hr))
  208. _GetMTime(*ppstm, pft);
  209. pbc->Release();
  210. }
  211. return hr;
  212. }
  213. STDMETHODIMP CLocalCopyHelper::Download(LCFLAGS flags, IBindCtx *pbc, LPWSTR *ppsz)
  214. {
  215. if (!_psi)
  216. return E_UNEXPECTED;
  217. HRESULT hr;
  218. if (_fIsLocalFile)
  219. {
  220. hr = SHStrDup(_pszName, ppsz);
  221. }
  222. else if (_fMadeLocal && !(flags & LC_FORCEROUNDTRIP))
  223. {
  224. hr = S_OK;
  225. }
  226. else if (flags & LC_SAVEAS)
  227. {
  228. hr = _InitCacheEntry();
  229. if (SUCCEEDED(hr))
  230. {
  231. _fMadeLocal = TRUE;
  232. }
  233. }
  234. else
  235. {
  236. // get the local stream first because it is the cheapest operation.
  237. IStream *pstmDst;
  238. hr = _GetLocalStream(STGM_WRITE, &pstmDst, &_ftLocalGet);
  239. if (SUCCEEDED(hr))
  240. {
  241. // we need to create the temp file here
  242. IStream *pstmSrc;
  243. hr = _GetRemoteStream(STGM_READ, pbc, &pstmSrc, &_ftRemoteGet);
  244. if (SUCCEEDED(hr))
  245. {
  246. hr = CopyStreamUI(pstmSrc, pstmDst, NULL, 0);
  247. pstmSrc->Release();
  248. // now that we have copied the stream
  249. }
  250. pstmDst->Release();
  251. // need to release teh dest stream first
  252. if (SUCCEEDED(hr))
  253. {
  254. // finish cleaning up the local file
  255. hr = _FinishLocal(flags & LCDOWN_READONLY);
  256. _fMadeLocal = SUCCEEDED(hr);
  257. }
  258. }
  259. }
  260. if (_fMadeLocal)
  261. {
  262. ASSERT(SUCCEEDED(hr));
  263. hr = SHStrDup(_szLocalPath, ppsz);
  264. }
  265. else
  266. ASSERT(_fIsLocalFile || FAILED(hr));
  267. return hr;
  268. }
  269. STDMETHODIMP CLocalCopyHelper::Upload(LCFLAGS flags, IBindCtx *pbc)
  270. {
  271. if (!_psi)
  272. return E_UNEXPECTED;
  273. HRESULT hr = S_OK;
  274. if (!_fIsLocalFile)
  275. {
  276. // get the local stream first because it is the cheapest operation.
  277. IStream *pstmSrc;
  278. hr = _GetLocalStream(STGM_READ, &pstmSrc, &_ftLocalCommit);
  279. if (SUCCEEDED(hr))
  280. {
  281. DWORD stgmRemote = STGM_WRITE;
  282. if (flags & LC_SAVEAS)
  283. {
  284. hr = _FinishLocal(FALSE);
  285. stgmRemote |= STGM_CREATE;
  286. }
  287. if (SUCCEEDED(hr))
  288. {
  289. IStream *pstmDst;
  290. hr = _GetRemoteStream(stgmRemote, pbc, &pstmDst, &_ftRemoteCommit);
  291. if (SUCCEEDED(hr))
  292. {
  293. // we only bother copying when the local copy changed
  294. // or caller forces us to.
  295. //
  296. // FEATURE - UI needs to handle when the remot changes
  297. // if the remote copy changes while the local
  298. // copy is being updated we will overwrite the remote copy
  299. // local changes WIN!
  300. //
  301. if (flags & LC_FORCEROUNDTRIP || 0 != CompareFileTime(&_ftLocalCommit, &_ftLocalGet))
  302. hr = CopyStreamUI(pstmSrc, pstmDst, NULL, 0);
  303. else
  304. hr = S_FALSE;
  305. pstmDst->Release();
  306. }
  307. }
  308. pstmSrc->Release();
  309. }
  310. }
  311. return hr;
  312. }
  313. STDAPI CLocalCopyHelper_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  314. {
  315. HRESULT hr;
  316. CLocalCopyHelper * p = new CLocalCopyHelper();
  317. if (p)
  318. {
  319. hr = p->QueryInterface(riid, ppv);
  320. p->Release();
  321. }
  322. else
  323. {
  324. *ppv = NULL;
  325. hr = E_OUTOFMEMORY;
  326. }
  327. return hr;
  328. }