/* 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; }