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.

386 lines
12 KiB

  1. /*****************************************************************************
  2. *
  3. * ftpstm.cpp - IStream interface
  4. *
  5. *****************************************************************************/
  6. #include "priv.h"
  7. #include "ftpstm.h"
  8. #include "ftpurl.h"
  9. #define UPDATE_PROGRESS_EVERY (10*1024) // Update progress every 10k
  10. /*****************************************************************************
  11. * CFtpStm::ReadOrWrite
  12. *****************************************************************************/
  13. HRESULT CFtpStm::ReadOrWrite(LPVOID pv, ULONG cb, ULONG * pcb, DWORD dwAccess, STMIO io, HRESULT hresFail)
  14. {
  15. HRESULT hr = STG_E_ACCESSDENIED;
  16. if (EVAL(m_dwAccessType & dwAccess))
  17. {
  18. ULONG cbOut;
  19. if (!pcb)
  20. pcb = &cbOut;
  21. hr = io(m_hint, TRUE, pv, cb, pcb);
  22. if (SUCCEEDED(hr) && m_ppd)
  23. {
  24. m_uliComplete.QuadPart += cb;
  25. m_ulBytesSinceProgressUpdate += cb;
  26. if (m_ulBytesSinceProgressUpdate > UPDATE_PROGRESS_EVERY)
  27. {
  28. m_ulBytesSinceProgressUpdate = 0;
  29. EVAL(SUCCEEDED(m_ppd->SetProgress64(m_uliComplete.QuadPart, m_uliTotal.QuadPart)));
  30. }
  31. if (TRUE == m_ppd->HasUserCancelled())
  32. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  33. }
  34. }
  35. return hr;
  36. }
  37. //===========================
  38. // *** IStream Interface ***
  39. //===========================
  40. /*****************************************************************************
  41. * IStream::Read
  42. *****************************************************************************/
  43. HRESULT CFtpStm::Read(LPVOID pv, ULONG cb, PULONG pcb)
  44. {
  45. return ReadOrWrite(pv, cb, pcb, GENERIC_READ, InternetReadFileWrap, S_FALSE);
  46. }
  47. /*****************************************************************************
  48. * IStream::Write
  49. *****************************************************************************/
  50. HRESULT CFtpStm::Write(LPCVOID pv, ULONG cb, PULONG pcb)
  51. {
  52. return ReadOrWrite((LPVOID)pv, cb, pcb, GENERIC_WRITE, (STMIO) InternetWriteFileWrap, STG_E_WRITEFAULT);
  53. }
  54. /*****************************************************************************
  55. * IStream::CopyTo
  56. *
  57. * _UNOBVIOUS_: Implementing CopyTo is mandatory for drag/drop to work.
  58. *****************************************************************************/
  59. #define SIZE_STREAM_COPY_BUFFER (1024*16) // 16k is the perfect size for
  60. HRESULT CFtpStm::CopyTo(IStream * pstmDest, ULARGE_INTEGER cbToCopy, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
  61. {
  62. HRESULT hr = E_FAIL;
  63. IStream * pstmSrc;
  64. if (EVAL(SUCCEEDED(hr = QueryInterface(IID_IStream, (LPVOID *) &pstmSrc))))
  65. {
  66. ULARGE_INTEGER uliTotalIn;
  67. ULARGE_INTEGER uliTotalOut;
  68. uliTotalIn.QuadPart = uliTotalOut.QuadPart = 0;
  69. BYTE buffer[SIZE_STREAM_COPY_BUFFER];
  70. for (;;)
  71. {
  72. // Very unusual loop control
  73. ULONG cbIn = 0; // In case pstmSrc forgets to
  74. // No matter how you write this, the compiler emits horrid code.
  75. ULONG cb = (ULONG)min(SIZE_STREAM_COPY_BUFFER, cbToCopy.LowPart);
  76. hr = pstmSrc->Read(buffer, cb, &cbIn);
  77. uliTotalIn.QuadPart += cbIn;
  78. if (SUCCEEDED(hr) && cbIn)
  79. {
  80. ULARGE_INTEGER uliOut; // In case pstmDest forgets to
  81. uliOut.QuadPart = 0;
  82. hr = pstmDest->Write(buffer, cbIn, &(uliOut.LowPart));
  83. uliTotalOut.QuadPart += uliOut.QuadPart;
  84. if (EVAL(SUCCEEDED(hr) && uliOut.QuadPart))
  85. {
  86. // Onward
  87. }
  88. else
  89. {
  90. break; // Error or medium full
  91. }
  92. }
  93. else
  94. {
  95. break; // Error or EOF reached
  96. }
  97. }
  98. if (pcbRead)
  99. pcbRead->QuadPart = uliTotalIn.QuadPart;
  100. if (pcbWritten)
  101. pcbWritten->QuadPart = uliTotalOut.QuadPart;
  102. pstmSrc->Release();
  103. }
  104. return hr;
  105. }
  106. /*****************************************************************************
  107. * IStream::Commit
  108. *
  109. * NOTE: WinINet doesn't really implement this, so I just do my best
  110. *****************************************************************************/
  111. HRESULT CFtpStm::Commit(DWORD grfCommitFlags)
  112. {
  113. return S_OK;
  114. }
  115. /*****************************************************************************
  116. * IStream::LockRegion
  117. *
  118. * You can't lock an ftp stream.
  119. *****************************************************************************/
  120. HRESULT CFtpStm::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  121. {
  122. return STG_E_INVALIDFUNCTION;
  123. }
  124. /*****************************************************************************
  125. * IStream::UnlockRegion
  126. *
  127. * You can't unlock an ftp stream because you can't lock one...
  128. *****************************************************************************/
  129. HRESULT CFtpStm::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  130. {
  131. return STG_E_INVALIDFUNCTION;
  132. }
  133. /*****************************************************************************
  134. * IStream::Stat
  135. *
  136. * We fill in what we can.
  137. *
  138. * As the pwcsName, we put the URL that the stream represents, and
  139. * install ourselves as the clsid.
  140. *****************************************************************************/
  141. HRESULT CFtpStm::Stat(STATSTG *pstat, DWORD grfStatFlag)
  142. {
  143. HRESULT hr;
  144. ZeroMemory(pstat, sizeof(*pstat));
  145. pstat->type = STGTY_STREAM;
  146. pstat->mtime = FtpPidl_GetFileTime(ILFindLastID(m_pidl));
  147. pstat->cbSize.QuadPart = FtpItemID_GetFileSize(ILFindLastID(m_pidl));
  148. pstat->grfMode |= STGM_SHARE_EXCLUSIVE | STGM_DIRECT;
  149. if (m_dwAccessType & GENERIC_READ)
  150. pstat->grfMode |= STGM_READ;
  151. if (m_dwAccessType & GENERIC_WRITE)
  152. pstat->grfMode |= STGM_WRITE;
  153. if (grfStatFlag & STATFLAG_NONAME)
  154. hr = S_OK;
  155. else
  156. {
  157. pstat->pwcsName = (LPWSTR) SHAlloc(MAX_PATH * sizeof(WCHAR));
  158. if (pstat->pwcsName)
  159. {
  160. hr = FtpPidl_GetLastFileDisplayName(m_pidl, pstat->pwcsName, MAX_PATH);
  161. }
  162. else
  163. hr = STG_E_INSUFFICIENTMEMORY; // N.B., not E_OUTOFMEMORY
  164. }
  165. return hr;
  166. }
  167. /*****************************************************************************\
  168. FUNCTION: CFtpStm_Create
  169. DESCRIPTION:
  170. The caller will display errors, so don't do that here.
  171. \*****************************************************************************/
  172. HRESULT CFtpStm_Create(CFtpDir * pfd, LPCITEMIDLIST pidl, DWORD dwAccess, IStream ** ppstream, ULARGE_INTEGER uliComplete, ULARGE_INTEGER uliTotal, IProgressDialog * ppd, BOOL fClosePrgDlg)
  173. {
  174. CFtpStm * pfstm = new CFtpStm();
  175. HRESULT hr = E_OUTOFMEMORY;
  176. DWORD dwError = ERROR_SUCCESS;
  177. *ppstream = NULL;
  178. if (pfstm)
  179. {
  180. Pidl_Set(&(pfstm->m_pidl), pidl);
  181. ASSERT(pfstm->m_pidl);
  182. pfstm->m_dwAccessType = dwAccess;
  183. IUnknown_Set(&pfstm->m_pfd, pfd);
  184. IUnknown_Set((IUnknown **)&pfstm->m_ppd, (IUnknown *)ppd);
  185. pfstm->m_uliComplete = uliComplete;
  186. pfstm->m_uliTotal = uliTotal;
  187. pfstm->m_fClosePrgDlg = fClosePrgDlg;
  188. // GetHint() is going to want to spew status into the Status Bar
  189. // But how do we get the hwnd? This is an architectural question that
  190. // we need to solve for all Shell Extensions. The answer is to not use
  191. // the progress bar in the status bar but a Progress Dialog. But it's
  192. // the responsibility of the caller to do that.
  193. HWND hwnd = NULL;
  194. hr = pfd->GetHint(hwnd, NULL, &pfstm->m_hintSession, NULL, NULL);
  195. if (EVAL(SUCCEEDED(hr)))
  196. {
  197. LPITEMIDLIST pidlVirtualRoot;
  198. hr = pfd->GetFtpSite()->GetVirtualRoot(&pidlVirtualRoot);
  199. if (EVAL(SUCCEEDED(hr)))
  200. {
  201. LPITEMIDLIST pidlOriginalFtpPath;
  202. CWireEncoding * pwe = pfd->GetFtpSite()->GetCWireEncoding();
  203. hr = FtpGetCurrentDirectoryPidlWrap(pfstm->m_hintSession, TRUE, pwe, &pidlOriginalFtpPath);
  204. if (SUCCEEDED(hr))
  205. {
  206. LPITEMIDLIST pidlWithVirtualRoot;
  207. hr = FtpPidl_InsertVirtualRoot(pidlVirtualRoot, pidl, &pidlWithVirtualRoot);
  208. if (SUCCEEDED(hr))
  209. {
  210. hr = FtpSetCurrentDirectoryPidlWrap(pfstm->m_hintSession, TRUE, pidlWithVirtualRoot, TRUE, TRUE);
  211. if (SUCCEEDED(hr))
  212. {
  213. DWORD dwDownloadType = FtpPidl_GetDownloadType(pidl);
  214. // PERF: I bet we would be faster if we delayed the open until
  215. // the first ::Read(), ::Write(), or ::CopyToStream() call.
  216. Pidl_Set(&pfstm->m_pidlOriginalFtpPath, pidlOriginalFtpPath);
  217. hr = FtpOpenFileWrap(pfstm->m_hintSession, TRUE, FtpPidl_GetLastItemWireName(pidl), pfstm->m_dwAccessType, dwDownloadType, 0, &pfstm->m_hint);
  218. }
  219. ILFree(pidlWithVirtualRoot);
  220. }
  221. ILFree(pidlOriginalFtpPath);
  222. }
  223. ILFree(pidlVirtualRoot);
  224. }
  225. }
  226. if (SUCCEEDED(hr))
  227. hr = pfstm->QueryInterface(IID_IStream, (LPVOID *) ppstream);
  228. pfstm->Release();
  229. }
  230. return hr;
  231. }
  232. /****************************************************\
  233. Constructor
  234. \****************************************************/
  235. CFtpStm::CFtpStm() : m_cRef(1)
  236. {
  237. DllAddRef();
  238. // This needs to be allocated in Zero Inited Memory.
  239. // Assert that all Member Variables are inited to Zero.
  240. ASSERT(!m_hint);
  241. ASSERT(!m_dwAccessType);
  242. ASSERT(!m_pfd);
  243. ASSERT(!m_hintSession);
  244. ASSERT(!m_pidl);
  245. ASSERT(!m_ppd);
  246. LEAK_ADDREF(LEAK_CFtpStm);
  247. }
  248. /****************************************************\
  249. Destructor
  250. \****************************************************/
  251. CFtpStm::~CFtpStm()
  252. {
  253. if (m_hint)
  254. {
  255. InternetCloseHandle(m_hint);
  256. }
  257. // This COM object works like this:
  258. // 1. The constructor opens a handle to the server and
  259. // Changes directory into the dir we are going to work in.
  260. // 2. The original dir is saved (m_pidlOriginalFtpPath) in order to be restored later
  261. // because we cache the internet handle for perf and to keep our place on the server.
  262. // 3. The caller of this COM object can then copy data.
  263. // 4. We then Change directory to the original dir here before we close the internet handle.s
  264. if (m_pidlOriginalFtpPath && EVAL(m_hintSession))
  265. {
  266. EVAL(SUCCEEDED(FtpSetCurrentDirectoryPidlWrap(m_hintSession, TRUE, m_pidlOriginalFtpPath, TRUE, TRUE)));
  267. Pidl_Set(&m_pidlOriginalFtpPath, NULL);
  268. }
  269. if (m_hintSession)
  270. m_pfd->ReleaseHint(m_hintSession);
  271. ATOMICRELEASE(m_pfd);
  272. if (m_ppd && m_fClosePrgDlg)
  273. EVAL(SUCCEEDED(m_ppd->StopProgressDialog()));
  274. ATOMICRELEASE(m_ppd);
  275. ILFree(m_pidl);
  276. ILFree(m_pidlOriginalFtpPath);
  277. DllRelease();
  278. LEAK_DELREF(LEAK_CFtpStm);
  279. }
  280. //===========================
  281. // *** IUnknown Interface ***
  282. //===========================
  283. ULONG CFtpStm::AddRef()
  284. {
  285. m_cRef++;
  286. return m_cRef;
  287. }
  288. ULONG CFtpStm::Release()
  289. {
  290. ASSERT(m_cRef > 0);
  291. m_cRef--;
  292. if (m_cRef > 0)
  293. return m_cRef;
  294. delete this;
  295. return 0;
  296. }
  297. HRESULT CFtpStm::QueryInterface(REFIID riid, void **ppvObj)
  298. {
  299. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IStream))
  300. {
  301. *ppvObj = SAFECAST(this, IStream*);
  302. }
  303. else
  304. {
  305. TraceMsg(TF_FTPQI, "CFtpStm::QueryInterface() failed.");
  306. *ppvObj = NULL;
  307. return E_NOINTERFACE;
  308. }
  309. AddRef();
  310. return S_OK;
  311. }