1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546 |
- /*
- * 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 <chrono>
- #include <memory>
- #include <mutex>
- #include <new>
- #include <utility>
- #include <pthread.h>
- #include <mapidefs.h>
- #include <mapitags.h>
- #include <kopano/lockhelper.hpp>
- #include <kopano/tie.hpp>
- #include "ECMAPI.h"
- #include "ECDatabase.h"
- #include "ECSessionGroup.h"
- #include "ECSessionManager.h"
- #include "ECStatsCollector.h"
- #include "ECTPropsPurge.h"
- #include "ECLicenseClient.h"
- #include "ECDatabaseUtils.h"
- #include "ECSecurity.h"
- #include "SSLUtil.h"
- #include <kopano/Trace.h>
- #include "kcore.hpp"
- #include "ECICS.h"
- #include <edkmdb.h>
- #include "logontime.hpp"
- using namespace KCHL;
- namespace KC {
- ECSessionManager::ECSessionManager(ECConfig *lpConfig, ECLogger *lpAudit,
- bool bHostedKopano, bool bDistributedKopano) :
- m_lpConfig(lpConfig), m_lpAudit(lpAudit),
- m_bHostedKopano(bHostedKopano),
- m_bDistributedKopano(bDistributedKopano)
- {
- int err = 0;
- if (m_lpAudit)
- m_lpAudit->AddRef();
- m_lpDatabaseFactory = new ECDatabaseFactory(lpConfig);
- m_lpPluginFactory = new ECPluginFactory(lpConfig, g_lpStatsCollector, bHostedKopano, bDistributedKopano);
- m_lpECCacheManager = new ECCacheManager(lpConfig, m_lpDatabaseFactory);
- m_lpSearchFolders = new ECSearchFolders(this, m_lpDatabaseFactory);
- m_lpTPropsPurge = new ECTPropsPurge(lpConfig, m_lpDatabaseFactory);
- m_ptrLockManager = ECLockManager::Create();
-
- // init SSL randomness for session IDs
- ssl_random_init();
- //Create session clean up thread
- err = pthread_create(&m_hSessionCleanerThread, NULL, SessionCleaner, (void*)this);
- set_thread_name(m_hSessionCleanerThread, "SessionCleanUp");
-
- if (err != 0)
- ec_log_crit("Unable to spawn thread for session cleaner! Sessions will live forever!: %s", strerror(err));
- m_lpNotificationManager = new ECNotificationManager();
- }
- ECSessionManager::~ECSessionManager()
- {
- ulock_normal l_exit(m_hExitMutex);
- bExit = TRUE;
- m_hExitSignal.notify_one();
- l_exit.unlock();
- delete m_lpTPropsPurge;
- delete m_lpDatabase;
- delete m_lpDatabaseFactory;
-
- int err = pthread_join(m_hSessionCleanerThread, NULL);
- if (err != 0)
- ec_log_crit("Unable to join session cleaner thread: %s", strerror(err));
- /* Clean up all sessions */
- std::lock_guard<KC::shared_mutex> l_cache(m_hCacheRWLock);
- auto iSession = m_mapSessions.begin();
- while (iSession != m_mapSessions.cend()) {
- delete iSession->second;
- auto iSessionNext = iSession;
- ++iSessionNext;
- ec_log_info("End of session (shutdown) %llu",
- static_cast<unsigned long long>(iSession->first));
- m_mapSessions.erase(iSession);
- iSession = iSessionNext;
- }
-
- delete m_lpNotificationManager;
- //#ifdef DEBUG
- // Clearing the cache takes too long while shutting down
- delete m_lpECCacheManager;
- //#endif
- delete m_lpSearchFolders;
- delete m_lpPluginFactory;
- delete m_lpServerGuid;
- if (m_lpAudit != NULL)
- m_lpAudit->Release();
- }
- ECRESULT ECSessionManager::LoadSettings(){
- ECRESULT er = erSuccess;
-
- ECDatabase * lpDatabase = NULL;
- DB_RESULT lpDBResult;
- DB_ROW lpDBRow = NULL;
- DB_LENGTHS lpDBLenths = NULL;
- std::string strQuery;
- if (m_lpServerGuid != nullptr)
- return KCERR_BAD_VALUE;
- er = GetThreadLocalDatabase(m_lpDatabaseFactory, &lpDatabase);
- if(er != erSuccess)
- return er;
-
- strQuery = "SELECT `value` FROM settings WHERE `name` = 'server_guid'";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLenths = lpDatabase->FetchRowLengths(lpDBResult);
- if (lpDBRow == nullptr || lpDBRow[0] == nullptr ||
- lpDBLenths == nullptr || lpDBLenths[0] != sizeof(GUID))
- return KCERR_NOT_FOUND;
- m_lpServerGuid = new GUID;
- memcpy(m_lpServerGuid, lpDBRow[0], sizeof(GUID));
- strQuery = "SELECT `value` FROM settings WHERE `name` = 'source_key_auto_increment'";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLenths = lpDatabase->FetchRowLengths(lpDBResult);
- if (lpDBRow == nullptr || lpDBRow[0] == nullptr ||
- lpDBLenths == nullptr || lpDBLenths[0] != 8)
- return KCERR_NOT_FOUND;
- memcpy(&m_ullSourceKeyAutoIncrement, lpDBRow[0], sizeof(m_ullSourceKeyAutoIncrement));
- return erSuccess;
- }
- ECRESULT ECSessionManager::CheckUserLicense()
- {
- ECRESULT er = erSuccess;
- ECSession *lpecSession = NULL;
- unsigned int ulLicense = 0;
- er = this->CreateSessionInternal(&lpecSession);
- if (er != erSuccess)
- goto exit;
- lpecSession->Lock();
- er = lpecSession->GetUserManagement()->CheckUserLicense(&ulLicense);
- if (er != erSuccess)
- goto exit;
- if (ulLicense & USERMANAGEMENT_USER_LICENSE_EXCEEDED) {
- ec_log_err("Failed to start server: Your license does not permit this amount of users.");
- er = KCERR_NO_ACCESS;
- goto exit;
- }
- exit:
- if(lpecSession) {
- lpecSession->Unlock(); // Lock the session
- this->RemoveSessionInternal(lpecSession);
- }
- return er;
- }
- /*
- * This function is threadsafe since we hold the lock the the group list, and the session retrieved from the grouplist
- * is locked so it cannot be deleted by other sessions, while we hold the lock for the group list.
- *
- * Other sessions may release the session group, even if they are the last, while we are in this function since
- * deletion of the session group only occurs within DeleteIfOrphaned(), and this function guarantees that the caller
- * will receive a sessiongroup that is not an orphan unless the caller releases the session group.
- */
- ECRESULT ECSessionManager::GetSessionGroup(ECSESSIONGROUPID sessionGroupID, ECSession *lpSession, ECSessionGroup **lppSessionGroup)
- {
- ECRESULT er = erSuccess;
- ECSessionGroup *lpSessionGroup = NULL;
- KC::shared_lock<KC::shared_mutex> lr_group(m_hGroupLock);
- std::unique_lock<KC::shared_mutex> lw_group(m_hGroupLock, std::defer_lock_t());
- /* Workaround for old clients, when sessionGroupID is 0 each session is its own group */
- if (sessionGroupID == 0) {
- lpSessionGroup = new ECSessionGroup(sessionGroupID, this);
- g_lpStatsCollector->Increment(SCN_SESSIONGROUPS_CREATED);
- } else {
- auto iter = m_mapSessionGroups.find(sessionGroupID);
- /* Check if the SessionGroup already exists on the server */
- if (iter == m_mapSessionGroups.cend()) {
- // "upgrade" lock to insert new session
- lr_group.unlock();
- lw_group.lock();
- lpSessionGroup = new ECSessionGroup(sessionGroupID, this);
- m_mapSessionGroups.insert(EC_SESSIONGROUPMAP::value_type(sessionGroupID, lpSessionGroup));
- g_lpStatsCollector->Increment(SCN_SESSIONGROUPS_CREATED);
- } else
- lpSessionGroup = iter->second;
- }
-
- lpSessionGroup->AddSession(lpSession);
- *lppSessionGroup = lpSessionGroup;
- return er;
- }
- ECRESULT ECSessionManager::DeleteIfOrphaned(ECSessionGroup *lpGroup)
- {
- ECSessionGroup *lpSessionGroup = NULL;
- ECSESSIONGROUPID id = lpGroup->GetSessionGroupId();
- if (id != 0) {
- std::lock_guard<KC::shared_mutex> l_group(m_hGroupLock);
- /* Check if the SessionGroup actually exists, if it doesn't just return without error */
- auto i = m_mapSessionGroups.find(id);
- if (i == m_mapSessionGroups.cend())
- return erSuccess;
- /* If this was the last Session, delete the SessionGroup */
- if (i->second->isOrphan()) {
- lpSessionGroup = i->second;
- m_mapSessionGroups.erase(i);
- }
- } else
- lpSessionGroup = lpGroup;
- if (lpSessionGroup) {
- delete lpSessionGroup;
- g_lpStatsCollector->Increment(SCN_SESSIONGROUPS_DELETED);
- }
- return erSuccess;
- }
- BTSession* ECSessionManager::GetSession(ECSESSIONID sessionID, bool fLockSession) {
- BTSession *lpSession = NULL;
-
- auto iIterator = m_mapSessions.find(sessionID);
- if (iIterator != m_mapSessions.cend()) {
- lpSession = iIterator->second;
- lpSession->UpdateSessionTime();
-
- if(fLockSession)
- lpSession->Lock();
- }else{
- //EC_SESSION_LOST
- }
- return lpSession;
- }
- // Clean up all current sessions
- ECRESULT ECSessionManager::RemoveAllSessions()
- {
- ECRESULT er = erSuccess;
- BTSession *lpSession = NULL;
- std::list<BTSession *> lstSessions;
-
- // Lock the session map since we're going to remove all the sessions.
- std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- ec_log_info("Shutdown all current sessions");
- auto iIterSession = m_mapSessions.begin();
- while (iIterSession != m_mapSessions.cend()) {
- lpSession = iIterSession->second;
- auto iSessionNext = iIterSession;
- ++iSessionNext;
- m_mapSessions.erase(iIterSession);
- iIterSession = iSessionNext;
- lstSessions.push_back(lpSession);
- }
- l_cache.unlock();
- // Do the actual session deletes, while the session map is not locked (!)
- for (auto sesp : lstSessions)
- delete sesp;
- return er;
- }
- ECRESULT ECSessionManager::CancelAllSessions(ECSESSIONID sessionIDException)
- {
- ECRESULT er = erSuccess;
- BTSession *lpSession = NULL;
- std::list<BTSession *> lstSessions;
-
- // Lock the session map since we're going to remove all the sessions.
- std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- ec_log_info("Shutdown all current sessions");
- auto iIterSession = m_mapSessions.begin();
- while (iIterSession != m_mapSessions.cend()) {
- if (iIterSession->first == sessionIDException) {
- ++iIterSession;
- continue;
- }
- lpSession = iIterSession->second;
- auto iSessionNext = iIterSession;
- ++iSessionNext;
- // Tell the notification manager to wake up anyone waiting for this session
- m_lpNotificationManager->NotifyChange(iIterSession->first);
- m_mapSessions.erase(iIterSession);
- iIterSession = iSessionNext;
- lstSessions.push_back(lpSession);
- }
- l_cache.unlock();
- // Do the actual session deletes, while the session map is not locked (!)
- for (auto sesp : lstSessions)
- delete sesp;
- return er;
- }
- // call a function for all sessions available
- // used by ECStatsTable
- ECRESULT ECSessionManager::ForEachSession(void(*callback)(ECSession*, void*), void *obj)
- {
- KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- for (const auto &p : m_mapSessions)
- callback(dynamic_cast<ECSession *>(p.second), obj);
- return erSuccess;
- }
- // Locking of sessions works as follows:
- //
- // - A session is requested by the caller thread through ValidateSession. ValidateSession
- // Locks the complete session table, then acquires a lock on the session, and then
- // frees the lock on the session table. This makes sure that when a session is returned,
- // it is guaranteed not to be deleted by another thread (due to a shutdown or logoff).
- // The caller of 'ValidateSession' is therefore responsible for unlocking the session
- // when it is finished.
- //
- // - When a session is terminated, a lock is opened on the session table, making sure no
- // new session can be opened, or session can be deleted. Then, the session is searched
- // in the table, and directly deleted from the table, making sure that no new threads can
- // open the session in question after this point. Then, the session is deleted, but the
- // session itself waits in the destructor until all threads holding a lock on the session
- // through Lock or ValidateSession have released their lock, before actually deleting the
- // session object.
- //
- // This means that exiting the server must wait until all client requests have exited. For
- // most operations, this is not a problem, but for some long requests (ie large deletes or
- // copies, or GetNextNotifyItem) may take quite a while to exit. This is compensated for, by
- // having the session call a 'cancel' request to long-running calls, which makes the calls
- // exit prematurely.
- //
- ECRESULT ECSessionManager::ValidateSession(struct soap *soap, ECSESSIONID sessionID, ECAuthSession **lppSession, bool fLockSession)
- {
- ECRESULT er;
- BTSession *lpSession = NULL;
- er = this->ValidateBTSession(soap, sessionID, &lpSession, fLockSession);
- if (er != erSuccess)
- return er;
- *lppSession = dynamic_cast<ECAuthSession*>(lpSession);
- return erSuccess;
- }
- ECRESULT ECSessionManager::ValidateSession(struct soap *soap, ECSESSIONID sessionID, ECSession **lppSession, bool fLockSession)
- {
- ECRESULT er;
- BTSession *lpSession = NULL;
- er = this->ValidateBTSession(soap, sessionID, &lpSession, fLockSession);
- if (er != erSuccess)
- return er;
- *lppSession = dynamic_cast<ECSession*>(lpSession);
- return erSuccess;
- }
- ECRESULT ECSessionManager::ValidateBTSession(struct soap *soap, ECSESSIONID sessionID, BTSession **lppSession, bool fLockSession)
- {
- ECRESULT er;
- BTSession* lpSession = NULL;
-
- // Read lock
- KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- lpSession = GetSession(sessionID, fLockSession);
- l_cache.unlock();
- if (lpSession == NULL)
- return KCERR_END_OF_SESSION;
- lpSession->RecordRequest(soap);
-
- er = lpSession->ValidateOriginator(soap);
- if (er != erSuccess) {
- if (fLockSession)
- lpSession->Unlock();
- lpSession = NULL;
- return er;
- }
- /* Enable compression if client desired and granted */
- if (lpSession->GetCapabilities() & KOPANO_CAP_COMPRESSION) {
- soap_set_imode(soap, SOAP_ENC_ZLIB);
- soap_set_omode(soap, SOAP_ENC_ZLIB | SOAP_IO_CHUNK);
- }
- // Enable streaming support if client is capable
- if (lpSession->GetCapabilities() & KOPANO_CAP_ENHANCED_ICS) {
- soap_set_omode(soap, SOAP_ENC_MTOM | SOAP_IO_CHUNK);
- soap_set_imode(soap, SOAP_ENC_MTOM);
- soap_post_check_mime_attachments(soap);
- }
- *lppSession = lpSession;
- return erSuccess;
- }
- ECRESULT ECSessionManager::CreateAuthSession(struct soap *soap, unsigned int ulCapabilities, ECSESSIONID *sessionID, ECAuthSession **lppAuthSession, bool bRegisterSession, bool bLockSession)
- {
- ECAuthSession *lpAuthSession = NULL;
- ECSESSIONID newSessionID;
- CreateSessionID(ulCapabilities, &newSessionID);
- lpAuthSession = new(std::nothrow) ECAuthSession(GetSourceAddr(soap), newSessionID, m_lpDatabaseFactory, this, ulCapabilities);
- if (lpAuthSession == NULL)
- return KCERR_NOT_ENOUGH_MEMORY;
- if (bLockSession)
- lpAuthSession->Lock();
- if (bRegisterSession) {
- std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- m_mapSessions.insert( SESSIONMAP::value_type(newSessionID, lpAuthSession) );
- l_cache.unlock();
- g_lpStatsCollector->Increment(SCN_SESSIONS_CREATED);
- }
- *sessionID = newSessionID;
- *lppAuthSession = lpAuthSession;
- return erSuccess;
- }
- ECRESULT ECSessionManager::CreateSession(struct soap *soap, const char *szName,
- const char *szPassword, const char *szImpersonateUser,
- const char *szClientVersion, const char *szClientApp,
- const char *szClientAppVersion, const char *szClientAppMisc,
- unsigned int ulCapabilities, ECSESSIONGROUPID sessionGroupID,
- ECSESSIONID *lpSessionID, ECSession **lppSession, bool fLockSession,
- bool fAllowUidAuth)
- {
- ECRESULT er = erSuccess;
- std::unique_ptr<ECAuthSession> lpAuthSession;
- ECSession *lpSession = NULL;
- const char *method = "error";
- std::string from;
- CONNECTION_TYPE ulType = SOAP_CONNECTION_TYPE(soap);
- if (ulType == CONNECTION_TYPE_NAMED_PIPE_PRIORITY)
- from = string("file://") + m_lpConfig->GetSetting("server_pipe_priority");
- else if (ulType == CONNECTION_TYPE_NAMED_PIPE)
- // connected through Unix socket
- from = string("file://") + m_lpConfig->GetSetting("server_pipe_name");
- else
- // connected over network
- from = soap->host;
- er = this->CreateAuthSession(soap, ulCapabilities, lpSessionID, &unique_tie(lpAuthSession), false, false);
- if (er != erSuccess)
- goto exit;
- // If we've connected with SSL, check if there is a certificate, and check if we accept that certificate for that user
- if (soap->ssl && lpAuthSession->ValidateUserCertificate(soap, szName, szImpersonateUser) == erSuccess) {
- g_lpStatsCollector->Increment(SCN_LOGIN_SSL);
- method = "SSL Certificate";
- goto authenticated;
- }
- // First, try socket authentication (dagent, won't print error)
- if(fAllowUidAuth && lpAuthSession->ValidateUserSocket(soap->socket, szName, szImpersonateUser) == erSuccess) {
- g_lpStatsCollector->Increment(SCN_LOGIN_SOCKET);
- method = "Pipe socket";
- goto authenticated;
- }
- // If that fails, try logon with supplied username/password (clients, may print logon error)
- if(lpAuthSession->ValidateUserLogon(szName, szPassword, szImpersonateUser) == erSuccess) {
- g_lpStatsCollector->Increment(SCN_LOGIN_PASSWORD);
- method = "User supplied password";
- goto authenticated;
- }
- // whoops, out of auth options.
- ec_log_warn("Failed to authenticate user \"%s\" from \"%s\" using program \"%s\"",
- szName, from.c_str(), szClientApp ? szClientApp : "<unknown>");
- ZLOG_AUDIT(m_lpAudit, "authenticate failed user='%s' from='%s' program='%s'",
- szName, from.c_str(), szClientApp ? szClientApp : "<unknown>");
- er = KCERR_LOGON_FAILED;
- g_lpStatsCollector->Increment(SCN_LOGIN_DENIED);
- goto exit;
- authenticated:
- ec_log_debug("User \"%s\" from \"%s\" authenticated through \"%s\" using program %s", szName, from.c_str(), method, szClientApp ? szClientApp : "<unknown>");
- if (strcmp(KOPANO_SYSTEM_USER, szName) != 0)
- /* Do not log successful SYSTEM logins */
- ZLOG_AUDIT(m_lpAudit, "authenticate ok user='%s' from='%s' method='%s' program='%s'",
- szName, from.c_str(), method, szClientApp ? szClientApp : "<unknown>");
- er = RegisterSession(lpAuthSession.get(), sessionGroupID,
- szClientVersion, szClientApp, szClientAppVersion, szClientAppMisc,
- lpSessionID, &lpSession, fLockSession);
- if (er != erSuccess) {
- if (er == KCERR_NO_ACCESS && szImpersonateUser != NULL && *szImpersonateUser != '\0') {
- ec_log_err("Failed attempt to impersonate user \"%s\" by user \"%s\"", szImpersonateUser, szName);
- ZLOG_AUDIT(m_lpAudit, "impersonate failed user='%s', from='%s' program='%s' impersonator='%s'",
- szImpersonateUser, from.c_str(), szClientApp ? szClientApp : "<unknown>", szName);
- } else
- ec_log_err("User \"%s\" authenticated, but failed to create session. Error 0x%08X", szName, er);
- goto exit;
- }
- if (!szImpersonateUser || *szImpersonateUser == '\0')
- ec_log_debug("User \"%s\" receives session %llu",
- szName, static_cast<unsigned long long>(*lpSessionID));
- else {
- ec_log_debug("User \"%s\" impersonated by \"%s\" receives session %llu",
- szImpersonateUser, szName,
- static_cast<unsigned long long>(*lpSessionID));
- ZLOG_AUDIT(m_lpAudit, "impersonate ok user='%s', from='%s' program='%s' impersonator='%s'",
- szImpersonateUser, from.c_str(), szClientApp ? szClientApp : "<unknown>", szName);
- }
- exit:
- *lppSession = lpSession;
- return er;
- }
- ECRESULT ECSessionManager::RegisterSession(ECAuthSession *lpAuthSession,
- ECSESSIONGROUPID sessionGroupID, const char *szClientVersion,
- const char *szClientApp, const char *szClientApplicationVersion,
- const char *szClientApplicationMisc, ECSESSIONID *lpSessionID,
- ECSession **lppSession, bool fLockSession)
- {
- ECRESULT er = erSuccess;
- ECSession *lpSession = NULL;
- ECSESSIONID newSID = 0;
- er = lpAuthSession->CreateECSession(sessionGroupID, szClientVersion ? szClientVersion : "", szClientApp ? szClientApp : "", szClientApplicationVersion ? szClientApplicationVersion : "", szClientApplicationMisc ? szClientApplicationMisc : "", &newSID, &lpSession);
- if (er != erSuccess)
- return er;
- if (fLockSession)
- lpSession->Lock();
- std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- m_mapSessions.insert( SESSIONMAP::value_type(newSID, lpSession) );
- l_cache.unlock();
- *lpSessionID = std::move(newSID);
- *lppSession = lpSession;
- g_lpStatsCollector->Increment(SCN_SESSIONS_CREATED);
- return er;
- }
- ECRESULT ECSessionManager::CreateSessionInternal(ECSession **lppSession, unsigned int ulUserId)
- {
- ECRESULT er;
- ECSession *lpSession = NULL;
- ECSESSIONID newSID;
- CreateSessionID(KOPANO_CAP_LARGE_SESSIONID, &newSID);
- lpSession = new(std::nothrow) ECSession("<internal>", newSID, 0,
- m_lpDatabaseFactory, this, 0, ECSession::METHOD_NONE, 0,
- "internal", "kopano-server", "", "");
- if (lpSession == NULL)
- return KCERR_LOGON_FAILED;
- er = lpSession->GetSecurity()->SetUserContext(ulUserId, EC_NO_IMPERSONATOR);
- if (er != erSuccess) {
- delete lpSession;
- return er;
- }
- ec_log_debug("New internal session (%llu)",
- static_cast<unsigned long long>(newSID));
- g_lpStatsCollector->Increment(SCN_SESSIONS_INTERNAL_CREATED);
- *lppSession = lpSession;
- return erSuccess;
- }
- void ECSessionManager::RemoveSessionInternal(ECSession *lpSession)
- {
- if (lpSession != NULL) {
- g_lpStatsCollector->Increment(SCN_SESSIONS_INTERNAL_DELETED);
- delete lpSession;
- }
- }
- ECRESULT ECSessionManager::RemoveSession(ECSESSIONID sessionID){
- ECRESULT hr = erSuccess;
- BTSession *lpSession = NULL;
-
- ec_log_debug("End of session (logoff) %llu",
- static_cast<unsigned long long>(sessionID));
- g_lpStatsCollector->Increment(SCN_SESSIONS_DELETED);
- // Make sure no other thread can read or write the sessions list
- std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- // Get a session, don't lock it ourselves
- lpSession = GetSession(sessionID, false);
- // Remove the session from the list. No other threads can start new
- // requests on the session after this point
- m_mapSessions.erase(sessionID);
- l_cache.unlock();
- // We know for sure that no other thread is attempting to remove the session
- // at this time because it would not have been in the m_mapSessions map
- // Delete the session. This will block until all requesters on the session
- // have released their lock on the session
- if(lpSession != NULL) {
- if(lpSession->Shutdown(5 * 60 * 1000) == erSuccess)
- delete lpSession;
- else
- ec_log_err("Session failed to shut down: skipping logoff");
- }
-
- // Tell the notification manager to wake up anyone waiting for this session
- m_lpNotificationManager->NotifyChange(sessionID);
- return hr;
- }
- /**
- * Add notification to a session group.
- * @note This function can't handle table notifications!
- *
- * @param[in] notifyItem The notification data to send
- * @param[in] ulKey The object (hierarchyid) the notification acts on
- * @param[in] ulStore The store the ulKey object resides in. 0 for unknown (default).
- * @param[in] ulFolderId Parent folder object for ulKey. 0 for unknown or not required (default).
- * @param[in] ulFlags Hierarchy flags for ulKey. 0 for unknown (default).
- *
- * @return Kopano error code
- */
- ECRESULT ECSessionManager::AddNotification(notification *notifyItem, unsigned int ulKey, unsigned int ulStore, unsigned int ulFolderId, unsigned int ulFlags) {
-
- std::set<ECSESSIONGROUPID> setGroups;
-
- ECRESULT hr = erSuccess;
-
- if(ulStore == 0) {
- hr = m_lpECCacheManager->GetStore(ulKey, &ulStore, NULL);
- if(hr != erSuccess)
- return hr;
- }
- // Send notification to subscribed sessions
- ulock_normal l_sub(m_mutexObjectSubscriptions);
- auto iterObjectSubscription = m_mapObjectSubscriptions.lower_bound(ulStore);
- while (iterObjectSubscription != m_mapObjectSubscriptions.cend() &&
- iterObjectSubscription->first == ulStore) {
- // Send a notification only once to a session group, even if it has subscribed multiple times
- setGroups.insert(iterObjectSubscription->second);
- ++iterObjectSubscription;
- }
- l_sub.unlock();
- // Send each subscribed session group one notification
- for (const auto &grp : setGroups) {
- KC::shared_lock<KC::shared_mutex> grplk(m_hGroupLock);
- auto iIterator = m_mapSessionGroups.find(grp);
- if (iIterator != m_mapSessionGroups.cend())
- iIterator->second->AddNotification(notifyItem, ulKey, ulStore);
- }
-
- // Next, do an internal notification to update searchfolder views for message updates.
- if (notifyItem->obj == nullptr || notifyItem->obj->ulObjType != MAPI_MESSAGE)
- return hr;
- if (ulFolderId == 0 && ulFlags == 0 &&
- GetCacheManager()->GetObject(ulKey, &ulFolderId, NULL, &ulFlags, NULL) != erSuccess) {
- assert(false);
- return hr;
- }
- // Skip changes on associated messages, and changes on deleted item. (but include DELETE of deleted items)
- if ((ulFlags & MAPI_ASSOCIATED) || (notifyItem->ulEventType != fnevObjectDeleted && (ulFlags & MSGFLAG_DELETED)))
- return hr;
- switch (notifyItem->ulEventType) {
- case fnevObjectMoved:
- // Only update the item in the new folder. The system will automatically delete the item from folders that were not in the search path
- m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_MODIFY);
- break;
- case fnevObjectDeleted:
- m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_DELETE);
- break;
- case fnevObjectCreated:
- m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_ADD);
- break;
- case fnevObjectCopied:
- m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_ADD);
- break;
- case fnevObjectModified:
- m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_MODIFY);
- break;
- }
- return hr;
- }
- void* ECSessionManager::SessionCleaner(void *lpTmpSessionManager)
- {
- time_t lCurTime;
- auto lpSessionManager = static_cast<ECSessionManager *>(lpTmpSessionManager);
- list<BTSession*> lstSessions;
- if (lpSessionManager == NULL)
- return 0;
- ECDatabase *db = NULL;
- if (GetThreadLocalDatabase(lpSessionManager->m_lpDatabaseFactory, &db) != erSuccess)
- ec_log_err("GTLD failed in SessionCleaner");
- while(true){
- std::unique_lock<KC::shared_mutex> l_cache(lpSessionManager->m_hCacheRWLock);
- lCurTime = GetProcessTime();
-
- // Find a session that has timed out
- auto iIterator = lpSessionManager->m_mapSessions.begin();
- while (iIterator != lpSessionManager->m_mapSessions.cend()) {
- bool del = iIterator->second->GetSessionTime() < lCurTime &&
- !lpSessionManager->IsSessionPersistent(iIterator->first);
- if (!del) {
- ++iIterator;
- continue;
- }
- // Remember all the session to be deleted
- lstSessions.push_back(iIterator->second);
- auto iRemove = iIterator++;
- // Remove the session from the list, no new threads can start on this session after this point.
- g_lpStatsCollector->Increment(SCN_SESSIONS_TIMEOUT);
- ec_log_info("End of session (timeout) %llu",
- static_cast<unsigned long long>(iRemove->first));
- lpSessionManager->m_mapSessions.erase(iRemove);
- }
- // Release ownership of the rwlock object. This makes sure all threads are free to run (and exit).
- l_cache.unlock();
- // Now, remove all the session. It will wait until all running threads for that session have exited.
- for (const auto ses : lstSessions) {
- if (ses->Shutdown(5 * 60 * 1000) == erSuccess)
- delete ses;
- else
- // The session failed to shut down within our timeout period. This means we probably hit a bug; this
- // should only happen if some bit of code has locked the session and failed to unlock it. There are now
- // two options: delete the session anyway and hope we don't segfault, or leak the session. We choose
- // the latter.
- ec_log_err("Session failed to shut down: skipping clean");
- }
- lstSessions.clear();
- KC::sync_logon_times(db);
- // Wait for a terminate signal or return after a few minutes
- ulock_normal l_exit(lpSessionManager->m_hExitMutex);
- if(lpSessionManager->bExit) {
- l_exit.unlock();
- break;
- }
- if (lpSessionManager->m_hExitSignal.wait_for(l_exit,
- std::chrono::seconds(5)) != std::cv_status::timeout)
- break;
- }
- return NULL;
- }
- ECRESULT ECSessionManager::UpdateOutgoingTables(ECKeyTable::UpdateType ulType, unsigned int ulStoreId, unsigned int ulObjId, unsigned int ulFlags, unsigned int ulObjType)
- {
- TABLESUBSCRIPTION sSubscription;
- std::list<unsigned int> lstObjId;
-
- lstObjId.push_back(ulObjId);
- sSubscription.ulType = TABLE_ENTRY::TABLE_TYPE_OUTGOINGQUEUE;
- sSubscription.ulRootObjectId = ulFlags & EC_SUBMIT_MASTER ? 0 : ulStoreId; // in the master queue, use 0 as root object id
- sSubscription.ulObjectType = ulObjType;
- sSubscription.ulObjectFlags = ulFlags & EC_SUBMIT_MASTER; // Only use MASTER flag as differentiator
- return UpdateSubscribedTables(ulType, sSubscription, lstObjId);
- }
- ECRESULT ECSessionManager::UpdateTables(ECKeyTable::UpdateType ulType, unsigned int ulFlags, unsigned ulObjId, unsigned ulChildId, unsigned int ulObjType)
- {
- std::list<unsigned int> lstChildId = {ulChildId};
- return UpdateTables(ulType, ulFlags, ulObjId, lstChildId, ulObjType);
- }
- ECRESULT ECSessionManager::UpdateTables(ECKeyTable::UpdateType ulType, unsigned int ulFlags, unsigned ulObjId, std::list<unsigned int>& lstChildId, unsigned int ulObjType)
- {
- TABLESUBSCRIPTION sSubscription;
-
- if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER)
- return erSuccess;
- sSubscription.ulType = TABLE_ENTRY::TABLE_TYPE_GENERIC;
- sSubscription.ulRootObjectId = ulObjId;
- sSubscription.ulObjectType = ulObjType;
- sSubscription.ulObjectFlags = ulFlags;
- return UpdateSubscribedTables(ulType, sSubscription, lstChildId);
- }
- ECRESULT ECSessionManager::UpdateSubscribedTables(ECKeyTable::UpdateType ulType, TABLESUBSCRIPTION sSubscription, std::list<unsigned int> &lstChildId)
- {
- ECRESULT er = erSuccess;
- std::set<ECSESSIONID> setSessions;
- BTSession *lpBTSession = NULL;
-
- // Find out which sessions our interested in this event by looking at our subscriptions
- ulock_normal l_sub(m_mutexTableSubscriptions);
-
- auto iterSubscriptions = m_mapTableSubscriptions.find(sSubscription);
- while (iterSubscriptions != m_mapTableSubscriptions.cend() &&
- iterSubscriptions->first == sSubscription) {
- setSessions.insert(iterSubscriptions->second);
- ++iterSubscriptions;
- }
- l_sub.unlock();
- // We now have a set of sessions that are interested in the notification. This list is normally quite small since not that many
- // sessions have the same table opened at one time.
- // For each of the sessions that are interested, send the table change
- for (const auto &ses : setSessions) {
- // Get session
- KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- lpBTSession = GetSession(ses, true);
- l_cache.unlock();
-
- // Send the change notification
- if(lpBTSession != NULL) {
- auto lpSession = dynamic_cast<ECSession *>(lpBTSession);
- if (lpSession == NULL) {
- lpBTSession->Unlock();
- continue;
- }
-
- if (sSubscription.ulType == TABLE_ENTRY::TABLE_TYPE_GENERIC)
- lpSession->GetTableManager()->UpdateTables(ulType, sSubscription.ulObjectFlags, sSubscription.ulRootObjectId, lstChildId, sSubscription.ulObjectType);
- else if (sSubscription.ulType == TABLE_ENTRY::TABLE_TYPE_OUTGOINGQUEUE)
- lpSession->GetTableManager()->UpdateOutgoingTables(ulType, sSubscription.ulRootObjectId, lstChildId, sSubscription.ulObjectFlags, sSubscription.ulObjectType);
- lpBTSession->Unlock();
- }
- }
- return er;
- }
- // FIXME: ulFolderId should be an entryid, because the parent is already deleted!
- // You must specify which store the object was deleted from, 'cause we can't find out afterwards
- ECRESULT ECSessionManager::NotificationDeleted(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulStoreId, entryId* lpEntryId, unsigned int ulFolderId, unsigned int ulFlags)
- {
- ECRESULT er = erSuccess;
- struct notification notify;
- memset(¬ify, 0, sizeof(notification));
- if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
- goto exit;
- notify.obj = s_alloc<notificationObject>(nullptr);
- memset(notify.obj, 0, sizeof(notificationObject));
-
- notify.ulEventType = fnevObjectDeleted;
-
- notify.obj->ulObjType = ulObjType;
- notify.obj->pEntryId = lpEntryId;
- if(ulFolderId > 0) {
- er = GetCacheManager()->GetEntryIdFromObject(ulFolderId, NULL, 0, ¬ify.obj->pParentId);
- if(er != erSuccess)
- goto exit;
- }
- AddNotification(¬ify, ulObjId, ulStoreId, ulFolderId, ulFlags);
- exit:
- notify.obj->pEntryId = NULL;
- FreeNotificationStruct(¬ify, false);
- return er;
- }
- ECRESULT ECSessionManager::NotificationModified(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulParentId)
- {
- ECRESULT er = erSuccess;
- struct notification notify;
- memset(¬ify, 0, sizeof(notification));
- if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
- goto exit;
- notify.obj = s_alloc<notificationObject>(nullptr);
- memset(notify.obj, 0, sizeof(notificationObject));
-
- notify.ulEventType = fnevObjectModified;
- notify.obj->ulObjType = ulObjType;
- er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, ¬ify.obj->pEntryId);
- if(er != erSuccess)
- goto exit;
- if(ulParentId > 0) {
- er = GetCacheManager()->GetEntryIdFromObject(ulParentId, NULL, 0, ¬ify.obj->pParentId);
- if(er != erSuccess)
- goto exit;
- }
- AddNotification(¬ify, ulObjId);
- exit:
- FreeNotificationStruct(¬ify, false);
- return er;
- }
- ECRESULT ECSessionManager::NotificationCreated(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulParentId)
- {
- ECRESULT er = erSuccess;
- struct notification notify;
- memset(¬ify, 0, sizeof(notification));
- if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
- goto exit;
- notify.obj = s_alloc<notificationObject>(nullptr);
- memset(notify.obj, 0, sizeof(notificationObject));
- notify.ulEventType = fnevObjectCreated;
- notify.obj->ulObjType = ulObjType;
- er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, ¬ify.obj->pEntryId);
- if(er != erSuccess)
- goto exit;
- er = GetCacheManager()->GetEntryIdFromObject(ulParentId, NULL, 0, ¬ify.obj->pParentId);
- if(er != erSuccess)
- goto exit;
-
- AddNotification(¬ify, ulObjId);
- exit:
- FreeNotificationStruct(¬ify, false);
- return er;
- }
- ECRESULT ECSessionManager::NotificationMoved(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulParentId, unsigned int ulOldParentId, entryId *lpOldEntryId)
- {
- ECRESULT er = erSuccess;
- struct notification notify;
- memset(¬ify, 0, sizeof(notification));
- if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
- goto exit;
- notify.obj = s_alloc<notificationObject>(nullptr);
- memset(notify.obj, 0, sizeof(notificationObject));
- notify.ulEventType = fnevObjectMoved;
- notify.obj->ulObjType = ulObjType;
-
- er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, ¬ify.obj->pEntryId);
- if(er != erSuccess)
- goto exit;
- er = GetCacheManager()->GetEntryIdFromObject(ulParentId, NULL, 0, ¬ify.obj->pParentId);
- if(er != erSuccess)
- goto exit;
- er = GetCacheManager()->GetEntryIdFromObject(ulOldParentId, NULL, 0, ¬ify.obj->pOldParentId);
- if(er != erSuccess)
- goto exit;
- notify.obj->pOldId = lpOldEntryId;
- AddNotification(¬ify, ulObjId);
- notify.obj->pOldId = NULL;
- exit:
- FreeNotificationStruct(¬ify, false);
- return er;
- }
- ECRESULT ECSessionManager::NotificationCopied(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulParentId, unsigned int ulOldObjId, unsigned int ulOldParentId)
- {
- ECRESULT er = erSuccess;
- struct notification notify;
- memset(¬ify, 0, sizeof(notification));
- if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
- goto exit;
- notify.obj = s_alloc<notificationObject>(nullptr);
- memset(notify.obj, 0, sizeof(notificationObject));
- notify.ulEventType = fnevObjectCopied;
-
- notify.obj->ulObjType = ulObjType;
- er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, ¬ify.obj->pEntryId);
- if(er != erSuccess)
- goto exit;
- er = GetCacheManager()->GetEntryIdFromObject(ulParentId, NULL, 0, ¬ify.obj->pParentId);
- if(er != erSuccess)
- goto exit;
- if(ulOldObjId > 0) {
- er = GetCacheManager()->GetEntryIdFromObject(ulOldObjId, NULL, 0, ¬ify.obj->pOldId);
- if(er != erSuccess)
- goto exit;
- }
- if(ulOldParentId > 0) {
- er = GetCacheManager()->GetEntryIdFromObject(ulOldParentId, NULL, 0, ¬ify.obj->pOldParentId);
- if(er != erSuccess)
- goto exit;
- }
- AddNotification(¬ify, ulObjId);
- exit:
- FreeNotificationStruct(¬ify, false);
- return er;
- }
- /**
- * Send "Search complete" notification to the client.
- *
- * @param ulObjId object id of the search folder
- *
- * @return Kopano error code
- */
- ECRESULT ECSessionManager::NotificationSearchComplete(unsigned int ulObjId, unsigned int ulStoreId)
- {
- ECRESULT er = erSuccess;
- struct notification notify;
- memset(¬ify, 0, sizeof(notification));
- notify.obj = s_alloc<notificationObject>(nullptr);
- memset(notify.obj, 0, sizeof(notificationObject));
- notify.ulEventType = fnevSearchComplete;
- notify.obj->ulObjType = MAPI_FOLDER;
- er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, ¬ify.obj->pEntryId);
- if(er != erSuccess)
- goto exit;
- AddNotification(¬ify, ulObjId, ulStoreId);
- exit:
- FreeNotificationStruct(¬ify, false);
- return er;
- }
- ECRESULT ECSessionManager::NotificationChange(const set<unsigned int> &syncIds, unsigned int ulChangeId, unsigned int ulChangeType)
- {
- KC::shared_lock<KC::shared_mutex> grplk(m_hGroupLock);
- // Send the notification to all sessionsgroups so that any client listening for these
- // notifications can receive them
- for (const auto &p : m_mapSessionGroups)
- p.second->AddChangeNotification(syncIds, ulChangeId, ulChangeType);
- return erSuccess;
- }
- /**
- * Get the sessionmanager statistics
- *
- * @param[in] callback Callback to the statistics collector
- * @param[in] obj pointer to the statistics collector
- */
- void ECSessionManager::GetStats(void(callback)(const std::string &, const std::string &, const std::string &, void*), void *obj)
- {
- sSessionManagerStats sSessionStats;
- sSearchFolderStats sSearchStats;
- GetStats(sSessionStats);
- callback("sessions", "Number of sessions", stringify(sSessionStats.session.ulItems), obj);
- callback("sessions_size", "Memory usage of sessions", stringify_int64(sSessionStats.session.ullSize), obj);
- callback("sessiongroups", "Number of session groups", stringify(sSessionStats.group.ulItems), obj);
- callback("sessiongroups_size", "Memory usage of session groups", stringify_int64(sSessionStats.group.ullSize), obj);
- callback("persist_conn", "Persistent connections", stringify(sSessionStats.ulPersistentByConnection), obj);
- callback("persist_conn_size", "Memory usage of persistent connections", stringify(sSessionStats.ulPersistentByConnectionSize), obj);
- callback("persist_sess", "Persistent sessions", stringify(sSessionStats.ulPersistentBySession), obj);
- callback("persist_sess_size", "Memory usage of persistent sessions", stringify(sSessionStats.ulPersistentBySessionSize), obj);
- callback("tables_subscr", "Tables subscribed", stringify(sSessionStats.ulTableSubscriptions), obj);
- callback("tables_subscr_size", "Memory usage of subscribed tables", stringify(sSessionStats.ulTableSubscriptionSize), obj);
- callback("object_subscr", "Objects subscribed", stringify(sSessionStats.ulObjectSubscriptions), obj);
- callback("object_subscr_size", "Memory usage of subscribed objects", stringify(sSessionStats.ulObjectSubscriptionSize), obj);
- m_lpSearchFolders->GetStats(sSearchStats);
- callback("searchfld_stores", "Number of stores in use by search folders", stringify(sSearchStats.ulStores), obj);
- callback("searchfld_folders", "Number of folders in use by search folders", stringify(sSearchStats.ulFolders), obj);
- callback("searchfld_events", "Number of events waiting for searchfolder updates", stringify(sSearchStats.ulEvents), obj);
- callback("searchfld_size", "Memory usage of search folders", stringify_int64(sSearchStats.ullSize), obj);
- }
- /**
- * Collect session statistics
- *
- * @param[out] sStats The statistics
- *
- */
- void ECSessionManager::GetStats(sSessionManagerStats &sStats)
- {
- list<ECSession*> vSessions;
- memset(&sStats, 0, sizeof(sSessionManagerStats));
- // Get session data
- KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- sStats.session.ulItems = m_mapSessions.size();
- sStats.session.ullSize = MEMORY_USAGE_MAP(sStats.session.ulItems, SESSIONMAP);
- l_cache.unlock();
- // Get group data
- KC::shared_lock<KC::shared_mutex> l_group(m_hGroupLock);
- sStats.group.ulItems = m_mapSessionGroups.size();
- sStats.group.ullSize = MEMORY_USAGE_HASHMAP(sStats.group.ulItems, EC_SESSIONGROUPMAP);
- for (const auto &psg : m_mapSessionGroups)
- sStats.group.ullSize += psg.second->GetObjectSize();
- l_group.unlock();
- // persistent connections/sessions
- ulock_normal l_per(m_mutexPersistent);
- sStats.ulPersistentByConnection = m_mapPersistentByConnection.size();
- sStats.ulPersistentByConnectionSize = MEMORY_USAGE_HASHMAP(sStats.ulPersistentByConnection, PERSISTENTBYCONNECTION);
- sStats.ulPersistentBySession = m_mapPersistentBySession.size();
- sStats.ulPersistentBySessionSize = MEMORY_USAGE_HASHMAP(sStats.ulPersistentBySession, PERSISTENTBYSESSION);
- l_per.unlock();
- // Table subscriptions
- ulock_normal l_tblsub(m_mutexTableSubscriptions);
- sStats.ulTableSubscriptions = m_mapTableSubscriptions.size();
- sStats.ulTableSubscriptionSize = MEMORY_USAGE_MULTIMAP(sStats.ulTableSubscriptions, TABLESUBSCRIPTIONMULTIMAP);
- l_tblsub.unlock();
- // Object subscriptions
- ulock_normal l_objsub(m_mutexObjectSubscriptions);
- sStats.ulObjectSubscriptions = m_mapObjectSubscriptions.size();
- sStats.ulObjectSubscriptionSize = MEMORY_USAGE_MULTIMAP(sStats.ulObjectSubscriptions, OBJECTSUBSCRIPTIONSMULTIMAP);
- l_objsub.unlock();
- }
- /**
- * Dump statistics
- */
- ECRESULT ECSessionManager::DumpStats()
- {
- sSessionManagerStats sSessionStats;
- sSearchFolderStats sSearchStats;
- GetStats(sSessionStats);
- ec_log_info("Session stats:");
- ec_log_info(" Sessions : %u (%llu bytes)", sSessionStats.session.ulItems, static_cast<unsigned long long>(sSessionStats.session.ullSize));
- ec_log_info(" Locked : %d", sSessionStats.session.ulLocked);
- ec_log_info(" Groups : %u (%llu bytes)" ,sSessionStats.group.ulItems, static_cast<unsigned long long>(sSessionStats.group.ullSize));
- ec_log_info(" PersistentByConnection : %u (%u bytes)" ,sSessionStats.ulPersistentByConnection, sSessionStats.ulPersistentByConnectionSize);
- ec_log_info(" PersistentBySession : %u (%u bytes)" , sSessionStats.ulPersistentBySession, sSessionStats.ulPersistentBySessionSize);
- ec_log_info("Subscription stats:");
- ec_log_info(" Table : %u (%u bytes)", sSessionStats.ulTableSubscriptions, sSessionStats.ulTableSubscriptionSize);
- ec_log_info(" Object: %u (%u bytes)", sSessionStats.ulObjectSubscriptions, sSessionStats.ulObjectSubscriptionSize);
- ec_log_info("Table stats:");
- ec_log_info(" Open tables: %u (%llu bytes)", sSessionStats.session.ulOpenTables, static_cast<unsigned long long>(sSessionStats.session.ulTableSize));
- m_lpSearchFolders->GetStats(sSearchStats);
- ec_log_info("SearchFolders:");
- ec_log_info(" Stores : %u", sSearchStats.ulStores);
- ec_log_info(" Folders : %u", sSearchStats.ulFolders);
- ec_log_info(" Queue : %u", sSearchStats.ulEvents);
- ec_log_info(" Mem usage : %llu Bytes", static_cast<unsigned long long>(sSearchStats.ullSize));
- return this->m_lpECCacheManager->DumpStats();
- }
- ECRESULT ECSessionManager::GetLicensedUsers(unsigned int ulServiceType, unsigned int* lpulLicensedUsers)
- {
- ECRESULT er = erSuccess;
- unsigned int ulLicensedUsers = 0;
- ECLicenseClient *lpLicenseClient = NULL;
- lpLicenseClient = new ECLicenseClient();
-
- er = lpLicenseClient->GetInfo(ulServiceType, &ulLicensedUsers);
-
- if(er != erSuccess) {
- ulLicensedUsers = 0;
- er = erSuccess;
- }
-
- delete lpLicenseClient;
- *lpulLicensedUsers = ulLicensedUsers;
- return er;
- }
- ECRESULT ECSessionManager::GetServerGUID(GUID* lpServerGuid){
- if (lpServerGuid == NULL)
- return KCERR_INVALID_PARAMETER;
- memcpy(lpServerGuid, m_lpServerGuid, sizeof(GUID));
- return erSuccess;
- }
- ECRESULT ECSessionManager::GetNewSourceKey(SOURCEKEY* lpSourceKey){
- ECRESULT er;
-
- if (lpSourceKey == NULL)
- return KCERR_INVALID_PARAMETER;
-
- scoped_lock l_inc(m_hSourceKeyAutoIncrementMutex);
- if (m_ulSourceKeyQueue == 0) {
- er = SaveSourceKeyAutoIncrement(m_ullSourceKeyAutoIncrement + 50);
- if (er != erSuccess)
- return er;
- m_ulSourceKeyQueue = 50;
- }
- *lpSourceKey = SOURCEKEY(*m_lpServerGuid, m_ullSourceKeyAutoIncrement + 1);
- ++m_ullSourceKeyAutoIncrement;
- --m_ulSourceKeyQueue;
- return erSuccess;
- }
- ECRESULT ECSessionManager::SaveSourceKeyAutoIncrement(unsigned long long ullNewSourceKeyAutoIncrement){
- ECRESULT er;
- std::string strQuery;
-
- er = CreateDatabaseConnection();
- if(er != erSuccess)
- return er;
- strQuery = "UPDATE `settings` SET `value` = "+ m_lpDatabase->EscapeBinary((unsigned char*)&ullNewSourceKeyAutoIncrement, 8) + " WHERE `name` = 'source_key_auto_increment'";
- return m_lpDatabase->DoUpdate(strQuery);
- // @TODO if this failed we want to retry this
- }
- ECRESULT ECSessionManager::SetSessionPersistentConnection(ECSESSIONID sessionID, unsigned int ulPersistentConnectionId)
- {
- scoped_lock lock(m_mutexPersistent);
- // maintain a bi-map of connection <-> session here
- m_mapPersistentByConnection[ulPersistentConnectionId] = sessionID;
- m_mapPersistentBySession[sessionID] = ulPersistentConnectionId;
- return erSuccess;
- }
- ECRESULT ECSessionManager::RemoveSessionPersistentConnection(unsigned int ulPersistentConnectionId)
- {
- ECRESULT er = erSuccess;
- PERSISTENTBYSESSION::const_iterator iterSession;
- scoped_lock lock(m_mutexPersistent);
- auto iterConnection = m_mapPersistentByConnection.find(ulPersistentConnectionId);
- if (iterConnection == m_mapPersistentByConnection.cend())
- // shouldn't really happen
- return KCERR_NOT_FOUND;
- iterSession = m_mapPersistentBySession.find(iterConnection->second);
- if (iterSession == m_mapPersistentBySession.cend())
- // really really shouldn't happen
- return KCERR_NOT_FOUND;
- m_mapPersistentBySession.erase(iterSession);
- m_mapPersistentByConnection.erase(iterConnection);
- return er;
- }
- BOOL ECSessionManager::IsSessionPersistent(ECSESSIONID sessionID)
- {
- scoped_lock lock(m_mutexPersistent);
- auto iterSession = m_mapPersistentBySession.find(sessionID);
- return iterSession != m_mapPersistentBySession.cend();
- }
- // @todo make this function with a map of seq ids
- ECRESULT ECSessionManager::GetNewSequence(SEQUENCE seq, unsigned long long *lpllSeqId)
- {
- ECRESULT er;
- std::string strSeqName;
- er = CreateDatabaseConnection();
- if(er != erSuccess)
- return er;
- if (seq == SEQ_IMAP)
- strSeqName = "imapseq";
- else
- return KCERR_INVALID_PARAMETER;
- scoped_lock lock(m_hSeqMutex);
- if (m_ulSeqIMAPQueue == 0)
- {
- er = m_lpDatabase->DoSequence(strSeqName, 50, &m_ulSeqIMAP);
- if (er != erSuccess)
- return er;
- m_ulSeqIMAPQueue = 50;
- }
- --m_ulSeqIMAPQueue;
- *lpllSeqId = m_ulSeqIMAP++;
- return erSuccess;
- }
- ECRESULT ECSessionManager::CreateDatabaseConnection()
- {
- ECRESULT er;
- std::string strError;
-
- if(m_lpDatabase == NULL) {
- er = m_lpDatabaseFactory->CreateDatabaseObject(&m_lpDatabase, strError);
- if(er != erSuccess) {
- ec_log_crit("Unable to open connection to database: %s", strError.c_str());
- return er;
- }
- }
- return erSuccess;
- }
- ECRESULT ECSessionManager::SubscribeTableEvents(TABLE_ENTRY::TABLE_TYPE ulType, unsigned int ulTableRootObjectId, unsigned int ulObjectType, unsigned int ulObjectFlags, ECSESSIONID sessionID)
- {
- ECRESULT er = erSuccess;
- TABLESUBSCRIPTION sSubscription;
- scoped_lock lock(m_mutexTableSubscriptions);
-
- sSubscription.ulType = ulType;
- sSubscription.ulRootObjectId = ulTableRootObjectId;
- sSubscription.ulObjectType = ulObjectType;
- sSubscription.ulObjectFlags = ulObjectFlags;
-
- m_mapTableSubscriptions.insert(std::pair<TABLESUBSCRIPTION, ECSESSIONID>(sSubscription, sessionID));
- return er;
- }
- ECRESULT ECSessionManager::UnsubscribeTableEvents(TABLE_ENTRY::TABLE_TYPE ulType, unsigned int ulTableRootObjectId, unsigned int ulObjectType, unsigned int ulObjectFlags, ECSESSIONID sessionID)
- {
- ECRESULT er = erSuccess;
- TABLESUBSCRIPTION sSubscription;
- scoped_lock lock(m_mutexTableSubscriptions);
-
- sSubscription.ulType = ulType;
- sSubscription.ulRootObjectId = ulTableRootObjectId;
- sSubscription.ulObjectType = ulObjectType;
- sSubscription.ulObjectFlags = ulObjectFlags;
-
- auto iter = m_mapTableSubscriptions.find(sSubscription);
- while (iter != m_mapTableSubscriptions.cend() &&
- iter->first == sSubscription) {
- if(iter->second == sessionID)
- break;
- ++iter;
- }
-
- if (iter != m_mapTableSubscriptions.cend())
- m_mapTableSubscriptions.erase(iter);
- else
- er = KCERR_NOT_FOUND;
- return er;
- }
- // Subscribes for all object notifications in store ulStoreID for session group sessionID
- ECRESULT ECSessionManager::SubscribeObjectEvents(unsigned int ulStoreId, ECSESSIONGROUPID sessionID)
- {
- scoped_lock lock(m_mutexObjectSubscriptions);
- m_mapObjectSubscriptions.insert(std::pair<unsigned int, ECSESSIONGROUPID>(ulStoreId, sessionID));
- return erSuccess;
- }
- ECRESULT ECSessionManager::UnsubscribeObjectEvents(unsigned int ulStoreId, ECSESSIONGROUPID sessionID)
- {
- ECRESULT er = erSuccess;
- scoped_lock lock(m_mutexObjectSubscriptions);
- auto i = m_mapObjectSubscriptions.find(ulStoreId);
- while (i != m_mapObjectSubscriptions.cend() && i->first == ulStoreId &&
- i->second != sessionID)
- ++i;
-
- if (i != m_mapObjectSubscriptions.cend())
- m_mapObjectSubscriptions.erase(i);
- return er;
- }
- ECRESULT ECSessionManager::DeferNotificationProcessing(ECSESSIONID ecSessionId, struct soap *soap)
- {
- // Let the notification manager handle this request. We don't do anything more with the notification
- // request since the notification manager will handle it all
-
- return m_lpNotificationManager->AddRequest(ecSessionId, soap);
- }
- // Called when a notification is ready for a session group
- ECRESULT ECSessionManager::NotifyNotificationReady(ECSESSIONID ecSessionId)
- {
- return m_lpNotificationManager->NotifyChange(ecSessionId);
- }
- ECRESULT ECSessionManager::GetStoreSortLCID(ULONG ulStoreId, ULONG *lpLcid)
- {
- ECRESULT er = erSuccess;
- ECDatabase *lpDatabase = NULL;
- DB_RESULT lpDBResult;
- DB_ROW lpDBRow = NULL;
- std::string strQuery;
- if (lpLcid == nullptr)
- return KCERR_INVALID_PARAMETER;
- er = GetThreadLocalDatabase(m_lpDatabaseFactory, &lpDatabase);
- if(er != erSuccess)
- return er;
- strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid=" + stringify(ulStoreId) +
- " AND tag=" + stringify(PROP_ID(PR_SORT_LOCALE_ID)) + " AND type=" + stringify(PROP_TYPE(PR_SORT_LOCALE_ID));
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if (lpDBRow == nullptr || lpDBRow[0] == nullptr)
- return KCERR_NOT_FOUND;
- *lpLcid = strtoul(lpDBRow[0], NULL, 10);
- return erSuccess;
- }
- LPCSTR ECSessionManager::GetDefaultSortLocaleID()
- {
- return GetConfig()->GetSetting("default_sort_locale_id");
- }
- ULONG ECSessionManager::GetSortLCID(ULONG ulStoreId)
- {
- ECRESULT er = erSuccess;
- ULONG ulLcid = 0;
- LPCSTR lpszLocaleId = NULL;
- er = GetStoreSortLCID(ulStoreId, &ulLcid);
- if (er == erSuccess)
- return ulLcid;
- lpszLocaleId = GetDefaultSortLocaleID();
- if (lpszLocaleId == NULL || *lpszLocaleId == '\0')
- return 0; // Select default LCID
- er = LocaleIdToLCID(lpszLocaleId, &ulLcid);
- if (er != erSuccess)
- return 0; // Select default LCID
- return ulLcid;
- }
- ECLocale ECSessionManager::GetSortLocale(ULONG ulStoreId)
- {
- ECRESULT er;
- ULONG ulLcid = 0;
- LPCSTR lpszLocaleId = NULL;
- er = GetStoreSortLCID(ulStoreId, &ulLcid);
- if (er == erSuccess)
- er = LCIDToLocaleId(ulLcid, &lpszLocaleId);
- if (er != erSuccess) {
- lpszLocaleId = GetDefaultSortLocaleID();
- if (lpszLocaleId == NULL || *lpszLocaleId == '\0')
- lpszLocaleId = ""; // Select default localeid
- }
- return createLocaleFromName(lpszLocaleId);
- }
- /**
- * Remove busy state for session
- *
- * Finds a session and calls RemoveBusyState on it if it is found
- *
- * @param[in] ecSessionId Session ID of session to remove busy state from
- * @param[in] thread Thread ID to remove busy state of
- * @return result
- */
- ECRESULT ECSessionManager::RemoveBusyState(ECSESSIONID ecSessionId, pthread_t thread)
- {
- ECRESULT er = erSuccess;
- BTSession *lpSession = NULL;
- ECSession *lpECSession = NULL;
-
- KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
- lpSession = GetSession(ecSessionId, true);
- l_cache.unlock();
- if(!lpSession)
- goto exit;
-
- lpECSession = dynamic_cast<ECSession *>(lpSession);
-
- if(!lpECSession) {
- assert(lpECSession != NULL);
- goto exit;
- }
-
- lpECSession->RemoveBusyState(thread);
-
- exit:
- if(lpSession)
- lpSession->Unlock();
-
- return er;
- }
- } /* namespace */
|