/* * This file is a part of Winix * and is not publicly distributed * * Copyright (c) 2008-2012, Tomasz Sowa * All rights reserved. * */ #include #include "sessionmanager.h" #include "request.h" #include "log.h" #include "session.h" #include "sessionparser.h" #include "plugin.h" SessionManager::SessionManager() { temporary_session.id = 0; session = &temporary_session; session_tab.SetTmpSession(&temporary_session); // thread work mode work_mode = 1; } void SessionManager::SetCur(Cur * pcur) { cur = pcur; session_tab.SetCur(pcur); } void SessionManager::SetConfig(Config * pconfig) { config = pconfig; session_tab.SetConfig(pconfig); } void SessionManager::SetSystem(System * psystem) { system = psystem; } void SessionManager::SetLastContainer(LastContainer * plast_container) { last_container = plast_container; } size_t SessionManager::Size() { return session_tab.Size(); } bool SessionManager::IsSession(long id) { if( session_tab.FindById(id) == session_tab.End() ) return false; return true; } long SessionManager::CreateSessionId() { long id; // make sure to call std::srand() somewhere at the beginning // id must be != 0 (0 is reserved) do { if( sizeof(long) == 8 ) { id = ((unsigned long)std::rand()) << 32 + std::rand(); } else { id = std::rand(); } id += std::time(0); if( id < 0 ) id = -id; } while( id == 0 ); // 0 reserved for a temporary session return id; } void SessionManager::CreateSession() { int attempts = 100; SessionContainer::Iterator i = session_tab.End(); if( config->session_max == 0 || session_tab.Size() < config->session_max ) { for( ; i == session_tab.End() && attempts > 0 ; --attempts ) { long id = CreateSessionId(); i = session_tab.AddSession(id); } } else { log << log2 << "SM: sessions limit exceeded (" << config->session_max << ")" << logend; } if( i != session_tab.End() ) { session = &(*i); session->new_session = true; session->SetTimesTo(cur->request->start_time); log << log2 << "SM: created a new session: " << session->id << logend; } else { // there is a problem with generating a new session id // we do not set a session cookie session = &temporary_session; session->Clear(false); session->SetTimesTo(cur->request->start_time); session->new_session = false; // temporary session was initialized at the beginning log << log1 << "SM: cannot create a session id (temporary used: with id 0)" << logend; } } bool SessionManager::SetSessionFromCookie(const std::string & cookie) { long id = Tol(cookie.c_str()); SessionContainer::Iterator s = session_tab.FindById(id); if( s == session_tab.End() || s->remove_me ) return false; // that session is in the table session = &(*s); session->new_session = false; session->last_time = cur->request->start_time; session->last_date = cur->request->start_time; if( cur->request->method == Request::get ) session->last_time_get = cur->request->start_time; log << log2 << "SM: session: " << session->id; if( session->puser ) log << log2 << ", user: " << session->puser->name << ", id: " << session->puser->id; log << log2 << logend; return true; } void SessionManager::SetSession() { CookieTab::iterator i = cur->request->cookie_tab.find(config->http_session_id_name); if( i == cur->request->cookie_tab.end() ) { CreateSession(); } else { if( !SetSessionFromCookie(i->second) ) { // there is no such a session // deleting the old cookie cur->request->cookie_tab.erase(i); // and creating a new one CreateSession(); } } } Session * SessionManager::FindSession(long id) { SessionContainer::Iterator i = session_tab.FindById(id); if( i != session_tab.End() ) return &*i; return 0; } SessionContainer::Iterator SessionManager::SessionBegin() { return session_tab.Begin(); } SessionContainer::Iterator SessionManager::SessionEnd() { return session_tab.End(); } void SessionManager::DeleteSessions() { SessionContainer::Iterator i; for(i=session_tab.Begin() ; i!=session_tab.End() ; ++i) { if( i->puser && !i->remember_me ) { plugin.Call(&(*i), WINIX_PREPARE_USER_TO_LOGOUT, i->puser); last_container->UserLogout(i->puser->id, i->id); } } session_tab.Clear(); } /* don't change a session's id when a user is logged the session id is in last_container and the user would not be correctly removed from the container */ bool SessionManager::ChangeSessionId(long old_id) { int attempts = 100; bool changed = false; long new_id; SessionContainer::Iterator i = session_tab.FindById(old_id); if( i != session_tab.End() ) { for( ; !changed && attempts > 0 ; --attempts ) { new_id = CreateSessionId(); changed = session_tab.ChangeSessionId(i, new_id); } if( changed ) plugin.Call(&(*i), WINIX_SESSION_CHANGED_ID, old_id, new_id); else log << log1 << "SM: I cannot create a new session id (still uses old one)" << logend; } else { log << log2 << "SM: there is no a session with id: " << old_id << logend; } return changed; } void SessionManager::InitTmpSession() { Session * old_session = cur->session; log << log4 << "SM: initializing temporary session" << logend; cur->session = &temporary_session; plugin.Call(WINIX_SESSION_CREATED); cur->session = old_session; } void SessionManager::UninitTmpSession() { Session * old_session = cur->session; log << log4 << "SM: uninitializing temporary session" << logend; cur->session = &temporary_session; cur->session->plugin_data.DeleteAll(); // this will call plugin.Call(WINIX_PLUGIN_SESSION_DATA_REMOVE); cur->session->plugin_data.Resize(0); cur->session = old_session; } void SessionManager::LoadSessions() { SessionParser sp; SessionContainer::Iterator i; // sessions will be overwritten (pointers are invalidated) cur->session = &temporary_session; sp.SetUsers(&system->users); sp.Parse(config->session_file, session_tab); for(i=session_tab.Begin() ; i != session_tab.End() ; ++i) { i->plugin_data.Resize(plugin.Size()); plugin.Call(&(*i), WINIX_SESSION_CREATED); /* !! IMPROVE ME we do not add it to the last_container (we don't have IP address stored yet) */ if( i->puser ) plugin.Call(&(*i), WINIX_USER_LOGGED); } cur->session = &temporary_session; } void SessionManager::SaveSessions() { if( config->session_file.empty() ) return; std::ofstream file(config->session_file.c_str()); if( !file ) { log << log1 << "SM: cannot open the session file for writing - sessions lost" << logend; return; } log << log2 << "SM: saving sessions" << logend; long len = 0; SessionContainer::Iterator i = session_tab.Begin(); for( ; i!=session_tab.End() ; ++i ) { if( i->id != 0 && i->puser && !i->remove_me ) { file << i->id << ' ' << i->puser->id << ' ' << i->remember_me << ' '; file << (long)i->start_time << ' ' << (long)i->last_time << std::endl; ++len; } } file.close(); chmod(config->session_file.c_str(), 0600); log << log2 << "SM: saved " << len << " session(s)" << logend; } Session * SessionManager::GetTmpSession() { return &temporary_session; } Session * SessionManager::GetCurSession() { return session; } // returns how many sessions was marked to remove size_t SessionManager::MarkAllSessionsToRemove(long user_id) { size_t how_many = 0; SessionContainer::Iterator i; for(i=session_tab.Begin() ; i!=session_tab.End() ; ++i) { if( i->puser && i->puser->id == user_id ) { plugin.Call(&(*i), WINIX_PREPARE_USER_TO_LOGOUT, i->puser); last_container->UserLogout(i->puser->id, i->id); i->remove_me = true; i->puser = 0; how_many += 1; } } return how_many; } /* * * * sessions gc (second thread) * sessions are only removed here * SessionContainer::IndexId can be removed from the other thread * (when ChangeSessionId() method is called) * */ void SessionManager::Work() { bool exit = false; SessionContainer::Iterator i; deleted = 0; Lock(); i = session_tab.Begin(); Unlock(); while( !exit ) { Lock(); CheckSession(i); exit = synchro->was_stop_signal; Unlock(); } } // it's called from the other thread (with Lock and Unlock) void SessionManager::CheckSession(SessionContainer::Iterator & i) { const int deleted_max_at_once = 10; if( i == session_tab.End() ) { if( deleted > 0 ) { deleted = 0; log << logsave; } i = session_tab.Begin(); WaitForSignalSleep(10); } else { if( i->remove_me || IsSessionOutdated(*i) ) { Session * ses = &(*i); ++i; DeleteSession(ses); ++deleted; } else { ++i; } if( deleted >= deleted_max_at_once ) { log << logsave; WaitForSignalSleep(1); deleted = 0; } } } // it's called from the other thread (with Lock and Unlock) bool SessionManager::IsSessionOutdated(const Session & s) const { bool outdated; if( s.remember_me ) { outdated = s.last_time < std::time(0) - config->session_remember_max_idle; } else { outdated = s.last_time < std::time(0) - config->session_max_idle; } return outdated; } // it's called from the other thread (with Lock and Unlock) void SessionManager::DeleteSession(Session * del_session) { if( del_session->puser ) { plugin.Call(del_session, WINIX_PREPARE_USER_TO_LOGOUT, del_session->puser); last_container->UserLogout(del_session->puser->id, del_session->id); del_session->puser = 0; } long id = del_session->id; plugin.Call(del_session, WINIX_PREPARE_SESSION_TO_REMOVE); session_tab.EraseById(del_session->id); plugin.Call((Session*)0, WINIX_SESSION_REMOVED, id); } /* * * * end of sessions gc * * */