#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>
#include <jsoncpp/json/json.h>
#include <mysql/my_global.h>
#include <mysql/mysql.h>
MYSQL* sql;
const char* rand_ch="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Json::Value do_login(Json::Value o)
{
MYSQL_RES* sqlr=NULL;
MYSQL_ROW row;
char q[256];
Json::Value ret;
std::string usrname=o["username"].asString();
std::string passwd=o["passwd"].asString();
std::string sessname=o["sessionname"].asString();
std::string token;
std::string qpwd;
if(usrname.length()<1){ret["result"]=1;goto fail;}
for(size_t i=0;i<usrname.length();++i)if(!isalnum(usrname[i])&&usrname[i]!='-'&&usrname[i]!='_'){ret["result"]=1;goto fail;}
for(size_t i=0;i<passwd.length();++i)if(!isalnum(passwd[i])){ret["result"]=1;goto fail;}
for(size_t i=0;i<sessname.length();++i)if(!isalnum(sessname[i])&&sessname[i]!='-'&&sessname[i]!='_'){ret["result"]=1;goto fail;}
snprintf(q,256,"select password from navigator_user where username='%s'",usrname.c_str());
mysql_query(sql,q);
sqlr=mysql_store_result(sql);
if(mysql_num_rows(sqlr)!=1){ret["result"]=4;goto fail;}
row=mysql_fetch_row(sqlr);
qpwd=std::string(row[0],64);
if(passwd!=qpwd)
{
ret["result"]=1;
goto fail;
}
mysql_free_result(sqlr);sqlr=NULL;
if(!sessname.length())
{
snprintf(q,256,"delete from navigator_session where sessionname='' and username='%s'",usrname.c_str());
mysql_query(sql,q);
mysql_commit(sql);
}
else
{
snprintf(q,256,"select sessionname from navigator_session where sessionname='%s' and username='%s'",
sessname.c_str(),usrname.c_str());
mysql_query(sql,q);
sqlr=mysql_store_result(sql);
if(mysql_num_rows(sqlr)>0){ret["result"]=2;goto fail;}
mysql_free_result(sqlr);sqlr=NULL;
}
do{
if(sqlr){mysql_free_result(sqlr);sqlr=NULL;}
token="";
for(int i=0;i<32;++i)token.push_back(rand_ch[rand()%62]);
snprintf(q,256,"select token from navigator_session where token='%s'",
token.c_str());
mysql_query(sql,q);
sqlr=mysql_store_result(sql);
}while(mysql_num_rows(sqlr));
mysql_free_result(sqlr);sqlr=NULL;
snprintf(q,256,"insert into navigator_session values('%s','%s','%s',%lld)",
usrname.c_str(),
token.c_str(),
sessname.c_str(),
time(NULL)
);
mysql_query(sql,q);
mysql_commit(sql);
ret["result"]=0;
ret["token"]=token;
fail:
if(sqlr){mysql_free_result(sqlr);sqlr=NULL;}
return ret;
}
const int tsl[]={1800,86400,604800,1296000,2592000,5184000,7776000,15552000,31104000};
void set_session_length(std::string user,int c)
{
char q[256];
if(c>7)c=7;if(c<0)c=0;
snprintf(q,256,"update navigator_user set session_length=%d where username='%s'",user.c_str());
mysql_query(sql,q);
mysql_commit(sql);
}
int get_session_length(std::string user)
{
MYSQL_RES* sqlr=NULL;
MYSQL_ROW row;
char q[256];
snprintf(q,256,"select session_length from navigator_user where username='%s'",user.c_str());
mysql_query(sql,q);
sqlr=mysql_store_result(sql);
if(mysql_num_rows(sqlr)!=1){mysql_free_result(sqlr);return -1;}
row=mysql_fetch_row(sqlr);
int sl=atoi(row[0]);
mysql_free_result(sqlr);
return sl;
}
std::string authenticate(std::string token)
{
MYSQL_RES* sqlr=NULL;
MYSQL_ROW row;
char q[256];
if(token.length()!=32)return "";
snprintf(q,256,"select username,sessionname,date from navigator_session where token='%s'",token.c_str());
mysql_query(sql,q);
sqlr=mysql_store_result(sql);
if(mysql_num_rows(sqlr)!=1){mysql_free_result(sqlr);return "";}
row=mysql_fetch_row(sqlr);
std::string ret=std::string(row[0]);
bool is_temporary=strlen(row[1])>0;
long long d=atoll(row[2]);
mysql_free_result(sqlr);
int sl=get_session_length(ret);
if(sl<0||sl>7){return "";}
int reall=tsl[is_temporary?0:sl+1];
if(time(NULL)-d>reall)
{
snprintf(q,256,"delete from navigator_session where token='%s'",token.c_str());
mysql_query(sql,q);
mysql_commit(sql);
return "";
}
return ret;
}
Json::Value set_option(Json::Value o)
{
Json::Value ret;
std::string token=o["token"].asString();
std::string usr=authenticate(token);
int opt=o["option"].asInt();
int val=o["value"].asInt();
if(!usr.length()){ret["result"]=1;goto fail;}
switch(opt)
{
case 0:
ret["result"]=0;
set_session_length(usr,val);
break;
default:
ret["result"]=1;
}
fail:
return ret;
}
Json::Value get_option(Json::Value o)
{
Json::Value ret;
std::string token=o["token"].asString();
std::string usr=authenticate(token);
int opt=o["option"].asInt();
if(!usr.length()){ret["result"]=1;goto fail;}
switch(opt)
{
case 0:
ret["result"]=0;
ret["value"]=get_session_length(usr);
break;
default:
ret["result"]=1;
}
fail:
return ret;
}
Json::Value get_bookmarks(Json::Value o)
{
MYSQL_RES* sqlr=NULL;
MYSQL_ROW row;
char q[256];
Json::Value ret;
std::string token=o["token"].asString();
std::string usr=authenticate(token);
if(!usr.length()){ret["result"]=1;goto fail;}
snprintf(q,256,"select bookmarks from navigator_user where username='%s'",usr.c_str());
mysql_query(sql,q);
sqlr=mysql_store_result(sql);
if(mysql_num_rows(sqlr)!=1){mysql_free_result(sqlr);sqlr=NULL;ret["result"]=4;goto fail;}
row=mysql_fetch_row(sqlr);
ret["result"]=0;
ret["bookmarks"]=std::string(row[0]);
mysql_free_result(sqlr);sqlr=NULL;
fail:
if(sqlr){mysql_free_result(sqlr);sqlr=NULL;}
return ret;
}
Json::Value set_bookmarks(Json::Value o)
{
char *q=(char*)malloc(65537);
Json::Value ret;
std::string token=o["token"].asString();
std::string usr=authenticate(token);
if(!usr.length()){ret["result"]=1;goto fail;}
snprintf(q,65536,"update navigator_user set bookmarks='%s' where username='%s'",o["bookmarks"].asString().c_str(),usr.c_str());
mysql_query(sql,q);
mysql_commit(sql);
ret["result"]=0;
fail:
free(q);
return ret;
}
Json::Value list_sessions(Json::Value o)
{
MYSQL_RES* sqlr=NULL;
MYSQL_ROW row;
char q[256];
Json::Value ret,ss;
std::string token=o["token"].asString();
std::string usr=authenticate(token);
if(!usr.length()){ret["result"]=1;goto fail;}
snprintf(q,256,"select sessionname,date from navigator_session where username='%s'",usr.c_str());
mysql_query(sql,q);
sqlr=mysql_store_result(sql);
for(int i=0;row=mysql_fetch_row(sqlr);++i)
{
Json::Value c;
c["sessionname"]=std::string(row[0]);
c["date"]=atoi(row[1]);
ss[i]=c;
}
mysql_free_result(sqlr);sqlr=NULL;
ret["result"]=0;ret["sessions"]=ss;
fail:
return ret;
}
Json::Value remove_session(Json::Value o)
{
char q[256];
Json::Value ret;
std::string token=o["token"].asString();
std::string usr=authenticate(token);
std::string sess=o["session"].asString();
if(!usr.length()){ret["result"]=1;goto fail;}
snprintf(q,256,"delete from navigator_session where username='%s' and sessionname='%s'",usr.c_str(),sess.c_str());
mysql_query(sql,q);
mysql_commit(sql);
ret["result"]=0;
fail:
return ret;
}
int main()
{
if(!getenv("CONTENT_LENGTH"))return -1;
int len=atoi(getenv("CONTENT_LENGTH"));
char *buf;buf=(char*)malloc(len+1);
fread(buf,1,len,stdin);buf[len]=0;
std::string sbuf(buf,len);
free(buf);
std::stringstream ss(sbuf);
Json::Value o,r;ss>>o;
sql=mysql_init(NULL);
if(!sql)return -1;
if(!mysql_real_connect(sql,"localhost","chrisoft",NULL,"chrisoft",0,"/var/run/mysqld/mysqld.sock",0))
return -1;
switch(o.get("op",-1).asInt())
{
case 0://login
r=do_login(o);
break;
case 1://get bookmarks
r=get_bookmarks(o);
break;
case 2://set bookmarks
r=set_bookmarks(o);
break;
case 3://list sessions
r=list_sessions(o);
break;
case 4://remove session
r=remove_session(o);
break;
case 5://set option
r=set_option(o);
break;
case 6://get option
r=get_option(o);
break;
}
printf("Status: 200 OK\r\n");
printf("Content-type: application/json; charset=utf-8\r\n\r\n");
std::ostringstream oss;
oss<<r;
fputs(oss.str().c_str(),stdout);
return 0;
}
//TODO: set cookie in HTTP header
/*
* session_length:
* 0=1d=86400
* 1=7d=604800
* 2=15d=1296000
* 3=30d=2592000
* 4=60d=5184000
* 5=90d=7776000
* 6=180d=15552000
* 7=360d=31104000
*/
/*navigator_user:
* username:varchar(PRI) passwd:char(64) bookmarks:text
*navigator_session:
* username:varchar token:char(32)(PRI) sessionname:varchar date:bigint
*/
/*
create table navigator_user(
username varchar(32),
password char(64),
bookmarks text,
session_length int,
primary key(username)
);
create table navigator_session(
username varchar(32) not null,
token char(32),
sessionname varchar(32),
date bigint,
primary key(token)
);
insert into navigator_user values('chrisoft','8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92','',6);
*/