123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <kopano/platform.h>
- #include <new>
- #include "archive.h"
- #include <kopano/ECLogger.h>
- #include <kopano/ECGetText.h>
- #include <kopano/charset/convert.h>
- #include <kopano/mapi_ptr.h>
- #include "helpers/StoreHelper.h"
- #include "operations/copier.h"
- #include "operations/instanceidmapper.h"
- #include "ArchiverSession.h"
- #include "helpers/ArchiveHelper.h"
- #include <list>
- #include <sstream>
- #include <kopano/Util.h>
- #include <kopano/ECDebug.h>
- using namespace std;
- using namespace KC::helpers;
- using namespace KC::operations;
- typedef std::unique_ptr<Copier::Helper> HelperPtr;
- #ifdef UNICODE
- typedef std::wostringstream tostringstream;
- #else
- typedef std::ostringstream tostringstream;
- #endif
- void ArchiveResult::AddMessage(MessagePtr ptrMessage) {
- m_lstMessages.push_back(ptrMessage);
- }
- void ArchiveResult::Undo(IMAPISession *lpSession) {
- for (const auto i : m_lstMessages)
- Util::HrDeleteMessage(lpSession, i);
- }
- HRESULT Archive::Create(IMAPISession *lpSession, ArchivePtr *lpptrArchive)
- {
- if (lpSession == NULL || lpptrArchive == NULL)
- return MAPI_E_INVALID_PARAMETER;
- auto x = new(std::nothrow) Archive(lpSession);
- if (x == nullptr)
- return MAPI_E_NOT_ENOUGH_MEMORY;
- lpptrArchive->reset(x);
- return hrSuccess;
- }
- Archive::Archive(IMAPISession *lpSession)
- : m_ptrSession(lpSession, true)
- {
- }
- HRESULT Archive::HrArchiveMessageForDelivery(IMessage *lpMessage)
- {
- HRESULT hr = hrSuccess;
- ULONG cMsgProps;
- SPropArrayPtr ptrMsgProps;
- MsgStorePtr ptrStore;
- ULONG ulType;
- MAPIFolderPtr ptrFolder;
- StoreHelperPtr ptrStoreHelper;
- SObjectEntry refMsgEntry;
- ObjectEntryList lstArchives;
- ArchiverSessionPtr ptrSession;
- InstanceIdMapperPtr ptrMapper;
- HelperPtr ptrHelper;
- list<pair<MessagePtr,PostSaveActionPtr> > lstArchivedMessages;
- ArchiveResult result;
- ObjectEntryList lstReferences;
- MAPIPropHelperPtr ptrMsgHelper;
- static constexpr const SizedSPropTagArray(3, sptaMessageProps) =
- {3, {PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID}};
- enum {IDX_ENTRYID, IDX_STORE_ENTRYID, IDX_PARENT_ENTRYID};
- if (lpMessage == NULL) {
- hr = MAPI_E_INVALID_PARAMETER;
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): invalid parameter");
- goto exit;
- }
- hr = lpMessage->GetProps(sptaMessageProps, 0, &cMsgProps, &~ptrMsgProps);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): GetProps failed %x", hr);
- goto exit;
- }
- refMsgEntry.sStoreEntryId.assign(ptrMsgProps[IDX_STORE_ENTRYID].Value.bin);
- refMsgEntry.sItemEntryId.assign(ptrMsgProps[IDX_ENTRYID].Value.bin);
- hr = m_ptrSession->OpenMsgStore(0, ptrMsgProps[IDX_STORE_ENTRYID].Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrMsgProps[IDX_STORE_ENTRYID].Value.bin.lpb), &ptrStore.iid(), MDB_WRITE, &~ptrStore);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): OpenMsgStore failed %x", hr);
- goto exit;
- }
- hr = StoreHelper::Create(ptrStore, &ptrStoreHelper);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): StoreHelper::Create failed %x", hr);
- goto exit;
- }
- hr = ptrStoreHelper->GetArchiveList(&lstArchives);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): StoreHelper::GetArchiveList failed %x", hr);
- goto exit;
- }
- if (lstArchives.empty()) {
- ec_log_debug("No archives attached to store");
- goto exit;
- }
- hr = ptrStore->OpenEntry(ptrMsgProps[IDX_PARENT_ENTRYID].Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrMsgProps[IDX_PARENT_ENTRYID].Value.bin.lpb), &ptrFolder.iid(), MAPI_MODIFY, &ulType, &~ptrFolder);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): StoreHelper::OpenEntry failed %x", hr);
- goto exit;
- }
- hr = ArchiverSession::Create(m_ptrSession, ec_log_get(), &ptrSession);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): ArchiverSession::Create failed %x", hr);
- goto exit;
- }
- /**
- * @todo: Create an archiver config object globally in the calling application to
- * avoid the creation of the configuration for each message to be archived.
- */
- hr = InstanceIdMapper::Create(ec_log_get(), NULL, &ptrMapper);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): InstanceIdMapper::Create failed %x", hr);
- goto exit;
- }
- // First create all (mostly one) the archive messages without saving them.
- ptrHelper.reset(new(std::nothrow) Copier::Helper(ptrSession,
- ec_log_get(), ptrMapper, nullptr, ptrFolder));
- if (ptrHelper == nullptr) {
- hr = MAPI_E_NOT_ENOUGH_MEMORY;
- goto exit;
- }
- for (const auto &arc : lstArchives) {
- MessagePtr ptrArchivedMsg;
- PostSaveActionPtr ptrPSAction;
- hr = ptrHelper->CreateArchivedMessage(lpMessage, arc, refMsgEntry, &~ptrArchivedMsg, &ptrPSAction);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): CreateArchivedMessage failed %x", hr);
- goto exit;
- }
- lstArchivedMessages.push_back(make_pair(ptrArchivedMsg, ptrPSAction));
- }
- // Now save the messages one by one. On failure all saved messages need to be deleted.
- for (const auto &msg : lstArchivedMessages) {
- ULONG cArchivedMsgProps;
- SPropArrayPtr ptrArchivedMsgProps;
- SObjectEntry refArchiveEntry;
- hr = msg.first->GetProps(sptaMessageProps, 0,
- &cArchivedMsgProps, &~ptrArchivedMsgProps);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): ArchivedMessage GetProps failed %x", hr);
- goto exit;
- }
- refArchiveEntry.sItemEntryId.assign(ptrArchivedMsgProps[IDX_ENTRYID].Value.bin);
- refArchiveEntry.sStoreEntryId.assign(ptrArchivedMsgProps[IDX_STORE_ENTRYID].Value.bin);
- lstReferences.push_back(refArchiveEntry);
- hr = msg.first->SaveChanges(KEEP_OPEN_READWRITE);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): ArchivedMessage SaveChanges failed %x", hr);
- goto exit;
- }
- if (msg.second) {
- HRESULT hrTmp = msg.second->Execute();
- if (hrTmp != hrSuccess)
- ec_log_warn("Failed to execute post save action. hr=0x%08x", hrTmp);
- }
- result.AddMessage(msg.first);
- }
- // Now add the references to the original message.
- lstReferences.sort();
- lstReferences.unique();
- hr = MAPIPropHelper::Create(MAPIPropPtr(lpMessage, true), &ptrMsgHelper);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForDelivery(): failed creating reference to original message %x", hr);
- goto exit;
- }
- hr = ptrMsgHelper->SetArchiveList(lstReferences, true);
- exit:
- // On error delete all saved archives
- if (FAILED(hr))
- result.Undo(m_ptrSession);
- return hr;
- }
- HRESULT Archive::HrArchiveMessageForSending(IMessage *lpMessage, ArchiveResult *lpResult)
- {
- HRESULT hr = hrSuccess;
- ULONG cMsgProps;
- SPropArrayPtr ptrMsgProps;
- MsgStorePtr ptrStore;
- StoreHelperPtr ptrStoreHelper;
- ObjectEntryList lstArchives;
- ArchiverSessionPtr ptrSession;
- InstanceIdMapperPtr ptrMapper;
- HelperPtr ptrHelper;
- list<pair<MessagePtr,PostSaveActionPtr> > lstArchivedMessages;
- ArchiveResult result;
- static constexpr const SizedSPropTagArray(2, sptaMessageProps) = {1, {PR_STORE_ENTRYID}};
- enum {IDX_STORE_ENTRYID};
- if (lpMessage == NULL) {
- hr = MAPI_E_INVALID_PARAMETER;
- goto exit;
- }
- hr = lpMessage->GetProps(sptaMessageProps, 0, &cMsgProps, &~ptrMsgProps);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForSending(): GetProps failed %x", hr);
- goto exit;
- }
- hr = m_ptrSession->OpenMsgStore(0, ptrMsgProps[IDX_STORE_ENTRYID].Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrMsgProps[IDX_STORE_ENTRYID].Value.bin.lpb), &ptrStore.iid(), 0, &~ptrStore);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForSending(): OpenMsgStore failed %x", hr);
- goto exit;
- }
- hr = StoreHelper::Create(ptrStore, &ptrStoreHelper);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForSending(): StoreHelper::Create failed %x", hr);
- goto exit;
- }
- hr = ptrStoreHelper->GetArchiveList(&lstArchives);
- if (hr != hrSuccess) {
- ec_log_err("Unable to obtain list of attached archives. hr=0x%08x", hr);
- SetErrorMessage(hr, _("Unable to obtain list of attached archives."));
- goto exit;
- }
- if (lstArchives.empty()) {
- ec_log_debug("No archives attached to store");
- goto exit;
- }
- hr = ArchiverSession::Create(m_ptrSession, ec_log_get(), &ptrSession);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForSending(): ArchiverSession::Create failed %x", hr);
- goto exit;
- }
- /**
- * @todo: Create an archiver config object globally in the calling application to
- * avoid the creation of the configuration for each message to be archived.
- */
- hr = InstanceIdMapper::Create(ec_log_get(), NULL, &ptrMapper);
- if (hr != hrSuccess) {
- ec_log_warn("Archive::HrArchiveMessageForSending(): InstanceIdMapper::Create failed %x", hr);
- goto exit;
- }
- // First create all (mostly one) the archive messages without saving them.
- // We pass an empty MAPIFolderPtr here!
- ptrHelper.reset(new(std::nothrow) Copier::Helper(ptrSession,
- ec_log_get(), ptrMapper, nullptr, MAPIFolderPtr()));
- if (ptrHelper == nullptr) {
- hr = MAPI_E_NOT_ENOUGH_MEMORY;
- goto exit;
- }
- for (const auto &arc : lstArchives) {
- ArchiveHelperPtr ptrArchiveHelper;
- MAPIFolderPtr ptrArchiveFolder;
- MessagePtr ptrArchivedMsg;
- PostSaveActionPtr ptrPSAction;
- hr = ArchiveHelper::Create(ptrSession, arc, ec_log_get(), &ptrArchiveHelper);
- if (hr != hrSuccess) {
- SetErrorMessage(hr, _("Unable to open archive."));
- goto exit;
- }
- hr = ptrArchiveHelper->GetOutgoingFolder(&~ptrArchiveFolder);
- if (hr != hrSuccess) {
- ec_log_err("Failed to get outgoing archive folder. hr=0x%08x", hr);
- SetErrorMessage(hr, _("Unable to get outgoing archive folder."));
- goto exit;
- }
- hr = ptrArchiveFolder->CreateMessage(&ptrArchivedMsg.iid(), 0, &~ptrArchivedMsg);
- if (hr != hrSuccess) {
- ec_log_err("Failed to create message in outgoing archive folder. hr=0x%08x", hr);
- SetErrorMessage(hr, _("Unable to create archive message in outgoing archive folder."));
- goto exit;
- }
- hr = ptrHelper->ArchiveMessage(lpMessage, NULL, ptrArchivedMsg, &ptrPSAction);
- if (hr != hrSuccess) {
- SetErrorMessage(hr, _("Unable to copy message data."));
- goto exit;
- }
- ec_log_info("Stored message in archive");
- lstArchivedMessages.push_back(make_pair(ptrArchivedMsg, ptrPSAction));
- }
- // Now save the messages one by one. On failure all saved messages need to be deleted.
- for (const auto &msg : lstArchivedMessages) {
- hr = msg.first->SaveChanges(KEEP_OPEN_READONLY);
- if (hr != hrSuccess) {
- ec_log_err("Failed to save message in archive. hr=0x%08x", hr);
- SetErrorMessage(hr, _("Unable to save archived message."));
- goto exit;
- }
- if (msg.second) {
- HRESULT hrTmp = msg.second->Execute();
- if (hrTmp != hrSuccess)
- ec_log_warn("Failed to execute post save action. hr=0x%08x", hrTmp);
- }
- result.AddMessage(msg.first);
- }
- if (lpResult)
- std::swap(result, *lpResult);
- exit:
- // On error delete all saved archives
- if (FAILED(hr))
- result.Undo(m_ptrSession);
- return hr;
- }
- void Archive::SetErrorMessage(HRESULT hr, LPCTSTR lpszMessage)
- {
- tostringstream oss;
- LPTSTR lpszDesc;
- oss << lpszMessage << endl;
- oss << _("Error code:") << _T(" ") << convert_to<tstring>(GetMAPIErrorDescription(hr))
- << _T(" (") << tstringify(hr, true) << _T(")") << endl;
- if (Util::HrMAPIErrorToText(hr, &lpszDesc) == hrSuccess)
- oss << _("Error description:") << _T(" ") << lpszDesc << endl;
- m_strErrorMessage.assign(oss.str());
- }
|