added: 'remember me' flag when logging

added: the session file
       sessions can still be available between starting and stopping the cmslu system


git-svn-id: svn://ttmath.org/publicrep/cmslu/trunk@529 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
Tomasz Sowa 2009-11-20 23:09:52 +00:00
parent 848afac803
commit 4827c116f0
21 changed files with 443 additions and 36 deletions

View File

@ -35,6 +35,7 @@ void Content::PostFunLogin()
{
std::string * login = request.PostVar("login");
std::string * pass = request.PostVar("password");
std::string * remem = request.PostVar("rememberme");
long user_id;
if( login && pass && db.CheckUser(*login, *pass, user_id) )
@ -47,6 +48,9 @@ void Content::PostFunLogin()
return;
}
if( remem )
request.session->remember_me = true;
data.last.UserLogin(user_id, *login, inet_addr(request.env_remote_addr), request.session->id);
log << log2 << "User " << login << " (id: " << user_id << ") logged" << logend;

View File

@ -31,6 +31,7 @@ void Content::FunLogout()
data.last.UserLogout(request.session->puser->id, request.session->id);
request.session->puser = 0;
request.session->remember_me = 0;
}
std::string path;

View File

@ -60,7 +60,7 @@ request.o: user.h rebus.h function.h thread.h compress.h
request.o: acceptencodingparser.h acceptbaseparser.h getparser.h
request.o: httpsimpleparser.h postparser.h cookieparser.h data.h dirs.h
request.o: dircontainer.h users.h ugcontainer.h groups.h group.h functions.h
request.o: lastcontainer.h mounts.h mount.h plugin.h pluginmsg.h
request.o: lastcontainer.h mounts.h mount.h plugin.h pluginmsg.h misc.h
requestcontroller.o: requestcontroller.h ../content/content.h ../core/item.h
requestcontroller.o: ../templates/templates.h ../templates/patterncacher.h
requestcontroller.o: ../core/thread.h sessionmanager.h sessioncontainer.h
@ -82,6 +82,10 @@ sessionmanager.o: error.h log.h user.h rebus.h request.h requesttypes.h
sessionmanager.o: function.h thread.h compress.h acceptencodingparser.h
sessionmanager.o: acceptbaseparser.h data.h dirs.h dircontainer.h users.h
sessionmanager.o: ugcontainer.h groups.h group.h functions.h lastcontainer.h
sessionmanager.o: mounts.h mount.h
sessionmanager.o: mounts.h mount.h sessionparser.h
sessionparser.o: sessionparser.h session.h done.h item.h error.h log.h user.h
sessionparser.o: rebus.h sessioncontainer.h data.h dirs.h dircontainer.h
sessionparser.o: users.h ugcontainer.h groups.h group.h functions.h
sessionparser.o: function.h lastcontainer.h mounts.h mount.h
users.o: users.h user.h ugcontainer.h log.h db.h item.h group.h thread.h
users.o: error.h dircontainer.h

View File

@ -1 +1 @@
o = acceptbaseparser.o compress.o config.o data.o db.o db_itemcolumns.o dircontainer.o dirs.o done.o error.o function.o functioncodeparser.o functionparser.o functions.o groups.o httpsimpleparser.o lastcontainer.o log.o misc.o mount.o mountparser.o mounts.o notify.o plugin.o rebus.o request.o requestcontroller.o session.o sessioncontainer.o sessionmanager.o users.o
o = acceptbaseparser.o compress.o config.o data.o db.o db_itemcolumns.o dircontainer.o dirs.o done.o error.o function.o functioncodeparser.o functionparser.o functions.o groups.o httpsimpleparser.o lastcontainer.o log.o misc.o mount.o mountparser.o mounts.o notify.o plugin.o rebus.o request.o requestcontroller.o session.o sessioncontainer.o sessionmanager.o sessionparser.o users.o

View File

