/*
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 .
*/
/*
Main server for the project. Hides database logic and controls security/access
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "filesystem.h"
#include "helper.h"
#include
#include
#include
#include
#include
#include "bolixo.h"
#include "bolixo.m"
#include
#define INSTRUMENT_DONOTOPEN
#include "instrument.h"
#include "helper.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;
std::string userid;
HANDLE_INFO(){
no = -1;
type = TYPE_NONE;
}
};
struct DIRENTRY{
std::string eventdate;
std::string modified;
VIEWED_STATUS viewed;
ENTRY_TYPE type;
FILE_TYPE file_type;
std::string owner;
std::string modifiedby;
std::string members;
std::string listmode;
std::string title;
unsigned size;
bool islarge;
DIRENTRY (const char *_eventdate, const char *_modified, ENTRY_TYPE _type, FILE_TYPE _file_type, bool _islarge, const char *_owner,
const char *_modifiedby,
const char *_members, const char *_listmode, unsigned _size, const char *_title, VIEWED_STATUS _viewed){
eventdate = _eventdate;
modified = _modified;
type = _type;
islarge = _islarge;
file_type = _file_type;
owner = _owner;
modifiedby = _modifiedby;
members = _members;
listmode = _listmode;
size = _size;
title = _title;
viewed = _viewed;
}
DIRENTRY(){
type = ENTRY_DELETED;
size = 0;
}
};
#define bod_control_status_NOTNEED
#define bod_control_quit_NOTNEED
#define bod_control_debug_NOTNEED
#define bod_control_debugfile_NOTNEED
#define bod_control_helptest_NOTNEED
#define bod_control_publishemail_NOTNEED
#define bod_control_help_connect_NOTNEED
#define bod_control_nodelogin_NOTNEED
#define bod_control_nodelogout_NOTNEED
#define bod_control_instrument_NOTNEED
#define bod_control_keepmsgs_NOTNEED
#include "proto/bod_control.protoch"
#define bolixoapi_test_NOTNEED
#define bolixoapi_registernode_NOTNEED
#include "proto/bolixod_client.protodef"
#include "proto/bolixoapi.protoch"
#define webapi_test_NOTNEED
#define webapi_login_NOTNEED
#define webapi_addfile_NOTNEED
#define webapi_addfile_bob_NOTNEED
#define webapi_delfile_NOTNEED
#define webapi_undelete_NOTNEED
#define webapi_modifyfile_NOTNEED
#define webapi_modifyfile_bob_NOTNEED
#define webapi_rename_NOTNEED
#define webapi_copy_NOTNEED
#define webapi_readfile_NOTNEED
#define webapi_mkdir_NOTNEED
#define webapi_rmdir_NOTNEED
#define webapi_listdir_NOTNEED
#define webapi_stat_NOTNEED
#define webapi_set_access_NOTNEED
#define webapi_markview_NOTNEED
#define webapi_verifysign_NOTNEED
#define webapi_list_inboxes_NOTNEED
#define webapi_list_msgs_NOTNEED
#define webapi_sendmsg_NOTNEED
#define webapi_sendmsg_project_NOTNEED
#define webapi_replymsg_NOTNEED
#define webapi_replymsg_project_NOTNEED
#define webapi_sendattach_NOTNEED
#define webapi_sendtalk_file_NOTNEED
#define webapi_list_talk_NOTNEED
#define webapi_public_listdir_NOTNEED
#define webapi_public_readfile_NOTNEED
#define webapi_public_list_talk_NOTNEED
#define webapi_config_read_NOTNEED
#define webapi_config_write_NOTNEED
#define webapi_contact_list_NOTNEED
#define webapi_list_lists_NOTNEED
#define webapi_list_groups_NOTNEED
#define webapi_list_contacts_NOTNEED
#include "proto/webapi.protoch"
#include "proto/bod_control.protoh"
#include "proto/bod_client.protoh"
#include "proto/bod_admin.protoh"
using namespace std;
#include "proto/bo-writed_client.protoch"
#define bo_sessiond_client_getsessioninfovars_NOTNEED
#define bo_sessiond_client_setvar_NOTNEED
#define bo_sessiond_client_delnotify_NOTNEED
#include "proto/bo-sessiond_client.protoch"
struct SESSION_CACHE{
time_t start;
string name;
unsigned userid;
bool is_admin;
string lang;
string timezone;
SESSION_CACHE(){
userid = 0;
is_admin = false;
start = 0;
}
SESSION_CACHE(const char *_name, unsigned _userid, bool _is_admin, const char *_lang, const char *_timezone){
start = time(NULL);
name = _name;
userid = _userid;
is_admin = _is_admin;
lang = _lang;
timezone = _timezone;
}
};
static map sessions;
/*
Erase old session (older than 10 seconds)
*/
static void bod_sessions_clean()
{
time_t t = time(NULL)-10;
for (auto it=sessions.begin(); it != sessions.end();){
if (it->second.start < t){
it = sessions.erase(it);
}else{
it++;
}
}
}
/*
Erase one session from the cache
*/
static void bod_sessions_erase(PARAM_STRING session)
{
auto it = sessions.find(session.ptr);
if (it != sessions.end()){
sessions.erase(it);
}
}
/*
Erase one session from the case and tell all other bod process to do the same
*/
static void bod_sessions_erase(PARAM_STRING session, const char *control)
{
glocal session;
glocal control;
("/var/run/blackhole");
// Avoid talking to itself
if (is_start_any_of(basename,NONEED,"bod-") && strcmp(glocal.control,path)!=0){
//tlmp_warning ("erase control=%s path=%s\n",glocal.control,path);
CONNECT_INFO con;
con.port = path;
(con,glocal.session.ptr);
}
bod_sessions_erase (session);
}
static string user_timezone="system"; // timezone of the current user
/*
Get the userid associated with the sessionid
*/
static unsigned trli_getsessionuser (CONNECT_INFO &con, const char *sessionid, bool &is_admin, string &name, string &msg)
{
unsigned userid;
auto s = sessions.find(sessionid);
if (s != sessions.end()){
userid = s->second.userid;
name = s->second.name;
is_admin = s->second.is_admin;
user_timezone = s->second.timezone;
translat_selectlang(s->second.lang.c_str());
}else{
glocal const char *sessionid = sessionid;
glocal unsigned userid = 0;
glocal bool is_admin = false;
glocal string *name = &name;
//long long start = fdpass_getnow();
//for (int i=0; i<10000; i++){
(con,sessionid);
// Node session are invalid here
if (success && userid != (unsigned)-1){
glocal.userid = userid;
glocal.is_admin = admin;
(*glocal.name) = name;
translat_selectlang(lang);
if (timezone[0] == '\0') timezone = "system";
user_timezone = timezone;
sessions[glocal.sessionid] = SESSION_CACHE(name,userid,admin,lang,timezone);
}
//}
//long long end = fdpass_getnow();
//printf ("exec time = %lf\n",(end-start)/1000000.0);
is_admin = glocal.is_admin;
userid = glocal.userid;
}
if (userid == 0) msg = MSG_U(E_IVLDSESSION,"Invalid session");
return userid;
}
static unsigned trli_getsessionuser (CONNECT_INFO &con, const char *sessionid, bool &is_admin, string &msg)
{
string username;
return trli_getsessionuser (con,sessionid,is_admin,username,msg);
}
static int bod_findentry (CONNECT_INFO &con, const char *sessionid, PARAM_STRING name, ENTRY &entry, const char *threshold)
{
int ret = -1;
entry.userid = trli_getsessionuser(con,sessionid,entry.is_admin,entry.msg);
if (entry.userid != 0){
ret = fs_findentry(name,entry,true,threshold);
}
return ret;
}
/*
Identify the userid who will perform some group administration.
Normally, user are only allowed to work on list they own.
But administrator can work on behalf of another user. The parameter "owner" is the other user.
For normal user, this parameter is ignored.
Return 0 if not a valid userid (sessionid is invalid)
*/
static unsigned bo_writed_get_group_owner (CONNECT_INFO &con, const char *sessionid, const char *owner, string &username, string &msg)
{
bool is_admin;
glocal unsigned userid = trli_getsessionuser (con,sessionid,is_admin,username,msg);
if (is_admin && owner[0] != '\0'){
("select userid from id2name where name='%s'",owner);
glocal.userid = atoi(row[0]);
glocal.userid = 0;
username = owner;
if (glocal.userid == 0) msg = MSG_R(E_UNKNOWNUSER);
}
return glocal.userid;
}
static unsigned bo_writed_get_group_owner (CONNECT_INFO &con, const char *sessionid, const char *owner, string &msg)
{
string username;
return bo_writed_get_group_owner (con,sessionid,owner,username,msg);
}
/*
Keeps only a slice of the vector
*/
template static void bod_trim (vector &tb, unsigned offset, unsigned nb)
{
if (offset >= tb.size()){
tb.clear();
}else if (offset + nb >= tb.size()){
tb.erase (tb.begin(),tb.begin()+offset);
}else{
if (offset > 0){
tb.erase (tb.begin(),tb.begin()+offset);
}
if (nb < tb.size()){
tb.erase (tb.begin()+nb,tb.end());
}
}
}
/*
Check that public access is enable for one account.
Translate the relative_path into absolute path by using the username and the public_dir
Return the user id.
Return -1 if any problem.
relative_path may be NULL. It means we do not wish to convert the path to absolute.
*/
static int bod_checkpublic(const char *username, const char *relative_path, string &msg, string &abspath, bool &pubdir)
{
glocal string *msg = &msg;
glocal string public_dir;
glocal int ret = -1;
pubdir = false;
("select public_view,public_dir,id2name.userid from id2name"
" join config on config.userid=id2name.userid"
" where id2name.name='%s'",username);
(*glocal.msg) = "no user";
bool public_view = atoi(row[0]);
if (!public_view){
(*glocal.msg) = MSG_U(E_NOPUBLICACC,"no public data available");
}else{
glocal.public_dir = row[1];
glocal.ret = atoi(row[2]);
}
pubdir = glocal.public_dir.size() > 0;
if (glocal.ret != -1 && relative_path != NULL){
if (relative_path[0] == '/') relative_path++;
if (strncmp(relative_path,"project/",8)==0){
relative_path += 7;
if (is_any_of(relative_path,"/mini-photo.jpg","/photo.jpg")
|| glocal.public_dir == "/"){
abspath = string_f("/projects/%s/public",username);
}else{
abspath = string_f("/projects/%s/public/%s",username,glocal.public_dir.c_str());
}
if (strcmp(relative_path,"/") !=0){
if (relative_path[0] == '/'){
abspath += relative_path;
}else{
abspath += string_f ("/%s",relative_path);
}
}
}else if (strncmp(relative_path,"msg/",4)==0){
relative_path += 4;
abspath = string_f("/msgs/%s/short-inbox/public/%s",username,relative_path);
}else{
msg = MSG_U(E_IVLDPUBPREFIX,"Invalid prefix for public access");
glocal.ret = -1;
}
}
return glocal.ret;
}
static int bod_checkpublic(const char *username, const char *relative_path, string &msg, string &abspath)
{
bool pubdir;
return bod_checkpublic(username,relative_path,msg,abspath,pubdir);
}
static int bod_checkpublic(const char *username, string &msg, bool &pubdir)
{
string abspath;
return bod_checkpublic (username,NULL,msg,abspath,pubdir);
}
static int bod_checkpublic(const char *username, string &msg)
{
string abspath;
bool pubdir;
return bod_checkpublic (username,NULL,msg,abspath,pubdir);
}
/*
Extract the first line of a text.
Handle long lines. Assumes that each chunk of 80 characters in a line accounts for one line.
But long lines may hold a URL. This has to be preserved.
*/
static string bod_first_lines (const char *txt)
{
const unsigned nblines = 8;
const char *pt = txt;
unsigned noline = 0;
const char *start = txt;
while (*pt != '\0' && noline < nblines){
while (*pt != '\0' && *pt != '\n') pt++;
{
unsigned add_lines = (pt-start)/80;
pt++;
noline += 1 + add_lines;
if (noline > nblines && add_lines > 0){
while (noline > nblines && add_lines > 0){
add_lines--;
noline--;
}
pt = start+(add_lines+1)*80;
// Check for URLs
const char *url = start;
while (url < pt){
const char *skip;
if (is_start_any_ofnc(url,skip,"http://","https://")){
// Find the end of the URL
if (*skip == '"') skip++;
while (*skip > ' ' && is_not_in(*skip,'"','>') && *skip > ' ') skip++;
if (skip > pt){
// The URL crossed the limit, so we extend it.
pt=skip;
break;
}else{
url = skip;
// Look for another URL.
}
}else{
url++;
}
}
break;
}
start = pt;
}
}
if (*pt == '\0'){
return txt;
}else{
return string(txt,pt-txt);
}
}
static VIEWED_STATUS bod_readviewed(const char *mark_modified, const char *modified)
{
VIEWED_STATUS ret = VIEWED_NEW;
// If there is no entry for the document or message in table marks, then it is new
if (mark_modified != NULL){
if (strcmp(mark_modified,modified)==0){
// Document has not changed since it was viewed
ret = VIEWED_OK;
}else{
ret = VIEWED_MODIFIED;
}
}
return ret;
}
static bool msg_compare (const SHORTMSG &s1, const SHORTMSG &s2)
{
return s2.eventdate < s1.eventdate;
}
static unsigned keepdays = 7;
/*
Remove new messages up to firstseen from the list.
*/
static unsigned bod_firstseen (vector &msgs, const char *firstseen)
{
unsigned nbnew = 0;
if (firstseen[0] != '\0'){
unsigned count = 0;
for (auto &m:msgs){
if (m.uuid == firstseen) break;
count++;
}
if (count > 0 && count != msgs.size()){
msgs.erase(msgs.begin(),msgs.begin()+count);
nbnew = count;
}
}
return nbnew;
}
static void bod_list_talk (
unsigned userid, // We are reading for this user, useful for read marks
vector &msgs,
unsigned ownerid,
const char *groupowner,
const char *groupname,
unsigned offset,
unsigned nb,
const char *firstseen,
bool &deletes,
unsigned &total,
unsigned &nbnew,
string &msg)
{
glocal unsigned nb = nb;
glocal map mp;
glocal bool deletes = false;
int dirid = fs_find_short_inbox (ownerid,groupowner,groupname,msg);
if (dirid != -1){
("select dirs_content.name,dirs_content.modified,id2name.name,id2name_copy.name,files.filetype"
",dirs_content.itemid,files.content,convert_tz(dirs_content.eventtime,'system','%s'),dirs_content.type,files.signature,dirs_content.eventtime"
",marks.modified"
" from dirs_content"
" join files on files.id = dirs_content.itemid and files.modified = dirs_content.modified"
" join ids on dirs_content.itemid = ids.id"
" join id2name on ids.ownerid = id2name.userid"
" left join id2name as id2name_copy on dirs_content.copiedby = id2name_copy.userid"
" left join marks on marks.itemid=dirs_content.itemid and marks.userid=%u"
" where dirid = %d and (type=%u or type=%u) order by eventtime"
,user_timezone.c_str(),userid,dirid,ENTRY_MSG,ENTRY_DELETED);
SHORTMSG msg;
msg.uuid = row[0];
const char *modified = row[1];
// tlmp_error ("eventtime=%s timezone=%s\n",row[7],user_timezone.c_str());
if (row[7] != nullptr){
// eventtime can't be null, unless the timezone is invalid.
msg.submit = string(row[7],19); // The file modification time is not useful for message
// An image paste into a message list will keep its
// modification time. So we use the eventtime.
}
msg.from = row[3] != NULL ? row[3] : row[2]; // We pick copiedby instead of the file owner if not NULL
ENTRY_TYPE type = (ENTRY_TYPE)atoi(row[8]);
if (type == ENTRY_DELETED){
msg.file_type = FILE_UNKNOWN;
glocal.deletes = true;
}else{
msg.file_type = row[4] == NULL ? FILE_UNKNOWN : (FILE_TYPE)atoi(row[4]);
}
msg.signature = row[9];
msg.eventdate = row[10];
msg.viewed = bod_readviewed(row[11],modified);
if (row[6] != NULL){
// Limit the number of lines if more than one message was requested
// Note the size is the complete size of the message, but what is sent may by few lines.
// So the application can tell this was truncated.
if (glocal.nb == 1){
msg.content = row[6];
msg.size = msg.content.size();
}else{
msg.content = bod_first_lines(row[6]);
msg.size = strlen(row[6]);
}
}else{
msg.size = fs_get_filesize(atoi(row[5]),modified);
}
glocal.mp[msg.uuid] = move(msg);
}
for (auto &m:glocal.mp){
if (m.second.file_type != FILE_UNKNOWN){
msgs.push_back(move(m.second));
}
}
sort (msgs.begin(),msgs.end(),msg_compare);
nbnew = bod_firstseen (msgs,firstseen);
total = msgs.size();
bod_trim (msgs,offset,nb);
deletes = glocal.deletes;
}
static bool nonstrict = false;
static int bod_bolixoapi_login(
CONNECT_HTTP_INFO &hcon, // Connection info to the bolixo directory
CONNECT_INFO &con, // Connection info to the writed
const char *nodename, // This system nodename as seen by the bolixo directory
string &session)
{
glocal string *session = &session;
glocal CONNECT_HTTP_INFO *hcon = &hcon;
glocal CONNECT_INFO *con = &con;
glocal int ret = -1;
(hcon,nodename);
if (!success){
tlmp_error ("nodelogin: internal_error=%d success=%d msg=%s session=%s\n",internal_error,success,msg,session);
}else{
glocal string sign;
*glocal.session = session;
(*glocal.con,session);
if (!success) tlmp_error ("nodepass: systemsign internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
glocal.sign = sign;
if (glocal.sign.size()==0){
tlmp_error ("nodepass: empty signature\n");
}else{
//tlmp_error ("session=%s sign=%s\n",session,glocal.sign.c_str());
(*glocal.hcon,session,glocal.sign);
if (!success){
tlmp_error ("nodepass: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
}else{
glocal.ret = 0;
}
}
}
return glocal.ret;
}
static void bod_loadphoto (BOB_TYPE &img, const char *user, const char *filename)
{
glocal BOB_TYPE *img = &img;
glocal ENTRY entry;
string fpath = string_f("/projects/%s/public/%s",user,filename);
if (fs_findentry(fpath,glocal.entry,true,"") != -1
&& bolixo_isfile(glocal.entry.type)){
("select content,title,signature,filetype from files where id=%d and modified='%s'"
,glocal.entry.entryid,glocal.entry.modified.c_str());
if (row[0] != NULL){
unsigned len = strlen(row[0]);
glocal.img->setbuffer(row[0],len,true);
}else{
string handle;
FILE *fin = fs_alloc_file_handle (glocal.entry.entryid,glocal.entry.modified,"r",handle,"xx");
if (fin == NULL){
tlmp_error ("bod_loadphoto: Internal error, reading content file\n");
}else{
struct stat64 st;
if (fstat64(fileno(fin),&st)==-1){
tlmp_error ("bod_loadphoto: Internal error, getting file size\n");
}else{
char buf[REQ_CONTENT_CHUNK];
int len = fread (buf,1,sizeof(buf),fin);
if (len > 0){
glocal.img->setbuffer (buf,len,true);
}
}
}
fs_delete_handle (handle);
}
}
}
/*
Publish the personnal information of one user (as requested by the user) to the bolixo.org directory
*/
static void bod_publish(
CONNECT_INFO &con,
PARAM_STRING user,
const USERPUBLICINFO &pinfo,
const char *dirserver, // URL of the directory server
const char *nodename)
{
CONNECT_HTTP_INFO hcon;
if (nonstrict) hcon.setnonstrictmode();
if (hcon.init(dirserver)==-1){
tlmp_error ("bod_publish: CONNECT_HTTP_INFO::init failed: dirserver=%s\n",dirserver);
}else{
hcon.setpageapi("bolixoapi");
string session;
if (bod_bolixoapi_login (hcon,con,nodename,session)!=-1){
if (pinfo.publish){
USERINFO info;
info.user = user.ptr;
info.fullname = pinfo.fullname;
info.address1 = pinfo.address1;
info.address2 = pinfo.address2;
info.city = pinfo.city;
info.state = pinfo.state;
info.country = pinfo.country;
info.zipcode = pinfo.zipcode;
info.email = pinfo.email;
info.phone = pinfo.phone;
info.fax = pinfo.fax;
info.bolixosite = pinfo.bosite_visible ? string_f("%s/%s/%s",nodename,MSG_U(I_PUBLIC,"public"),user.ptr) : "";
info.website = pinfo.website;
info.interest = pinfo.interest;
if (pinfo.photo) bod_loadphoto (info.photo,user.ptr,"photo.jpg");
if (pinfo.mini_photo) bod_loadphoto (info.mini_photo,user.ptr,"mini-photo.jpg");
(hcon,session,info);
if (!success) tlmp_error ("publish: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
}else{
(hcon,session,user);
if (!success) tlmp_error ("remove: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
}
(hcon,session);
if (!success) tlmp_error ("nodelogout: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
}
}
}
/*
Record the email of a user to the bolixo.org directory to simply login
*/
static void bod_record_email(
CONNECT_INFO &con,
PARAM_STRING userid,
PARAM_STRING email,
const char *dirserver, // URL of the directory server
const char *nodename)
{
CONNECT_HTTP_INFO hcon;
if (nonstrict) hcon.setnonstrictmode();
if (hcon.init(dirserver)==-1){
tlmp_error ("bod_publish: CONNECT_HTTP_INFO::init failed: dirserver=%s\n",dirserver);
}else{
hcon.setpageapi("bolixoapi");
string session;
if (bod_bolixoapi_login (hcon,con,nodename,session)!=-1){
(hcon,session,userid,email);
if (!success) tlmp_error ("recordemail: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
(hcon,session);
if (!success) tlmp_error ("nodelogout: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
}
}
}
static int bod_nodelogin(
CONNECT_HTTP_INFO &hcon, // Connection info to the bolixo server (node)
CONNECT_INFO &con, // Connection info to the writed
const char *nodename,
string &session)
{
glocal string *session = &session;
glocal CONNECT_HTTP_INFO *hcon = &hcon;
glocal CONNECT_INFO *con = &con;
glocal int ret = -1;
if (nonstrict) hcon.setnonstrictmode();
(hcon,nodename);
// tlmp_error ("login1 success=%d msg=%s session=%s\n",success,msg,session);
if (!success){
tlmp_error ("nodelogin: internal_error=%d success=%d msg=%s session=%s\n",internal_error,success,msg,session);
}else{
glocal string sign;
*glocal.session = session;
(*glocal.con,session);
glocal.sign = sign;
// tlmp_error ("session=%s sign=%s\n",session,glocal.sign.c_str());
(*glocal.hcon,session,glocal.sign);
if (!success){
tlmp_error ("nodepass: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
}else{
glocal.ret = 0;
}
}
return glocal.ret;
}
static void bod_nodelogout(CONNECT_HTTP_INFO &hcon, PARAM_STRING session)
{
if (nonstrict) hcon.setnonstrictmode();
(hcon,session);
}
static vector usehttp;
static void bod_inithcon (CONNECT_HTTP_INFO &hcon, PARAM_STRING remote)
{
hcon.host = remote.ptr;
if(find(usehttp.begin(),usehttp.end(),remote.ptr)!=usehttp.end()){
hcon.port = "80";
hcon.use_ssl = false;
}else{
if (nonstrict) hcon.setnonstrictmode();
hcon.port = "443";
hcon.use_ssl = true;
}
}
/*
Record user interest in another bolixo server
*/
static void bod_remote_interest_set(
CONNECT_INFO &con,
bool set_command,
PARAM_STRING user,
PARAM_STRING server,
const char *nodename)
{
CONNECT_HTTP_INFO hcon;
bod_inithcon (hcon,server);
string session;
(hcon,nodename);
if (bod_nodelogin (hcon,con,nodename,session)!=-1){
if (set_command){
(hcon,session,user);
if (!success) tlmp_error ("remote_interest_set: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
}else{
(hcon,session,user);
if (!success) tlmp_error ("remote_interest_set: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
}
bod_nodelogout(hcon,session);
}
}
static string bod_format_remote_user(PARAM_STRING user, PARAM_STRING our_nodename)
{
const char *nodename = our_nodename.ptr;
if (strncasecmp(nodename,"http://",7)==0){
nodename += 7;
}else if (strncasecmp(nodename,"https://",8)==0){
nodename += 8;
}
return string_f("%s@%s",user.ptr,nodename);
}
static int bod_remotelogin (
CONNECT_INFO &con, // To reach bo-writed
CONNECT_HTTP_INFO &hcon, // To reach remote server
PARAM_STRING local_user, // local user name
PARAM_STRING nodename, // Our node name
const char *local_session,
string &sessionid)
{
glocal int ret = -1;
glocal CONNECT_INFO *con = &con;
glocal string *sessionid = &sessionid;
glocal CONNECT_HTTP_INFO *hcon = &hcon;
glocal const char *local_session = local_session;
glocal string remote_user = bod_format_remote_user (local_user,nodename);
(hcon,glocal.remote_user);
if (!success){
tlmp_error ("remotelogin failed for %s: %s\n"
,glocal.remote_user.c_str(),msg);
}else{
glocal string sign;
*glocal.sessionid = sessionid;
(*glocal.con,glocal.local_session,sessionid);
if (success){
glocal.sign = sign;
}else{
tlmp_error ("remotelogin can't sign for %s: %s\n",glocal.remote_user.c_str(),msg);
}
if (glocal.sign.size() > 0){
(*glocal.hcon,sessionid,glocal.remote_user,glocal.sign);
if (success){
glocal.ret = 0;
}else{
tlmp_error ("remotepass failed for %s: %s\n",glocal.remote_user.c_str(),msg);
}
}
}
return glocal.ret;
}
static void bod_remotelogout(CONNECT_HTTP_INFO &hcon, PARAM_STRING sessionid)
{
(hcon,sessionid);
}
/*
Perform a remote contact request
*/
static void bod_remote_contact_request(
CONNECT_INFO &con,
const char *sessionid,
PARAM_STRING caller, // User who created this request
PARAM_STRING user, // User we wish to contact
PARAM_STRING intro, // Small message
PARAM_STRING server, // Server where this user lives
const char *nodename) // Our nodename
{
CONNECT_HTTP_INFO hcon;
bod_inithcon (hcon,server);
string session;
if (bod_remotelogin (con,hcon,caller,nodename,sessionid,session)!=-1){
(hcon,session,"",user,intro);
if (!success) tlmp_error ("remote_contact_request: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
bod_remotelogout(hcon,session);
}
}
/*
Perform a remote contact manage
*/
static void bod_remote_contact_manage(
CONNECT_INFO &con,
const char *sessionid,
PARAM_STRING caller, // User who created this request
PARAM_STRING user, // User we wish to contact
CONTACT_STATUS status, // Was the contact request accepted
PARAM_STRING server, // Server where this user lives
const char *nodename) // Our nodename
{
CONNECT_HTTP_INFO hcon;
bod_inithcon(hcon,server);
string session;
if (bod_remotelogin (con,hcon,caller,nodename,sessionid,session)!=-1){
(hcon,session,"",user,status);
if (!success) tlmp_error ("remote_contact_manage: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
bod_remotelogout(hcon,session);
}
}
/*
When bod performs a remote file operation and it must return a handle to the caller, it allocates this
to keep the context, like filesystem.tlcc does with local file handle.
*/
struct REMOTE_INFO{
string server;
string sessionid;
string handle;
CONNECT_HTTP_INFO con;
void logout();
~REMOTE_INFO();
void initcon();
};
void REMOTE_INFO::initcon()
{
bod_inithcon(con,server);
}
struct REMOTE_INFO_FROM: public REMOTE_INFO{
string user;
};
struct REMOTE_INFO_TO: public REMOTE_INFO{
vector users;
};
void REMOTE_INFO::logout()
{
if (sessionid.size() > 0){
bod_remotelogout(con,sessionid);
sessionid.clear();
}
}
REMOTE_INFO::~REMOTE_INFO()
{
logout();
}
struct REMOTE_HANDLE{
string username;
string handle;
REMOTE_INFO_FROM from;
vector to;
void setfrom (PARAM_STRING server){
from.server = server.ptr;
}
void addto (PARAM_STRING server, const vector &users){
REMOTE_INFO_TO r;
r.server = server.ptr;
r.users = users;
to.push_back(r);
}
};
using REMOTE_HANDLE_P = unique_ptr;
static map rem_handles;
/*
Perform a sendtalk to one remote user
*/
static void bod_remote_sendtalk(
CONNECT_INFO &con,
const char *sessionid,
REMOTE_HANDLE &rem,
const BOB_TYPE &content,
const char *nodename, // Our nodename
bool more,
PARAM_STRING name,
PARAM_STRING sign,
PARAM_STRING createdby)
{
for (auto &r:rem.to){
glocal REMOTE_INFO *r = &r;
r.initcon();
if (bod_remotelogin (con,r.con,rem.username,nodename,sessionid,r.sessionid)!=-1){
(r.con,r.sessionid,"",r.users,"","",content,more,name,sign,createdby);
glocal.r->handle = handle;
if (!success) tlmp_error ("remote_sendtalk: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
if (!more){
r.logout();
}
}
}
}
static void bod_remote_appendfile(
REMOTE_HANDLE &rem,
const BOB_TYPE &content,
bool more)
{
for (auto &r:rem.to){
glocal REMOTE_INFO_TO *r = &r;
(r.con,r.sessionid,r.handle,content,more);
if (!success) tlmp_error ("remote_appendfile failed: %s@%s %s\n"
,glocal.r->users[0].c_str(),glocal.r->server.c_str(),msg);
}
}
/*
Convert a local name to remote name if it is local.
*/
static void bod_patchname(string &name, const string &remote)
{
if (name.size() > 0 && name.find('@') == string::npos){
name += string("@") + remote;
}
}
static void bod_remote_readfile_bob (
CONNECT_INFO &con,
const char *sessionid,
REMOTE_HANDLE_P &rem,
PARAM_STRING name,
bool nomore,
const char *nodename,
BOB_TYPE &bob,
READINFO &info,
bool &more,
string &msg)
{
glocal BOB_TYPE *bob = &bob;
glocal bool *more = &more;
glocal string *msg = &msg;
glocal READINFO *info = &info;
glocal REMOTE_INFO_FROM *from = &rem->from;
glocal.from->initcon ();
if (bod_remotelogin (con,glocal.from->con,rem->username,nodename,sessionid,glocal.from->sessionid)!=-1){
(glocal.from->con,glocal.from->sessionid,name,"",nomore);
glocal.from->handle = handle;
(*glocal.msg) = msg;
if (success){
(*glocal.bob) = content;
(*glocal.more) = more;
(*glocal.info) = info;
}else{
tlmp_error ("remote_readfile_bob: internal_error=%d success=%d msg=%s\n",internal_error,success,msg);
}
if (!more){
glocal.from->logout();
}
}
}
static void bod_remote_readmore(
REMOTE_HANDLE_P &rem,
bool &success,
string &msg,
BOB_TYPE &content,
bool &more)
{
glocal bool *success = &success;
glocal string *msg = &msg;
glocal bool *more = &more;
glocal BOB_TYPE *content = &content;
(rem->from.con,rem->from.sessionid,rem->from.handle);
(*glocal.success) = success;
(*glocal.msg) = msg;
(*glocal.more) = more;
(*glocal.content) = content;
}
/*
Parse a user[@nodename]
Strip the nodename if it is this node.
If this is a local user, local_user will contain just the user part wihtout @our_nodename.
If this is a remote user
local_user will contain user
remote_user will contain the user part without the @some_server
server will contain some_server.
*/
static int bod_parse_user (
const char *user,
string &local_user,
string &remote_user,
string &server,
const char *our_nodename)
{
int ret = -1;
// Database use varchar(100)
if (strlen(user)<95){
local_user = user;
const char *pt = user;
bool bad = false;
#if 0
// What are the rules for hostname ?
while (*pt != '\0'){
// Better validation of hostname. More character are allowed
if (*pt <= ' '){
bad = true;
break;
}else if (!isalpha(*pt) && !isdigit(*pt) && *pt != '.' && *pt != '-' && *pt != '_' && *pt != '@'){
bad = true;
break;
}
pt++;
}
#endif
if (!bad){
pt = strchr(user,'@');
if (pt == NULL){
local_user = user;
ret = 0;
}else if (pt[1] != '-'){
// A hostname can't start with -
// This is user@nodename
const char *ptnode;
if (is_start_any_ofnc(our_nodename,ptnode,"http://","https://")){
our_nodename = ptnode;
}
if (strcasecmp(pt+1,our_nodename)==0){
// Local user
local_user = string(user,pt-user);
}else{
remote_user = string(user,pt-user);
server = pt+1;
}
ret = 0;
}
}
}
return ret;
}
static int bod_getpubkey (const char *nodename, string &pubkey)
{
glocal int ret = -1;
glocal const char *hostname = nodename;
glocal string *pubkey = &pubkey;
CONNECT_HTTP_INFO con;
if (con.init (nodename) != -1){
if (nonstrict) con.setnonstrictmode();
(con);
if (pubkey[0] == '\0'){
tlmp_error ("Enpty public key for host %s\n",glocal.hostname);
}else if (fs_valid_pubkey(pubkey)==-1){
tlmp_error ("Invalid public key for host %s\n",glocal.hostname);
}else{
*glocal.pubkey = pubkey;
glocal.ret = 0;
}
}
return glocal.ret;
}
// Get the name associated with a userid
static string bod_getname(unsigned userid)
{
glocal string ret;
("select name from id2name where userid=%u",userid);
glocal.ret = row[0];
return glocal.ret;
}
/*
We do some validation here, even if bo-writed does the same.
if "user" is an account on another node, we do the validation here
if the remote user does not exist in this node (remote accounts),
we tell bo-writed to create it.
Return 0 if everything is ok.
*/
static int bod_check_remote_user(
CONNECT_INFO &con,
const char *user,
const char *nodename,
string &msg,
string &local_user,
string &remote_user,
string &remote)
{
glocal CONNECT_INFO *con = &con;
glocal const char *user = user;
glocal int ret = -1;
glocal string *msg = &msg;
if (bod_parse_user(user,local_user,remote_user,remote,nodename)==-1){
msg = MSG_R(E_IVLDUSER);
}else{
glocal const char *remote_user = remote_user.c_str();
glocal const char *remote = remote.c_str();
glocal.user = local_user.c_str();
("select userid from id2name where name='%s'",glocal.user);
// User unknown
if (glocal.remote[0] != '\0'){
// Check if this user exists on the remote node
CONNECT_HTTP_INFO hcon;
bod_inithcon (hcon,glocal.remote);
(hcon,glocal.remote_user);
if (success){
if (fs_valid_pubkey(pubkey)==-1){
*glocal.msg = MSG_U(E_IVLDPUBKEY,"Invalid public key for remote user");
}else{
(*glocal.con,glocal.user,pubkey);
if (!success){
*glocal.msg = msg;
}else{
glocal.ret = 0;
}
}
}else{
*glocal.msg = MSG_U(E_CANTGETPUBKEY,"Can't get public key for remote user");
}
}else{
*glocal.msg = MSG_R(E_IVLDUSER);
}
// User exist on the system, remote user or local user, does not matter
glocal.ret = 0;
}
return glocal.ret;
}
static int bod_main (
const char *bind,
const char *portstr,
const char *control, // Unix socket
const CONNECT_INFO &con,
const CONNECT_INFO &con_sess,
const char *user,
const char *pidfile,
bool daemon,
const map &admin_secrets,
const map &client_secrets,
const char *nodename,
const char *dirserver,
unsigned maxaccts)
{
int ret = -1;
glocal const char *control = control;
glocal const char *nodename = nodename;
glocal const char *dirserver = dirserver;
glocal CONNECT_INFO con = con;
glocal CONNECT_INFO con_sess = con_sess;
glocal const map *admin_secrets = &admin_secrets;
glocal const map *client_secrets = &client_secrets;
glocal unsigned long nbrequest_client = 0;
glocal unsigned long nbrequest_admin = 0;
glocal string unixportclient = string_f("unix:/tmp/bod-client-%s.sock",portstr);
glocal string unixportadmin = string_f("unix:/tmp/bod-admin-%s.sock",portstr);
glocal unsigned maxaccts = maxaccts;
glocal string instrument_file = string_f("/tmp/instrument-%s.log",portstr);
(bind,glocal.unixportclient,5);
HANDLE_INFO *n = new HANDLE_INFO;
info.data = n;
if (strcmp(info.port,glocal.unixportclient.c_str())==0){
n->req.secret = fdpass_findsecret (*glocal.client_secrets,info.port);
if (n->req.secret.size() > 0){
n->type = TYPE_CLIENT;
}else{
tlmp_error ("Rejected client connexion from port %s\n",info.port);
endclient = true;
}
}else if (strcmp(info.port,glocal.unixportadmin.c_str())==0){
n->req.secret = fdpass_findsecret (*glocal.admin_secrets,info.port);
if (n->req.secret.size() > 0){
n->type = TYPE_ADMIN;
}else{
tlmp_error ("Rejected admin connexion from port %s\n",info.port);
endclient = true;
}
}else if (strncmp(info.port,"unix:",5)==0){
n->type = TYPE_CONTROL;
}else{
settcpnodelay(true);
char addr[20];
ipnum_ip2a (from,addr);
n->host = addr;
n->req.secret = fdpass_findsecret (*glocal.client_secrets,addr);
if (n->req.secret.size() > 0){
n->type = TYPE_CLIENT;
}else{
n->req.secret = fdpass_findsecret (*glocal.admin_secrets,addr);
if (n->req.secret.size() > 0){
n->type = TYPE_ADMIN;
}else{
tlmp_error ("Rejected connexion from IP %s\n",addr);
endclient = true;
}
}
}
HANDLE_INFO *c = (HANDLE_INFO*)info.data;
glocal HANDLE_INFO *c = c;
glocal string userid = c->userid;
debug_printf (D_PROTO,"receive line[%d]: %s\n",info.linelen,line);
{
// Clean the session cache once in a while
static time_t last_clean=0;
time_t now = time(NULL);
if (now > last_clean+10){
last_clean = now;
bod_sessions_clean();
}
}
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("dirserver: %s",glocal.dirserver));
tb.push_back(string_f("nodename: %s",glocal.nodename));
tb.push_back(string_f("maxaccts: %u",glocal.maxaccts));
tb.push_back(string_f("keepmsgs: %u",keepdays));
tb.push_back(string_f ("nbrequest_admin=%lu",glocal.nbrequest_admin));
tb.push_back(string_f ("nbrequest_client=%lu",glocal.nbrequest_client));
tb.push_back(string_f("filehandle %u",fs_getnbhandle()));
tb.push_back(string_f("rem_filehandle %lu",rem_handles.size()));
tb.push_back(string_f("sessions %lu",sessions.size()));
string useline = "usehttp:";
for (auto &s:usehttp) useline += string(" ") + s;
tb.push_back(useline);
instrument_status(tb);
tb.push_back(string_f("nonstrict: %d",nonstrict));
rep_status(tb);
// dotimes:b readproc:b = lines:v
// This performs various test from inside the container that may failed or not.
// Bolixo containers are so minimal that they do not contain bash.
glocal vector lines;
if (dotimes){
struct rusage u;
if (getrusage(RUSAGE_SELF,&u)==-1){
glocal.lines.push_back(string_f("syscall getrusage failed: %s",strerror(errno)));
}else{
glocal.lines.push_back(string_f("rusage: %lu.%06lu,%lu.%06lu"
,u.ru_utime.tv_sec
,u.ru_utime.tv_usec
,u.ru_stime.tv_sec
,u.ru_stime.tv_usec));
}
}
if (readproc){
glocal.lines.push_back("readproc:");
("/proc");
return false;
glocal.lines.push_back(path);
}
rep_helptest(glocal.lines);
// connectto port send = lines:v
glocal const char *send = send;
glocal vector lines;
// We want to test bod connectivity to the outside
(connectto,port,5);
sendf ("%s\n",glocal.send);
glocal.lines.push_back(line);
end = true;
glocal.lines.emplace_back(string_f("fail: %s\n",strerror(errno)));
rep_help_connect (glocal.lines);
// session
bod_sessions_erase(session);
rep_erase_session (true,"");
endserver = true;
glocal const char *name = name;
glocal const char *email = email;
glocal string msg;
("select userid from id2name where name='%s'",name);
glocal.msg = "User not found";
bod_record_email (glocal.con,glocal.name,glocal.email,glocal.dirserver,glocal.nodename);
if (glocal.msg.size() > 0){
rep_publishemail (false,glocal.msg);
}else{
rep_publishemail (true,"");
}
// node = success:b msg session
CONNECT_HTTP_INFO hcon;
hcon.init (node);
string session;
bool success = false;
if (nonstrict) hcon.setnonstrictmode();
(hcon,glocal.nodename);
if (bod_nodelogin(hcon,glocal.con,glocal.nodename,session)!=-1){
success = true;
}
rep_nodelogin (success,"",session);
// node session = success:b msg
CONNECT_HTTP_INFO hcon;
hcon.init (node);
bod_nodelogout(hcon,session);
rep_nodelogout (true);
toggle_instrument_file(on,glocal.instrument_file);
if (on){
debug_seton();
}else{
debug_setoff();
}
debug_setfdebug (filename);
keepdays = days;
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());
(glocal.con);
glocal.bod_client.rep_createsession(sessionid);
(glocal.con,sessionid,email,password);
glocal.bod_client.rep_login(success,incomplete);
(glocal.con,sessionid);
glocal string confirmid;
glocal string msg;
("select count(*) from id2name where name not like '%%@%%'");
unsigned nb = atoi(row[0]);
if (nb >= glocal.maxaccts){
glocal.msg = MSG_U(E_MAXACCOUNTS,"Maximum account number reach on this server");
}
if (glocal.msg.size() == 0){
(glocal.con,name,email,password,lang);
glocal.msg = msg;
glocal.confirmid = confirmid;
}
rep_adduser (glocal.confirmid,glocal.msg);
(glocal.con,id,lang);
if (success) bod_record_email (glocal.con,userid,email,glocal.dirserver,glocal.nodename);
glocal.bod_client.rep_confirmuser(success,msg);
(glocal.con,sessionid);
glocal.bod_client.rep_deleteuser (email,confirmid);
(glocal.con,confirmid);
glocal.bod_client.rep_confirmdelete(success,msg);
// sessionid name = success:b dirid msg
(glocal.con,sessionid,name);
glocal.bod_client.rep_mkdir(success,msg);
// sessionid name = success:b msg
(glocal.con,sessionid,name);
glocal.bod_client.rep_rmdir(success,msg);
// sessionid name content = success:b fileid msg
(glocal.con,sessionid,name,content);
glocal.bod_client.rep_addfile(success,msg);
// sessionid name content:o more:b = success:b fileid msg
debug_printf ("bob size=%lu\n",content.getsize());
(glocal.con,sessionid,name,content,more);
glocal.bod_client.rep_addfile_bob(success,handle,msg);
// sessionid handle content:o more:b = success:b msg
glocal bool success = false;
glocal string msg;
// handle may be a remote_handle.
auto r = rem_handles.find(handle);
if (r != rem_handles.end()){
handle = r->second->handle.c_str(); // Retrieve the local handle.
}
(glocal.con,sessionid,handle,content,more);
glocal.success = success;
glocal.msg = msg;
if (r != rem_handles.end()){
if (glocal.success){
bod_remote_appendfile(*r->second,content,more);
}else{
more = false;
}
if (!more){
rem_handles.erase(r);
}
}
rep_appendfile(glocal.success,glocal.msg);
// sessionid name = success:b msg
(glocal.con,sessionid,name);
glocal.bod_client.rep_delfile(success,msg);
// sessionid dirname = success:b msg
(glocal.con,sessionid,dirname);
glocal.bod_client.rep_undelete(success,msg);
// sessionid name content = success:b msg
(glocal.con,sessionid,name,content);
glocal.bod_client.rep_modifyfile(success,msg);
// sessionid name content:o more:b = success:b handle msg
(glocal.con,sessionid,name,content,more);
glocal.bod_client.rep_modifyfile_bob(success,handle,msg);
// sessionid oldname newname = success:b msg
// Rename file, directory or symlink
(glocal.con,sessionid,oldname,newname);
glocal.bod_client.rep_rename(success,msg);
// sessionid srcname srcdate srcname = success:b msg
// Copy file, directory or symlink
(glocal.con,sessionid,srcname,srcdate,dstname);
glocal.bod_client.rep_copy(success,msg);
// sessionid name = success:b msg content
glocal ENTRY entry;
glocal READINFO info;
if (bod_findentry (glocal.con_sess,sessionid,name,glocal.entry,threshold)!=-1){
if (!bolixo_isfile(glocal.entry.type)){
glocal.entry.msg = MSG_U(E_NOTAFILE,"Entry is not a file");
}else{
("select content,title,signature,filetype,modifiedby from files where id=%d and modified='%s'"
,glocal.entry.entryid,glocal.entry.modified.c_str());
if (row[0] != NULL){
glocal.info.title = row[1] != NULL ? row[1] : "";
glocal.info.signature = row[2] != NULL ? row[2] : "";
glocal.info.modified = glocal.entry.modified;
glocal.info.file_type = (FILE_TYPE)atoi(row[3]);
glocal.info.size = strlen(row[0]);
glocal.bod_client.rep_readfile (true,"",row[0],glocal.info);
glocal.info.owner = bod_getname (glocal.entry.ownerid);
unsigned ownerid = atoi(row[4]);
glocal.info.modifiedby = bod_getname (ownerid);
}else{
glocal.entry.msg = MSG_U(E_USEREADBOB,"Internal error, must use readfile_bob for large content");
}
glocal.entry.msg = "Internal error, reading table files";
}
}
if (glocal.entry.msg.size()!=0){
rep_readfile(false,glocal.entry.msg,"",glocal.info);
}
// sessionid name threshold nomore:b = success:b msg content:o handle more:b
glocal bool nomore = nomore;
glocal READINFO info;
glocal ENTRY entry;
glocal const char *sessionid = sessionid;
// Are we trying to read /projects/user@remote_node/...
// In general, you are trying to read the public photo of the user.
string remote_user,remote,remote_name;
if (strncmp(name,"/projects/",10)==0){
// All failure in this parsing end up with remote_user being empty,
// so the normal case (bod_findentry) is used.
const char *user = name+10;
const char *slash = strchr(user,'/');
if (slash != NULL){
string tmp = string(user,slash-user);
user = tmp.c_str();
const char *sepat = strchr(user,'@');
if (sepat != NULL){
remote = sepat+1;
remote_user = string(user,sepat-user);
remote_name = string_f("/projects/%s/%s",remote_user.c_str(),slash+1);
}
}
}
if (remote_user.size() > 0){
auto rem = make_unique();
rem->setfrom (remote);
bool is_admin; // Just a place holder, admin rights have no effect on remote server.
unsigned userid = trli_getsessionuser (glocal.con_sess,sessionid,is_admin, rem->username, glocal.entry.msg);
if (userid != 0){
BOB_TYPE bob;
bool more = false;
READINFO info;
bod_remote_readfile_bob (glocal.con,sessionid
,rem,remote_name,nomore,glocal.nodename
,bob,info,more,glocal.entry.msg);
if (glocal.entry.msg.size() == 0){
// info come from the remote system.
// We have to patch the owner and modifiedby field to reflect that
bod_patchname (info.owner,remote);
bod_patchname (info.modifiedby,remote);
string handle;
if (more){
handle = fs_makeid();
rem_handles[handle] = move(rem);
}
rep_readfile_bob (true,"",bob,info,handle,more);
}
}
}else if (bod_findentry (glocal.con_sess,sessionid,name,glocal.entry,threshold)!=-1){
if (!bolixo_isfile(glocal.entry.type)){
glocal.entry.msg = MSG_R(E_NOTAFILE);
}else{
("select content,title,signature,filetype,modifiedby from files where id=%d and modified='%s'"
,glocal.entry.entryid,glocal.entry.modified.c_str());
glocal.info.title = row[1] != NULL ? row[1] : "";
glocal.info.signature = row[2] != NULL ? row[2] : "";
glocal.info.modified = glocal.entry.modified;
glocal.info.file_type = (FILE_TYPE)atoi(row[3]);
glocal.info.owner = bod_getname (glocal.entry.ownerid);
unsigned ownerid = atoi(row[4]);
glocal.info.modifiedby = bod_getname (ownerid);
if (row[0] != NULL){
unsigned len = strlen(row[0]);
glocal.info.size = len;
BOB_TYPE bob (row[0],len,false);
glocal.bod_client.rep_readfile_bob (true,"",bob,glocal.info,"",false);
}else{
string handle;
FILE *fin = fs_alloc_file_handle (glocal.entry.entryid,glocal.entry.modified,"r",handle,glocal.sessionid);
if (fin == NULL){
glocal.entry.msg == "Internal error, reading content file";
fs_delete_handle (handle);
}else{
struct stat64 st;
if (fstat64(fileno(fin),&st)==-1){
glocal.entry.msg = "Internal error, getting file size";
fs_delete_handle (handle);
}else{
char buf[REQ_CONTENT_CHUNK];
int len = fread (buf,1,sizeof(buf),fin);
BOB_TYPE bob (buf,len,false);
bool more = len==REQ_CONTENT_CHUNK;
if (!more || glocal.nomore){
fs_delete_handle(handle);
handle.clear();
}
glocal.info.size = st.st_size;
glocal.bod_client.rep_readfile_bob (true,""
,bob,glocal.info,handle,more);
}
}
}
glocal.entry.msg = "Internal error, reading table files";
}
}
if (glocal.entry.msg.size()!=0){
rep_readfile_bob(false,glocal.entry.msg,BOB_TYPE(),glocal.info,"",false);
}
// sessionid handle = success:b msg content:o more:b
auto r = rem_handles.find(handle);
if (r != rem_handles.end()){
bool success = false;
string msg;
bool more = false;
BOB_TYPE content;
bod_remote_readmore(r->second,success,msg,content,more);
if (!more){
rem_handles.erase (r);
}
rep_readmore (success,msg,content,more);
}else{
FILE *fin = fs_get_file (handle,sessionid);
if (fin == NULL){
rep_readmore (false,"Internal error, reading content file",BOB_TYPE(),false);
fs_delete_handle (handle);
}else{
char buf[REQ_CONTENT_CHUNK];
int len = fread (buf,1,sizeof(buf),fin);
BOB_TYPE bob (buf,len,false);
bool more = len == REQ_CONTENT_CHUNK;
rep_readmore (true,"Ok",bob,more);
if (!more){
fs_delete_handle(handle);
}
}
}
// sessionid name username listname = success:b msg
(glocal.con,sessionid,name,username,listname,listmode);
glocal.bod_client.rep_set_access (success,msg);
// sessionid name = success:b msg
(glocal.con,sessionid,name);
glocal.bod_client.rep_set_access(success,msg);
// sessionid name threshold = success:b msg file:U{FILEINFO}
glocal FILEINFO file;
glocal ENTRY entry;
if (bod_findentry (glocal.con_sess,sessionid,name,glocal.entry,threshold)!=-1){
if (threshold[0] == '\0') threshold = END_OF_TIME;
("select dirs_content.name,dirs_content.modified,type,id2name.name"
",group_list_id,group_lists.name,ids.listmode"
",length(files.content),dirs_content.itemid,files.title,dirs_content.eventtime"
",files.filetype,marks.modified"
" from dirs_content"
" join ids on dirs_content.itemid=ids.id"
" join id2name on ids.ownerid=id2name.userid"
" left join group_lists on ids.group_list_id=group_lists.id"
" left join files on dirs_content.itemid=files.id and dirs_content.modified=files.modified"
" left join marks on marks.itemid=dirs_content.itemid and marks.userid=%u"
" where dirid=%d and dirs_content.name='%s' and dirs_content.eventtime <= '%s' order by eventtime desc limit 1"
,glocal.entry.userid,glocal.entry.dirid,glocal.entry.basename.c_str(),threshold);
const char *name = row[0];
const char *modified = row[1];
ENTRY_TYPE type = (ENTRY_TYPE) atoi(row[2]);
const char *owner = row[3];
const char *listid = row[4];
const char *listname = row[5];
const char *listmode = row[6] != NULL ? row[6] : "";
if (listname == NULL){
listname = "";
if (listid != NULL){
if (strcmp(listid,"0")==0){
listname = ALL_MAY_READ;
}
}
}
unsigned size=0;
if (row[7] != NULL){
size = atoi(row[7]);
}else if (bolixo_isfile(type)){
// Content stored as a file
size = fs_get_filesize(atoi(row[8]),modified);
}
const char *title = row[9] == NULL ? "" : row[9];
const char *eventdate = row[10];
FILE_TYPE file_type = row[11] == NULL ? FILE_UNKNOWN : (FILE_TYPE)atoi(row[11]);
VIEWED_STATUS viewed = bod_readviewed(row[12],modified);
if (!bolixo_isdeleted(type)){
glocal.file.name = name;
glocal.file.modified = modified;
glocal.file.type = type;
glocal.file.owner = owner;
glocal.file.listname = listname;
glocal.file.listmode = listmode;
glocal.file.size = size;
glocal.file.title = title;
glocal.file.eventdate = eventdate;
glocal.file.file_type = file_type;
glocal.file.viewed = viewed;
glocal.entry.msg.clear();
}
}
if (glocal.entry.msg.size() > 0){
glocal.file.clear();
rep_stat (false,glocal.entry.msg,glocal.file);
}else{
rep_stat (true,"",glocal.file);
}
ENTRY entry;
glocal bool deletes=false; // Is there some entries to undelete
glocal vector files;
glocal map mp;
glocal bool history = history;
#ifdef INSTRUMENT
glocal long long start = fdpass_getnow();
#endif
if (bod_findentry (glocal.con_sess,sessionid,name,entry,threshold)!=-1){
#ifdef INSTRUMENT
if (f_instrument != NULL){
long long end = fdpass_getnow();
fprintf (f_instrument,"%Ld listdir:findentry %lf userid=%u dirid=%d name=%s\n"
,glocal.start,(end-glocal.start)/1000000.0,entry.userid,entry.entryid,name);
glocal.start = end;
}
#endif
if (!bolixo_isdir(entry.type)){
entry.msg = MSG_U(E_NOTDIR,"Is not a directory");
}else if (entry.ownerid != entry.userid && entry.listmode == 'p' && !entry.is_admin){
entry.msg = MSG_U(E_NOTALLOWEDLIST,"Not allowed to list");
}else{
if (threshold[0] == '\0') threshold = END_OF_TIME;
// We read dirs_content.modified twice because we let MariaDB perform the timezone conversion
// but we need the original date as well
("select dirs_content.name,dirs_content.modified,type,id2name.name"
",group_list_id,group_lists.name,ids.listmode"
",length(files.content),dirs_content.itemid,files.title,dirs_content.eventtime"
",files.filetype,files.content is null,marks.modified,id2name2.name"
",convert_tz(dirs_content.modified,'system','%s')"
" from dirs_content"
" join ids on dirs_content.itemid=ids.id"
" join id2name on ids.ownerid=id2name.userid"
" left join group_lists on ids.group_list_id=group_lists.id"
" left join files on dirs_content.itemid=files.id and dirs_content.modified=files.modified"
" left join id2name id2name2 on files.modifiedby=id2name2.userid"
" left join marks on marks.itemid=dirs_content.itemid and marks.userid=%u"
" where dirid=%d and dirs_content.eventtime <= '%s' order by eventtime"
,user_timezone.c_str(),entry.userid,entry.entryid,threshold);
#ifdef INSTRUMENT
if (rownum==0 && f_instrument != NULL){
long long end = fdpass_getnow();
fprintf (f_instrument,"%Ld listdir:row0 %lf\n",glocal.start,(end-glocal.start)/1000000.0);
glocal.start = end;
}
#endif
const char *name = row[0];
const char *modified = row[1];
ENTRY_TYPE type = (ENTRY_TYPE) atoi(row[2]);
const char *owner = row[3];
const char *listid = row[4];
const char *listname = row[5];
const char *listmode = row[6] != NULL ? row[6] : "";
if (listname == NULL){
listname = "";
if (listid != NULL){
if (strcmp(listid,"0")==0){
listname = ALL_MAY_READ;
}
}
}
unsigned size=0;
if (row[7] != NULL){
size = atoi(row[7]);
}else if (bolixo_isfile(type)){
// Content stored as a file
size = fs_get_filesize(atoi(row[8]),modified);
}
const char *title = row[9] == NULL ? "" : row[9];
const char *eventdate = row[10];
FILE_TYPE file_type = row[11] == NULL ? FILE_UNKNOWN : (FILE_TYPE)atoi(row[11]);
bool islarge = atoi(row[12])!=0;
VIEWED_STATUS viewed = bod_readviewed(row[13],modified);
const char *modifiedby = row[14] != NULL ? row[14] : "";
modified = row[15] != nullptr ? row[15] : "";
if (glocal.history){
FILEINFO info;
info.name = name;
info.eventdate = eventdate;
info.modified = modified;
info.type = type;
info.file_type = file_type;
info.owner = owner;
info.modifiedby = modifiedby;
info.listname = listname;
info.listmode = listmode;
info.size = size;
info.title = title;
info.viewed = viewed;
info.islarge = islarge;
glocal.files.push_back(move(info));
}else{
glocal.mp[name] = DIRENTRY(eventdate,modified,type,file_type,islarge
,owner,modifiedby,listname,listmode,size,title,viewed);
}
#ifdef INSTRUMENT
if (f_instrument != NULL){
long long end = fdpass_getnow();
fprintf (f_instrument,"%Ld listdir:endsql %lf\n",glocal.start,(end-glocal.start)/1000000.0);
glocal.start = end;
}
#endif
if (!glocal.history){
for (auto &x:glocal.mp){
if (!bolixo_isdeleted(x.second.type)){
FILEINFO info;
info.name = x.first;
info.type = x.second.type;
info.file_type = x.second.file_type;
info.eventdate = x.second.eventdate;
info.modified = x.second.modified;
info.owner = x.second.owner;
info.modifiedby = x.second.modifiedby;
info.listname = x.second.members;
info.listmode = x.second.listmode;
info.size = x.second.size;
info.title = x.second.title;
info.viewed = x.second.viewed;
info.islarge = x.second.islarge;
glocal.files.push_back(move(info));
}else{
glocal.deletes = true;
}
}
}
}
}
#ifdef INSTRUMENT
if (f_instrument != NULL){
long long end = fdpass_getnow();
fprintf (f_instrument,"%Ld listdir:rep %lf\n",glocal.start,(end-glocal.start)/1000000.0);
}
#endif
if (entry.msg.size() > 0){
glocal.files.clear();
rep_listdir (false,entry.msg,glocal.files,false);
}else{
bod_trim (glocal.files,offset,nb);
rep_listdir (true,"",glocal.files,glocal.deletes);
}
// sessionid listname owner = success:b msg
(glocal.con,sessionid,listname,owner);
glocal.bod_client.rep_create_project_dir(success,msg);
// sessionid listname owner = success:b msg
(glocal.con,sessionid,listname,owner);
glocal.bod_client.rep_create_group_list(success,msg);
// sessionid groupname owner = success:b msg
(glocal.con,sessionid,groupname,owner);
glocal.bod_client.rep_create_group(success,msg);
// sessionid listname groupname defaultaccess owner = success:b msg
(glocal.con,sessionid,listname,groupname,defaultaccess,owner);
glocal.bod_client.rep_set_group(success,msg);
// sessionid groupname user defaultaccess owner = success:b msg
(glocal.con,sessionid,groupname,user,access,role,owner);
glocal.bod_client.rep_set_member(success,msg);
// sessionid listname description owner = success:b msg
(glocal.con,sessionid,listname,description,owner);
glocal.bod_client.rep_set_list_desc (success,msg);
// sessionid groupname description owner = success:b msg
(glocal.con,sessionid,groupname,description,owner);
glocal.bod_client.rep_set_group_desc (success,msg);
// sessionid listname owner = success:b msg
(glocal.con,sessionid,listname,owner);
glocal.bod_client.rep_delete_list(success,msg);
// sessionid groupname owner = success:b msg
(glocal.con,sessionid,groupname,owner);
glocal.bod_client.rep_delete_group(success,msg);
// sessionid owner = success:b msg lists:v groups:vv access:v
glocal string msg;
glocal vector lists;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.msg);
if (userid != 0){
glocal string last_list;
("select group_lists.name,group_lists.description,groups.name,group_list_members.defaultaccess"
" from group_lists"
" left join group_list_members on group_lists.id=group_list_members.group_list_id"
" left join groups on group_list_members.groupid=groups.id"
" where group_lists.ownerid=%u order by group_lists.name,groups.name",userid);
if (strcmp(glocal.last_list.c_str(),row[0])!=0){
glocal.last_list = row[0];
LIST l;
l.name = row[0];
l.description = row[1];
glocal.lists.push_back(l);
}
if (row[1] != NULL && row[2] != NULL){
LIST &l = glocal.lists[glocal.lists.size()-1];
l.groups.push_back(row[2]);
l.access.push_back(row[3]);
}
glocal.msg.clear();
glocal.msg = MSG_U(E_NOLIST,"No list");
}
bool ret = true;
if (glocal.msg.size() > 0){
ret = false;
glocal.lists.clear();
}
rep_list_lists (ret,glocal.msg,glocal.lists);
// sessionid owner = success:b msg members:v
glocal string msg;
glocal vector members;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.msg);
if (userid != 0){
("select id2name.name from groups"
" join group_members on group_members.groupid = groups.id"
" join id2name on id2name.userid = group_members.userid"
" where groups.name='contacts' and groups.ownerid=%u",userid);
glocal.members.push_back(row[0]);
}
if (glocal.msg.size() > 0){
glocal.members.clear();
rep_list_contacts(false,glocal.msg,glocal.members);
}else{
rep_list_contacts(true,"",glocal.members);
}
// sessionid owner only_onwer:b = success:b msg groups:v users:vv access:vv
// List either all groups own by the logged user
// or all groups it belongs to
glocal string msg;
glocal vector groups;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.msg);
if (userid != 0){
glocal unsigned last_group_id = (unsigned)-1;
string sql = "select groups.id,groups.name,id2.name,id2name.name,group_members.access,group_members.role,groups.description"
" from groups"
" left join group_members on groups.id=group_members.groupid"
" left join id2name on group_members.userid=id2name.userid"
" left join id2name id2 on groups.ownerid=id2.userid"
" where groups.name != 'contacts' and ";
if (only_owner){
sql += "groups.ownerid=%u";
}else{
sql += "groups.id in (select groupid from group_members where userid=%u)";
}
sql += " order by id2.name,groups.name,id2name.name";
(sql.c_str(),userid);
unsigned group_id = atoi(row[0]);
if (glocal.last_group_id != group_id){
glocal.last_group_id = group_id;
GROUP group;
group.name = row[1];
group.owner = row[2];
group.description = row[6];
glocal.groups.push_back(group);
}
if (row[3] != NULL && row[4] != NULL){
GROUP &g = glocal.groups[glocal.groups.size()-1];
g.users.push_back(row[3]);
g.access.push_back(row[4]);
const char *role = row[5];
if (role == NULL) role = "";
g.roles.push_back(role);
}
glocal.msg.clear();
glocal.msg = MSG_U(E_NOGROUP,"No group");
}
bool ret = true;
if (glocal.msg.size() > 0){
ret = false;
glocal.groups.clear();
}
rep_list_groups (ret,glocal.msg,glocal.groups);
// sessionid owner showroles = success:b msg inboxes:U{INBOX}v
// &INBOX manager project role
glocal string msg;
glocal vector inboxes;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.msg);
if (userid != 0){
glocal.msg.clear();
vector listids;
fs_list_inboxes (userid,glocal.inboxes,listids,showroles);
}
bool ret = true;
if (glocal.msg.size() > 0){
ret = false;
glocal.inboxes.clear();
}
rep_list_inboxes (ret,glocal.msg,glocal.inboxes);
// sessionid owner project deleted:b offset:u nb:u = success:b msg messages:U{MESSAGE}v
// &MESSAGE manager project role uuid title submit viewed
// We can extract the messasges for either all inboxes or a single project
// if project is empty
glocal string msg;
glocal vector msgs;
string username;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, username, glocal.msg);
if (userid != 0){
glocal map id2prj;
string dirids;
const char *sep = "";
if (project[0] == '\0'){
int dirid = fs_find_inbox(userid,username.c_str(),false,glocal.msg);
if (dirid != -1){
dirids = string_f("%d",dirid);
sep = ",";
}
}
vector listids;
vector inboxes;
fs_list_inboxes (userid,inboxes,listids,true);
for (unsigned i=0; i roles;
roles.push_back("");
if (inb.role.size() > 0) roles.push_back(inb.role.c_str());
int ownerid = fs_rec_getid ("select userid from id2name where name='%s'",manager);
if (ownerid == -1){
glocal.msg = string_f("manager %s unknown",manager);
break;
}else{
INBOX tmp = inb;
for (auto role:roles){
int dirid = fs_find_project_inbox(ownerid, listid, manager,inb.project.c_str(),role,false,glocal.msg);
if (dirid != -1){
tmp.role = role;
glocal.id2prj[dirid] = tmp; //inb;
dirids += string_f ("%s%d",sep,dirid);
sep = ",";
}
}
}
}
if (dirids.size() > 0){
glocal.msg.clear();
("select dirs_content.name,files.title,dirs_content.modified,id2name.name,marks.modified,dirs_content.dirid"
" from dirs_content"
" join files on files.id = dirs_content.itemid and files.modified = dirs_content.modified"
" join ids on dirs_content.itemid = ids.id"
" join id2name on ids.ownerid = id2name.userid"
" left join marks on marks.itemid=dirs_content.itemid and marks.userid=%u"
" where dirid in (%s) and type=%u order by eventtime desc limit %u,%u"
,userid,dirids.c_str(),ENTRY_MSG,offset,nb);
MESSAGE msg;
msg.uuid = row[0];
msg.title = row[1];
msg.submit = row[2];
msg.from = row[3];
msg.viewed = bod_readviewed(row[4],row[2]);
int dirid = atoi(row[5]);
auto pt = glocal.id2prj.find(dirid);
if (pt != glocal.id2prj.end()){
msg.manager = pt->second.manager;
msg.project = pt->second.project;
msg.role = pt->second.role;
}
glocal.msgs.push_back(msg);
}
}
bool ret = true;
if (glocal.msg.size() > 0){
ret = false;
glocal.msgs.clear();
}
rep_list_msgs (ret,glocal.msg,glocal.msgs);
// sessionid owner recipients:v title content = success:b msg msgid
(glocal.con,sessionid,owner,recipients,title,content);
glocal.bod_client.rep_sendmsg(success,msg,msgid);
// sessionid owner manager project role title content = success:b msg msgid
(glocal.con,sessionid,owner,manager,project,role,title,content);
glocal.bod_client.rep_sendmsg_project(success,msg,msgid);
// sessionid owner msgid recipients:v title content = success:b msg replyid
(glocal.con,sessionid,owner,msgid,recipients,title,content);
glocal.bod_client.rep_replymsg(success,msg,replyid);
// sessionid owner manager project role msgid title content = success:b msg replyid
(glocal.con,sessionid,owner,manager,project,role,msgid,title,content);
glocal.bod_client.rep_replymsg_project(success,msg,replyid);
// sessionid owner msgid content:o more:b = success:b msg handle
(glocal.con,sessionid,owner,msgid,content,more);
glocal.bod_client.rep_sendattach(success,msg,handle);
// nickname = pubkey
("select pub_key from id2name where name='%s'",nickname);
glocal.bod_client.rep_getpubkey(false,"user unknown");
if (row[0] == NULL){
glocal.bod_client.rep_getpubkey(false,"no public key");
}else{
glocal.bod_client.rep_getpubkey(true,row[0]);
}
// nickname msg = status:e{ERR_CODE} msg
glocal const char *content = msg;
glocal ERR_CODE status = ERR_CODE_IVLDACCOUNT;
glocal string msg;
("select pub_key from id2name where name='%s'",nickname);
if (row[0] == NULL){
glocal.status = ERR_CODE_CANTVERIFY;
glocal.msg = MSG_U(E_NOPUBKEY,"No public key for this account");
}else{
EVP_PKEY *p = fs_load_public (row[0]);
if (p != NULL){
// Find the signature in the content
const char *pt = strstr(glocal.content,"------\n");
if (pt == NULL){
glocal.status = ERR_CODE_NOSIGFOUND;
glocal.msg = "Signature not found";
}else{
string tmp = string(glocal.content,pt-glocal.content);
string sig = pt+7;
strip_end (sig);
if (fs_verify(tmp,p,sig)==-1){
glocal.status = ERR_CODE_VERIFYFAILED;
glocal.msg = MSG_U(E_SIGNOMATCH,"Signature does not match");
}else{
glocal.status = ERR_CODE_NONE;
glocal.msg = MSG_U(I_SIGOK,"Signature OK");
}
}
fs_free_public (p);
}
}
rep_verifysign (glocal.status,glocal.msg);
// sessionid owner to:v groupname groupowner content:o filename sign createdby = success:b msg handle
glocal const char *sessionid = sessionid;
glocal string msg;
glocal string handle;
glocal const vector *to = &to;
glocal const BOB_TYPE *content = &content;
glocal string username;
glocal const char *filename = filename;
glocal bool more = more;
glocal const char *createdby = createdby;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.username, glocal.msg);
if (userid != 0){
(glocal.con,sessionid,owner,to,groupname,groupowner,content,more,filename,sign);
glocal.msg = msg;
glocal.handle = handle;
if (success){
// We have to find if there are remote users and we organize them by servers
map> mp;
for (auto s:*glocal.to){
string tmpuser,remote_user,remote;
if (bod_parse_user(s,tmpuser,remote_user,remote,glocal.nodename)!=-1
&& remote.size() > 0){
mp[remote].push_back(remote_user);
}
}
if (mp.size() > 0){
auto rem = make_unique();
rem->username = glocal.username;
for (auto p:mp) rem->addto(p.first,p.second);
bod_remote_sendtalk (glocal.con,glocal.sessionid,*rem,*glocal.content,glocal.nodename
,glocal.more,glocal.filename,sign,glocal.createdby);
if (handle[0] != '\0'){
rem->handle = handle;
glocal.handle = fs_makeid();
rem_handles[glocal.handle] = move(rem);
}
}
}
}
if (glocal.msg.size() > 0){
rep_sendtalk (false,glocal.msg,"");
}else{
rep_sendtalk(true,glocal.msg,glocal.handle);
}
// sessionid owner to:v groupname groupowner filename filedate = success:b msg handle
(glocal.con,sessionid,owner,to,groupname,groupowner,filename,filedate);
glocal.bod_client.rep_sendtalk_file(success,msg);
// to content:o = success:b msg handle
(glocal.con,sessionid,to,content,more);
glocal.bod_client.rep_sendtalk_anon(success,msg,handle);
// sessionid owner groupname groupowner offset:u nb:u firstseen = success:b msg messages:U{MESSAGE}v deletes:b total:u nbnew:u
/*
firstseen is ID of the message which was the first the last time with made this call.
This establish a context for the current call. The result produced won't be influenced by new
messages which may have shown between this call and the previous. nbnew will show how many
new messages are "hidden".
This functionnality make the user interface more predictable. If you are clicking on the second message of the third
page, the UI will refresh the content to show that the message was viewed. But even if new messages came, the current
message will be the second message of the third page.
The UI present the number of new messages in the header. Clicking on it simply resets firstseen (set to empty string).
*/
glocal const char *groupname = groupname;
glocal const char *groupowner = groupowner;
glocal unsigned offset = offset;
glocal unsigned nb = nb;
glocal bool deletes = false; // Is there some deleted entries
glocal unsigned total = 0; // Total number of records
glocal unsigned nbnew = 0;
glocal firstseen;
glocal string msg;
glocal vector msgs;
glocal unsigned userid;
string username;
glocal.userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, username, glocal.msg);
if (glocal.userid != 0){
// Is the current user a member of the group
("select groups.ownerid from groups"
" join id2name on id2name.userid=groups.ownerid"
" join group_members on group_members.groupid = groups.id"
" where id2name.name='%s' and groups.name='%s' and group_members.userid=%u"
,groupowner,groupname,glocal.userid);
glocal.msg = MSG_U(E_GROUPPROBLEMS,"Unknown group name, empty group, or user not a member");
bod_list_talk (glocal.userid,glocal.msgs,atoi(row[0]),glocal.groupowner,glocal.groupname
,glocal.offset,glocal.nb,glocal.firstseen,glocal.deletes,glocal.total,glocal.nbnew,glocal.msg);
}
if (glocal.msg.size()>0){
glocal.msgs.clear();
rep_list_talk(false,glocal.msg,glocal.msgs,false,0,0);
}else{
rep_list_talk(true,glocal.msg,glocal.msgs,glocal.deletes,glocal.total,glocal.nbnew);
}
// sessionid owner user intro = success:b msg
glocal const char *intro = intro;
glocal bool success = false;
glocal string msg;
glocal string username;
glocal const char *sessionid = sessionid;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.username, glocal.msg);
if (userid != 0){
glocal string remote_user;
glocal string remote;
string local_user;
if (bod_check_remote_user(glocal.con,user,glocal.nodename,glocal.msg
,local_user,glocal.remote_user,glocal.remote)!=-1){
(glocal.con,sessionid,owner,local_user,intro);
glocal.success = success;
glocal.msg = msg;
if (success && glocal.remote.size() > 0){
bod_remote_contact_request (glocal.con,glocal.sessionid,glocal.username,glocal.remote_user,glocal.intro,glocal.remote,glocal.nodename);
}
}
}
glocal.bod_client.rep_contact_request (glocal.success,glocal.msg);
// sessionid owner user status = success:b msg
glocal bool success = false;
glocal string msg;
glocal CONTACT_STATUS status = status;
glocal const char *user = user;
glocal const char *sessionid = sessionid;
glocal string username;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.username, glocal.msg);
if (userid != 0){
(glocal.con,sessionid,owner,user,status);
glocal.success = success;
glocal.msg = msg;
if (success){
// We check if the target user of this call is a remote user.
// If this is the case, we have to execute the contact_manage on his server.
string tmpuser,remote_user,remote;
if (bod_parse_user(glocal.user,tmpuser,remote_user,remote,glocal.nodename)!=-1
&& remote.size() > 0){
bod_remote_contact_manage (glocal.con,glocal.sessionid,glocal.username,remote_user
,glocal.status,remote,glocal.nodename);
}
}
}
rep_contact_manage (glocal.success,glocal.msg);
// sessionid owner to_me:b contact offset:u nb:u = success:b msg contacts:U{CONTACT}v
// CONTACT user reqdate message status
glocal vector contacts;
glocal string msg;
string username;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, username, glocal.msg);
if (userid != 0){
if (to_me){
string where;
if (contact[0] != '\0'){
int contact_id = fs_rec_getid("select userid from id2name where name='%s'",contact);
if (contact_id != -1){
where = string_f (" and contact_requests.reqid = %d",contact_id);
}
}
// Some people made contact request to me
("select id2name.name,message,status,reqdate from contact_requests"
" join id2name on id2name.userid=contact_requests.reqid"
" where contact_requests.userid=%u %s order by id2name.name limit %u,%u"
,userid,where.c_str(),offset,nb);
CONTACT c;
c.user = row[0];
c.message = row[1];
c.status = (CONTACT_STATUS)atoi(row[2]);
c.reqdate = row[3];
glocal.contacts.push_back(c);
}else{
// I made the request
("select id2name.name,message,status,reqdate from contact_requests"
" join id2name on id2name.userid=contact_requests.userid"
" where contact_requests.reqid=%u order by id2name.name limit %u,%u"
,userid,offset,nb);
CONTACT c;
c.user = row[0];
c.message = row[1];
c.status = (CONTACT_STATUS)atoi(row[2]);
c.reqdate = row[3];
glocal.contacts.push_back(c);
}
}
if (glocal.msg.size() != 0){
glocal.contacts.clear();
rep_contact_list (false,glocal.msg,glocal.contacts);
}else{
rep_contact_list (true,"",glocal.contacts);
}
// sessionid owner = success:b msg config:U{CONFIG}
// &CONFIG lang public_view:b public_dir
glocal string msg;
glocal CONFIG config;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.msg);
if (userid != 0){
("select lang,dateformat,public_view,public_dir,anon_messages,timezone from config where userid=%u",userid);
glocal.msg = "Internal error, no configuration for user";
glocal.config.lang = row[0];
glocal.config.dateformat = atoi(row[1]);
glocal.config.public_view = atoi(row[2]);
glocal.config.public_dir = row[3];
glocal.config.anon_messages = atoi(row[4]);
glocal.config.timezone = row[5];
}
if (glocal.msg.size() > 0){
rep_config_read (false,glocal.msg,glocal.config);
}else{
rep_config_read (true,glocal.msg,glocal.config);
}
// sessionid user = success:b msg
(glocal.con,sessionid,user);
glocal.bod_client.rep_remote_interest_set(success,msg);
// sessionid user = success:b msg
(glocal.con,sessionid,user);
glocal.bod_client.rep_remote_interest_unset(success,msg);
// sessionid user owner = success:b msg
glocal bool success = false;
glocal string msg;
string username;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, username, glocal.msg);
if (userid != 0){
glocal string remote_user;
glocal string remote;
string local_user;
if (bod_check_remote_user(glocal.con,user,glocal.nodename,glocal.msg
,local_user,glocal.remote_user,glocal.remote)!=-1){
(glocal.con,sessionid,local_user,owner);
if (success && glocal.remote.size() > 0){
bod_remote_interest_set (glocal.con,true,glocal.remote_user,glocal.remote,glocal.nodename);
}
glocal.success = success;
glocal.msg = msg;
}
}
rep_interest_set (glocal.success,glocal.msg);
// sessionid user = success:b msg
glocal string msg;
glocal const char *user = user;
(glocal.con,sessionid,user,owner);
if (!success) glocal.msg = msg;
if(glocal.msg.size() > 0){
rep_interest_unset (false,glocal.msg);
}else{
rep_interest_unset (true,"");
// Now we check if this is a remote user.
// If this is the case and there is no more user interested, we tell the remote system
const char *pt = strchr(user,'@');
if (pt != NULL){
glocal string remote_user;
glocal string remote;
glocal.remote_user = string(user,pt-user);
glocal.remote = pt+1;
("select id2name.userid from id2name join interests on check_userid=id2name.userid"
" where id2name.name='%s' limit 1",user);
bod_remote_interest_set (glocal.con,false,glocal.remote_user,glocal.remote,glocal.nodename);
}
}
// sessionid owner = success:b users:v
glocal string msg;
glocal vector users;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.msg);
if (userid != 0){
("select id2name.name,public_view,interests.since from interests"
" join id2name on id2name.userid = interests.check_userid"
" left join config on config.userid = interests.check_userid"
" where interests.userid=%u order by id2name.name",userid);
INTUSER u;
u.name = row[0];
u.visible = row[1] == NULL ? 1 : atoi(row[1]) != 0;
u.since = row[2];
glocal.users.push_back(u);
}
if (glocal.msg.size() > 0){
glocal.users.clear();
rep_interest_list (false,glocal.msg,glocal.users);
}else{
rep_interest_list (true,glocal.msg,glocal.users);
}
// sessionid owner fulltext:v offset:u nb:u firstseen = success:b msg messages:U{SHORTMSG}v total:u nbnew:u
glocal unsigned nb = nb;
glocal string msg;
glocal map mp;
glocal set fulltext;
for (auto f:fulltext) glocal.fulltext.insert(f);
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.msg);
if (userid != 0){
#ifdef INSTRUMENT
glocal long long start = fdpass_getnow();
#endif
DATEASC keepdate;
fdpass_asctime(time(NULL)-keepdays*24*60*60,keepdate);
("select id2name.name,dirs2.name,convert_tz(dirs2.modified,'system','%s')"
",files.filetype,dirs2.itemid,files.content,dirs2.type,files.signature,dirs2.eventtime"
" from interests"
" join id2name on id2name.userid = interests.check_userid"
" left join dirs_content dirs2 on interests.dirid=dirs2.dirid and dirs2.eventtime > '%s'"
//" left join dirs_content on interests.dirid=dirs_content.itemid"
//" left join marks on marks.userid=%u and marks.itemid=interests.dirid"
//" left join dirs_content dirs2 on interests.dirid = dirs2.dirid and (marks.modified is null or dirs2.modified > marks.modified)"
" join files on dirs2.itemid = files.id and dirs2.modified = files.modified"
" where interests.userid=%u order by dirs2.eventtime",user_timezone.c_str(),keepdate.buf,userid); //,userid);
#ifdef INSTRUMENT
if (rownum==0 && f_instrument != NULL){
long long end = fdpass_getnow();
fprintf (f_instrument,"%Ld interest_check:row0 %lf\n",glocal.start,(end-glocal.start)/1000000.0);
}
#endif
// If the dirid is -1 in interests, there nothing to show
// row[1] is null
if (row[1] != NULL){
SHORTMSG msg;
msg.from = row[0];
msg.uuid = row[1];
const char *modified = row[2] != nullptr ? row[2] : "";
msg.submit = modified;
ENTRY_TYPE type = row[6] == NULL ? ENTRY_DELETED : (ENTRY_TYPE)atoi(row[6]);
if (type == ENTRY_DELETED){
msg.file_type = FILE_UNKNOWN;
}else{
msg.file_type = row[3] == NULL ? FILE_UNKNOWN : (FILE_TYPE)atoi(row[3]);
}
msg.signature = row[7];
msg.eventdate = row[8];
if (row[5] != NULL){
// Limit the number of lines if more than one message was requested
if (glocal.nb == 1 || glocal.fulltext.count(msg.uuid) > 0){
msg.content = row[5];
msg.size = msg.content.size();
}else{
msg.content = bod_first_lines(row[5]);
msg.size = strlen(row[5]);
}
}else{
msg.size = fs_get_filesize(atoi(row[4]),modified);
}
glocal.mp[msg.uuid] = move(msg);
}
}
vector msgs;
if (glocal.msg.size() > 0){
rep_interest_check (false,glocal.msg,msgs,0,0);
}else{
for (auto &m:glocal.mp){
if (m.second.file_type != FILE_UNKNOWN){
msgs.push_back(move(m.second));
}
}
sort (msgs.begin(),msgs.end(),msg_compare);
unsigned nbnew = bod_firstseen (msgs,firstseen);
unsigned total = msgs.size();
bod_trim (msgs,offset,nb);
rep_interest_check (true,glocal.msg,msgs,total,nbnew);
}
// sessionid owner config:U{CONFIG} = success:b msg
glocal sessionid;
(glocal.con,sessionid,owner,config);
if (success) bod_sessions_erase (glocal.sessionid,glocal.control);
glocal.bod_client.rep_config_write (success,msg);
// sessionid owner key = success:b msg ui:b active_ui:b email:b digest:b
glocal string msg;
glocal bool ui = true;
glocal bool active_ui = false;
glocal bool email = false;
glocal bool digest = false;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, owner, glocal.msg);
if (userid != 0){
// If not found in the database, just return the defaults.
("select ui,active_ui,email,digest from notifications where userid=%u and notify_key='%s'"
,userid,key);
glocal.ui = atoi(row[0]);
glocal.active_ui = atoi(row[1]);
glocal.email = atoi(row[2]);
glocal.digest = atoi(row[3]);
}
if (glocal.msg.size() > 0){
rep_get_notification (false,glocal.msg,false,false,false,false);
}else{
rep_get_notification (true,"",glocal.ui,glocal.active_ui,glocal.email,glocal.digest);
}
// sessionid owner key ui:b active_ui:b email:b digest:b = success:b msg
(glocal.con,sessionid,owner,key,ui,active_ui,email,digest);
glocal.bod_client.rep_set_notification(success,msg);
// username = success:b msg exist:b
string msg;
bool pubdir;
if (bod_checkpublic(username,msg,pubdir) != -1){
glocal string website;
glocal string interest;
("select publish,website,interest from id2name"
" join userinfo on userinfo.userid=id2name.userid"
" where id2name.name='%s'",username);
if (atoi(row[0])){
glocal.website = row[1];
glocal.interest = row[2];
}
rep_public_checkuser(true,"",true,pubdir,glocal.website,glocal.interest);
}else{
rep_public_checkuser(true,"",false,false,"","");
}
// username dirpath offset:u nb:u = success:b msg files:U{FILEINFO}v
//glocal const char *username = username;
//glocal const char *dirpath = dirpath;
glocal unsigned offset = offset;
glocal unsigned nb = nb;
glocal string msg;
glocal vector files;
glocal map mp;
ENTRY entry;
string abspath;
entry.userid = bod_checkpublic(username,dirpath,glocal.msg,abspath);
if (entry.userid != (unsigned)-1){
if (fs_findentry(abspath,entry,true,"")==-1){
glocal.msg = entry.msg;
}else if (!bolixo_isdir(entry.type)){
glocal.msg = MSG_R(E_NOTDIR);
}else{
("select dirs_content.name,dirs_content.modified,type,id2name.name"
",group_list_id,group_lists.name,ids.listmode"
",length(files.content),dirs_content.itemid,files.title,dirs_content.eventtime"
",files.filetype,files.content is null"
" from dirs_content"
" join ids on dirs_content.itemid=ids.id"
" join id2name on ids.ownerid=id2name.userid"
" left join group_lists on ids.group_list_id=group_lists.id"
" left join files on dirs_content.itemid=files.id and dirs_content.modified=files.modified"
" where dirid=%d and dirs_content.eventtime <= '%s' order by eventtime"
,entry.entryid,END_OF_TIME);
const char *name = row[0];
const char *modified = row[1];
ENTRY_TYPE type = (ENTRY_TYPE) atoi(row[2]);
const char *owner = row[3];
const char *listid = row[4];
const char *listname = row[5];
const char *listmode = row[6] != NULL ? row[6] : "";
if (listname == NULL){
listname = "";
if (listid != NULL){
if (strcmp(listid,"0")==0){
listname = ALL_MAY_READ;
}
}
}
unsigned size=0;
if (row[7] != NULL){
size = atoi(row[7]);
}else if (bolixo_isfile(type)){
// Content stored as a file
size = fs_get_filesize(atoi(row[8]),modified);
}
const char *title = row[9] == NULL ? "" : row[9];
const char *eventdate = row[10];
FILE_TYPE file_type = row[11] == NULL ? FILE_UNKNOWN : (FILE_TYPE)atoi(row[11]);
bool islarge = atoi(row[12])!=0;
const char *modifiedby = "";
glocal.mp[name] = DIRENTRY(eventdate,modified,type,file_type,islarge
,owner,modifiedby,listname,listmode,size,title,VIEWED_NEW);
for (auto &x:glocal.mp){
if (!bolixo_isdeleted(x.second.type)){
FILEINFO info;
info.name = x.first;
info.type = x.second.type;
info.file_type = x.second.file_type;
info.eventdate = x.second.eventdate;
info.modified = x.second.modified;
info.owner = x.second.owner;
info.modifiedby = x.second.modifiedby;
info.listname = x.second.members;
info.listmode = x.second.listmode;
info.size = x.second.size;
info.title = x.second.title;
info.viewed = x.second.viewed;
info.islarge = x.second.islarge;
glocal.files.push_back(info);
}
}
}
}
if (glocal.msg.size() > 0){
glocal.files.clear();
rep_public_listdir (false,glocal.msg,glocal.files);
}else{
rep_public_listdir (true,"",glocal.files);
}
// username filepath offset:u = success:b msg content:o more:b size:u
glocal unsigned offset = offset;
glocal string msg;
glocal ENTRY entry;
string abspath;
glocal string handle;
glocal.entry.userid = bod_checkpublic(username,filepath,glocal.msg,abspath);
if (glocal.entry.userid != (unsigned)-1){
if (fs_findentry(abspath,glocal.entry,true,"")==-1){
glocal.msg = glocal.entry.msg;
}else if (!bolixo_isfile(glocal.entry.type)){
glocal.msg = MSG_R(E_NOTAFILE);
}else{
("select content,signature,modified,filetype from files where id=%d and modified='%s'"
,glocal.entry.entryid,glocal.entry.modified.c_str());
READINFO info;
info.signature = row[1] != NULL ? row[1] : "";
info.modified = row[2];
info.file_type = (FILE_TYPE)atoi(row[3]);
if (row[0] != NULL){
unsigned len = strlen(row[0]);
BOB_TYPE bob (row[0],len,false);
info.size = len;
glocal.bod_client.rep_public_readfile(true,"",bob,false,info,"");
}else{
FILE *fin = fs_alloc_file_handle (glocal.entry.entryid,info.modified,"r",glocal.handle,"public");
//string path = fs_createpath (glocal.entry.entryid,glocal.entry.modified);
//FILE *fin = fopen (path.c_str(),"r");
if (fin == NULL){
glocal.msg = "Internal error, reading content file";
}else{
struct stat64 st;
if (fstat64(fileno(fin),&st)==-1){
glocal.msg = "Internal error, getting file size";
}else if (glocal.offset >= st.st_size){
glocal.msg = MSG_U(E_OFFSETLARGE,"offset too large, no seek");
}else if (fseek(fin,glocal.offset,SEEK_SET)==-1){
glocal.msg = MSG_U(E_SEEKFAIL,"Seek failed");
}else{
char buf[REQ_CONTENT_CHUNK];
int len = fread (buf,1,sizeof(buf),fin);
BOB_TYPE bob (buf,len,false);
bool more = len==REQ_CONTENT_CHUNK;
if (!more){
fs_delete_handle(glocal.handle);
glocal.handle.clear();
}
info.size = st.st_size;
glocal.bod_client.rep_public_readfile (true,""
,bob,more,info,glocal.handle);
}
}
}
glocal.msg = "Internal error, reading table files";
}
}
if (glocal.msg.size() > 0){
BOB_TYPE empty;
READINFO info;
fs_delete_handle (glocal.handle);
rep_public_readfile(false,glocal.msg,empty,false,info,"");
}
// username offset:u nb:u = success:b msg messages:U{SHORTMSG}v
string msg;
vector msgs;
unsigned userid = bod_checkpublic(username,msg);
if (userid != (unsigned)-1){
bool deletes;
unsigned total;
unsigned nbnew;
bod_list_talk (userid,msgs,userid,username,"public",offset,nb,"",deletes,total,nbnew,msg);
}
if (msg.size() > 0){
msgs.clear();
rep_public_list_talk(false,msg,msgs);
}else{
rep_public_list_talk(true,msg,msgs);
}
// session form:U{FORMVARS} = success:b msg
// &FORMVAR name val
// &FORMVARS id vars:U{FORMVAR}v
glocal const char *sessionid = sessionid;
glocal const FORMVARS_receive *form = &form;
glocal string msg;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, "", glocal.msg);
if (userid != 0){
glocal unsigned id=(unsigned)-1;
("select id from temp.formids where sessionid='%s' and formid='%s'",sessionid,form.id);
if (sql_action("insert into temp.formids (sessionid,formid) values ('%s','%s')"
,glocal.sessionid,glocal.form->id)==-1){
glocal.msg = "Internal error, can insert into formids";
}else{
glocal.id = query_getdefaultdb()->getlastid();
}
glocal.id = atoi(row[0]);
if (sql_action("delete from temp.formvars where id=%u",glocal.id)==-1){
glocal.msg = "Internal error, can't delete in formvars";
}
if (glocal.id != (unsigned)-1){
for (auto &v:form.vars){
if (sql_action("insert into temp.formvars (id,name,val) values (%u,'%s','%s')"
,glocal.id,v.name,v.val)==-1){
glocal.msg = "Internal error, can't insert in table formvars";
break;
}
}
}
}
if (glocal.msg.size() > 0){
rep_form_savevar(false,glocal.msg);
}else{
rep_form_savevar(true,"");
}
// sessionid id = success:b msg vars:U{FORMVAR}v
glocal vector vars;
glocal string msg;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, "", glocal.msg);
if (userid != 0){
// It is not a bug if there is none
("select id from temp.formids where sessionid='%s' and formid='%s'",sessionid,id);
("select name,val from temp.formvars where id=%s",row[0]);
FORMVAR v;
v.name = row[0];
v.val = row[1];
glocal.vars.push_back(v);
}
if (glocal.msg.size() > 0){
glocal.vars.clear();
rep_form_readvar(false,glocal.msg,glocal.vars);
}else{
rep_form_readvar(true,"",glocal.vars);
}
// sessiond id = success:b msg
glocal string msg;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, "", glocal.msg);
if (userid != 0){
("select id from temp.formids where sessionid='%s' and formid='%s'",sessionid,id);
if (sql_action("delete from temp.formvars where id=%s",row[0])==-1){
glocal.msg = "Internal error, can't delete from formvars table";
}else if (sql_action("delete from temp.formids where id=%s",row[0])==-1){
glocal.msg = "Internal error, can't delete from formids table";
}
}
if (glocal.msg.size() > 0){
rep_form_deletevar(false,glocal.msg);
}else{
rep_form_deletevar(true,"");
}
// session = success:b msg
glocal string msg;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, "", glocal.msg);
if (userid != 0){
("select id from temp.formids where sessionid='%s'",sessionid);
if (sql_action("delete from temp.formvars where id=%s",row[0])==-1){
glocal.msg = "Internal error, can't delete from formvars table";
}else if (sql_action("delete from temp.formids where id=%s",row[0])==-1){
glocal.msg = "Internal error, can't delete from formids table";
}
}
if (glocal.msg.size() > 0){
rep_form_deleteall(false,glocal.msg);
}else{
rep_form_deleteall(true,"");
}
glocal string pubkey;
("select pub_key from id2name where userid=-2");
if (row[0] != NULL) glocal.pubkey = row[0];
rep_systempubkey (glocal.pubkey);
glocal string msg;
string pubkey;
if (bod_getpubkey(nodename,pubkey)==-1){
glocal.msg = "Can't get public key";
}else{
(glocal.con,nodename,pubkey);
}
if (glocal.msg.size() > 0){
rep_registernode(false,glocal.msg);
}else{
rep_registernode(true,"");
}
// msg = sign
glocal string sign;
glocal string msg;
glocal bool success = false;
(glocal.con,msg);
glocal.success = success;
glocal.msg = msg;
glocal.sign = sign;
rep_systemsign (glocal.success,glocal.msg, glocal.sign);
// nodename = success:b msg session
(glocal.con,nodename);
glocal.bod_client.rep_nodelogin(success,msg,session);
// session sign = success:b msg
(glocal.con,session,sign);
glocal.bod_client.rep_nodepass(success,msg);
// user = success:b msg sessionid
// We simply create a session, but we check that the user do exist in id2name.
// The caller will sign the session with the private key and get back
// using remotepass.
glocal string msg;
glocal string sessionid;
string remote_user;
string remote;
string local_user;
// If the remote user does not exist, the account is created on the fly. But the remote user
// has to exist on his server and must have a public key.
if (bod_check_remote_user(glocal.con,user,glocal.nodename,glocal.msg
,local_user,remote_user,remote)!=-1){
if (remote.size() == 0){
glocal.msg = MSG_R(E_IVLDUSER);
}else{
(glocal.con);
glocal.sessionid = sessionid;
}
}
if (glocal.msg.size()>0){
rep_remotelogin (false,glocal.msg,"");
}else{
rep_remotelogin (true,"",glocal.sessionid);
}
// sessionid user sign = success:b msg
glocal string msg;
(glocal.con,sessionid,user,sign);
if (!success) glocal.msg = msg;
if (glocal.msg.size() > 0){
rep_remotepass(false,glocal.msg);
}else{
rep_remotepass(true,"");
}
// sessionid user owner = success:b msg info:U{USERPUBLICINFO}
glocal USERPUBLICINFO info;
glocal string msg;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, "", glocal.msg);
if (userid != 0){
("select publish,bosite_visible,fullname,address1,address2"
",city,zipcode,state,country,email,phone,fax,website,interest"
",publish_photo,publish_miniphoto"
" from userinfo where userid=%u",userid);
glocal.info.publish = atoi(row[0]);
glocal.info.bosite_visible = atoi(row[1]);
glocal.info.fullname = row[2];
glocal.info.address1 = row[3];
glocal.info.address2 = row[4];
glocal.info.city = row[5];
glocal.info.zipcode = row[6];
glocal.info.state = row[7];
glocal.info.country = row[8];
glocal.info.email = row[9];
glocal.info.phone = row[10];
glocal.info.fax = row[11];
glocal.info.website = row[12];
glocal.info.interest = row[13];
glocal.info.photo = atoi(row[14]);
glocal.info.mini_photo = atoi(row[15]);
}
if (glocal.msg.size() > 0){
glocal.info.clear();
rep_info_read (false,glocal.msg,glocal.info);
}else{
rep_info_read (true,"",glocal.info);
}
// sessionid user owner info:U{USERPUBLICINFO} = success:b msg
glocal string msg;
string username;
unsigned userid = bo_writed_get_group_owner (glocal.con_sess, sessionid, "", username,glocal.msg);
if (userid != 0){
(glocal.con,sessionid,owner,info);
glocal.msg = msg;
}
if (glocal.msg.size() != 0){
rep_info_write(false,glocal.msg);
}else{
rep_info_write(true,"");
bod_publish (glocal.con,username,info,glocal.dirserver,glocal.nodename);
}
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());
glocal bool sessiond1=false;
glocal bool sessiond2=false;
glocal bool writed = false;
glocal bool bdfiles1 = false;
glocal bool bdfiles2 = false;
glocal bool bdusers = false;
glocal bool keysd = false;
glocal bool fsok = false;
glocal bool publish_dbfiles = false;
glocal bool publish_fsok = false;
glocal bool internal_error1 = false;
(glocal.con_sess);
glocal.internal_error1 = internal_error;
glocal.sessiond1 = success;
(glocal.con);
glocal.writed = !internal_error;
glocal.sessiond2 = sessiond;
glocal.bdusers = bdusers;
glocal.bdfiles2 = bdfiles;
glocal.keysd = keysd;
glocal.fsok = fsok;
glocal.publish_dbfiles = publish_dbfiles;
glocal.publish_fsok = publish_fsok;
("select count(*) from id2name");
glocal.bdfiles1=true;
glocal.bod_admin.rep_test(glocal.internal_error1
,glocal.writed,glocal.bdfiles1,glocal.bdfiles2,glocal.bdusers
,glocal.sessiond1,glocal.sessiond2,glocal.keysd,glocal.fsok
,glocal.publish_dbfiles,glocal.publish_fsok);
tlmp_error ("ADMIN: Invalid command: %s\n",line);
endclient = true;
}
bool some_errors = false;
if (control != NULL && fdpass_setcontrol(s,control,user)==-1){
some_errors = true;
}
if (s.listen(NULL,glocal.unixportadmin)==-1){
tlmp_error ("Can't setup socket on %s (%s)\n",glocal.unixportadmin.c_str(),strerror(errno));
some_errors = true;
}
if (!some_errors && s.is_ok()){
chmod (glocal.unixportclient.c_str()+5,0666);
chmod (glocal.unixportadmin.c_str()+5,0666);
s.setrawmode (true);
if (daemon){
daemon_init(pidfile,user);
}
open_instrument_file (glocal.instrument_file);
s.loop();
ret = 0;
}
return ret;
}
int main (int argc, char *argv[])
{
glocal int ret = -1;
glocal const char *admin_secretfile = "/etc/bolixo/secrets.admin";
glocal const char *client_secretfile = "/etc/bolixo/secrets.client";
glocal const char *bind = "0.0.0.0";
glocal const char *port = "9000";
glocal const char *control = "/var/run/bod.sock";
glocal const char *adminhost = "127.0.0.3";
glocal const char *adminport = "9100";
glocal const char *sesshost = "127.0.0.4";
glocal const char *sessport = "9200";
glocal const char *user = "bolixo";
glocal const char *mysecret = NULL;
glocal const char *dbserv = "localhost";
glocal const char *dbname = "trli";
glocal const char *dbuser = NULL;
glocal const char *sql_tcpport = NULL;
glocal bool daemon = false;
glocal unsigned maxaccts = 200;
glocal int workers = 1;
glocal const char *pidfile = "/var/run/bod.pid";
glocal const char *nodename = NULL; // Who am I
glocal const char *dirserver = "https://bolixo.org"; // Directory server for all nodes
signal (SIGPIPE,SIG_IGN);
translat_setlang("eng,fr");
static const char *tbdict[]={"bolixo","tlmpsql",NULL};
glocal.ret = (argc,argv,tbdict);
setproginfo ("bod",VERSION,"Implement all business logic");
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 trlid-control",glocal.control,false);
setgrouparg ("Admin server");
setarg (' ',"adminhost","Host running the bo-writed server",glocal.adminhost,false);
setarg (' ',"adminport","Port to reach the bo-writed server",glocal.adminport,false);
setarg (' ',"sesshost","Host running the bo-sessiond server",glocal.sesshost,false);
setarg (' ',"sessport",MSG_U(O_SESSPORT,"Port or Unix socket to reach the bo-sessiond server"),glocal.sessport,false);
setarg (' ',"mysecret",MSG_U(O_MYSECRET,"Secret used to communicate with other services"),glocal.mysecret,true);
setgrouparg ("Misc.");
setarg (' ',"admin_secrets",MSG_U(O_ADMINSECRETS,"File holding admin secrets for communication"),glocal.admin_secretfile,false);
setarg (' ',"client_secrets",MSG_U(O_CLIENTSECRETS,"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 (' ',"workers","Number of sub-process to lauch",glocal.workers,false);
setarg (' ',"pidfile","File holding the PID of the process",glocal.pidfile,false);
setarg (' ',"nodename",MSG_U(O_NODENAME,"My node name (URL)"),glocal.nodename,true);
setarg (' ',"dirserver",MSG_U(O_DIRSERVE,"Directory server"),glocal.dirserver,false);
setarg (' ',"maxaccts",MSG_U(O_MAXACCOUNTS,"Maximum accounts on this server"),glocal.maxaccts,false);
setarg (' ',"nonstrict",MSG_R(O_NONSTRICT),nonstrict,false);
setarg (' ',"usehttp",MSG_U(O_USEHTTP,"Use HTTP (not HTTPS) to reach this node"),usehttp,false);
setarg (' ',"keepmsgs",MSG_U(O_KEEPMSGS,"Show only messages younger than N days"),keepdays,false);
setgrouparg ("Database");
setarg (' ',"dbserv","Database server",glocal.dbserv,false);
setarg (' ',"dbname","Database name",glocal.dbname,false);
setarg (' ',"dbuser","Database user",glocal.dbuser,true);
setarg (' ',"sqltcpport","Database TCP port",glocal.sql_tcpport,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 CONNECT_INFO con;
glocal CONNECT_INFO con_sess;
glocal map admin_secrets;
glocal map client_secrets;
fdpass_readsecrets (glocal.client_secretfile,glocal.client_secrets);
fdpass_readsecrets (glocal.admin_secretfile,glocal.admin_secrets);
//glocal.con.host = glocal.adminhost;
glocal.con.port = glocal.adminport;
glocal.con.secret = glocal.mysecret;
glocal.con.bind = glocal.bind;
//glocal.con_sess.host = glocal.sesshost;
glocal.con_sess.port = glocal.sessport;
glocal.con_sess.secret = glocal.mysecret;
glocal.con_sess.bind = glocal.bind;
if (glocal.sql_tcpport != NULL) nsql_settcpport (atoi(glocal.sql_tcpport));
{
const char *dbpass = getenv("BOD_PWD");
if (dbpass == NULL){
tlmp_error ("Can't get database password from environment,aborting\n");
exit (-1);
}
query_setdefaultdb (glocal.dbserv,glocal.dbname,glocal.dbuser,dbpass);
}
int ret = -1;
if (glocal.workers==1){
ret = bod_main (glocal.bind,glocal.port,glocal.control
,glocal.con,glocal.con_sess,glocal.user,glocal.pidfile,glocal.daemon
,glocal.admin_secrets,glocal.client_secrets,glocal.nodename,glocal.dirserver
,glocal.maxaccts);
}else{
int port = atoi(glocal.port);
for (int w=0; w
return glocal.ret;
}