Leaked source code of windows server 2003
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.

413 lines
10 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. ASSERT( 0 != _cRef );
  72. ULONG cRef = InterlockedDecrement(&_cRef);
  73. if ( 0 == cRef )
  74. {
  75. delete this;
  76. }
  77. return cRef;
  78. }
  79. STDMETHODIMP CLocalCopyHelper::SetItem(IShellItem *psi)
  80. {
  81. if (!_psi)
  82. {
  83. SFGAOF flags = SFGAO_STREAM;
  84. if (SUCCEEDED(psi->GetAttributes(flags, &flags))
  85. && (flags & SFGAO_STREAM))
  86. {
  87. HRESULT hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &_pszName);
  88. if (SUCCEEDED(hr))
  89. {
  90. _fIsLocalFile = TRUE;
  91. }
  92. else
  93. hr = psi->GetDisplayName(SIGDN_PARENTRELATIVEEDITING, &_pszName);
  94. if (SUCCEEDED(hr))
  95. {
  96. _psi = psi;
  97. _psi->AddRef();
  98. }
  99. return hr;
  100. }
  101. }
  102. return E_UNEXPECTED;
  103. }
  104. STDMETHODIMP CLocalCopyHelper::GetItem(IShellItem **ppsi)
  105. {
  106. *ppsi = _psi;
  107. if (_psi)
  108. {
  109. _psi->AddRef();
  110. return S_OK;
  111. }
  112. else
  113. return E_UNEXPECTED;
  114. }
  115. #define SZTEMPURL TEXTW("temp:")
  116. #define CCHTEMPURL SIZECHARS(SZTEMPURL) -1
  117. HRESULT CLocalCopyHelper::_SetCacheName(void)
  118. {
  119. ASSERT(!_pszCacheName);
  120. int cchCacheName = lstrlenW(_pszName) + CCHTEMPURL + 1;
  121. _pszCacheName = (LPWSTR) LocalAlloc(LPTR, CbFromCchW(cchCacheName));
  122. if (_pszCacheName)
  123. {
  124. LPCWSTR pszName = _pszName;
  125. StringCchCopy(_pszCacheName, cchCacheName, SZTEMPURL);
  126. StringCchCat(_pszCacheName, cchCacheName, _pszName);
  127. if (UrlIs(_pszName, URLIS_URL))
  128. {
  129. // need to push past all slashes
  130. pszName = StrRChr(pszName, NULL, TEXT('/'));
  131. }
  132. // the cache APIs need the extension without the dot
  133. if (pszName)
  134. {
  135. _pszExt = PathFindExtension(pszName);
  136. if (*_pszExt)
  137. _pszExt++;
  138. }
  139. return S_OK;
  140. }
  141. return E_OUTOFMEMORY;
  142. }
  143. void _GetMTime(IStream *pstm, FILETIME *pft)
  144. {
  145. // see if we can get an accurate Mod time
  146. STATSTG stat;
  147. if (S_OK == pstm->Stat(&stat, STATFLAG_NONAME))
  148. *pft = stat.mtime;
  149. else
  150. {
  151. GetSystemTimeAsFileTime(pft);
  152. }
  153. }
  154. HRESULT CLocalCopyHelper::_InitCacheEntry(void)
  155. {
  156. if (!_pszCacheName)
  157. {
  158. HRESULT hr = _SetCacheName();
  159. if (SUCCEEDED(hr) && !CreateUrlCacheEntryW(_pszCacheName, 0, _pszExt, _szLocalPath, 0))
  160. {
  161. hr = HRESULT_FROM_WIN32(GetLastError());
  162. LocalFree(_pszCacheName);
  163. _pszCacheName = NULL;
  164. }
  165. return hr;
  166. }
  167. return S_OK;
  168. }
  169. HRESULT CLocalCopyHelper::_GetLocalStream(DWORD grfMode, IStream **ppstm, FILETIME *pft)
  170. {
  171. HRESULT hr = _InitCacheEntry();
  172. if (SUCCEEDED(hr))
  173. {
  174. hr = SHCreateStreamOnFileW(_szLocalPath, grfMode, ppstm);
  175. if (SUCCEEDED(hr))
  176. _GetMTime(*ppstm, pft);
  177. }
  178. return hr;
  179. }
  180. HRESULT CLocalCopyHelper::_FinishLocal(BOOL fReadOnly)
  181. {
  182. HRESULT hr = S_OK;
  183. FILETIME ftExp = {0};
  184. if (CommitUrlCacheEntryW(_pszCacheName, _szLocalPath, ftExp, _ftLocalGet, STICKY_CACHE_ENTRY, NULL, 0, NULL, NULL))
  185. {
  186. // we could also check _GetRemoteStream(STGM_WRITE)
  187. // and if it fails we could fail this as well.
  188. if (fReadOnly)
  189. SetFileAttributesW(_szLocalPath, FILE_ATTRIBUTE_READONLY);
  190. }
  191. else
  192. hr = HRESULT_FROM_WIN32(GetLastError());
  193. return hr;
  194. }
  195. HRESULT CLocalCopyHelper::_GetRemoteStream(DWORD grfMode, IBindCtx *pbc, IStream **ppstm, FILETIME *pft)
  196. {
  197. HRESULT hr = E_OUTOFMEMORY;
  198. if (!pbc)
  199. CreateBindCtx(0, &pbc);
  200. else
  201. pbc->AddRef();
  202. if (pbc)
  203. {
  204. BIND_OPTS bo = {sizeof(bo)}; // Requires size filled in.
  205. if (SUCCEEDED(pbc->GetBindOptions(&bo)))
  206. {
  207. bo.grfMode = grfMode;
  208. pbc->SetBindOptions(&bo);
  209. }
  210. hr = _psi->BindToHandler(pbc, BHID_Storage, IID_PPV_ARG(IStream, ppstm));
  211. if (SUCCEEDED(hr))
  212. _GetMTime(*ppstm, pft);
  213. pbc->Release();
  214. }
  215. return hr;
  216. }
  217. STDMETHODIMP CLocalCopyHelper::Download(LCFLAGS flags, IBindCtx *pbc, LPWSTR *ppsz)
  218. {
  219. if (!_psi)
  220. return E_UNEXPECTED;
  221. HRESULT hr;
  222. if (_fIsLocalFile)
  223. {
  224. hr = SHStrDup(_pszName, ppsz);
  225. }
  226. else if (_fMadeLocal && !(flags & LC_FORCEROUNDTRIP))
  227. {
  228. hr = S_OK;
  229. }
  230. else if (flags & LC_SAVEAS)
  231. {
  232. hr = _InitCacheEntry();
  233. if (SUCCEEDED(hr))
  234. {
  235. _fMadeLocal = TRUE;
  236. }
  237. }
  238. else
  239. {
  240. // get the local stream first because it is the cheapest operation.
  241. IStream *pstmDst;
  242. hr = _GetLocalStream(STGM_WRITE, &pstmDst, &_ftLocalGet);
  243. if (SUCCEEDED(hr))
  244. {
  245. // we need to create the temp file here
  246. IStream *pstmSrc;
  247. hr = _GetRemoteStream(STGM_READ, pbc, &pstmSrc, &_ftRemoteGet);
  248. if (SUCCEEDED(hr))
  249. {
  250. hr = CopyStreamUI(pstmSrc, pstmDst, NULL, 0);
  251. pstmSrc->Release();
  252. // now that we have copied the stream
  253. }
  254. pstmDst->Release();
  255. // need to release teh dest stream first
  256. if (SUCCEEDED(hr))
  257. {
  258. // finish cleaning up the local file
  259. hr = _FinishLocal(flags & LCDOWN_READONLY);
  260. _fMadeLocal = SUCCEEDED(hr);
  261. }
  262. }
  263. }
  264. if (_fMadeLocal)
  265. {
  266. ASSERT(SUCCEEDED(hr));
  267. hr = SHStrDup(_szLocalPath, ppsz);
  268. }
  269. else
  270. ASSERT(_fIsLocalFile || FAILED(hr));
  271. return hr;
  272. }
  273. STDMETHODIMP CLocalCopyHelper::Upload(LCFLAGS flags, IBindCtx *pbc)
  274. {
  275. if (!_psi)
  276. return E_UNEXPECTED;
  277. HRESULT hr = S_OK;
  278. if (!_fIsLocalFile)
  279. {
  280. // get the local stream first because it is the cheapest operation.
  281. IStream *pstmSrc;
  282. hr = _GetLocalStream(STGM_READ, &pstmSrc, &_ftLocalCommit);
  283. if (SUCCEEDED(hr))
  284. {
  285. DWORD stgmRemote = STGM_WRITE;
  286. if (flags & LC_SAVEAS)
  287. {
  288. hr = _FinishLocal(FALSE);
  289. stgmRemote |= STGM_CREATE;
  290. }
  291. if (SUCCEEDED(hr))
  292. {
  293. IStream *pstmDst;
  294. hr = _GetRemoteStream(stgmRemote, pbc, &pstmDst, &_ftRemoteCommit);
  295. if (SUCCEEDED(hr))
  296. {
  297. // we only bother copying when the local copy changed
  298. // or caller forces us to.
  299. //
  300. // FEATURE - UI needs to handle when the remot changes
  301. // if the remote copy changes while the local
  302. // copy is being updated we will overwrite the remote copy
  303. // local changes WIN!
  304. //
  305. if (flags & LC_FORCEROUNDTRIP || 0 != CompareFileTime(&_ftLocalCommit, &_ftLocalGet))
  306. hr = CopyStreamUI(pstmSrc, pstmDst, NULL, 0);
  307. else
  308. hr = S_FALSE;
  309. pstmDst->Release();
  310. }
  311. }
  312. pstmSrc->Release();
  313. }
  314. }
  315. return hr;
  316. }
  317. STDAPI CLocalCopyHelper_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  318. {
  319. HRESULT hr;
  320. CLocalCopyHelper * p = new CLocalCopyHelper();
  321. if (p)
  322. {
  323. hr = p->QueryInterface(riid, ppv);
  324. p->Release();
  325. }
  326. else
  327. {
  328. *ppv = NULL;
  329. hr = E_OUTOFMEMORY;
  330. }
  331. return hr;
  332. }