/*
This file is part of Bolixo.
Bolixo is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bolixo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bolixo. If not, see .
*/
/*
Manage web session ID
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define INSTRUMENT_DONOTOPEN
#include "instrument.h"
static DEBUG_KEY D_PROTO ("proto","Protocol information");
enum CONNECT_TYPE { TYPE_NONE, TYPE_CONTROL, TYPE_CLIENT, TYPE_ADMIN };
struct HANDLE_INFO: public ARRAY_OBJ{
CONNECT_TYPE type;
int no;
std::string host;
REQUEST_INFO req;
HANDLE_INFO(){
no = -1;
type = TYPE_NONE;
}
};
#include "proto/bo-sessiond_control.protoh"
#include "proto/bo-sessiond_client.protoh"
#include "proto/bo-sessiond_admin.protoh"
#define session_log_session_NOTNEED
#include "proto/session_log.protoch"
#include "proto/session_log.protoh"
using namespace std;
struct VARIABLE{
string name;
string val;
VARIABLE (PARAM_STRING _name, PARAM_STRING _val){
name = _name.ptr;
val = _val.ptr;
}
};
struct SESSION_INFO{
unsigned userid;
string userid_str;
string name;
string email;
string lang;
unsigned char dateformat;
string timezone;
bool admin;
vector vars;
struct {
time_t lastvisit;
unsigned nbacc;
} stats;
SESSION_INFO(){
userid = (unsigned)-1;
stats.lastvisit = time(NULL);
stats.nbacc = 0;
admin = false;
dateformat = 0;
}
SESSION_INFO (unsigned _userid,
const string &_userid_str,
const char *_name,
const char *_email,
const char *_lang,
bool _admin,
unsigned _dateformat,
const char *_timezone){
userid = _userid;
userid_str = _userid_str;
name = _name;
email = _email;
lang = _lang;
admin = _admin;
dateformat = _dateformat;
timezone = _timezone;
stats.lastvisit = time(NULL);
stats.nbacc=1;
}
};
static const char *VAR_NOTIFIES = "notifies";
int main (int argc, char *argv[])
{
glocal int ret = -1;
glocal int noproc = 1;
glocal const char *client_secretfile = "/etc/bolixo/secrets.client";
glocal const char *admin_secretfile = "/etc/bolixo/secrets.admin";
glocal const char *bind = "0.0.0.0";
glocal const char *port = "9200";
glocal const char *control = "/var/run/bo-sessiond.sock";
glocal const char *user = "bolixo";
glocal bool daemon = false;
glocal const char *pidfile = "/var/run/bo-sessiond.pid";
glocal vector variables;
glocal.ret = (argc,argv,"tlmpsql");
setproginfo ("bo-sessiond",VERSION,"Manage web sessions");
setgrouparg ("Networking");
setarg ('b',"bindaddr","Bind to this address (TCP)",glocal.bind,false);
setarg ('p',"tcpport","Listen for command on this TCP port",glocal.port,false);
setarg ('c',"control","Unix socket for bo-sessiond",glocal.control,false);
setarg (' ',"variable","Supported web variable",glocal.variables,false);
setgrouparg ("Misc.");
setarg (' ',"admin-secrets","File holding admin secrets for communication",glocal.admin_secretfile,false);
setarg (' ',"client-secrets","File holding client secrets for communication",glocal.client_secretfile,false);
setarg (' ',"user","Run the program as this user",glocal.user,false);
setarg (' ',"daemon","Run in background",glocal.daemon,false);
setarg (' ',"pidfile","File holding the PID of the process",glocal.pidfile,false);
glocal const char *msg = msg;
("/tmp/err.log",true);
fprintf (fout,"%s\n",glocal.msg);
return 0;
if (glocal.daemon){
syslog (LOG_ERR,"%s",msg);
}else{
fprintf (stderr,"%s",msg);
}
if (glocal.daemon){
syslog (LOG_WARNING,"%s",msg);
}else{
fprintf (stderr,"%s",msg);
}
glocal set vars;
glocal map sessions;
glocal map> notifies;
glocal map admin_secrets;
glocal map client_secrets;
glocal unsigned long nbrequest_admin = 0;
glocal unsigned long nbrequest_client = 0;
glocal unsigned long nbsessions_created = 0;
glocal CONNECT_INFO con;
glocal string controlport = string_f("unix:%s",glocal.control);
string clientport = string_f ("unix:/tmp/sessiond-client-%s.sock",glocal.port);
string adminport = string_f ("unix:/tmp/sessiond-admin-%s.sock",glocal.port);
fdpass_readsecrets (glocal.admin_secretfile,glocal.admin_secrets);
fdpass_readsecrets (glocal.client_secretfile,glocal.client_secrets);
int ret = -1;
for (auto s:glocal.variables) glocal.vars.insert(s);
{
FILE *fin = fopen ("/tmp/sessions.log","r");
if (fin != NULL){
(fin);
SESSION_INFO sess(userid, userid_str, name, email, lang, admin, dateformat,"");
sess.stats.lastvisit = lastvisit;
sess.stats.nbacc = nbacc;
for (auto &v:vars) sess.vars.push_back(v);
glocal.sessions[session] = move(sess);
SESSION_INFO sess(userid, userid_str, name, email, lang, admin, dateformat,timezone);
sess.stats.lastvisit = lastvisit;
sess.stats.nbacc = nbacc;
for (auto &v:vars) sess.vars.push_back(v);
glocal.sessions[session] = move(sess);
auto &v = glocal.notifies[userid];
for (auto &n:notifies) v.push_back(n);
tlmp_error ("Invalid entry, reading sessions.log\n");
fclose (fin);
}
}
(glocal.bind,clientport,5);
HANDLE_INFO *n = new HANDLE_INFO;
info.data = n;
if (string_cmp(info.port,glocal.controlport)==0){
n->type = TYPE_CONTROL;
}else{
settcpnodelay(true);
char addr[20];
const char *fromstr = addr;
if (strncmp(info.port,"unix:",5)==0){
fromstr = info.port;
}else{
ipnum_ip2a (from,addr);
}
n->host = fromstr;
n->req.secret = fdpass_findsecret (glocal.client_secrets,fromstr);
if (n->req.secret.size() > 0){
n->type = TYPE_CLIENT;
}else{
n->req.secret = fdpass_findsecret (glocal.admin_secrets,fromstr);
if (n->req.secret.size() > 0){
n->type = TYPE_ADMIN;
}else{
tlmp_error ("Rejected connexion from IP %s\n",fromstr);
endclient = true;
}
}
}
debug_printf (D_PROTO,"receive line: %s\n",line);
HANDLE_INFO *c = (HANDLE_INFO*)info.data;
static const char *tbtype[]={"none","control request","client request","admin request"};
ERROR_PREFIX prefix ("%s: host %s secret %s:",tbtype[c->type],c->host.c_str(),c->req.secret.c_str());
if (c->type == TYPE_CONTROL){
(this,c->req,line, info.linelen,endserver, endclient, no,c,c->host.c_str());
vector tb;
tb.push_back(string_f ("Version %s",VERSION));
tb.push_back(string_f ("nbsessions=%lu",glocal.sessions.size()));
unsigned long nbanon = 0;
unsigned long nbadmin = 0;
for (auto &x:glocal.sessions){
if (x.second.userid ==(unsigned)-1) nbanon++;
if (x.second.admin) nbadmin++;
}
tb.push_back(string_f ("nbanon=%lu",nbanon));
tb.push_back(string_f ("nbadmin=%lu",nbadmin));
tb.push_back(string_f ("nbnotifies=%lu",glocal.notifies.size()));
tb.push_back(string_f ("nbsesssions_created=%lu",glocal.nbsessions_created));
tb.push_back(string_f ("nbrequest_admin=%lu",glocal.nbrequest_admin));
tb.push_back(string_f ("nbrequest_client=%lu",glocal.nbrequest_client));
instrument_status (tb);
rep_status(tb);
toggle_instrument_file(on);
("/tmp/sessions.log",false);
for (auto const &s:glocal.sessions){
session_log_sessionv1(fout,s.first,s.second.userid,s.second.userid_str
,s.second.name,s.second.email,s.second.lang,s.second.timezone,s.second.admin
,s.second.dateformat
,s.second.stats.lastvisit,s.second.stats.nbacc,s.second.vars);
}
for (auto const &s:glocal.notifies){
session_log_notify(fout,s.first,s.second);
}
return 0;
endserver = true;
unsigned pos = 0;
unsigned end = offset+nb-1;
vector tb;
tb.push_back(string_f("listsessions %u -> %u",offset,end));
for (auto &it:glocal.sessions){
if (pos >end){
break;
}else if (pos >= offset){
DATEASC datetime;
fdpass_asctime (it.second.stats.lastvisit,datetime);
string tmp;
if (strncasecmp(it.second.userid_str.c_str(),"http://",7)==0
|| strncasecmp(it.second.userid_str.c_str(),"https://",8)==0){
const char *status = "unknown";
if (it.second.userid == (unsigned)-1){
status = "logged";
}else if (it.second.userid == (unsigned)-2){
status = "login";
}
tmp = string_f ("%07u: %s %s-%s %s %s %s %u %u %d %s",pos,it.first.c_str()
,status,it.second.userid_str.c_str(),it.second.name.c_str(),it.second.email.c_str()
,datetime.buf,it.second.stats.nbacc
,it.second.userid,it.second.admin,it.second.lang.c_str());
}else if (it.second.userid==(unsigned)-1){
tmp = string_f ("%07u: %s anonymous %s %u",pos,it.first.c_str()
,datetime.buf,it.second.stats.nbacc);
}else{
tmp = string_f ("%07u: %s %d-%s %s %s %s %u %u %d %s %u %s",pos,it.first.c_str()
,it.second.userid,it.second.userid_str.c_str(),it.second.name.c_str(),it.second.email.c_str()
,datetime.buf,it.second.stats.nbacc
,it.second.userid,it.second.admin,it.second.lang.c_str()
,it.second.dateformat,it.second.timezone.c_str());
}
tb.push_back(move(tmp));
for (auto &v:it.second.vars){
tmp = string_f("\t%s:[",v.name.c_str());
bool geometry = v.name == "geometry";
for (auto &vv:v.vals){
if (geometry && vv.sname == "id") tmp += "\n\t";
tmp += string_f (" %s=%s",vv.sname.c_str(),vv.sval.c_str());
}
tmp += "]";
tb.push_back(move(tmp));
}
auto note = glocal.notifies.find(it.second.userid);
if (note != glocal.notifies.end()){
tmp = string_f("\t%s:",VAR_NOTIFIES);
for (auto &n:note->second){
tmp += ' ' + n.key;
}
tb.push_back(move(tmp));
}
}
pos++;
}
rep_listsessions(tb);
glocal.notifies.clear();
time_t t = time(NULL);
for (unsigned i=0; i
unsigned deletedanon = 0;
unsigned deleteduser = 0;
unsigned deletedadmin = 0;
time_t oldtime = time(NULL)-nbseconds;
for (auto it=glocal.sessions.begin(); it!= glocal.sessions.end(); ){
auto next = it;
next++;
if (it->second.stats.lastvisit < oldtime){
if (it->second.userid == (unsigned)-1){
if (anonymous){
next = glocal.sessions.erase(it);
deletedanon++;
}
}else if (it->second.admin){
if (adminuser){
next = glocal.sessions.erase(it);
deleteduser++;
}
}else{
if (normaluser){
next = glocal.sessions.erase(it);
deletedadmin++;
}
}
}
it = next;
}
rep_eraseold(deletedanon,deleteduser,deletedadmin);
if (on){
debug_seton();
}else{
debug_setoff();
}
debug_setfdebug (filename);
tlmp_error ("Control: Invalid command: %s\n",line);
endclient = true;
}else if (c->type == TYPE_CLIENT){
glocal.nbrequest_client++;
(this,c->req,line, info.linelen,endserver, endclient, no,c,c->host.c_str());
auto it = glocal.sessions.find(sessionid);
if (it != glocal.sessions.end()){
it->second.stats.lastvisit = time(NULL);
it->second.stats.nbacc++;
rep_getsessioninfo(true,it->second.name,it->second.lang,it->second.dateformat
,it->second.admin,it->second.userid,it->second.timezone);
}else{
rep_getsessioninfo(false,"","",0,false,0,"");
}
auto it = glocal.sessions.find(sessionid);
if (it != glocal.sessions.end()){
it->second.stats.lastvisit = time(NULL);
it->second.stats.nbacc++;
vector vars = it->second.vars;
auto note = glocal.notifies.find(it->second.userid);
if (note != glocal.notifies.end()){
VAR var;
var.name = VAR_NOTIFIES;
for (auto &n:note->second){
SNAMEVAL val;
val.sname = n.key;
var.vals.push_back(val);
}
vars.push_back(var);
}
rep_getsessioninfovars(true,it->second.name,it->second.lang,it->second.dateformat,it->second.admin,it->second.userid,vars);
}else{
vector vars;
rep_getsessioninfovars(false,"","",0,false,0,vars);
}
string msg;
bool success = false;
if (glocal.vars.count(var.name)==0){
msg = "unknown variable";
}else{
auto it = glocal.sessions.find(sessionid);
if (it != glocal.sessions.end()){
bool found = false;
for (auto &v:it->second.vars){
if (v.name == var.name){
v.vals.clear();
for (auto &vv:var.vals){
SNAMEVAL vals;
vals.sname = vv.sname;
vals.sval = vv.sval;
v.vals.push_back(vals);
}
found = true;
success = true;
msg = "Variable updated";
break;
}
}
if (!found){
it->second.vars.push_back(var);
msg = "Variable added";
success = true;
}
}else{
msg = "invalid session";
}
}
rep_setvar (success,msg);
bool success = false;
string msg;
auto it = glocal.sessions.find(sessionid);
if (it == glocal.sessions.end()){
msg = "Invalid sessionid";
}else{
auto note = glocal.notifies.find(it->second.userid);
if (note != glocal.notifies.end()){
for (auto vit=note->second.begin(); vit != note->second.end(); vit++){
if (vit->key == name){
note->second.erase(vit);
break;
}
}
if (note->second.size()==0) glocal.notifies.erase(note);
}
success = true;
}
rep_delnotify(success,msg);
rep_test (true);
tlmp_error ("Client: Invalid command: %s\n",line);
endclient = true;
}else if (c->type == TYPE_ADMIN){
glocal.nbrequest_admin++;
(this,c->req,line, info.linelen,endserver, endclient, no,c,c->host.c_str());
auto it = glocal.sessions.find(sessionid);
if (it != glocal.sessions.end()){
it->second.stats.lastvisit = time(NULL);
it->second.stats.nbacc++;
rep_getsessioninfo(true,it->second.name.c_str(),it->second.email.c_str(),it->second.lang,it->second.admin,it->second.userid);
}else{
rep_getsessioninfo(false,"","",0,false,0);
}
auto it = glocal.sessions.find(sessionid);
if (it != glocal.sessions.end()){
rep_getsession(true,it->second.userid,it->second.userid_str.c_str(),it->second.admin,it->second.lang);
}else{
rep_getsession(false,0,"",false,"");
}
auto it = glocal.sessions.find(sessionid);
if (it == glocal.sessions.end()){
tlmp_error ("setsession: %s does not exist\n",sessionid);
}else{
it->second = SESSION_INFO(userid,userid_str,name,email,lang,admin,dateformat,timezone);
}
auto it = glocal.sessions.find(sessionid);
if (it == glocal.sessions.end()){
tlmp_error ("setsession: %s does not exist\n",sessionid);
}else{
it->second.lang = lang;
it->second.dateformat = dateformat;
// Update all sessions for this user
for (auto &s:glocal.sessions){
if (s.second.userid==it->second.userid){
s.second.lang = lang;
s.second.dateformat = dateformat;
s.second.timezone = timezone;
}
}
}
// Assigned a notification to a list of userids
time_t now = time(NULL);
for (auto userid:userids){
auto &v = glocal.notifies[userid];
bool found = false;
for (auto &n:v){
if (n.key == name){
found = true;
break;
}
}
if (!found){
NOTIFY n;
n.key = name;
n.when = now;
v.push_back(n);
}
}
auto it = glocal.sessions.find(sessionid);
if (it != glocal.sessions.end()){
glocal.sessions.erase(it);
}else{
tlmp_error ("deletesession: unknown sessionid %s\n",sessionid);
}
vector sessionids;
for (auto &it:glocal.sessions){
if (it.second.userid==userid) sessionids.push_back(it.first);
}
for (auto &it:sessionids){
auto s = glocal.sessions.find(it);
if (s != glocal.sessions.end()){
glocal.sessions.erase(s);
}
}
auto it = glocal.sessions.find(sessionid);
if (it == glocal.sessions.end()){
glocal.nbsessions_created++;
glocal.sessions[sessionid]=SESSION_INFO();
}else{
tlmp_error ("createsession: %s already exists\n",sessionid);
}
rep_test (true);
tlmp_error ("Admin: Invalid command: %s\n",line);
endclient = true;
}
bool some_errors = false;
if (fdpass_setcontrol(s,glocal.control,glocal.user)==-1){
some_errors = true;
}
if (s.listen (NULL,adminport)==-1){
some_errors = true;
}
if (!some_errors && s.is_ok()){
chmod (clientport.c_str()+5,0666);
chmod (adminport.c_str()+5,0666);
s.setrawmode(true);
if (glocal.daemon){
daemon_init(glocal.pidfile,glocal.user);
}
open_instrument_file();
s.loop();
ret = 0;
}
return ret;
return glocal.ret;
}