123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- /*
- * 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/zcdefs.h>
- #include <kopano/platform.h>
- #include <utility>
- #include <mapi.h>
- #include <mapiutil.h>
- #include <kopano/ECLogger.h>
- #include <kopano/memory.hpp>
- #include <kopano/userutil.h>
- #include <kopano/charset/utf8string.h>
- #include <kopano/charset/convert.h>
- #include <kopano/ECDefs.h>
- #include <kopano/ECGuid.h>
- #include <kopano/IECServiceAdmin.h>
- #include <edkmdb.h>
- #include <edkguid.h>
- #include <kopano/IECLicense.h>
- #include <kopano/CommonUtil.h>
- #include <kopano/ECRestriction.h>
- #include <kopano/mapi_ptr.h>
- #include <kopano/mapiguidext.h>
- #include <kopano/Util.h>
- using namespace std;
- namespace KC {
- typedef KCHL::object_ptr<IECLicense, IID_IECLicense> ECLicensePtr;
- class servername _kc_final {
- public:
- servername(LPCTSTR lpszName): m_strName(lpszName) {}
- servername(const servername &other): m_strName(other.m_strName) {}
- servername& operator=(const servername &other) {
- if (&other != this)
- m_strName = other.m_strName;
- return *this;
- }
- LPTSTR c_str() const {
- return (LPTSTR)m_strName.c_str();
- }
- bool operator<(const servername &other) const {
- return wcscasecmp(m_strName.c_str(), other.m_strName.c_str()) < 0;
- }
- private:
- wstring m_strName;
- };
- static HRESULT GetMailboxDataPerServer(const char *lpszPath, const char *lpSSLKey, const char *lpSSLPass, DataCollector *lpCollector);
- static HRESULT GetMailboxDataPerServer(IMAPISession *lpSession, const char *lpszPath, DataCollector *lpCollector);
- static HRESULT UpdateServerList(IABContainer *lpContainer, std::set<servername> &listServers);
- class UserCountCollector _kc_final : public DataCollector {
- public:
- virtual HRESULT CollectData(LPMAPITABLE store_table) _kc_override;
- unsigned int result() const;
- private:
- unsigned int m_ulUserCount = 0;
- };
- template <typename string_type, ULONG prAccount>
- class UserListCollector _kc_final : public DataCollector {
- public:
- UserListCollector(IMAPISession *lpSession);
- virtual HRESULT GetRequiredPropTags(LPMAPIPROP prop, LPSPropTagArray *) const _kc_override;
- virtual HRESULT CollectData(LPMAPITABLE store_table) _kc_override;
- void swap_result(std::list<string_type> *lplstUsers);
- private:
- void push_back(LPSPropValue lpPropAccount);
- std::list<string_type> m_lstUsers;
- MAPISessionPtr m_ptrSession;
- };
- HRESULT DataCollector::GetRequiredPropTags(LPMAPIPROP /*lpProp*/, LPSPropTagArray *lppPropTagArray) const {
- static constexpr const SizedSPropTagArray(1, sptaDefaultProps) = {1, {PR_DISPLAY_NAME}};
- return Util::HrCopyPropTagArray(sptaDefaultProps, lppPropTagArray);
- }
- HRESULT DataCollector::GetRestriction(LPMAPIPROP lpProp, LPSRestriction *lppRestriction) {
- HRESULT hr = hrSuccess;
- SPropValue sPropOrphan;
- PROPMAP_START(1)
- PROPMAP_NAMED_ID(STORE_ENTRYIDS, PT_MV_BINARY, PSETID_Archive, "store-entryids")
- PROPMAP_INIT(lpProp);
- sPropOrphan.ulPropTag = PR_EC_DELETED_STORE;
- sPropOrphan.Value.b = TRUE;
- hr = ECAndRestriction(
- ECNotRestriction(
- ECAndRestriction(
- ECExistRestriction(PR_EC_DELETED_STORE) +
- ECPropertyRestriction(RELOP_EQ, PR_EC_DELETED_STORE, &sPropOrphan, ECRestriction::Cheap)
- )
- ) +
- ECExistRestriction(CHANGE_PROP_TYPE(PROP_STORE_ENTRYIDS, PT_MV_BINARY))
- ).CreateMAPIRestriction(lppRestriction, ECRestriction::Full);
- exitpm:
- return hr;
- }
- HRESULT UserCountCollector::CollectData(LPMAPITABLE lpStoreTable) {
- ULONG ulCount = 0;
- HRESULT hr = lpStoreTable->GetRowCount(0, &ulCount);
- if (hr != hrSuccess)
- return hr;
- m_ulUserCount += ulCount;
- return hrSuccess;
- }
- inline unsigned int UserCountCollector::result() const {
- return m_ulUserCount;
- }
- template<typename string_type, ULONG prAccount>
- UserListCollector<string_type, prAccount>::UserListCollector(IMAPISession *lpSession): m_ptrSession(lpSession, true) {}
- template<typename string_type, ULONG prAccount>
- HRESULT UserListCollector<string_type, prAccount>::GetRequiredPropTags(LPMAPIPROP /*lpProp*/, LPSPropTagArray *lppPropTagArray) const {
- static constexpr const SizedSPropTagArray(1, sptaDefaultProps) =
- {1, {PR_MAILBOX_OWNER_ENTRYID}};
- return Util::HrCopyPropTagArray(sptaDefaultProps, lppPropTagArray);
- }
- template<typename string_type, ULONG prAccount>
- HRESULT UserListCollector<string_type, prAccount>::CollectData(LPMAPITABLE lpStoreTable) {
- while (true) {
- SRowSetPtr ptrRows;
- HRESULT hr = lpStoreTable->QueryRows(50, 0, &ptrRows);
- if (hr != hrSuccess)
- return hr;
- for (SRowSetPtr::size_type i = 0; i < ptrRows.size(); ++i) {
- if (ptrRows[i].lpProps[0].ulPropTag != PR_MAILBOX_OWNER_ENTRYID)
- continue;
- HRESULT hrTmp;
- ULONG ulType;
- MAPIPropPtr ptrUser;
- SPropValuePtr ptrAccount;
- hrTmp = m_ptrSession->OpenEntry(ptrRows[i].lpProps[0].Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrRows[i].lpProps[0].Value.bin.lpb), &ptrUser.iid(), 0, &ulType, &~ptrUser);
- if (hrTmp != hrSuccess)
- continue;
- hrTmp = HrGetOneProp(ptrUser, prAccount, &~ptrAccount);
- if (hrTmp != hrSuccess)
- continue;
- push_back(std::move(ptrAccount));
- }
- if (ptrRows.size() < 50)
- break;
- }
- return hrSuccess;
- }
- template<typename string_type, ULONG prAccount>
- void UserListCollector<string_type, prAccount>::swap_result(std::list<string_type> *lplstUsers) {
- lplstUsers->swap(m_lstUsers);
- }
- template<>
- void UserListCollector<std::string, PR_ACCOUNT_A>::push_back(LPSPropValue lpPropAccount) {
- m_lstUsers.push_back(lpPropAccount->Value.lpszA);
- }
- template<>
- void UserListCollector<std::wstring, PR_ACCOUNT_W>::push_back(LPSPropValue lpPropAccount) {
- m_lstUsers.push_back(lpPropAccount->Value.lpszW);
- }
- HRESULT GetArchivedUserList(IMAPISession *lpMapiSession, const char *lpSSLKey,
- const char *lpSSLPass, std::list<std::string> *lplstUsers, bool bLocalOnly)
- {
- UserListCollector<std::string, PR_ACCOUNT_A> collector(lpMapiSession);
- HRESULT hr = GetMailboxData(lpMapiSession, lpSSLKey, lpSSLPass,
- bLocalOnly, &collector);
- if (hr != hrSuccess)
- return hr;
- collector.swap_result(lplstUsers);
- return hrSuccess;
- }
- HRESULT GetArchivedUserList(IMAPISession *lpMapiSession, const char *lpSSLKey,
- const char *lpSSLPass, std::list<std::wstring> *lplstUsers, bool bLocalOnly)
- {
- UserListCollector<std::wstring, PR_ACCOUNT_W> collector(lpMapiSession);
- HRESULT hr = GetMailboxData(lpMapiSession, lpSSLKey, lpSSLPass,
- bLocalOnly, &collector);
- if (hr != hrSuccess)
- return hr;
- collector.swap_result(lplstUsers);
- return hrSuccess;
- }
- HRESULT GetMailboxData(IMAPISession *lpMapiSession, const char *lpSSLKey,
- const char *lpSSLPass, bool bLocalOnly, DataCollector *lpCollector)
- {
- HRESULT hr = S_OK;
- AddrBookPtr ptrAdrBook;
- EntryIdPtr ptrDDEntryID;
- ABContainerPtr ptrDefaultDir;
- ABContainerPtr ptrCompanyDir;
- MAPITablePtr ptrHierarchyTable;
- SRowSetPtr ptrRows;
- MsgStorePtr ptrStore;
- ECServiceAdminPtr ptrServiceAdmin;
- ULONG ulObj = 0;
- ULONG cbDDEntryID = 0;
- ULONG ulCompanyCount = 0;
- std::set<servername> listServers;
- convert_context converter;
- KCHL::memory_ptr<ECSVRNAMELIST> lpSrvNameList;
- KCHL::memory_ptr<ECSERVERLIST> lpSrvList;
- static constexpr const SizedSPropTagArray(1, sCols) = {1, {PR_ENTRYID}};
- if (lpMapiSession == nullptr || lpCollector == nullptr)
- return MAPI_E_INVALID_PARAMETER;
- hr = lpMapiSession->OpenAddressBook(0, &IID_IAddrBook, 0, &~ptrAdrBook);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to open addressbook: 0x%08X", hr);
- return hr;
- }
- hr = ptrAdrBook->GetDefaultDir(&cbDDEntryID, &~ptrDDEntryID);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to open default addressbook: 0x%08X", hr);
- return hr;
- }
- hr = ptrAdrBook->OpenEntry(cbDDEntryID, ptrDDEntryID, NULL, 0, &ulObj, &~ptrDefaultDir);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to open GAB: 0x%08X", hr);
- return hr;
- }
- /* Open Hierarchy Table to see if we are running in multi-tenancy mode or not */
- hr = ptrDefaultDir->GetHierarchyTable(0, &~ptrHierarchyTable);
- if (hr != hrSuccess) {
- ec_log_crit("Unable to open hierarchy table: 0x%08X", hr);
- return hr;
- }
- hr = ptrHierarchyTable->GetRowCount(0, &ulCompanyCount);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to get hierarchy row count: 0x%08X", hr);
- return hr;
- }
- if( ulCompanyCount > 0) {
- hr = ptrHierarchyTable->SetColumns(sCols, TBL_BATCH);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to set set columns on user table: 0x%08X", hr);
- return hr;
- }
- /* multi-tenancy, loop through all subcontainers to find all users */
- hr = ptrHierarchyTable->QueryRows(ulCompanyCount, 0, &ptrRows);
- if (hr != hrSuccess)
- return hr;
-
- for (unsigned int i = 0; i < ptrRows.size(); ++i) {
- if (ptrRows[i].lpProps[0].ulPropTag != PR_ENTRYID) {
- ec_log_crit("Unable to get entryid to open tenancy Address Book");
- return MAPI_E_INVALID_PARAMETER;
- }
- hr = ptrAdrBook->OpenEntry(ptrRows[i].lpProps[0].Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrRows[i].lpProps[0].Value.bin.lpb), NULL, 0, &ulObj, &~ptrCompanyDir);
- if (hr != hrSuccess) {
- ec_log_crit("Unable to open tenancy Address Book: 0x%08X", hr);
- return hr;
- }
- hr = UpdateServerList(ptrCompanyDir, listServers);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to create tenancy server list");
- return hr;
- }
- }
- } else {
- hr = UpdateServerList(ptrDefaultDir, listServers);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to create server list");
- return hr;
- }
- }
- hr = HrOpenDefaultStore(lpMapiSession, &~ptrStore);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to open default store: 0x%08X", hr);
- return hr;
- }
- //@todo use PT_OBJECT to queryinterface
- hr = ptrStore->QueryInterface(IID_IECServiceAdmin, &~ptrServiceAdmin);
- if (hr != hrSuccess)
- return hr;
- hr = MAPIAllocateBuffer(sizeof(ECSVRNAMELIST), &~lpSrvNameList);
- if (hr != hrSuccess)
- return hr;
- hr = MAPIAllocateMore(sizeof(WCHAR *) * listServers.size(), lpSrvNameList, (LPVOID *)&lpSrvNameList->lpszaServer);
- if (hr != hrSuccess)
- return hr;
- lpSrvNameList->cServers = 0;
- for (const auto &i : listServers)
- lpSrvNameList->lpszaServer[lpSrvNameList->cServers++] = i.c_str();
- hr = ptrServiceAdmin->GetServerDetails(lpSrvNameList, MAPI_UNICODE, &~lpSrvList);
- if (hr == MAPI_E_NETWORK_ERROR) {
- //support single server
- hr = GetMailboxDataPerServer(lpMapiSession, "", lpCollector);
- if (hr != hrSuccess)
- return hr;
- return hrSuccess;
- } else if (FAILED(hr)) {
- ec_log_err("Unable to get server details: 0x%08X", hr);
- if (hr == MAPI_E_NOT_FOUND) {
- ec_log_err("Details for one or more requested servers was not found.");
- ec_log_err("This usually indicates a misconfigured home server for a user.");
- ec_log_err("Requested servers:");
- for (const auto &i : listServers)
- ec_log_err("* %ls", i.c_str());
- }
- return hr;
- }
- for (ULONG i = 0; i < lpSrvList->cServers; ++i) {
- wchar_t *wszPath = NULL;
- ec_log_info("Check server: \"%ls\" ssl=\"%ls\" flag=%08x",
- (lpSrvList->lpsaServer[i].lpszName)?lpSrvList->lpsaServer[i].lpszName : L"<UNKNOWN>",
- (lpSrvList->lpsaServer[i].lpszSslPath)?lpSrvList->lpsaServer[i].lpszSslPath : L"<UNKNOWN>",
- lpSrvList->lpsaServer[i].ulFlags);
- if (bLocalOnly && (lpSrvList->lpsaServer[i].ulFlags & EC_SDFLAG_IS_PEER) == 0) {
- ec_log_info("Skipping remote server: \"%ls\".",
- (lpSrvList->lpsaServer[i].lpszName)?lpSrvList->lpsaServer[i].lpszName : L"<UNKNOWN>");
- continue;
- }
- if (lpSrvList->lpsaServer[i].ulFlags & EC_SDFLAG_IS_PEER &&
- lpSrvList->lpsaServer[i].lpszFilePath != nullptr)
- wszPath = lpSrvList->lpsaServer[i].lpszFilePath;
- if (wszPath == NULL) {
- if(lpSrvList->lpsaServer[i].lpszSslPath == NULL) {
- ec_log_err("No SSL or File path found for server: \"%ls\", please fix your configuration.", lpSrvList->lpsaServer[i].lpszName);
- return hr;
- }
- wszPath = lpSrvList->lpsaServer[i].lpszSslPath;
- }
- hr = GetMailboxDataPerServer(converter.convert_to<char *>(wszPath), lpSSLKey, lpSSLPass, lpCollector);
- if(FAILED(hr)) {
- ec_log_err("Failed to collect data from server: \"%ls\", hr: 0x%08x", wszPath, hr);
- return hr;
- }
- }
- return hrSuccess;
- }
- HRESULT GetMailboxDataPerServer(const char *lpszPath, const char *lpSSLKey,
- const char *lpSSLPass, DataCollector *lpCollector)
- {
- MAPISessionPtr ptrSessionServer;
- HRESULT hr = HrOpenECAdminSession(&~ptrSessionServer, "userutil.cpp",
- "GetMailboxDataPerServer", lpszPath, 0, lpSSLKey,
- lpSSLPass);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to open admin session on server \"%s\": 0x%08X", lpszPath, hr);
- return hr;
- }
- return GetMailboxDataPerServer(ptrSessionServer, lpszPath, lpCollector);
- }
- /**
- * Get archived user count per server
- *
- * @param[in] lpszPath Path to a server
- * @param[out] lpulArchivedUsers The amount of archived user on the give server
- *
- * @return Mapi errors
- */
- HRESULT GetMailboxDataPerServer(IMAPISession *lpSession, const char *lpszPath,
- DataCollector *lpCollector)
- {
- MsgStorePtr ptrStoreAdmin;
- MAPITablePtr ptrStoreTable;
- SPropTagArrayPtr ptrPropTagArray;
- SRestrictionPtr ptrRestriction;
- ExchangeManageStorePtr ptrEMS;
- HRESULT hr = HrOpenDefaultStore(lpSession, &~ptrStoreAdmin);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to open default store on server \"%s\": 0x%08X", lpszPath, hr);
- return hr;
- }
- //@todo use PT_OBJECT to queryinterface
- hr = ptrStoreAdmin->QueryInterface(IID_IExchangeManageStore, &~ptrEMS);
- if (hr != hrSuccess)
- return hr;
- hr = ptrEMS->GetMailboxTable(nullptr, &~ptrStoreTable, MAPI_DEFERRED_ERRORS);
- if (hr != hrSuccess)
- return hr;
- hr = lpCollector->GetRequiredPropTags(ptrStoreAdmin, &~ptrPropTagArray);
- if (hr != hrSuccess)
- return hr;
- hr = ptrStoreTable->SetColumns(ptrPropTagArray, TBL_BATCH);
- if (hr != hrSuccess)
- return hr;
- hr = lpCollector->GetRestriction(ptrStoreAdmin, &~ptrRestriction);
- if (hr != hrSuccess)
- return hr;
- hr = ptrStoreTable->Restrict(ptrRestriction, TBL_BATCH);
- if (hr != hrSuccess)
- return hr;
- return lpCollector->CollectData(ptrStoreTable);
- }
- /**
- * Build a server list from a countainer with users
- *
- * @param[in] lpContainer A container to get users, groups and other objects
- * @param[in,out] A set with server names. The new servers will be added
- *
- * @return MAPI error codes
- */
- HRESULT UpdateServerList(IABContainer *lpContainer,
- std::set<servername> &listServers)
- {
- SRowSetPtr ptrRows;
- MAPITablePtr ptrTable;
- SPropValue sPropUser;
- SPropValue sPropDisplayType;
- static constexpr const SizedSPropTagArray(2, sCols) =
- {2, {PR_EC_HOMESERVER_NAME_W, PR_DISPLAY_NAME_W}};
- sPropDisplayType.ulPropTag = PR_DISPLAY_TYPE;
- sPropDisplayType.Value.ul = DT_REMOTE_MAILUSER;
- sPropUser.ulPropTag = PR_OBJECT_TYPE;
- sPropUser.Value.ul = MAPI_MAILUSER;
- HRESULT hr = lpContainer->GetContentsTable(MAPI_DEFERRED_ERRORS, &~ptrTable);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to open contents table: 0x%08X", hr);
- return hr;
- }
- hr = ptrTable->SetColumns(sCols, TBL_BATCH);
- if(hr != hrSuccess) {
- ec_log_crit("Unable to set set columns on user table: 0x%08X", hr);
- return hr;
- }
- // Restrict to users (not groups)
- hr = ECAndRestriction(
- ECPropertyRestriction(RELOP_NE, PR_DISPLAY_TYPE, &sPropDisplayType, ECRestriction::Cheap) +
- ECPropertyRestriction(RELOP_EQ, PR_OBJECT_TYPE, &sPropUser, ECRestriction::Cheap))
- .RestrictTable(ptrTable, TBL_BATCH);
- if (hr != hrSuccess) {
- ec_log_crit("Unable to get total user count: 0x%08X", hr);
- return hr;
- }
- while (true) {
- hr = ptrTable->QueryRows(50, 0, &ptrRows);
- if (hr != hrSuccess)
- return hr;
- if (ptrRows.empty())
- break;
- for (unsigned int i = 0; i < ptrRows.size(); ++i) {
- if (ptrRows[i].lpProps[0].ulPropTag != PR_EC_HOMESERVER_NAME_W)
- continue;
- listServers.insert(ptrRows[i].lpProps[0].Value.lpszW);
- if (ptrRows[i].lpProps[1].ulPropTag == PR_DISPLAY_NAME_W)
- ec_log_info("User: %ls on server \"%ls\"", ptrRows[i].lpProps[1].Value.lpszW, ptrRows[i].lpProps[0].Value.lpszW);
- }
- }
- return hrSuccess;
- }
- } /* namespace */
|