stubber.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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 <kopano/ECConfig.h>
  19. #include "ECArchiverLogger.h"
  20. #include "stubber.h"
  21. #include <kopano/archiver-common.h>
  22. #include "helpers/MAPIPropHelper.h"
  23. #include <kopano/mapiext.h>
  24. using namespace KC::helpers;
  25. namespace KC { namespace operations {
  26. /**
  27. * @param[in] lpLogger
  28. * Pointer to the logger.
  29. * @param[in] ulptStubbed
  30. * The proptag of the stubbed property {72e98ebc-57d2-4ab5-b0aad50a7b531cb9}/stubbed
  31. */
  32. Stubber::Stubber(ECArchiverLogger *lpLogger, ULONG ulptStubbed, int ulAge, bool bProcessUnread)
  33. : ArchiveOperationBase(lpLogger, ulAge, bProcessUnread, ARCH_NEVER_STUB)
  34. , m_ulptStubbed(ulptStubbed)
  35. { }
  36. HRESULT Stubber::ProcessEntry(LPMAPIFOLDER lpFolder, ULONG cProps, const LPSPropValue lpProps)
  37. {
  38. HRESULT hr;
  39. MessagePtr ptrMessage;
  40. ULONG ulType = 0;
  41. assert(lpFolder != NULL);
  42. if (lpFolder == NULL)
  43. return MAPI_E_INVALID_PARAMETER;
  44. auto lpEntryId = PCpropFindProp(lpProps, cProps, PR_ENTRYID);
  45. if (lpEntryId == NULL) {
  46. Logger()->Log(EC_LOGLEVEL_FATAL, "PR_ENTRYID missing");
  47. return MAPI_E_NOT_FOUND;
  48. }
  49. Logger()->Log(EC_LOGLEVEL_DEBUG, "Opening message (%s)", bin2hex(lpEntryId->Value.bin.cb, lpEntryId->Value.bin.lpb).c_str());
  50. hr = lpFolder->OpenEntry(lpEntryId->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpEntryId->Value.bin.lpb), &IID_IECMessageRaw, MAPI_BEST_ACCESS, &ulType, &~ptrMessage);
  51. if (hr == MAPI_E_NOT_FOUND) {
  52. Logger()->Log(EC_LOGLEVEL_WARNING, "Failed to open message. This can happen if the search folder is lagging.");
  53. return hrSuccess;
  54. } else if (hr != hrSuccess) {
  55. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to open message. (hr=%s)", stringify(hr, true).c_str());
  56. return hr;
  57. }
  58. return ProcessEntry(ptrMessage);
  59. }
  60. HRESULT Stubber::ProcessEntry(LPMESSAGE lpMessage)
  61. {
  62. HRESULT hr;
  63. SPropValue sProps[3];
  64. SPropValue sProp = {0};
  65. MAPITablePtr ptrAttTable;
  66. SRowSetPtr ptrRowSet;
  67. AttachPtr ptrAttach;
  68. ULONG ulAttachNum = 0;
  69. MAPIPropHelperPtr ptrMsgHelper;
  70. ObjectEntryList lstMsgArchives;
  71. static constexpr const SizedSPropTagArray(1, sptaTableProps) = {1, {PR_ATTACH_NUM}};
  72. assert(lpMessage != NULL);
  73. if (lpMessage == NULL)
  74. return MAPI_E_INVALID_PARAMETER;
  75. hr = VerifyRestriction(lpMessage);
  76. if (hr == MAPI_E_NOT_FOUND) {
  77. // This is not an error
  78. Logger()->Log(EC_LOGLEVEL_WARNING, "Ignoring message because it doesn't match the criteria for begin stubbed.");
  79. Logger()->Log(EC_LOGLEVEL_WARNING, "This can happen when huge amounts of message are being processed.");
  80. return hrSuccess;
  81. } else if (hr != hrSuccess) {
  82. Logger()->Log(EC_LOGLEVEL_WARNING, "Failed to verify message criteria. (hr=%s)", stringify(hr, true).c_str());
  83. return hr;
  84. }
  85. // Verify if we have at least one archive that's in the current multi-server cluster.
  86. hr = MAPIPropHelper::Create(MAPIPropPtr(lpMessage, true), &ptrMsgHelper);
  87. if (hr != hrSuccess) {
  88. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to create prop helper. (hr=%s)", stringify(hr, true).c_str());
  89. return hr;
  90. }
  91. hr = ptrMsgHelper->GetArchiveList(&lstMsgArchives);
  92. if (hr != hrSuccess) {
  93. if (hr == MAPI_E_CORRUPT_DATA) {
  94. Logger()->Log(EC_LOGLEVEL_ERROR, "Existing list of archives is corrupt, skipping message.");
  95. hr = hrSuccess;
  96. } else
  97. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to get list of archives. (hr=%s)", stringify(hr, true).c_str());
  98. return hr;
  99. }
  100. if (find_if(lstMsgArchives.begin(), lstMsgArchives.end(), IsNotWrapped()) == lstMsgArchives.end()) {
  101. Logger()->Log(EC_LOGLEVEL_WARNING, "Message has no archives that are directly accessible, message will not be stubbed.");
  102. return hr;
  103. }
  104. sProps[0].ulPropTag = m_ulptStubbed;
  105. sProps[0].Value.b = 1;
  106. sProps[1].ulPropTag = PR_BODY;
  107. sProps[1].Value.LPSZ = const_cast<TCHAR *>(_T("This message is archived..."));
  108. sProps[2].ulPropTag = PR_ICON_INDEX;
  109. sProps[2].Value.l = 2;
  110. hr = lpMessage->SetProps(3, sProps, NULL);
  111. if (hr != hrSuccess) {
  112. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to set properties. (hr=%s)", stringify(hr, true).c_str());
  113. return hr;
  114. }
  115. hr = lpMessage->GetAttachmentTable(fMapiDeferredErrors, &~ptrAttTable);
  116. if (hr != hrSuccess) {
  117. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to get attachment table. (hr=%s)", stringify(hr, true).c_str());
  118. return hr;
  119. }
  120. hr = HrQueryAllRows(ptrAttTable, sptaTableProps, NULL, NULL, 0, &ptrRowSet);
  121. if (hr != hrSuccess) {
  122. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to get attachment numbers. (hr=%s)", stringify(hr, true).c_str());
  123. return hr;
  124. }
  125. if (!ptrRowSet.empty()) {
  126. Logger()->Log(EC_LOGLEVEL_INFO, "Removing %u attachments", ptrRowSet.size());
  127. for (ULONG i = 0; i < ptrRowSet.size(); ++i) {
  128. hr = lpMessage->DeleteAttach(ptrRowSet[i].lpProps[0].Value.ul, 0, NULL, 0);
  129. if (hr != hrSuccess) {
  130. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to delete attachment %u. (hr=%s)", i, stringify(hr, true).c_str());
  131. return hr;
  132. }
  133. }
  134. Logger()->Log(EC_LOGLEVEL_INFO, "Adding placeholder attachment");
  135. hr = lpMessage->CreateAttach(&ptrAttach.iid(), 0, &ulAttachNum, &~ptrAttach);
  136. if (hr != hrSuccess) {
  137. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to create attachment. (hr=%s)", stringify(hr, true).c_str());
  138. return hr;
  139. }
  140. sProp.ulPropTag = PR_ATTACH_FILENAME;
  141. sProp.Value.LPSZ = const_cast<TCHAR *>(_T("dummy"));
  142. hr = ptrAttach->SetProps(1, &sProp, NULL);
  143. if (hr != hrSuccess) {
  144. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to set attachment properties. (hr=%s)", stringify(hr, true).c_str());
  145. return hr;
  146. }
  147. hr = ptrAttach->SaveChanges(0);
  148. if (hr != hrSuccess) {
  149. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to save attachment. (hr=%s)", stringify(hr, true).c_str());
  150. return hr;
  151. }
  152. }
  153. hr = lpMessage->SaveChanges(0);
  154. if (hr != hrSuccess) {
  155. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to save stubbed message. (hr=%s)", stringify(hr, true).c_str());
  156. return hr;
  157. }
  158. return hrSuccess;
  159. }
  160. }} /* namespace */