@ -118,7 +118,10 @@ void Config::AssignValues()
data.priv_no_user = Text("priv_no_user");
data.priv_no_group = Text("priv_no_group");
data.session_max_iddle = Int("session_max_iddle");
data.session_max_iddle = Int("session_max_iddle");
data.session_remember_max_iddle = Int("session_remember_max_iddle");
data.session_file = Text("session_file");
data.compression = Bool("compression");
std::string p = Text("plugin");

View File

@ -82,9 +82,17 @@ public:
std::string priv_no_user;
std::string priv_no_group;
// time in second when the user will be automatically logged out (iddle time)
// time in seconds when the user will be automatically logged out (iddle time)
int session_max_iddle;
// time in seconds when the user will be automatically logged out (when he selected 'remember me' option)
// this time is usually greater than session_max_iddle
int session_remember_max_iddle;
// this file is used when the program is starting and ending
std::string session_file;
// allow the html ouput to be compressed
bool compression;
// plugins

View File

@ -261,15 +261,15 @@ static char buffer[100];
--month;
if( month < 0 )
month = 0;
month = 0;
if( month > 11 )
month = 11;
if( year == 0 )
sprintf(buffer, "%s.%02d %02d:%02d:%02d", month_letter[month], day, hour, min, sec);
sprintf(buffer, "%s %02d %02d:%02d:%02d", month_letter[month], day, hour, min, sec);
else
sprintf(buffer, "%02d.%s.%02d %02d:%02d:%02d", year, month_letter[month], day, hour, min, sec);
sprintf(buffer, "%02d %s %02d %02d:%02d:%02d", year, month_letter[month], day, hour, min, sec);
// warning: not thread safe (we do not use threads)
return buffer;
@ -290,6 +290,40 @@ return DateToStr(ptm);
}
// this format is used with cookies
const char * DateToStrCookie(int year, int month, int day, int hour, int min, int sec)
{
static const char * month_str[]={ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static char buffer[100];
--month;
if( month < 0 )
month = 0;
if( month > 11 )
month = 11;
sprintf(buffer, "%02d-%s-%04d %02d:%02d:%02d", day, month_str[month], year, hour, min, sec);
return buffer;
}
const char * DateToStrCookie(tm * ptm)
{
return DateToStrCookie(ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
}
const char * DateToStrCookie(time_t t)
{
tm * ptm = std::localtime(&t);
return DateToStrCookie(ptm);
}
const char * IpToStr(unsigned int ip_)
{
static char buffer[100];

View File

@ -37,6 +37,11 @@ std::string HtmlEscapeFormTxt(const std::string & in);
const char * DateToStr(int year, int month, int day, int hour, int min, int sec);
const char * DateToStr(tm * ptm);
const char * DateToStr(time_t t);
const char * DateToStrCookie(int year, int month, int day, int hour, int min, int sec);
const char * DateToStrCookie(tm * ptm);
const char * DateToStrCookie(time_t t);
const char * IpToStr(unsigned int ip_);
bool IsWhite(int s);

View File

@ -6,7 +6,8 @@
* All rights reserved.
*
*/
#include <ctime>
#include "request.h"
#include "getparser.h"
#include "postparser.h"
@ -14,7 +15,7 @@
#include "log.h"
#include "data.h"
#include "plugin.h"
#include "misc.h"
@ -84,29 +85,35 @@ void Request::Clear()
// value can be null
void Request::SetCookie(const char * name, const char * value)
void Request::SetCookie(const char * name, const char * value, tm * expires)
{
request.headers << "Set-Cookie: " << name << "=";
headers << "Set-Cookie: " << name << "=";
if( value && value[0]!=0 )
request.headers << value;
headers << value;
else
request.headers << "\"\"";
headers << "\"\"";
request.headers << "; path=/\r\n";
if( expires )
headers << "; expires=" << DateToStrCookie(expires) << " GMT";
headers << "; path=/\r\n";
}
void Request::SetCookie(const char * name, long value)
void Request::SetCookie(const char * name, long value, tm * expires)
{
request.headers << "Set-Cookie: " << name << "=" << value << "; path=/\r\n";
headers << "Set-Cookie: " << name << "=" << value;
if( expires )
headers << "; expires=" << DateToStrCookie(expires) << " GMT";
headers << "; path=/\r\n";
}
bool Request::IsPostVar(const char * var)
{
PostTable::iterator p;
@ -298,8 +305,36 @@ void Request::Read()
void Request::SendSessionCookie()
{
if( !session || session->id==0 )
return;
if( !session->puser || !session->remember_me )
{
SetCookie(data.http_session_id_name.c_str(), session->id);
return;
}
time_t t = time(0) + data.session_remember_max_iddle;
tm * expires = localtime(&t);
if( !expires )
{
// oops, something wrong
SetCookie(data.http_session_id_name.c_str(), session->id);
return;
}
SetCookie(data.http_session_id_name.c_str(), session->id, expires);
}
void Request::SendAll()
{
SendSessionCookie();
if( !redirect_to.empty() )
{
FCGX_PutS("Status: 301 Moved Permanently\r\n", out);

View File

@ -112,8 +112,8 @@ struct Request
bool IsParam(const char * s);
void SetCookie(const char * name, const char * value);
void SetCookie(const char * name, long value);
void SetCookie(const char * name, const char * value, tm * expires = 0);
void SetCookie(const char * name, long value, tm * expires = 0);
bool IsPostVar(const char * var);
std::string * PostVar(const char * var); // it can return null when there is no such a post variable
@ -147,6 +147,7 @@ struct Request
private:
void SendSessionCookie();
void CheckIE();
// used to set some env_* variables into it, when the server didn't set that variable

View File

@ -19,6 +19,7 @@
RequestController::RequestController()
{
last_sessions_save = time(0);
}
@ -121,6 +122,29 @@ return true;
}
void RequestController::LoadSessions()
{
session_manager.LoadSessions();
}
void RequestController::SaveSessions()
{
session_manager.SaveSessions();
}
void RequestController::SaveSessionsIfNeeded()
{
time_t t = time(0);
if( last_sessions_save + 86400 > t )
return;
// saving once a day for safety
last_sessions_save = t;
SaveSessions();
}
bool RequestController::BaseUrlRedirect()
{
@ -182,6 +206,7 @@ void RequestController::Loop()
log << log1 << "uncaught exception" << logend;
}
SaveSessionsIfNeeded();
// !! this should be immediately after FCGX_Accept() but signals don't want to break FCGX_Accept
if( data.signal_hup )

View File

@ -17,6 +17,7 @@
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <ctime>
#include "../content/content.h"
#include "sessionmanager.h"
@ -42,10 +43,14 @@ public:
void Close();
void Loop();
void LoadSessions();
void SaveSessions();
void SaveSessionsIfNeeded(); // saving only once a day
SessionContainer::Iterator SessionBegin();
SessionContainer::Iterator SessionEnd();
time_t last_sessions_save;
};

View File

@ -33,6 +33,8 @@ void Session::Clear()
item.Clear();
done_timer = 0;
rebus_item = 0;
remember_me = false;
new_session = true;
dir_old.clear();
}

View File

@ -23,6 +23,9 @@ struct Session
// 0 - means that there is no session
long id;
// true if the session was created now
bool new_session;
// when this session was created
// (the same values)
time_t time;
@ -37,6 +40,9 @@ struct Session
// 0 - means that nobody is logged
User * puser;
// if false the session will end when the user browser is shutdown
bool remember_me;
// what is done
Done done;
Error done_status;
@ -47,7 +53,7 @@ struct Session
// used for many purposes, depending on 'done'
Item item;
// rebus - set by rebus_question(Info & i) from templates
Rebus::Item * rebus_item;
std::string dir_old;
@ -55,7 +61,7 @@ struct Session
// -------------------
Session();
virtual void Clear();
void Clear();
bool operator==(const Session & s) const;
bool operator<(const Session & s) const;
bool DecTimer(int & timer);

View File

@ -20,6 +20,12 @@ void SessionContainer::Clear()
}
SessionContainer::TableSize SessionContainer::Size()
{
return table.size();
}
SessionContainer::Iterator SessionContainer::Begin()
{
return table.begin();
@ -71,18 +77,22 @@ return i->second;
void SessionContainer::DelFirstByTimeInterval(time_t interval)
void SessionContainer::DelFirstByTimeInterval(time_t interval, bool skip_remember_flag)
{
IndexTime::iterator i = index_time.begin();
IndexTime::iterator iold;
time_t limit = std::time(0) - interval;
for( ; i != index_time.end() && i->second->last_time < limit ; )
while( i != index_time.end() && i->second->last_time < limit )
{
long id = i->second->id;
iold = i;
++i; // incrementing before deleting old one
if( skip_remember_flag && iold->second->puser && iold->second->remember_me )
// don't delete sessions which have 'remember_me' flag (and a user is logged)
continue;
if( iold->second->puser )
data.last.UserLogout(iold->second->puser->id, iold->second->id);

View File

@ -25,6 +25,7 @@ class SessionContainer
public:
typedef std::list<Session> Table;
typedef Table::iterator Iterator;
typedef Table::size_type TableSize;
typedef std::map<long, Iterator> IndexId;
typedef std::multimap<time_t, Iterator> IndexTime;
@ -41,7 +42,9 @@ private:
public:
void Clear();
TableSize Size();
Iterator Begin();
Iterator End();
@ -50,8 +53,8 @@ public:
bool PushBack(const Session & session);
Iterator FindById(long);
void DelFirstByTimeInterval(time_t interval);
void DelFirstByTimeInterval(time_t interval, bool skip_remember_flag = true);
void UpdateLastTime(Iterator iter, time_t new_time);
};

View File

@ -7,13 +7,20 @@
*
*/
#include <sys/stat.h>
#include "sessionmanager.h"
#include "request.h"
#include "log.h"
#include "data.h"
#include "session.h"
#include "sessionparser.h"
SessionManager::SessionManager()
{
session_checker = 0;
}
bool SessionManager::IsSession(long id)
{
@ -87,7 +94,8 @@ int attempts = 100;
if( added )
{
request.session = &session_table.Back();
request.SetCookie(data.http_session_id_name.c_str(), request.session->id);
request.session->new_session = true;
log << log2 << "SM: created a new session: " << request.session->id << logend;
return;
@ -119,6 +127,7 @@ void SessionManager::SetSession()
{
// that session is in the table
request.session = &(*s);
request.session->new_session = false;
session_table.UpdateLastTime(s, std::time(0));
@ -163,12 +172,63 @@ SessionContainer::Iterator SessionManager::SessionEnd()
void SessionManager::DeleteOldSessions()
{
session_table.DelFirstByTimeInterval(data.session_max_iddle);
if( ++session_checker > 1000 )
{
// we make the test after 1000 requests
log << log3 << "SM: checking sessions which have 'remember me' flag set" << logend;
session_checker = 0;
session_table.DelFirstByTimeInterval(data.session_remember_max_iddle, false);
}
}
void SessionManager::LoadSessions()
{
SessionParser sp;
sp.Parse(data.session_file, session_table);
}
void SessionManager::SaveSessions()
{
if( data.session_file.empty() )
return;
std::ofstream file(data.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_table.Begin();
for( ; i!=session_table.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(data.session_file.c_str(), 0600);
log << log2 << "SM: saved " << len << " session(s)" << logend;
}

View File

@ -29,12 +29,17 @@ class SessionManager
void CreateTemporarySession();
void CreateSession();
int session_checker;
public:
SessionManager();
void SetSession();
void DeleteOldSessions();
void LoadSessions();
void SaveSessions();
SessionContainer::Iterator SessionBegin();
SessionContainer::Iterator SessionEnd();
};

148
core/sessionparser.cpp Executable file
View File

@ -0,0 +1,148 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008-2009, Tomasz Sowa
* All rights reserved.
*
*/
#include "sessionparser.h"
#include "log.h"
#include "data.h"
bool SessionParser::Parse(const std::string & path, SessionContainer & container)
{
return Parse(path.c_str(), container);
}
bool SessionParser::Parse(const char * path, SessionContainer & container)
{
container.Clear();
file.open(path, std::ios_base::in | std::ios_base::binary);
if( !file )
{
log << log1 << "SP: cannot open the session file for reading (file: '" << path << "')" << logend;
return false;
}
bool res = Parse(container);
file.close();
return res;
}
bool SessionParser::Parse(SessionContainer & container)
{
/*
file format, each rows:
session_id(long) user_id(long) remember_me(0|1) time(long) last_time(long)
*/
long user_id;
session.Clear();
last = file.get();
log << log2 << "SP: reading sessions from the session file" << logend;
while( true )
{
session.id = ReadLong();
if( file.eof() )
break;
user_id = ReadLong();
session.remember_me = ReadLong();
session.time = ReadLong();
session.last_time = ReadLong();
if( MakeSession(user_id) )
{
container.PushBack(session);
log << log2 << "SP: read session id: " << session.id << " for user: " << session.puser->name << logend;
}
SkipLine();
}
log << log2 << "SP: read " << container.Size() << " session(s)" << logend;
return true;
}
bool SessionParser::MakeSession(long user_id)
{
User * puser = data.users.GetUser(user_id);
if( !puser )
{
log << log1 << "SP: there is no a user with id: " << user_id << " (skipped)" << logend;
return false;
}
session.puser = puser;
session.new_session = true;
session.tm_time = *std::localtime(&session.time);
session.tm_last_time = *std::localtime(&session.last_time);
return true;
}
bool SessionParser::IsWhite(int c)
{
if( c==' ' || c=='\t' || c==13 )
return true;
return false;
}
bool SessionParser::IsDigit(int c)
{
if( c>='0' && c<='9' )
return true;
return false;
}
void SessionParser::SkipWhite()
{
while( IsWhite(last) )
last = file.get();
}
void SessionParser::SkipLine()
{
while( last != 10 )
last = file.get();
last = file.get(); // first character from the new line
}
long SessionParser::ReadLong()
{
long res = 0;
SkipWhite();
while( IsDigit(last) )
{
res = res*10 + (last-'0');
last = file.get();
}
return res;
}

46
core/sessionparser.h Executable file
View File

@ -0,0 +1,46 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008-2009, Tomasz Sowa
* All rights reserved.
*
*/
#ifndef headerfilecmslucoresessionparser
#define headerfilecmslucoresessionparser
#include <fstream>
#include <string>
#include "session.h"
#include "sessioncontainer.h"
class SessionParser
{
public:
bool Parse(const char * path, SessionContainer & container);
bool Parse(const std::string & path, SessionContainer & container);
private:
bool Parse(SessionContainer & container);
bool MakeSession(long user_id);
bool IsWhite(int c);
bool IsDigit(int c);
void SkipWhite();
void SkipLine();
long ReadLong();
std::ifstream file;
int last; // last character
Session session;
};
#endif

View File

@ -37,7 +37,9 @@ Plugin plugin;
void signal_term(int)
{
req_controller.SaveSessions();
log << log1 << "cmslu stopped" << logend;
exit(0);
}
@ -108,14 +110,14 @@ int main(int argv, char ** argc)
signal(SIGINT, signal_term);
signal(SIGHUP, signal_hup);
req_controller.LoadSessions();
log << log1 << "cmslu started" << logend;
while( true )
{
//log << log2 << "checking for table consistency:" << logend;
// !! zrobic wyjatek dla root
//db.CheckAllUrlSubject();
log << log1 << "cmslu started" << logend;
req_controller.Loop();