/* * This file is a part of Winix * and is not publicly distributed * * Copyright (c) 2008-2010, 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; bool added = false; new_session.SetTimeToNow(); new_session.Clear(); if( config->session_max == 0 || session_tab.Size() < config->session_max ) { for( ; !added && attempts > 0 ; --attempts ) { new_session.id = CreateSessionId(); added = session_tab.PushBack(new_session); } } else { log << log2 << "SM: sessions limit exceeded (" << config->session_max << ")" << logend; } if( added ) { session = &session_tab.Back(); session->new_session = true; 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->SetTimeToNow(); session->Clear(); // !! uwaga ten Clear wyczysci plugins data 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() ) return false; // that session is in the table session = &(*s); session->new_session = false; session->last_time = std::time(0); session->tm_last_time = Time(session->last_time); if( cur->request->method == Request::get ) session->last_time_get = session->last_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(); } } } SessionContainer::Iterator SessionManager::SessionBegin() { return session_tab.Begin(); } SessionContainer::Iterator SessionManager::SessionEnd() { return session_tab.End(); } void SessionManager::DeleteSessions() { session_tab.Clear(); } 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_SESSION_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()); cur->session = &(*i); plugin.Call(WINIX_SESSION_CREATED); } 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 ) { file << i->id << ' ' << i->puser->id << ' ' << i->remember_me << ' '; file << (long)i->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; } /* * * * sessions gc (second thread) * * */ void SessionManager::Work() { bool exit = false; SessionContainer::IndexId::iterator i; deleted = 0; Lock(); i = session_tab.IdBegin(); 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::IndexId::iterator & i) { const int deleted_max_at_once = 10; if( i == session_tab.IdEnd() ) { if( deleted > 0 ) { deleted = 0; log << logsave; } i = session_tab.IdBegin(); WaitForSignalSleep(10); } else { if( IsSessionOutdated(*i->second) ) { DeleteSession(i++); ++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(SessionContainer::IdIterator i) { Session * del_session = &(*i->second); if( del_session->puser ) last_container->UserLogout(del_session->puser->id, del_session->id); session_tab.EraseById(i); } /* * * * end of sessions gc * * */