ECSessionManager.cpp 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546
  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. #include <kopano/platform.h>
  17. #include <chrono>
  18. #include <memory>
  19. #include <mutex>
  20. #include <new>
  21. #include <utility>
  22. #include <pthread.h>
  23. #include <mapidefs.h>
  24. #include <mapitags.h>
  25. #include <kopano/lockhelper.hpp>
  26. #include <kopano/tie.hpp>
  27. #include "ECMAPI.h"
  28. #include "ECDatabase.h"
  29. #include "ECSessionGroup.h"
  30. #include "ECSessionManager.h"
  31. #include "ECStatsCollector.h"
  32. #include "ECTPropsPurge.h"
  33. #include "ECLicenseClient.h"
  34. #include "ECDatabaseUtils.h"
  35. #include "ECSecurity.h"
  36. #include "SSLUtil.h"
  37. #include <kopano/Trace.h>
  38. #include "kcore.hpp"
  39. #include "ECICS.h"
  40. #include <edkmdb.h>
  41. #include "logontime.hpp"
  42. using namespace KCHL;
  43. namespace KC {
  44. ECSessionManager::ECSessionManager(ECConfig *lpConfig, ECLogger *lpAudit,
  45. bool bHostedKopano, bool bDistributedKopano) :
  46. m_lpConfig(lpConfig), m_lpAudit(lpAudit),
  47. m_bHostedKopano(bHostedKopano),
  48. m_bDistributedKopano(bDistributedKopano)
  49. {
  50. int err = 0;
  51. if (m_lpAudit)
  52. m_lpAudit->AddRef();
  53. m_lpDatabaseFactory = new ECDatabaseFactory(lpConfig);
  54. m_lpPluginFactory = new ECPluginFactory(lpConfig, g_lpStatsCollector, bHostedKopano, bDistributedKopano);
  55. m_lpECCacheManager = new ECCacheManager(lpConfig, m_lpDatabaseFactory);
  56. m_lpSearchFolders = new ECSearchFolders(this, m_lpDatabaseFactory);
  57. m_lpTPropsPurge = new ECTPropsPurge(lpConfig, m_lpDatabaseFactory);
  58. m_ptrLockManager = ECLockManager::Create();
  59. // init SSL randomness for session IDs
  60. ssl_random_init();
  61. //Create session clean up thread
  62. err = pthread_create(&m_hSessionCleanerThread, NULL, SessionCleaner, (void*)this);
  63. set_thread_name(m_hSessionCleanerThread, "SessionCleanUp");
  64. if (err != 0)
  65. ec_log_crit("Unable to spawn thread for session cleaner! Sessions will live forever!: %s", strerror(err));
  66. m_lpNotificationManager = new ECNotificationManager();
  67. }
  68. ECSessionManager::~ECSessionManager()
  69. {
  70. ulock_normal l_exit(m_hExitMutex);
  71. bExit = TRUE;
  72. m_hExitSignal.notify_one();
  73. l_exit.unlock();
  74. delete m_lpTPropsPurge;
  75. delete m_lpDatabase;
  76. delete m_lpDatabaseFactory;
  77. int err = pthread_join(m_hSessionCleanerThread, NULL);
  78. if (err != 0)
  79. ec_log_crit("Unable to join session cleaner thread: %s", strerror(err));
  80. /* Clean up all sessions */
  81. std::lock_guard<KC::shared_mutex> l_cache(m_hCacheRWLock);
  82. auto iSession = m_mapSessions.begin();
  83. while (iSession != m_mapSessions.cend()) {
  84. delete iSession->second;
  85. auto iSessionNext = iSession;
  86. ++iSessionNext;
  87. ec_log_info("End of session (shutdown) %llu",
  88. static_cast<unsigned long long>(iSession->first));
  89. m_mapSessions.erase(iSession);
  90. iSession = iSessionNext;
  91. }
  92. delete m_lpNotificationManager;
  93. //#ifdef DEBUG
  94. // Clearing the cache takes too long while shutting down
  95. delete m_lpECCacheManager;
  96. //#endif
  97. delete m_lpSearchFolders;
  98. delete m_lpPluginFactory;
  99. delete m_lpServerGuid;
  100. if (m_lpAudit != NULL)
  101. m_lpAudit->Release();
  102. }
  103. ECRESULT ECSessionManager::LoadSettings(){
  104. ECRESULT er = erSuccess;
  105. ECDatabase * lpDatabase = NULL;
  106. DB_RESULT lpDBResult;
  107. DB_ROW lpDBRow = NULL;
  108. DB_LENGTHS lpDBLenths = NULL;
  109. std::string strQuery;
  110. if (m_lpServerGuid != nullptr)
  111. return KCERR_BAD_VALUE;
  112. er = GetThreadLocalDatabase(m_lpDatabaseFactory, &lpDatabase);
  113. if(er != erSuccess)
  114. return er;
  115. strQuery = "SELECT `value` FROM settings WHERE `name` = 'server_guid'";
  116. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  117. if(er != erSuccess)
  118. return er;
  119. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  120. lpDBLenths = lpDatabase->FetchRowLengths(lpDBResult);
  121. if (lpDBRow == nullptr || lpDBRow[0] == nullptr ||
  122. lpDBLenths == nullptr || lpDBLenths[0] != sizeof(GUID))
  123. return KCERR_NOT_FOUND;
  124. m_lpServerGuid = new GUID;
  125. memcpy(m_lpServerGuid, lpDBRow[0], sizeof(GUID));
  126. strQuery = "SELECT `value` FROM settings WHERE `name` = 'source_key_auto_increment'";
  127. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  128. if(er != erSuccess)
  129. return er;
  130. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  131. lpDBLenths = lpDatabase->FetchRowLengths(lpDBResult);
  132. if (lpDBRow == nullptr || lpDBRow[0] == nullptr ||
  133. lpDBLenths == nullptr || lpDBLenths[0] != 8)
  134. return KCERR_NOT_FOUND;
  135. memcpy(&m_ullSourceKeyAutoIncrement, lpDBRow[0], sizeof(m_ullSourceKeyAutoIncrement));
  136. return erSuccess;
  137. }
  138. ECRESULT ECSessionManager::CheckUserLicense()
  139. {
  140. ECRESULT er = erSuccess;
  141. ECSession *lpecSession = NULL;
  142. unsigned int ulLicense = 0;
  143. er = this->CreateSessionInternal(&lpecSession);
  144. if (er != erSuccess)
  145. goto exit;
  146. lpecSession->Lock();
  147. er = lpecSession->GetUserManagement()->CheckUserLicense(&ulLicense);
  148. if (er != erSuccess)
  149. goto exit;
  150. if (ulLicense & USERMANAGEMENT_USER_LICENSE_EXCEEDED) {
  151. ec_log_err("Failed to start server: Your license does not permit this amount of users.");
  152. er = KCERR_NO_ACCESS;
  153. goto exit;
  154. }
  155. exit:
  156. if(lpecSession) {
  157. lpecSession->Unlock(); // Lock the session
  158. this->RemoveSessionInternal(lpecSession);
  159. }
  160. return er;
  161. }
  162. /*
  163. * This function is threadsafe since we hold the lock the the group list, and the session retrieved from the grouplist
  164. * is locked so it cannot be deleted by other sessions, while we hold the lock for the group list.
  165. *
  166. * Other sessions may release the session group, even if they are the last, while we are in this function since
  167. * deletion of the session group only occurs within DeleteIfOrphaned(), and this function guarantees that the caller
  168. * will receive a sessiongroup that is not an orphan unless the caller releases the session group.
  169. */
  170. ECRESULT ECSessionManager::GetSessionGroup(ECSESSIONGROUPID sessionGroupID, ECSession *lpSession, ECSessionGroup **lppSessionGroup)
  171. {
  172. ECRESULT er = erSuccess;
  173. ECSessionGroup *lpSessionGroup = NULL;
  174. KC::shared_lock<KC::shared_mutex> lr_group(m_hGroupLock);
  175. std::unique_lock<KC::shared_mutex> lw_group(m_hGroupLock, std::defer_lock_t());
  176. /* Workaround for old clients, when sessionGroupID is 0 each session is its own group */
  177. if (sessionGroupID == 0) {
  178. lpSessionGroup = new ECSessionGroup(sessionGroupID, this);
  179. g_lpStatsCollector->Increment(SCN_SESSIONGROUPS_CREATED);
  180. } else {
  181. auto iter = m_mapSessionGroups.find(sessionGroupID);
  182. /* Check if the SessionGroup already exists on the server */
  183. if (iter == m_mapSessionGroups.cend()) {
  184. // "upgrade" lock to insert new session
  185. lr_group.unlock();
  186. lw_group.lock();
  187. lpSessionGroup = new ECSessionGroup(sessionGroupID, this);
  188. m_mapSessionGroups.insert(EC_SESSIONGROUPMAP::value_type(sessionGroupID, lpSessionGroup));
  189. g_lpStatsCollector->Increment(SCN_SESSIONGROUPS_CREATED);
  190. } else
  191. lpSessionGroup = iter->second;
  192. }
  193. lpSessionGroup->AddSession(lpSession);
  194. *lppSessionGroup = lpSessionGroup;
  195. return er;
  196. }
  197. ECRESULT ECSessionManager::DeleteIfOrphaned(ECSessionGroup *lpGroup)
  198. {
  199. ECSessionGroup *lpSessionGroup = NULL;
  200. ECSESSIONGROUPID id = lpGroup->GetSessionGroupId();
  201. if (id != 0) {
  202. std::lock_guard<KC::shared_mutex> l_group(m_hGroupLock);
  203. /* Check if the SessionGroup actually exists, if it doesn't just return without error */
  204. auto i = m_mapSessionGroups.find(id);
  205. if (i == m_mapSessionGroups.cend())
  206. return erSuccess;
  207. /* If this was the last Session, delete the SessionGroup */
  208. if (i->second->isOrphan()) {
  209. lpSessionGroup = i->second;
  210. m_mapSessionGroups.erase(i);
  211. }
  212. } else
  213. lpSessionGroup = lpGroup;
  214. if (lpSessionGroup) {
  215. delete lpSessionGroup;
  216. g_lpStatsCollector->Increment(SCN_SESSIONGROUPS_DELETED);
  217. }
  218. return erSuccess;
  219. }
  220. BTSession* ECSessionManager::GetSession(ECSESSIONID sessionID, bool fLockSession) {
  221. BTSession *lpSession = NULL;
  222. auto iIterator = m_mapSessions.find(sessionID);
  223. if (iIterator != m_mapSessions.cend()) {
  224. lpSession = iIterator->second;
  225. lpSession->UpdateSessionTime();
  226. if(fLockSession)
  227. lpSession->Lock();
  228. }else{
  229. //EC_SESSION_LOST
  230. }
  231. return lpSession;
  232. }
  233. // Clean up all current sessions
  234. ECRESULT ECSessionManager::RemoveAllSessions()
  235. {
  236. ECRESULT er = erSuccess;
  237. BTSession *lpSession = NULL;
  238. std::list<BTSession *> lstSessions;
  239. // Lock the session map since we're going to remove all the sessions.
  240. std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  241. ec_log_info("Shutdown all current sessions");
  242. auto iIterSession = m_mapSessions.begin();
  243. while (iIterSession != m_mapSessions.cend()) {
  244. lpSession = iIterSession->second;
  245. auto iSessionNext = iIterSession;
  246. ++iSessionNext;
  247. m_mapSessions.erase(iIterSession);
  248. iIterSession = iSessionNext;
  249. lstSessions.push_back(lpSession);
  250. }
  251. l_cache.unlock();
  252. // Do the actual session deletes, while the session map is not locked (!)
  253. for (auto sesp : lstSessions)
  254. delete sesp;
  255. return er;
  256. }
  257. ECRESULT ECSessionManager::CancelAllSessions(ECSESSIONID sessionIDException)
  258. {
  259. ECRESULT er = erSuccess;
  260. BTSession *lpSession = NULL;
  261. std::list<BTSession *> lstSessions;
  262. // Lock the session map since we're going to remove all the sessions.
  263. std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  264. ec_log_info("Shutdown all current sessions");
  265. auto iIterSession = m_mapSessions.begin();
  266. while (iIterSession != m_mapSessions.cend()) {
  267. if (iIterSession->first == sessionIDException) {
  268. ++iIterSession;
  269. continue;
  270. }
  271. lpSession = iIterSession->second;
  272. auto iSessionNext = iIterSession;
  273. ++iSessionNext;
  274. // Tell the notification manager to wake up anyone waiting for this session
  275. m_lpNotificationManager->NotifyChange(iIterSession->first);
  276. m_mapSessions.erase(iIterSession);
  277. iIterSession = iSessionNext;
  278. lstSessions.push_back(lpSession);
  279. }
  280. l_cache.unlock();
  281. // Do the actual session deletes, while the session map is not locked (!)
  282. for (auto sesp : lstSessions)
  283. delete sesp;
  284. return er;
  285. }
  286. // call a function for all sessions available
  287. // used by ECStatsTable
  288. ECRESULT ECSessionManager::ForEachSession(void(*callback)(ECSession*, void*), void *obj)
  289. {
  290. KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  291. for (const auto &p : m_mapSessions)
  292. callback(dynamic_cast<ECSession *>(p.second), obj);
  293. return erSuccess;
  294. }
  295. // Locking of sessions works as follows:
  296. //
  297. // - A session is requested by the caller thread through ValidateSession. ValidateSession
  298. // Locks the complete session table, then acquires a lock on the session, and then
  299. // frees the lock on the session table. This makes sure that when a session is returned,
  300. // it is guaranteed not to be deleted by another thread (due to a shutdown or logoff).
  301. // The caller of 'ValidateSession' is therefore responsible for unlocking the session
  302. // when it is finished.
  303. //
  304. // - When a session is terminated, a lock is opened on the session table, making sure no
  305. // new session can be opened, or session can be deleted. Then, the session is searched
  306. // in the table, and directly deleted from the table, making sure that no new threads can
  307. // open the session in question after this point. Then, the session is deleted, but the
  308. // session itself waits in the destructor until all threads holding a lock on the session
  309. // through Lock or ValidateSession have released their lock, before actually deleting the
  310. // session object.
  311. //
  312. // This means that exiting the server must wait until all client requests have exited. For
  313. // most operations, this is not a problem, but for some long requests (ie large deletes or
  314. // copies, or GetNextNotifyItem) may take quite a while to exit. This is compensated for, by
  315. // having the session call a 'cancel' request to long-running calls, which makes the calls
  316. // exit prematurely.
  317. //
  318. ECRESULT ECSessionManager::ValidateSession(struct soap *soap, ECSESSIONID sessionID, ECAuthSession **lppSession, bool fLockSession)
  319. {
  320. ECRESULT er;
  321. BTSession *lpSession = NULL;
  322. er = this->ValidateBTSession(soap, sessionID, &lpSession, fLockSession);
  323. if (er != erSuccess)
  324. return er;
  325. *lppSession = dynamic_cast<ECAuthSession*>(lpSession);
  326. return erSuccess;
  327. }
  328. ECRESULT ECSessionManager::ValidateSession(struct soap *soap, ECSESSIONID sessionID, ECSession **lppSession, bool fLockSession)
  329. {
  330. ECRESULT er;
  331. BTSession *lpSession = NULL;
  332. er = this->ValidateBTSession(soap, sessionID, &lpSession, fLockSession);
  333. if (er != erSuccess)
  334. return er;
  335. *lppSession = dynamic_cast<ECSession*>(lpSession);
  336. return erSuccess;
  337. }
  338. ECRESULT ECSessionManager::ValidateBTSession(struct soap *soap, ECSESSIONID sessionID, BTSession **lppSession, bool fLockSession)
  339. {
  340. ECRESULT er;
  341. BTSession* lpSession = NULL;
  342. // Read lock
  343. KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  344. lpSession = GetSession(sessionID, fLockSession);
  345. l_cache.unlock();
  346. if (lpSession == NULL)
  347. return KCERR_END_OF_SESSION;
  348. lpSession->RecordRequest(soap);
  349. er = lpSession->ValidateOriginator(soap);
  350. if (er != erSuccess) {
  351. if (fLockSession)
  352. lpSession->Unlock();
  353. lpSession = NULL;
  354. return er;
  355. }
  356. /* Enable compression if client desired and granted */
  357. if (lpSession->GetCapabilities() & KOPANO_CAP_COMPRESSION) {
  358. soap_set_imode(soap, SOAP_ENC_ZLIB);
  359. soap_set_omode(soap, SOAP_ENC_ZLIB | SOAP_IO_CHUNK);
  360. }
  361. // Enable streaming support if client is capable
  362. if (lpSession->GetCapabilities() & KOPANO_CAP_ENHANCED_ICS) {
  363. soap_set_omode(soap, SOAP_ENC_MTOM | SOAP_IO_CHUNK);
  364. soap_set_imode(soap, SOAP_ENC_MTOM);
  365. soap_post_check_mime_attachments(soap);
  366. }
  367. *lppSession = lpSession;
  368. return erSuccess;
  369. }
  370. ECRESULT ECSessionManager::CreateAuthSession(struct soap *soap, unsigned int ulCapabilities, ECSESSIONID *sessionID, ECAuthSession **lppAuthSession, bool bRegisterSession, bool bLockSession)
  371. {
  372. ECAuthSession *lpAuthSession = NULL;
  373. ECSESSIONID newSessionID;
  374. CreateSessionID(ulCapabilities, &newSessionID);
  375. lpAuthSession = new(std::nothrow) ECAuthSession(GetSourceAddr(soap), newSessionID, m_lpDatabaseFactory, this, ulCapabilities);
  376. if (lpAuthSession == NULL)
  377. return KCERR_NOT_ENOUGH_MEMORY;
  378. if (bLockSession)
  379. lpAuthSession->Lock();
  380. if (bRegisterSession) {
  381. std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  382. m_mapSessions.insert( SESSIONMAP::value_type(newSessionID, lpAuthSession) );
  383. l_cache.unlock();
  384. g_lpStatsCollector->Increment(SCN_SESSIONS_CREATED);
  385. }
  386. *sessionID = newSessionID;
  387. *lppAuthSession = lpAuthSession;
  388. return erSuccess;
  389. }
  390. ECRESULT ECSessionManager::CreateSession(struct soap *soap, const char *szName,
  391. const char *szPassword, const char *szImpersonateUser,
  392. const char *szClientVersion, const char *szClientApp,
  393. const char *szClientAppVersion, const char *szClientAppMisc,
  394. unsigned int ulCapabilities, ECSESSIONGROUPID sessionGroupID,
  395. ECSESSIONID *lpSessionID, ECSession **lppSession, bool fLockSession,
  396. bool fAllowUidAuth)
  397. {
  398. ECRESULT er = erSuccess;
  399. std::unique_ptr<ECAuthSession> lpAuthSession;
  400. ECSession *lpSession = NULL;
  401. const char *method = "error";
  402. std::string from;
  403. CONNECTION_TYPE ulType = SOAP_CONNECTION_TYPE(soap);
  404. if (ulType == CONNECTION_TYPE_NAMED_PIPE_PRIORITY)
  405. from = string("file://") + m_lpConfig->GetSetting("server_pipe_priority");
  406. else if (ulType == CONNECTION_TYPE_NAMED_PIPE)
  407. // connected through Unix socket
  408. from = string("file://") + m_lpConfig->GetSetting("server_pipe_name");
  409. else
  410. // connected over network
  411. from = soap->host;
  412. er = this->CreateAuthSession(soap, ulCapabilities, lpSessionID, &unique_tie(lpAuthSession), false, false);
  413. if (er != erSuccess)
  414. goto exit;
  415. // If we've connected with SSL, check if there is a certificate, and check if we accept that certificate for that user
  416. if (soap->ssl && lpAuthSession->ValidateUserCertificate(soap, szName, szImpersonateUser) == erSuccess) {
  417. g_lpStatsCollector->Increment(SCN_LOGIN_SSL);
  418. method = "SSL Certificate";
  419. goto authenticated;
  420. }
  421. // First, try socket authentication (dagent, won't print error)
  422. if(fAllowUidAuth && lpAuthSession->ValidateUserSocket(soap->socket, szName, szImpersonateUser) == erSuccess) {
  423. g_lpStatsCollector->Increment(SCN_LOGIN_SOCKET);
  424. method = "Pipe socket";
  425. goto authenticated;
  426. }
  427. // If that fails, try logon with supplied username/password (clients, may print logon error)
  428. if(lpAuthSession->ValidateUserLogon(szName, szPassword, szImpersonateUser) == erSuccess) {
  429. g_lpStatsCollector->Increment(SCN_LOGIN_PASSWORD);
  430. method = "User supplied password";
  431. goto authenticated;
  432. }
  433. // whoops, out of auth options.
  434. ec_log_warn("Failed to authenticate user \"%s\" from \"%s\" using program \"%s\"",
  435. szName, from.c_str(), szClientApp ? szClientApp : "<unknown>");
  436. ZLOG_AUDIT(m_lpAudit, "authenticate failed user='%s' from='%s' program='%s'",
  437. szName, from.c_str(), szClientApp ? szClientApp : "<unknown>");
  438. er = KCERR_LOGON_FAILED;
  439. g_lpStatsCollector->Increment(SCN_LOGIN_DENIED);
  440. goto exit;
  441. authenticated:
  442. ec_log_debug("User \"%s\" from \"%s\" authenticated through \"%s\" using program %s", szName, from.c_str(), method, szClientApp ? szClientApp : "<unknown>");
  443. if (strcmp(KOPANO_SYSTEM_USER, szName) != 0)
  444. /* Do not log successful SYSTEM logins */
  445. ZLOG_AUDIT(m_lpAudit, "authenticate ok user='%s' from='%s' method='%s' program='%s'",
  446. szName, from.c_str(), method, szClientApp ? szClientApp : "<unknown>");
  447. er = RegisterSession(lpAuthSession.get(), sessionGroupID,
  448. szClientVersion, szClientApp, szClientAppVersion, szClientAppMisc,
  449. lpSessionID, &lpSession, fLockSession);
  450. if (er != erSuccess) {
  451. if (er == KCERR_NO_ACCESS && szImpersonateUser != NULL && *szImpersonateUser != '\0') {
  452. ec_log_err("Failed attempt to impersonate user \"%s\" by user \"%s\"", szImpersonateUser, szName);
  453. ZLOG_AUDIT(m_lpAudit, "impersonate failed user='%s', from='%s' program='%s' impersonator='%s'",
  454. szImpersonateUser, from.c_str(), szClientApp ? szClientApp : "<unknown>", szName);
  455. } else
  456. ec_log_err("User \"%s\" authenticated, but failed to create session. Error 0x%08X", szName, er);
  457. goto exit;
  458. }
  459. if (!szImpersonateUser || *szImpersonateUser == '\0')
  460. ec_log_debug("User \"%s\" receives session %llu",
  461. szName, static_cast<unsigned long long>(*lpSessionID));
  462. else {
  463. ec_log_debug("User \"%s\" impersonated by \"%s\" receives session %llu",
  464. szImpersonateUser, szName,
  465. static_cast<unsigned long long>(*lpSessionID));
  466. ZLOG_AUDIT(m_lpAudit, "impersonate ok user='%s', from='%s' program='%s' impersonator='%s'",
  467. szImpersonateUser, from.c_str(), szClientApp ? szClientApp : "<unknown>", szName);
  468. }
  469. exit:
  470. *lppSession = lpSession;
  471. return er;
  472. }
  473. ECRESULT ECSessionManager::RegisterSession(ECAuthSession *lpAuthSession,
  474. ECSESSIONGROUPID sessionGroupID, const char *szClientVersion,
  475. const char *szClientApp, const char *szClientApplicationVersion,
  476. const char *szClientApplicationMisc, ECSESSIONID *lpSessionID,
  477. ECSession **lppSession, bool fLockSession)
  478. {
  479. ECRESULT er = erSuccess;
  480. ECSession *lpSession = NULL;
  481. ECSESSIONID newSID = 0;
  482. er = lpAuthSession->CreateECSession(sessionGroupID, szClientVersion ? szClientVersion : "", szClientApp ? szClientApp : "", szClientApplicationVersion ? szClientApplicationVersion : "", szClientApplicationMisc ? szClientApplicationMisc : "", &newSID, &lpSession);
  483. if (er != erSuccess)
  484. return er;
  485. if (fLockSession)
  486. lpSession->Lock();
  487. std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  488. m_mapSessions.insert( SESSIONMAP::value_type(newSID, lpSession) );
  489. l_cache.unlock();
  490. *lpSessionID = std::move(newSID);
  491. *lppSession = lpSession;
  492. g_lpStatsCollector->Increment(SCN_SESSIONS_CREATED);
  493. return er;
  494. }
  495. ECRESULT ECSessionManager::CreateSessionInternal(ECSession **lppSession, unsigned int ulUserId)
  496. {
  497. ECRESULT er;
  498. ECSession *lpSession = NULL;
  499. ECSESSIONID newSID;
  500. CreateSessionID(KOPANO_CAP_LARGE_SESSIONID, &newSID);
  501. lpSession = new(std::nothrow) ECSession("<internal>", newSID, 0,
  502. m_lpDatabaseFactory, this, 0, ECSession::METHOD_NONE, 0,
  503. "internal", "kopano-server", "", "");
  504. if (lpSession == NULL)
  505. return KCERR_LOGON_FAILED;
  506. er = lpSession->GetSecurity()->SetUserContext(ulUserId, EC_NO_IMPERSONATOR);
  507. if (er != erSuccess) {
  508. delete lpSession;
  509. return er;
  510. }
  511. ec_log_debug("New internal session (%llu)",
  512. static_cast<unsigned long long>(newSID));
  513. g_lpStatsCollector->Increment(SCN_SESSIONS_INTERNAL_CREATED);
  514. *lppSession = lpSession;
  515. return erSuccess;
  516. }
  517. void ECSessionManager::RemoveSessionInternal(ECSession *lpSession)
  518. {
  519. if (lpSession != NULL) {
  520. g_lpStatsCollector->Increment(SCN_SESSIONS_INTERNAL_DELETED);
  521. delete lpSession;
  522. }
  523. }
  524. ECRESULT ECSessionManager::RemoveSession(ECSESSIONID sessionID){
  525. ECRESULT hr = erSuccess;
  526. BTSession *lpSession = NULL;
  527. ec_log_debug("End of session (logoff) %llu",
  528. static_cast<unsigned long long>(sessionID));
  529. g_lpStatsCollector->Increment(SCN_SESSIONS_DELETED);
  530. // Make sure no other thread can read or write the sessions list
  531. std::unique_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  532. // Get a session, don't lock it ourselves
  533. lpSession = GetSession(sessionID, false);
  534. // Remove the session from the list. No other threads can start new
  535. // requests on the session after this point
  536. m_mapSessions.erase(sessionID);
  537. l_cache.unlock();
  538. // We know for sure that no other thread is attempting to remove the session
  539. // at this time because it would not have been in the m_mapSessions map
  540. // Delete the session. This will block until all requesters on the session
  541. // have released their lock on the session
  542. if(lpSession != NULL) {
  543. if(lpSession->Shutdown(5 * 60 * 1000) == erSuccess)
  544. delete lpSession;
  545. else
  546. ec_log_err("Session failed to shut down: skipping logoff");
  547. }
  548. // Tell the notification manager to wake up anyone waiting for this session
  549. m_lpNotificationManager->NotifyChange(sessionID);
  550. return hr;
  551. }
  552. /**
  553. * Add notification to a session group.
  554. * @note This function can't handle table notifications!
  555. *
  556. * @param[in] notifyItem The notification data to send
  557. * @param[in] ulKey The object (hierarchyid) the notification acts on
  558. * @param[in] ulStore The store the ulKey object resides in. 0 for unknown (default).
  559. * @param[in] ulFolderId Parent folder object for ulKey. 0 for unknown or not required (default).
  560. * @param[in] ulFlags Hierarchy flags for ulKey. 0 for unknown (default).
  561. *
  562. * @return Kopano error code
  563. */
  564. ECRESULT ECSessionManager::AddNotification(notification *notifyItem, unsigned int ulKey, unsigned int ulStore, unsigned int ulFolderId, unsigned int ulFlags) {
  565. std::set<ECSESSIONGROUPID> setGroups;
  566. ECRESULT hr = erSuccess;
  567. if(ulStore == 0) {
  568. hr = m_lpECCacheManager->GetStore(ulKey, &ulStore, NULL);
  569. if(hr != erSuccess)
  570. return hr;
  571. }
  572. // Send notification to subscribed sessions
  573. ulock_normal l_sub(m_mutexObjectSubscriptions);
  574. auto iterObjectSubscription = m_mapObjectSubscriptions.lower_bound(ulStore);
  575. while (iterObjectSubscription != m_mapObjectSubscriptions.cend() &&
  576. iterObjectSubscription->first == ulStore) {
  577. // Send a notification only once to a session group, even if it has subscribed multiple times
  578. setGroups.insert(iterObjectSubscription->second);
  579. ++iterObjectSubscription;
  580. }
  581. l_sub.unlock();
  582. // Send each subscribed session group one notification
  583. for (const auto &grp : setGroups) {
  584. KC::shared_lock<KC::shared_mutex> grplk(m_hGroupLock);
  585. auto iIterator = m_mapSessionGroups.find(grp);
  586. if (iIterator != m_mapSessionGroups.cend())
  587. iIterator->second->AddNotification(notifyItem, ulKey, ulStore);
  588. }
  589. // Next, do an internal notification to update searchfolder views for message updates.
  590. if (notifyItem->obj == nullptr || notifyItem->obj->ulObjType != MAPI_MESSAGE)
  591. return hr;
  592. if (ulFolderId == 0 && ulFlags == 0 &&
  593. GetCacheManager()->GetObject(ulKey, &ulFolderId, NULL, &ulFlags, NULL) != erSuccess) {
  594. assert(false);
  595. return hr;
  596. }
  597. // Skip changes on associated messages, and changes on deleted item. (but include DELETE of deleted items)
  598. if ((ulFlags & MAPI_ASSOCIATED) || (notifyItem->ulEventType != fnevObjectDeleted && (ulFlags & MSGFLAG_DELETED)))
  599. return hr;
  600. switch (notifyItem->ulEventType) {
  601. case fnevObjectMoved:
  602. // Only update the item in the new folder. The system will automatically delete the item from folders that were not in the search path
  603. m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_MODIFY);
  604. break;
  605. case fnevObjectDeleted:
  606. m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_DELETE);
  607. break;
  608. case fnevObjectCreated:
  609. m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_ADD);
  610. break;
  611. case fnevObjectCopied:
  612. m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_ADD);
  613. break;
  614. case fnevObjectModified:
  615. m_lpSearchFolders->UpdateSearchFolders(ulStore, ulFolderId, ulKey, ECKeyTable::TABLE_ROW_MODIFY);
  616. break;
  617. }
  618. return hr;
  619. }
  620. void* ECSessionManager::SessionCleaner(void *lpTmpSessionManager)
  621. {
  622. time_t lCurTime;
  623. auto lpSessionManager = static_cast<ECSessionManager *>(lpTmpSessionManager);
  624. list<BTSession*> lstSessions;
  625. if (lpSessionManager == NULL)
  626. return 0;
  627. ECDatabase *db = NULL;
  628. if (GetThreadLocalDatabase(lpSessionManager->m_lpDatabaseFactory, &db) != erSuccess)
  629. ec_log_err("GTLD failed in SessionCleaner");
  630. while(true){
  631. std::unique_lock<KC::shared_mutex> l_cache(lpSessionManager->m_hCacheRWLock);
  632. lCurTime = GetProcessTime();
  633. // Find a session that has timed out
  634. auto iIterator = lpSessionManager->m_mapSessions.begin();
  635. while (iIterator != lpSessionManager->m_mapSessions.cend()) {
  636. bool del = iIterator->second->GetSessionTime() < lCurTime &&
  637. !lpSessionManager->IsSessionPersistent(iIterator->first);
  638. if (!del) {
  639. ++iIterator;
  640. continue;
  641. }
  642. // Remember all the session to be deleted
  643. lstSessions.push_back(iIterator->second);
  644. auto iRemove = iIterator++;
  645. // Remove the session from the list, no new threads can start on this session after this point.
  646. g_lpStatsCollector->Increment(SCN_SESSIONS_TIMEOUT);
  647. ec_log_info("End of session (timeout) %llu",
  648. static_cast<unsigned long long>(iRemove->first));
  649. lpSessionManager->m_mapSessions.erase(iRemove);
  650. }
  651. // Release ownership of the rwlock object. This makes sure all threads are free to run (and exit).
  652. l_cache.unlock();
  653. // Now, remove all the session. It will wait until all running threads for that session have exited.
  654. for (const auto ses : lstSessions) {
  655. if (ses->Shutdown(5 * 60 * 1000) == erSuccess)
  656. delete ses;
  657. else
  658. // The session failed to shut down within our timeout period. This means we probably hit a bug; this
  659. // should only happen if some bit of code has locked the session and failed to unlock it. There are now
  660. // two options: delete the session anyway and hope we don't segfault, or leak the session. We choose
  661. // the latter.
  662. ec_log_err("Session failed to shut down: skipping clean");
  663. }
  664. lstSessions.clear();
  665. KC::sync_logon_times(db);
  666. // Wait for a terminate signal or return after a few minutes
  667. ulock_normal l_exit(lpSessionManager->m_hExitMutex);
  668. if(lpSessionManager->bExit) {
  669. l_exit.unlock();
  670. break;
  671. }
  672. if (lpSessionManager->m_hExitSignal.wait_for(l_exit,
  673. std::chrono::seconds(5)) != std::cv_status::timeout)
  674. break;
  675. }
  676. return NULL;
  677. }
  678. ECRESULT ECSessionManager::UpdateOutgoingTables(ECKeyTable::UpdateType ulType, unsigned int ulStoreId, unsigned int ulObjId, unsigned int ulFlags, unsigned int ulObjType)
  679. {
  680. TABLESUBSCRIPTION sSubscription;
  681. std::list<unsigned int> lstObjId;
  682. lstObjId.push_back(ulObjId);
  683. sSubscription.ulType = TABLE_ENTRY::TABLE_TYPE_OUTGOINGQUEUE;
  684. sSubscription.ulRootObjectId = ulFlags & EC_SUBMIT_MASTER ? 0 : ulStoreId; // in the master queue, use 0 as root object id
  685. sSubscription.ulObjectType = ulObjType;
  686. sSubscription.ulObjectFlags = ulFlags & EC_SUBMIT_MASTER; // Only use MASTER flag as differentiator
  687. return UpdateSubscribedTables(ulType, sSubscription, lstObjId);
  688. }
  689. ECRESULT ECSessionManager::UpdateTables(ECKeyTable::UpdateType ulType, unsigned int ulFlags, unsigned ulObjId, unsigned ulChildId, unsigned int ulObjType)
  690. {
  691. std::list<unsigned int> lstChildId = {ulChildId};
  692. return UpdateTables(ulType, ulFlags, ulObjId, lstChildId, ulObjType);
  693. }
  694. ECRESULT ECSessionManager::UpdateTables(ECKeyTable::UpdateType ulType, unsigned int ulFlags, unsigned ulObjId, std::list<unsigned int>& lstChildId, unsigned int ulObjType)
  695. {
  696. TABLESUBSCRIPTION sSubscription;
  697. if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER)
  698. return erSuccess;
  699. sSubscription.ulType = TABLE_ENTRY::TABLE_TYPE_GENERIC;
  700. sSubscription.ulRootObjectId = ulObjId;
  701. sSubscription.ulObjectType = ulObjType;
  702. sSubscription.ulObjectFlags = ulFlags;
  703. return UpdateSubscribedTables(ulType, sSubscription, lstChildId);
  704. }
  705. ECRESULT ECSessionManager::UpdateSubscribedTables(ECKeyTable::UpdateType ulType, TABLESUBSCRIPTION sSubscription, std::list<unsigned int> &lstChildId)
  706. {
  707. ECRESULT er = erSuccess;
  708. std::set<ECSESSIONID> setSessions;
  709. BTSession *lpBTSession = NULL;
  710. // Find out which sessions our interested in this event by looking at our subscriptions
  711. ulock_normal l_sub(m_mutexTableSubscriptions);
  712. auto iterSubscriptions = m_mapTableSubscriptions.find(sSubscription);
  713. while (iterSubscriptions != m_mapTableSubscriptions.cend() &&
  714. iterSubscriptions->first == sSubscription) {
  715. setSessions.insert(iterSubscriptions->second);
  716. ++iterSubscriptions;
  717. }
  718. l_sub.unlock();
  719. // We now have a set of sessions that are interested in the notification. This list is normally quite small since not that many
  720. // sessions have the same table opened at one time.
  721. // For each of the sessions that are interested, send the table change
  722. for (const auto &ses : setSessions) {
  723. // Get session
  724. KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  725. lpBTSession = GetSession(ses, true);
  726. l_cache.unlock();
  727. // Send the change notification
  728. if(lpBTSession != NULL) {
  729. auto lpSession = dynamic_cast<ECSession *>(lpBTSession);
  730. if (lpSession == NULL) {
  731. lpBTSession->Unlock();
  732. continue;
  733. }
  734. if (sSubscription.ulType == TABLE_ENTRY::TABLE_TYPE_GENERIC)
  735. lpSession->GetTableManager()->UpdateTables(ulType, sSubscription.ulObjectFlags, sSubscription.ulRootObjectId, lstChildId, sSubscription.ulObjectType);
  736. else if (sSubscription.ulType == TABLE_ENTRY::TABLE_TYPE_OUTGOINGQUEUE)
  737. lpSession->GetTableManager()->UpdateOutgoingTables(ulType, sSubscription.ulRootObjectId, lstChildId, sSubscription.ulObjectFlags, sSubscription.ulObjectType);
  738. lpBTSession->Unlock();
  739. }
  740. }
  741. return er;
  742. }
  743. // FIXME: ulFolderId should be an entryid, because the parent is already deleted!
  744. // You must specify which store the object was deleted from, 'cause we can't find out afterwards
  745. ECRESULT ECSessionManager::NotificationDeleted(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulStoreId, entryId* lpEntryId, unsigned int ulFolderId, unsigned int ulFlags)
  746. {
  747. ECRESULT er = erSuccess;
  748. struct notification notify;
  749. memset(&notify, 0, sizeof(notification));
  750. if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
  751. goto exit;
  752. notify.obj = s_alloc<notificationObject>(nullptr);
  753. memset(notify.obj, 0, sizeof(notificationObject));
  754. notify.ulEventType = fnevObjectDeleted;
  755. notify.obj->ulObjType = ulObjType;
  756. notify.obj->pEntryId = lpEntryId;
  757. if(ulFolderId > 0) {
  758. er = GetCacheManager()->GetEntryIdFromObject(ulFolderId, NULL, 0, &notify.obj->pParentId);
  759. if(er != erSuccess)
  760. goto exit;
  761. }
  762. AddNotification(&notify, ulObjId, ulStoreId, ulFolderId, ulFlags);
  763. exit:
  764. notify.obj->pEntryId = NULL;
  765. FreeNotificationStruct(&notify, false);
  766. return er;
  767. }
  768. ECRESULT ECSessionManager::NotificationModified(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulParentId)
  769. {
  770. ECRESULT er = erSuccess;
  771. struct notification notify;
  772. memset(&notify, 0, sizeof(notification));
  773. if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
  774. goto exit;
  775. notify.obj = s_alloc<notificationObject>(nullptr);
  776. memset(notify.obj, 0, sizeof(notificationObject));
  777. notify.ulEventType = fnevObjectModified;
  778. notify.obj->ulObjType = ulObjType;
  779. er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, &notify.obj->pEntryId);
  780. if(er != erSuccess)
  781. goto exit;
  782. if(ulParentId > 0) {
  783. er = GetCacheManager()->GetEntryIdFromObject(ulParentId, NULL, 0, &notify.obj->pParentId);
  784. if(er != erSuccess)
  785. goto exit;
  786. }
  787. AddNotification(&notify, ulObjId);
  788. exit:
  789. FreeNotificationStruct(&notify, false);
  790. return er;
  791. }
  792. ECRESULT ECSessionManager::NotificationCreated(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulParentId)
  793. {
  794. ECRESULT er = erSuccess;
  795. struct notification notify;
  796. memset(&notify, 0, sizeof(notification));
  797. if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
  798. goto exit;
  799. notify.obj = s_alloc<notificationObject>(nullptr);
  800. memset(notify.obj, 0, sizeof(notificationObject));
  801. notify.ulEventType = fnevObjectCreated;
  802. notify.obj->ulObjType = ulObjType;
  803. er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, &notify.obj->pEntryId);
  804. if(er != erSuccess)
  805. goto exit;
  806. er = GetCacheManager()->GetEntryIdFromObject(ulParentId, NULL, 0, &notify.obj->pParentId);
  807. if(er != erSuccess)
  808. goto exit;
  809. AddNotification(&notify, ulObjId);
  810. exit:
  811. FreeNotificationStruct(&notify, false);
  812. return er;
  813. }
  814. ECRESULT ECSessionManager::NotificationMoved(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulParentId, unsigned int ulOldParentId, entryId *lpOldEntryId)
  815. {
  816. ECRESULT er = erSuccess;
  817. struct notification notify;
  818. memset(&notify, 0, sizeof(notification));
  819. if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
  820. goto exit;
  821. notify.obj = s_alloc<notificationObject>(nullptr);
  822. memset(notify.obj, 0, sizeof(notificationObject));
  823. notify.ulEventType = fnevObjectMoved;
  824. notify.obj->ulObjType = ulObjType;
  825. er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, &notify.obj->pEntryId);
  826. if(er != erSuccess)
  827. goto exit;
  828. er = GetCacheManager()->GetEntryIdFromObject(ulParentId, NULL, 0, &notify.obj->pParentId);
  829. if(er != erSuccess)
  830. goto exit;
  831. er = GetCacheManager()->GetEntryIdFromObject(ulOldParentId, NULL, 0, &notify.obj->pOldParentId);
  832. if(er != erSuccess)
  833. goto exit;
  834. notify.obj->pOldId = lpOldEntryId;
  835. AddNotification(&notify, ulObjId);
  836. notify.obj->pOldId = NULL;
  837. exit:
  838. FreeNotificationStruct(&notify, false);
  839. return er;
  840. }
  841. ECRESULT ECSessionManager::NotificationCopied(unsigned int ulObjType, unsigned int ulObjId, unsigned int ulParentId, unsigned int ulOldObjId, unsigned int ulOldParentId)
  842. {
  843. ECRESULT er = erSuccess;
  844. struct notification notify;
  845. memset(&notify, 0, sizeof(notification));
  846. if(ulObjType != MAPI_MESSAGE && ulObjType != MAPI_FOLDER && ulObjType != MAPI_STORE)
  847. goto exit;
  848. notify.obj = s_alloc<notificationObject>(nullptr);
  849. memset(notify.obj, 0, sizeof(notificationObject));
  850. notify.ulEventType = fnevObjectCopied;
  851. notify.obj->ulObjType = ulObjType;
  852. er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, &notify.obj->pEntryId);
  853. if(er != erSuccess)
  854. goto exit;
  855. er = GetCacheManager()->GetEntryIdFromObject(ulParentId, NULL, 0, &notify.obj->pParentId);
  856. if(er != erSuccess)
  857. goto exit;
  858. if(ulOldObjId > 0) {
  859. er = GetCacheManager()->GetEntryIdFromObject(ulOldObjId, NULL, 0, &notify.obj->pOldId);
  860. if(er != erSuccess)
  861. goto exit;
  862. }
  863. if(ulOldParentId > 0) {
  864. er = GetCacheManager()->GetEntryIdFromObject(ulOldParentId, NULL, 0, &notify.obj->pOldParentId);
  865. if(er != erSuccess)
  866. goto exit;
  867. }
  868. AddNotification(&notify, ulObjId);
  869. exit:
  870. FreeNotificationStruct(&notify, false);
  871. return er;
  872. }
  873. /**
  874. * Send "Search complete" notification to the client.
  875. *
  876. * @param ulObjId object id of the search folder
  877. *
  878. * @return Kopano error code
  879. */
  880. ECRESULT ECSessionManager::NotificationSearchComplete(unsigned int ulObjId, unsigned int ulStoreId)
  881. {
  882. ECRESULT er = erSuccess;
  883. struct notification notify;
  884. memset(&notify, 0, sizeof(notification));
  885. notify.obj = s_alloc<notificationObject>(nullptr);
  886. memset(notify.obj, 0, sizeof(notificationObject));
  887. notify.ulEventType = fnevSearchComplete;
  888. notify.obj->ulObjType = MAPI_FOLDER;
  889. er = GetCacheManager()->GetEntryIdFromObject(ulObjId, NULL, 0, &notify.obj->pEntryId);
  890. if(er != erSuccess)
  891. goto exit;
  892. AddNotification(&notify, ulObjId, ulStoreId);
  893. exit:
  894. FreeNotificationStruct(&notify, false);
  895. return er;
  896. }
  897. ECRESULT ECSessionManager::NotificationChange(const set<unsigned int> &syncIds, unsigned int ulChangeId, unsigned int ulChangeType)
  898. {
  899. KC::shared_lock<KC::shared_mutex> grplk(m_hGroupLock);
  900. // Send the notification to all sessionsgroups so that any client listening for these
  901. // notifications can receive them
  902. for (const auto &p : m_mapSessionGroups)
  903. p.second->AddChangeNotification(syncIds, ulChangeId, ulChangeType);
  904. return erSuccess;
  905. }
  906. /**
  907. * Get the sessionmanager statistics
  908. *
  909. * @param[in] callback Callback to the statistics collector
  910. * @param[in] obj pointer to the statistics collector
  911. */
  912. void ECSessionManager::GetStats(void(callback)(const std::string &, const std::string &, const std::string &, void*), void *obj)
  913. {
  914. sSessionManagerStats sSessionStats;
  915. sSearchFolderStats sSearchStats;
  916. GetStats(sSessionStats);
  917. callback("sessions", "Number of sessions", stringify(sSessionStats.session.ulItems), obj);
  918. callback("sessions_size", "Memory usage of sessions", stringify_int64(sSessionStats.session.ullSize), obj);
  919. callback("sessiongroups", "Number of session groups", stringify(sSessionStats.group.ulItems), obj);
  920. callback("sessiongroups_size", "Memory usage of session groups", stringify_int64(sSessionStats.group.ullSize), obj);
  921. callback("persist_conn", "Persistent connections", stringify(sSessionStats.ulPersistentByConnection), obj);
  922. callback("persist_conn_size", "Memory usage of persistent connections", stringify(sSessionStats.ulPersistentByConnectionSize), obj);
  923. callback("persist_sess", "Persistent sessions", stringify(sSessionStats.ulPersistentBySession), obj);
  924. callback("persist_sess_size", "Memory usage of persistent sessions", stringify(sSessionStats.ulPersistentBySessionSize), obj);
  925. callback("tables_subscr", "Tables subscribed", stringify(sSessionStats.ulTableSubscriptions), obj);
  926. callback("tables_subscr_size", "Memory usage of subscribed tables", stringify(sSessionStats.ulTableSubscriptionSize), obj);
  927. callback("object_subscr", "Objects subscribed", stringify(sSessionStats.ulObjectSubscriptions), obj);
  928. callback("object_subscr_size", "Memory usage of subscribed objects", stringify(sSessionStats.ulObjectSubscriptionSize), obj);
  929. m_lpSearchFolders->GetStats(sSearchStats);
  930. callback("searchfld_stores", "Number of stores in use by search folders", stringify(sSearchStats.ulStores), obj);
  931. callback("searchfld_folders", "Number of folders in use by search folders", stringify(sSearchStats.ulFolders), obj);
  932. callback("searchfld_events", "Number of events waiting for searchfolder updates", stringify(sSearchStats.ulEvents), obj);
  933. callback("searchfld_size", "Memory usage of search folders", stringify_int64(sSearchStats.ullSize), obj);
  934. }
  935. /**
  936. * Collect session statistics
  937. *
  938. * @param[out] sStats The statistics
  939. *
  940. */
  941. void ECSessionManager::GetStats(sSessionManagerStats &sStats)
  942. {
  943. list<ECSession*> vSessions;
  944. memset(&sStats, 0, sizeof(sSessionManagerStats));
  945. // Get session data
  946. KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  947. sStats.session.ulItems = m_mapSessions.size();
  948. sStats.session.ullSize = MEMORY_USAGE_MAP(sStats.session.ulItems, SESSIONMAP);
  949. l_cache.unlock();
  950. // Get group data
  951. KC::shared_lock<KC::shared_mutex> l_group(m_hGroupLock);
  952. sStats.group.ulItems = m_mapSessionGroups.size();
  953. sStats.group.ullSize = MEMORY_USAGE_HASHMAP(sStats.group.ulItems, EC_SESSIONGROUPMAP);
  954. for (const auto &psg : m_mapSessionGroups)
  955. sStats.group.ullSize += psg.second->GetObjectSize();
  956. l_group.unlock();
  957. // persistent connections/sessions
  958. ulock_normal l_per(m_mutexPersistent);
  959. sStats.ulPersistentByConnection = m_mapPersistentByConnection.size();
  960. sStats.ulPersistentByConnectionSize = MEMORY_USAGE_HASHMAP(sStats.ulPersistentByConnection, PERSISTENTBYCONNECTION);
  961. sStats.ulPersistentBySession = m_mapPersistentBySession.size();
  962. sStats.ulPersistentBySessionSize = MEMORY_USAGE_HASHMAP(sStats.ulPersistentBySession, PERSISTENTBYSESSION);
  963. l_per.unlock();
  964. // Table subscriptions
  965. ulock_normal l_tblsub(m_mutexTableSubscriptions);
  966. sStats.ulTableSubscriptions = m_mapTableSubscriptions.size();
  967. sStats.ulTableSubscriptionSize = MEMORY_USAGE_MULTIMAP(sStats.ulTableSubscriptions, TABLESUBSCRIPTIONMULTIMAP);
  968. l_tblsub.unlock();
  969. // Object subscriptions
  970. ulock_normal l_objsub(m_mutexObjectSubscriptions);
  971. sStats.ulObjectSubscriptions = m_mapObjectSubscriptions.size();
  972. sStats.ulObjectSubscriptionSize = MEMORY_USAGE_MULTIMAP(sStats.ulObjectSubscriptions, OBJECTSUBSCRIPTIONSMULTIMAP);
  973. l_objsub.unlock();
  974. }
  975. /**
  976. * Dump statistics
  977. */
  978. ECRESULT ECSessionManager::DumpStats()
  979. {
  980. sSessionManagerStats sSessionStats;
  981. sSearchFolderStats sSearchStats;
  982. GetStats(sSessionStats);
  983. ec_log_info("Session stats:");
  984. ec_log_info(" Sessions : %u (%llu bytes)", sSessionStats.session.ulItems, static_cast<unsigned long long>(sSessionStats.session.ullSize));
  985. ec_log_info(" Locked : %d", sSessionStats.session.ulLocked);
  986. ec_log_info(" Groups : %u (%llu bytes)" ,sSessionStats.group.ulItems, static_cast<unsigned long long>(sSessionStats.group.ullSize));
  987. ec_log_info(" PersistentByConnection : %u (%u bytes)" ,sSessionStats.ulPersistentByConnection, sSessionStats.ulPersistentByConnectionSize);
  988. ec_log_info(" PersistentBySession : %u (%u bytes)" , sSessionStats.ulPersistentBySession, sSessionStats.ulPersistentBySessionSize);
  989. ec_log_info("Subscription stats:");
  990. ec_log_info(" Table : %u (%u bytes)", sSessionStats.ulTableSubscriptions, sSessionStats.ulTableSubscriptionSize);
  991. ec_log_info(" Object: %u (%u bytes)", sSessionStats.ulObjectSubscriptions, sSessionStats.ulObjectSubscriptionSize);
  992. ec_log_info("Table stats:");
  993. ec_log_info(" Open tables: %u (%llu bytes)", sSessionStats.session.ulOpenTables, static_cast<unsigned long long>(sSessionStats.session.ulTableSize));
  994. m_lpSearchFolders->GetStats(sSearchStats);
  995. ec_log_info("SearchFolders:");
  996. ec_log_info(" Stores : %u", sSearchStats.ulStores);
  997. ec_log_info(" Folders : %u", sSearchStats.ulFolders);
  998. ec_log_info(" Queue : %u", sSearchStats.ulEvents);
  999. ec_log_info(" Mem usage : %llu Bytes", static_cast<unsigned long long>(sSearchStats.ullSize));
  1000. return this->m_lpECCacheManager->DumpStats();
  1001. }
  1002. ECRESULT ECSessionManager::GetLicensedUsers(unsigned int ulServiceType, unsigned int* lpulLicensedUsers)
  1003. {
  1004. ECRESULT er = erSuccess;
  1005. unsigned int ulLicensedUsers = 0;
  1006. ECLicenseClient *lpLicenseClient = NULL;
  1007. lpLicenseClient = new ECLicenseClient();
  1008. er = lpLicenseClient->GetInfo(ulServiceType, &ulLicensedUsers);
  1009. if(er != erSuccess) {
  1010. ulLicensedUsers = 0;
  1011. er = erSuccess;
  1012. }
  1013. delete lpLicenseClient;
  1014. *lpulLicensedUsers = ulLicensedUsers;
  1015. return er;
  1016. }
  1017. ECRESULT ECSessionManager::GetServerGUID(GUID* lpServerGuid){
  1018. if (lpServerGuid == NULL)
  1019. return KCERR_INVALID_PARAMETER;
  1020. memcpy(lpServerGuid, m_lpServerGuid, sizeof(GUID));
  1021. return erSuccess;
  1022. }
  1023. ECRESULT ECSessionManager::GetNewSourceKey(SOURCEKEY* lpSourceKey){
  1024. ECRESULT er;
  1025. if (lpSourceKey == NULL)
  1026. return KCERR_INVALID_PARAMETER;
  1027. scoped_lock l_inc(m_hSourceKeyAutoIncrementMutex);
  1028. if (m_ulSourceKeyQueue == 0) {
  1029. er = SaveSourceKeyAutoIncrement(m_ullSourceKeyAutoIncrement + 50);
  1030. if (er != erSuccess)
  1031. return er;
  1032. m_ulSourceKeyQueue = 50;
  1033. }
  1034. *lpSourceKey = SOURCEKEY(*m_lpServerGuid, m_ullSourceKeyAutoIncrement + 1);
  1035. ++m_ullSourceKeyAutoIncrement;
  1036. --m_ulSourceKeyQueue;
  1037. return erSuccess;
  1038. }
  1039. ECRESULT ECSessionManager::SaveSourceKeyAutoIncrement(unsigned long long ullNewSourceKeyAutoIncrement){
  1040. ECRESULT er;
  1041. std::string strQuery;
  1042. er = CreateDatabaseConnection();
  1043. if(er != erSuccess)
  1044. return er;
  1045. strQuery = "UPDATE `settings` SET `value` = "+ m_lpDatabase->EscapeBinary((unsigned char*)&ullNewSourceKeyAutoIncrement, 8) + " WHERE `name` = 'source_key_auto_increment'";
  1046. return m_lpDatabase->DoUpdate(strQuery);
  1047. // @TODO if this failed we want to retry this
  1048. }
  1049. ECRESULT ECSessionManager::SetSessionPersistentConnection(ECSESSIONID sessionID, unsigned int ulPersistentConnectionId)
  1050. {
  1051. scoped_lock lock(m_mutexPersistent);
  1052. // maintain a bi-map of connection <-> session here
  1053. m_mapPersistentByConnection[ulPersistentConnectionId] = sessionID;
  1054. m_mapPersistentBySession[sessionID] = ulPersistentConnectionId;
  1055. return erSuccess;
  1056. }
  1057. ECRESULT ECSessionManager::RemoveSessionPersistentConnection(unsigned int ulPersistentConnectionId)
  1058. {
  1059. ECRESULT er = erSuccess;
  1060. PERSISTENTBYSESSION::const_iterator iterSession;
  1061. scoped_lock lock(m_mutexPersistent);
  1062. auto iterConnection = m_mapPersistentByConnection.find(ulPersistentConnectionId);
  1063. if (iterConnection == m_mapPersistentByConnection.cend())
  1064. // shouldn't really happen
  1065. return KCERR_NOT_FOUND;
  1066. iterSession = m_mapPersistentBySession.find(iterConnection->second);
  1067. if (iterSession == m_mapPersistentBySession.cend())
  1068. // really really shouldn't happen
  1069. return KCERR_NOT_FOUND;
  1070. m_mapPersistentBySession.erase(iterSession);
  1071. m_mapPersistentByConnection.erase(iterConnection);
  1072. return er;
  1073. }
  1074. BOOL ECSessionManager::IsSessionPersistent(ECSESSIONID sessionID)
  1075. {
  1076. scoped_lock lock(m_mutexPersistent);
  1077. auto iterSession = m_mapPersistentBySession.find(sessionID);
  1078. return iterSession != m_mapPersistentBySession.cend();
  1079. }
  1080. // @todo make this function with a map of seq ids
  1081. ECRESULT ECSessionManager::GetNewSequence(SEQUENCE seq, unsigned long long *lpllSeqId)
  1082. {
  1083. ECRESULT er;
  1084. std::string strSeqName;
  1085. er = CreateDatabaseConnection();
  1086. if(er != erSuccess)
  1087. return er;
  1088. if (seq == SEQ_IMAP)
  1089. strSeqName = "imapseq";
  1090. else
  1091. return KCERR_INVALID_PARAMETER;
  1092. scoped_lock lock(m_hSeqMutex);
  1093. if (m_ulSeqIMAPQueue == 0)
  1094. {
  1095. er = m_lpDatabase->DoSequence(strSeqName, 50, &m_ulSeqIMAP);
  1096. if (er != erSuccess)
  1097. return er;
  1098. m_ulSeqIMAPQueue = 50;
  1099. }
  1100. --m_ulSeqIMAPQueue;
  1101. *lpllSeqId = m_ulSeqIMAP++;
  1102. return erSuccess;
  1103. }
  1104. ECRESULT ECSessionManager::CreateDatabaseConnection()
  1105. {
  1106. ECRESULT er;
  1107. std::string strError;
  1108. if(m_lpDatabase == NULL) {
  1109. er = m_lpDatabaseFactory->CreateDatabaseObject(&m_lpDatabase, strError);
  1110. if(er != erSuccess) {
  1111. ec_log_crit("Unable to open connection to database: %s", strError.c_str());
  1112. return er;
  1113. }
  1114. }
  1115. return erSuccess;
  1116. }
  1117. ECRESULT ECSessionManager::SubscribeTableEvents(TABLE_ENTRY::TABLE_TYPE ulType, unsigned int ulTableRootObjectId, unsigned int ulObjectType, unsigned int ulObjectFlags, ECSESSIONID sessionID)
  1118. {
  1119. ECRESULT er = erSuccess;
  1120. TABLESUBSCRIPTION sSubscription;
  1121. scoped_lock lock(m_mutexTableSubscriptions);
  1122. sSubscription.ulType = ulType;
  1123. sSubscription.ulRootObjectId = ulTableRootObjectId;
  1124. sSubscription.ulObjectType = ulObjectType;
  1125. sSubscription.ulObjectFlags = ulObjectFlags;
  1126. m_mapTableSubscriptions.insert(std::pair<TABLESUBSCRIPTION, ECSESSIONID>(sSubscription, sessionID));
  1127. return er;
  1128. }
  1129. ECRESULT ECSessionManager::UnsubscribeTableEvents(TABLE_ENTRY::TABLE_TYPE ulType, unsigned int ulTableRootObjectId, unsigned int ulObjectType, unsigned int ulObjectFlags, ECSESSIONID sessionID)
  1130. {
  1131. ECRESULT er = erSuccess;
  1132. TABLESUBSCRIPTION sSubscription;
  1133. scoped_lock lock(m_mutexTableSubscriptions);
  1134. sSubscription.ulType = ulType;
  1135. sSubscription.ulRootObjectId = ulTableRootObjectId;
  1136. sSubscription.ulObjectType = ulObjectType;
  1137. sSubscription.ulObjectFlags = ulObjectFlags;
  1138. auto iter = m_mapTableSubscriptions.find(sSubscription);
  1139. while (iter != m_mapTableSubscriptions.cend() &&
  1140. iter->first == sSubscription) {
  1141. if(iter->second == sessionID)
  1142. break;
  1143. ++iter;
  1144. }
  1145. if (iter != m_mapTableSubscriptions.cend())
  1146. m_mapTableSubscriptions.erase(iter);
  1147. else
  1148. er = KCERR_NOT_FOUND;
  1149. return er;
  1150. }
  1151. // Subscribes for all object notifications in store ulStoreID for session group sessionID
  1152. ECRESULT ECSessionManager::SubscribeObjectEvents(unsigned int ulStoreId, ECSESSIONGROUPID sessionID)
  1153. {
  1154. scoped_lock lock(m_mutexObjectSubscriptions);
  1155. m_mapObjectSubscriptions.insert(std::pair<unsigned int, ECSESSIONGROUPID>(ulStoreId, sessionID));
  1156. return erSuccess;
  1157. }
  1158. ECRESULT ECSessionManager::UnsubscribeObjectEvents(unsigned int ulStoreId, ECSESSIONGROUPID sessionID)
  1159. {
  1160. ECRESULT er = erSuccess;
  1161. scoped_lock lock(m_mutexObjectSubscriptions);
  1162. auto i = m_mapObjectSubscriptions.find(ulStoreId);
  1163. while (i != m_mapObjectSubscriptions.cend() && i->first == ulStoreId &&
  1164. i->second != sessionID)
  1165. ++i;
  1166. if (i != m_mapObjectSubscriptions.cend())
  1167. m_mapObjectSubscriptions.erase(i);
  1168. return er;
  1169. }
  1170. ECRESULT ECSessionManager::DeferNotificationProcessing(ECSESSIONID ecSessionId, struct soap *soap)
  1171. {
  1172. // Let the notification manager handle this request. We don't do anything more with the notification
  1173. // request since the notification manager will handle it all
  1174. return m_lpNotificationManager->AddRequest(ecSessionId, soap);
  1175. }
  1176. // Called when a notification is ready for a session group
  1177. ECRESULT ECSessionManager::NotifyNotificationReady(ECSESSIONID ecSessionId)
  1178. {
  1179. return m_lpNotificationManager->NotifyChange(ecSessionId);
  1180. }
  1181. ECRESULT ECSessionManager::GetStoreSortLCID(ULONG ulStoreId, ULONG *lpLcid)
  1182. {
  1183. ECRESULT er = erSuccess;
  1184. ECDatabase *lpDatabase = NULL;
  1185. DB_RESULT lpDBResult;
  1186. DB_ROW lpDBRow = NULL;
  1187. std::string strQuery;
  1188. if (lpLcid == nullptr)
  1189. return KCERR_INVALID_PARAMETER;
  1190. er = GetThreadLocalDatabase(m_lpDatabaseFactory, &lpDatabase);
  1191. if(er != erSuccess)
  1192. return er;
  1193. strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid=" + stringify(ulStoreId) +
  1194. " AND tag=" + stringify(PROP_ID(PR_SORT_LOCALE_ID)) + " AND type=" + stringify(PROP_TYPE(PR_SORT_LOCALE_ID));
  1195. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  1196. if(er != erSuccess)
  1197. return er;
  1198. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  1199. if (lpDBRow == nullptr || lpDBRow[0] == nullptr)
  1200. return KCERR_NOT_FOUND;
  1201. *lpLcid = strtoul(lpDBRow[0], NULL, 10);
  1202. return erSuccess;
  1203. }
  1204. LPCSTR ECSessionManager::GetDefaultSortLocaleID()
  1205. {
  1206. return GetConfig()->GetSetting("default_sort_locale_id");
  1207. }
  1208. ULONG ECSessionManager::GetSortLCID(ULONG ulStoreId)
  1209. {
  1210. ECRESULT er = erSuccess;
  1211. ULONG ulLcid = 0;
  1212. LPCSTR lpszLocaleId = NULL;
  1213. er = GetStoreSortLCID(ulStoreId, &ulLcid);
  1214. if (er == erSuccess)
  1215. return ulLcid;
  1216. lpszLocaleId = GetDefaultSortLocaleID();
  1217. if (lpszLocaleId == NULL || *lpszLocaleId == '\0')
  1218. return 0; // Select default LCID
  1219. er = LocaleIdToLCID(lpszLocaleId, &ulLcid);
  1220. if (er != erSuccess)
  1221. return 0; // Select default LCID
  1222. return ulLcid;
  1223. }
  1224. ECLocale ECSessionManager::GetSortLocale(ULONG ulStoreId)
  1225. {
  1226. ECRESULT er;
  1227. ULONG ulLcid = 0;
  1228. LPCSTR lpszLocaleId = NULL;
  1229. er = GetStoreSortLCID(ulStoreId, &ulLcid);
  1230. if (er == erSuccess)
  1231. er = LCIDToLocaleId(ulLcid, &lpszLocaleId);
  1232. if (er != erSuccess) {
  1233. lpszLocaleId = GetDefaultSortLocaleID();
  1234. if (lpszLocaleId == NULL || *lpszLocaleId == '\0')
  1235. lpszLocaleId = ""; // Select default localeid
  1236. }
  1237. return createLocaleFromName(lpszLocaleId);
  1238. }
  1239. /**
  1240. * Remove busy state for session
  1241. *
  1242. * Finds a session and calls RemoveBusyState on it if it is found
  1243. *
  1244. * @param[in] ecSessionId Session ID of session to remove busy state from
  1245. * @param[in] thread Thread ID to remove busy state of
  1246. * @return result
  1247. */
  1248. ECRESULT ECSessionManager::RemoveBusyState(ECSESSIONID ecSessionId, pthread_t thread)
  1249. {
  1250. ECRESULT er = erSuccess;
  1251. BTSession *lpSession = NULL;
  1252. ECSession *lpECSession = NULL;
  1253. KC::shared_lock<KC::shared_mutex> l_cache(m_hCacheRWLock);
  1254. lpSession = GetSession(ecSessionId, true);
  1255. l_cache.unlock();
  1256. if(!lpSession)
  1257. goto exit;
  1258. lpECSession = dynamic_cast<ECSession *>(lpSession);
  1259. if(!lpECSession) {
  1260. assert(lpECSession != NULL);
  1261. goto exit;
  1262. }
  1263. lpECSession->RemoveBusyState(thread);
  1264. exit:
  1265. if(lpSession)
  1266. lpSession->Unlock();
  1267. return er;
  1268. }
  1269. } /* namespace */