ECMemStream.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/platform.h>
  18. #include <new>
  19. #include <mapix.h>
  20. #include <kopano/ECGuid.h>
  21. #include <kopano/ECInterfaceDefs.h>
  22. #include <kopano/memory.hpp>
  23. #include "ECMemStream.h"
  24. #include <kopano/Trace.h>
  25. #include <kopano/ECDebug.h>
  26. #define EC_MEMBLOCK_SIZE 8192
  27. namespace KC {
  28. ECMemBlock::ECMemBlock(const char *buffer, ULONG ulDataLen, ULONG ulFlags) :
  29. ECUnknown("ECMemBlock")
  30. {
  31. this->ulFlags = ulFlags;
  32. if (ulDataLen == 0)
  33. return;
  34. cbTotal = ulDataLen;
  35. cbCurrent = ulDataLen;
  36. lpCurrent = (char *)malloc(ulDataLen);
  37. if (lpCurrent == nullptr)
  38. throw std::bad_alloc();
  39. memcpy(lpCurrent, buffer, ulDataLen);
  40. if (!(ulFlags & STGM_TRANSACTED))
  41. return;
  42. cbOriginal = ulDataLen;
  43. lpOriginal = (char *)malloc(ulDataLen);
  44. if (lpOriginal == nullptr)
  45. throw std::bad_alloc();
  46. memcpy(lpOriginal, buffer, ulDataLen);
  47. }
  48. ECMemBlock::~ECMemBlock()
  49. {
  50. free(lpCurrent);
  51. if (ulFlags & STGM_TRANSACTED)
  52. free(lpOriginal);
  53. }
  54. HRESULT ECMemBlock::Create(const char *buffer, ULONG ulDataLen, ULONG ulFlags,
  55. ECMemBlock **lppStream)
  56. {
  57. return alloc_wrap<ECMemBlock>(buffer, ulDataLen, ulFlags)
  58. .as(IID_ECMemBlock, lppStream);
  59. }
  60. HRESULT ECMemBlock::QueryInterface(REFIID refiid, void **lppInterface)
  61. {
  62. REGISTER_INTERFACE2(ECMemBlock, this);
  63. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  64. }
  65. // Reads at most ulLen chars, may be shorter due to shorter data len
  66. HRESULT ECMemBlock::ReadAt(ULONG ulPos, ULONG ulLen, char *buffer, ULONG *ulBytesRead)
  67. {
  68. HRESULT hr = hrSuccess;
  69. ULONG ulToRead = cbCurrent - ulPos;
  70. ulToRead = ulLen < ulToRead ? ulLen : ulToRead;
  71. memcpy(buffer, lpCurrent+ulPos, ulToRead);
  72. if(ulBytesRead)
  73. *ulBytesRead = ulToRead;
  74. return hr;
  75. }
  76. HRESULT ECMemBlock::WriteAt(ULONG ulPos, ULONG ulLen, const char *buffer,
  77. ULONG *ulBytesWritten)
  78. {
  79. ULONG dsize = ulPos + ulLen;
  80. if(cbTotal < dsize) {
  81. ULONG newsize = cbTotal + ((dsize/EC_MEMBLOCK_SIZE)+1)*EC_MEMBLOCK_SIZE; // + atleast 8k
  82. auto lpNew = static_cast<char *>(realloc(lpCurrent, newsize));
  83. if (lpNew == NULL)
  84. return MAPI_E_NOT_ENOUGH_MEMORY;
  85. lpCurrent = lpNew;
  86. memset(lpCurrent+cbTotal, 0, newsize-cbTotal); // clear new alloced mem
  87. cbTotal = newsize; // set new size
  88. }
  89. if (dsize > cbCurrent) // if write part is bigger than actual data
  90. cbCurrent = ulPos + ulLen; // set _real_ buffer size
  91. memcpy(lpCurrent+ulPos, buffer, ulLen);
  92. if(ulBytesWritten)
  93. *ulBytesWritten = ulLen;
  94. return hrSuccess;
  95. }
  96. HRESULT ECMemBlock::Commit()
  97. {
  98. if (!(ulFlags & STGM_TRANSACTED))
  99. return hrSuccess;
  100. free(lpOriginal);
  101. lpOriginal = NULL;
  102. lpOriginal = (char *)malloc(cbCurrent);
  103. if (lpOriginal == NULL)
  104. return MAPI_E_NOT_ENOUGH_MEMORY;
  105. cbOriginal = cbCurrent;
  106. memcpy(lpOriginal, lpCurrent, cbCurrent);
  107. return hrSuccess;
  108. }
  109. HRESULT ECMemBlock::Revert()
  110. {
  111. if (!(ulFlags & STGM_TRANSACTED))
  112. return hrSuccess;
  113. free(lpCurrent);
  114. lpCurrent = NULL;
  115. lpCurrent = (char *)malloc(cbOriginal);
  116. if (lpCurrent == NULL)
  117. return MAPI_E_NOT_ENOUGH_MEMORY;
  118. cbCurrent = cbTotal = cbOriginal;
  119. memcpy(lpCurrent, lpOriginal, cbOriginal);
  120. return hrSuccess;
  121. }
  122. HRESULT ECMemBlock::SetSize(ULONG ulSize)
  123. {
  124. auto lpNew = static_cast<char *>(realloc(lpCurrent, ulSize));
  125. if (lpNew == NULL && ulSize != 0)
  126. return MAPI_E_NOT_ENOUGH_MEMORY;
  127. if(ulSize > cbCurrent)
  128. memset(lpNew+cbCurrent, 0, ulSize-cbCurrent);
  129. lpCurrent = lpNew;
  130. cbCurrent = ulSize;
  131. cbTotal = ulSize;
  132. return hrSuccess;
  133. }
  134. HRESULT ECMemBlock::GetSize(ULONG *ulSize) const
  135. {
  136. *ulSize = cbCurrent;
  137. return hrSuccess;
  138. }
  139. /*
  140. * ECMemStream, IStream compatible in-memory stream object
  141. */
  142. ECMemStream::ECMemStream(char *buffer, ULONG ulDataLen, ULONG ulFlags, CommitFunc lpCommitFunc, DeleteFunc lpDeleteFunc,
  143. void *lpParam) : ECUnknown("IStream")
  144. {
  145. this->liPos.QuadPart = 0;
  146. ECMemBlock::Create(buffer, ulDataLen, ulFlags, &this->lpMemBlock);
  147. this->lpCommitFunc = lpCommitFunc;
  148. this->lpDeleteFunc = lpDeleteFunc;
  149. this->lpParam = lpParam;
  150. this->ulFlags = ulFlags;
  151. }
  152. ECMemStream::ECMemStream(ECMemBlock *lpMemBlock, ULONG ulFlags, CommitFunc lpCommitFunc, DeleteFunc lpDeleteFunc,
  153. void *lpParam) : ECUnknown("IStream")
  154. {
  155. this->liPos.QuadPart = 0;
  156. this->lpMemBlock = lpMemBlock;
  157. lpMemBlock->AddRef();
  158. this->lpCommitFunc = lpCommitFunc;
  159. this->lpDeleteFunc = lpDeleteFunc;
  160. this->lpParam = lpParam;
  161. this->ulFlags = ulFlags;
  162. }
  163. ECMemStream::~ECMemStream()
  164. {
  165. ULONG refs = 0;
  166. if(this->lpMemBlock)
  167. refs = this->lpMemBlock->Release();
  168. if (refs == 0 && this->lpDeleteFunc)
  169. lpDeleteFunc(lpParam);
  170. }
  171. HRESULT ECMemStream::QueryInterface(REFIID refiid, void **lppInterface)
  172. {
  173. REGISTER_INTERFACE2(IStream, &this->m_xStream);
  174. REGISTER_INTERFACE2(ISequentialStream, &this->m_xStream);
  175. REGISTER_INTERFACE2(IUnknown, &this->m_xStream);
  176. REGISTER_INTERFACE2(ECMemStream, this);
  177. REGISTER_INTERFACE2(ECUnknown, this);
  178. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  179. }
  180. ULONG ECMemStream::Release()
  181. {
  182. // Releasing last reference
  183. // If you read the docs on STGM_SHARE_EXCLUSIVE it doesn't say you need
  184. // to Commit() at the end, so if the client hasn't called Commit() yet,
  185. // we need to do it for them before throwing away the data.
  186. if (this->m_cRef == 1 && this->ulFlags & STGM_SHARE_EXCLUSIVE &&
  187. this->fDirty)
  188. this->Commit(0);
  189. return ECUnknown::Release();
  190. }
  191. HRESULT ECMemStream::Create(char *buffer, ULONG ulDataLen, ULONG ulFlags, CommitFunc lpCommitFunc, DeleteFunc lpDeleteFunc,
  192. void *lpParam, ECMemStream **lppStream)
  193. {
  194. return alloc_wrap<ECMemStream>(buffer, ulDataLen, ulFlags,
  195. lpCommitFunc, lpDeleteFunc, lpParam)
  196. .as(IID_ECMemStream, lppStream);
  197. }
  198. HRESULT ECMemStream::Create(ECMemBlock *lpMemBlock, ULONG ulFlags, CommitFunc lpCommitFunc, DeleteFunc lpDeleteFunc,
  199. void *lpParam, ECMemStream **lppStream)
  200. {
  201. return alloc_wrap<ECMemStream>(lpMemBlock, ulFlags, lpCommitFunc,
  202. lpDeleteFunc, lpParam).as(IID_ECMemStream, lppStream);
  203. }
  204. HRESULT ECMemStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
  205. {
  206. HRESULT hr = hrSuccess;
  207. ULONG ulRead = 0;
  208. // FIXME we currently accept any block size for reading, should this be capped at say 64k ?
  209. // cb = cb > 65536 ? 65536 : cb;
  210. // Outlookspy tries to read the whole thing into a small textbox in one go which takes rather long
  211. // so I suspect PST files and Exchange have some kind of limit here (it should never be a problem
  212. // if the client is correctly coded, but hey ...)
  213. hr = this->lpMemBlock->ReadAt((ULONG)this->liPos.QuadPart, cb, (char *)pv, &ulRead);
  214. liPos.QuadPart += ulRead;
  215. if(pcbRead)
  216. *pcbRead = ulRead;
  217. return hr;
  218. }
  219. HRESULT ECMemStream::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
  220. {
  221. HRESULT hr;
  222. ULONG ulWritten = 0;
  223. if(!(ulFlags&STGM_WRITE))
  224. return MAPI_E_NO_ACCESS;
  225. hr = this->lpMemBlock->WriteAt((ULONG)this->liPos.QuadPart, cb, (char *)pv, &ulWritten);
  226. if(hr != hrSuccess)
  227. return hr;
  228. liPos.QuadPart += ulWritten;
  229. if(pcbWritten)
  230. *pcbWritten = ulWritten;
  231. fDirty = TRUE;
  232. // If we're not in transacted mode, don't auto-commit; we should wait for the user
  233. // to commit() the flags. In exclusive mode, nobody else can see this stream, so there's
  234. // no point in committing already. We simply defer the commit until the stream is Released
  235. if(!(ulFlags & STGM_TRANSACTED) && !(ulFlags & STGM_SHARE_EXCLUSIVE))
  236. Commit(0);
  237. return hrSuccess;
  238. }
  239. HRESULT ECMemStream::Seek(LARGE_INTEGER dlibmove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
  240. {
  241. HRESULT hr;
  242. ULONG ulSize = 0;
  243. hr = this->lpMemBlock->GetSize(&ulSize);
  244. if(hr != hrSuccess)
  245. return hr;
  246. switch(dwOrigin) {
  247. case SEEK_SET:
  248. liPos.QuadPart = dlibmove.QuadPart;
  249. break;
  250. case SEEK_CUR:
  251. liPos.QuadPart += dlibmove.QuadPart;
  252. break;
  253. case SEEK_END:
  254. liPos.QuadPart = ulSize + dlibmove.QuadPart;
  255. break;
  256. }
  257. if(liPos.QuadPart > ulSize)
  258. liPos.QuadPart = ulSize;
  259. if(plibNewPosition)
  260. plibNewPosition->QuadPart = liPos.QuadPart;
  261. return hrSuccess;
  262. }
  263. HRESULT ECMemStream::SetSize(ULARGE_INTEGER libNewSize)
  264. {
  265. HRESULT hr;
  266. if(!(ulFlags&STGM_WRITE))
  267. return MAPI_E_NO_ACCESS;
  268. hr = lpMemBlock->SetSize((ULONG)libNewSize.QuadPart);
  269. this->fDirty = TRUE;
  270. return hr;
  271. }
  272. HRESULT ECMemStream::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
  273. {
  274. HRESULT hr;
  275. ULONG ulOffset = 0;
  276. ULONG ulWritten = 0;
  277. ULONG ulSize = 0;
  278. hr = lpMemBlock->GetSize(&ulSize);
  279. if(hr != hrSuccess)
  280. return hr;
  281. assert(liPos.u.HighPart == 0);
  282. ulOffset = liPos.u.LowPart;
  283. while(cb.QuadPart && ulSize > ulOffset) {
  284. pstm->Write(this->lpMemBlock->GetBuffer() + ulOffset, std::min(ulSize - ulOffset, cb.u.LowPart), &ulWritten);
  285. ulOffset += ulWritten;
  286. cb.QuadPart -= ulWritten;
  287. }
  288. if(pcbRead)
  289. pcbRead->QuadPart = ulOffset - liPos.u.LowPart;
  290. if(pcbWritten)
  291. pcbWritten->QuadPart = ulOffset - liPos.u.LowPart;
  292. liPos.QuadPart = ulOffset;
  293. return hrSuccess;
  294. }
  295. HRESULT ECMemStream::Commit(DWORD grfCommitFlags)
  296. {
  297. HRESULT hr = hrSuccess;
  298. KCHL::object_ptr<IStream> lpClonedStream;
  299. hr = this->lpMemBlock->Commit();
  300. if(hr != hrSuccess)
  301. return hr;
  302. // If there is no commit func, just ignore the commit
  303. if(this->lpCommitFunc) {
  304. hr = this->Clone(&~lpClonedStream);
  305. if(hr != hrSuccess)
  306. return hr;
  307. hr = this->lpCommitFunc(lpClonedStream, lpParam);
  308. }
  309. this->fDirty = FALSE;
  310. return hr;
  311. }
  312. HRESULT ECMemStream::Revert()
  313. {
  314. HRESULT hr = hrSuccess;
  315. hr = this->lpMemBlock->Revert();
  316. this->liPos.QuadPart = 0;
  317. return hr;
  318. }
  319. /* we don't support locking ! */
  320. HRESULT ECMemStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  321. {
  322. /* return STG_E_INVALIDFUNCTION; */
  323. return hrSuccess; /* hack for loadsim */
  324. }
  325. HRESULT ECMemStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  326. {
  327. return hrSuccess; //hack for loadsim
  328. //return STG_E_INVALIDFUNCTION;
  329. }
  330. HRESULT ECMemStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
  331. {
  332. HRESULT hr;
  333. ULONG ulSize = 0;
  334. if (pstatstg == NULL)
  335. return MAPI_E_INVALID_PARAMETER;
  336. hr = this->lpMemBlock->GetSize(&ulSize);
  337. if(hr != hrSuccess)
  338. return hr;
  339. memset(pstatstg, 0, sizeof(STATSTG));
  340. pstatstg->cbSize.QuadPart = ulSize;
  341. pstatstg->type = STGTY_STREAM;
  342. pstatstg->grfMode = ulFlags;
  343. return hrSuccess;
  344. }
  345. HRESULT ECMemStream::Clone(IStream **ppstm)
  346. {
  347. HRESULT hr = hrSuccess;
  348. ECMemStream *lpStream = NULL;
  349. ECMemStream::Create(this->lpMemBlock, ulFlags, this->lpCommitFunc, this->lpDeleteFunc, lpParam, &lpStream);
  350. hr = lpStream->QueryInterface(IID_IStream, (void **)ppstm);
  351. lpStream->Release();
  352. return hr;
  353. }
  354. ULONG ECMemStream::GetSize()
  355. {
  356. ULONG ulSize = 0;
  357. this->lpMemBlock->GetSize(&ulSize);
  358. return ulSize;
  359. }
  360. char* ECMemStream::GetBuffer()
  361. {
  362. return this->lpMemBlock->GetBuffer();
  363. }
  364. DEF_ULONGMETHOD1(TRACE_MAPI, ECMemStream, Stream, AddRef, (void))
  365. DEF_ULONGMETHOD1(TRACE_MAPI, ECMemStream, Stream, Release, (void))
  366. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, QueryInterface, (REFIID, refiid), (LPVOID *, lppInterface))
  367. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, Read, (void *, pv), (ULONG, cb), (ULONG *, pcbRead))
  368. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, Write, (const void *, pv), (ULONG, cb), (ULONG *, pcbWritten))
  369. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, Seek, (LARGE_INTEGER, dlibmove), (DWORD, dwOrigin), (ULARGE_INTEGER *, plibNewPosition))
  370. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, SetSize, (ULARGE_INTEGER, libNewSize))
  371. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, CopyTo, (IStream *, pstm), (ULARGE_INTEGER, cb), (ULARGE_INTEGER *, pcbRead), (ULARGE_INTEGER *, pcbWritten))
  372. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, Commit, (DWORD, grfCommitFlags))
  373. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, Revert, (void))
  374. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, LockRegion, (ULARGE_INTEGER, libOffset), (ULARGE_INTEGER, cb), (DWORD, dwLockType))
  375. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, UnlockRegion, (ULARGE_INTEGER, libOffset), (ULARGE_INTEGER, cb), (DWORD, dwLockType))
  376. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, Stat, (STATSTG *, pstatstg), (DWORD, grfStatFlag))
  377. DEF_HRMETHOD1(TRACE_MAPI, ECMemStream, Stream, Clone, (IStream **, ppstm))
  378. } /* namespace */