aboutsummaryrefslogtreecommitdiff
path: root/xp
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2019-02-09 00:04:45 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2019-02-09 00:04:45 +0800
commit9d0b51a2abdacb5fc4eed95cb3249934b607fdc7 (patch)
tree298358cf2685f8ef87feaa3b4e46348d69929377 /xp
parente1af5e214c389aea2b55daf82bdec92472db3f19 (diff)
downloadoddities-9d0b51a2abdacb5fc4eed95cb3249934b607fdc7.tar.xz
Navigator.
Diffstat (limited to 'xp')
-rw-r--r--xp/navigator/cgi-src/navigator_cgi.cpp329
-rw-r--r--xp/navigator/index.html133
-rw-r--r--xp/navigator/main.js435
-rw-r--r--xp/navigator/sha256.js173
-rw-r--r--xp/navigator/shit.php38
5 files changed, 1108 insertions, 0 deletions
diff --git a/xp/navigator/cgi-src/navigator_cgi.cpp b/xp/navigator/cgi-src/navigator_cgi.cpp
new file mode 100644
index 0000000..20e152e
--- /dev/null
+++ b/xp/navigator/cgi-src/navigator_cgi.cpp
@@ -0,0 +1,329 @@
+#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);
+*/
diff --git a/xp/navigator/index.html b/xp/navigator/index.html
new file mode 100644
index 0000000..6ae2586
--- /dev/null
+++ b/xp/navigator/index.html
@@ -0,0 +1,133 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta name="viewport" content="width=device-width">
+<title>Chrisoft::Navigator</title>
+<link rel="icon" href="/favicon.png">
+<link rel="stylesheet" type="text/css" href="/common.css">
+<link rel="stylesheet" type="text/css" href="/panel.css">
+<style>
+input[type=text],input[type=password]{
+ margin-left:5px;
+ display:table-cell;
+ max-width:40%;
+ border:none;
+ border-bottom:2px solid #4A4;
+ outline:none !important;
+ background-color:rgba(0,0,0,0) !important;
+}
+button{
+ border:none;
+ color:white;
+ padding:0.5em 2em;
+ text-align:center;
+ background-color:#4A4;
+ -webkit-transition-duration:0.4s;
+ transition-duration:0.4s;
+}
+ul#container li{
+ padding:1em;
+}
+.ghost{
+ opacity:0.4;
+}
+a.listitem{
+ padding-left:160px !important;
+ margin-left:-160px;
+}
+ul#folderlist{
+ list-style-type:none;
+}
+</style>
+</head>
+<!--
+The source code of this site, including all HTML, JavaScript and CSS
+files, are licensed under the terms of the Expat(MIT) License.
+Contents are licensed under the CC BY-SA 4.0 license.
+-->
+<body onload="init()">
+<script type="text/javascript" src="/panel.js"></script>
+<script type="text/javascript" src="main.js"></script>
+<script type="text/javascript" src="sha256.js"></script>
+<script type="text/javascript">
+function changeImage(img,e){
+ document.getElementById(e).src=img;
+}
+function changeTheme(thm){
+ document.cookie="thm="+thm;
+}
+var x1,y1,x2,y2;
+var link=document.createElement("link");
+var thm=document.cookie.replace(new RegExp("(?:(?:^|.*;\\s*)thm\\s*\\=\\s*([^;]*).*$)|^.*$"),"$1");
+switch(thm)
+{
+ case "day":
+ link.href="/colors-day.css";
+ break;
+ case "night":
+ link.href="/colors-night.css";
+ break;
+ case "auto":
+ default:
+ var c=new Date();
+ if(c.getHours()>=6&&c.getHours()<18)
+ link.href = "/colors-day.css";
+ else
+ link.href = "/colors-night.css";
+ break;
+}
+link.type="text/css";
+link.rel="stylesheet";
+document.getElementsByTagName("head")[0].appendChild(link);
+</script>
+ <div id="panel" class="TText">
+ <ul id="panellist">
+ <li><a href="/"><h1>Chrisoft</h1></a></li>
+ <li><a href="#" onclick="">Navigator</a></li>
+ <ul id="folderlist">
+ </ul>
+ <li><a href="#settings" onclick="showsettings()">Settings</a></li>
+ </ul>
+ </div>
+ <div id="content" class="TText">
+ <div class="block" id="login">
+ <h2>Login</h2><hr>
+ <form style="text-align:center;" class="TText">
+ <input class="TText" type="text" placeholder="Username" id="usrname"></input><br><br>
+ <input class="TText" type="password" placeholder="Password" id="passwd"></input><br><br>
+ <input class="TText" type="text" placeholder="Session name" id="session"></input><br><br>
+ <span id="loginerr" style="color:red;"></span><br><br>
+ <button class="TText" type="button" onclick="login()">Login</button>
+ </form>
+ </div>
+ <div class="block" id="main">
+ <ul id="container" style="list-style:none;">
+ </ul>
+ </div>
+ <div class="block" id="settings">
+ <h2>Sessions</h2>
+ <table style="text-align:center;">
+ <thead>
+ <tr>
+ <th>Session name</th>
+ <th>Login date</th>
+ <th>Invalidate session</th>
+ </tr>
+ </thead>
+ <tbody id="sessions">
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <div id="templates" style="display:none;">
+ <div id="_lnkmod">
+ <input class="TText" type="text" placeholder="Name" id="_lnkname" style="max-width:auto;width:45%;" onkeypress="kp(event)"></input>
+ <input class="TText" type="text" placeholder="URL" id="_lnkhref" style="max-width:auto;width:45%;" onkeypress="kp(event)"></input>
+ </div>
+ <div id="_fdrmod">
+ <input class="TText" type="text" placeholder="Name" id="_fdrname" style="max-width:auto;width:90%;" onkeypress="kp(event)"></input>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/xp/navigator/main.js b/xp/navigator/main.js
new file mode 100644
index 0000000..c17d025
--- /dev/null
+++ b/xp/navigator/main.js
@@ -0,0 +1,435 @@
+let o={};
+const ui={};
+let curo,curp;
+let editinge=null;
+function getcookie(key)
+{return document.cookie.replace(new RegExp('(?:(?:^|.*;\\s*)'+key+'\\s*\\=\\s*([^;]*).*$)|^.*$'),'$1');}
+function make_sortable(e,updatefunc)
+{
+ let dragging,nxt;
+ function _drag(ev)
+ {
+ ev.preventDefault();
+ ev.dataTransfer.dropEffect='move';
+ const targ=ev.target;
+ if(targ&&targ!=dragging&&targ.draggable&&targ.parentNode==e)
+ {
+ const rect=targ.getBoundingClientRect();
+ const after=(ev.clientY-rect.top)/(rect.bottom-rect.top)>.5;
+ e.insertBefore(dragging,after?targ.nextSibling:targ);
+ }
+ }
+ function _drag_end(ev)
+ {
+ ev.preventDefault();
+ dragging.classList.remove('ghost');
+ e.removeEventListener('dragover',_drag,false);
+ e.removeEventListener('dragend',_drag_end,false);
+ if(nxt!=dragging.nextSibling)updatefunc(dragging);
+ }
+ function _drag_start(ev)
+ {
+ dragging=ev.target;
+ nxt=dragging.nextSibling;
+ ev.dataTransfer.effectAllowed='move';
+ ev.dataTransfer.setData('Text',dragging.textContent);
+ e.addEventListener('dragover',_drag,false);
+ e.addEventListener('dragend',_drag_end,false);
+ setTimeout(function(){dragging.classList.add('ghost');},0);
+ }
+ e.addEventListener('dragstart',_drag_start,false);
+}
+function remove_children(e)
+{
+ while(e.firstChild)e.removeChild(e.firstChild);
+}
+function switch_folder(path)
+{
+ //path starts and ends with /
+ ui.main.style.display='block';
+ ui.settings.style.display=null;
+ const p=path.split('/');
+ remove_children(ui.cont);
+ let cur=o;
+ for(let i=1;cur&&i<p.length-1;++i)
+ {
+ if(cur.type!='folder')break;
+ let k=-1;
+ for(let j=0;j<cur.content.length;++j)
+ if(cur.content[j].name==p[i]){k=j;break;}
+ if(k==-1||cur.content[k].type!='folder')break;
+ cur=cur.content[k];
+ }
+ curo=cur;
+ for(let i=0;i<curo.content.length;++i)
+ {
+ const e=document.createElement('li');
+ const a=document.createElement('a');
+ a.innerHTML=e.contname=curo.content[i].name;
+ if(curo.content[i].href)
+ a.href=curo.content[i].href;
+ e.draggable=true;
+ e.ondblclick=function(e){
+ edit(this);
+ }
+ e.appendChild(a);
+ if(curo.content[i].type=='folder')
+ {
+ e.style.backgroundColor='rgba(255,255,128,0.4)';
+ e.isfolder=true;
+ a.onclick=function(e){
+ e.preventDefault();
+ switch_folder(curp+=this.innerHTML+'/');
+ }
+ }
+ ui.cont.appendChild(e);
+ }
+ const e=document.createElement('li');
+ e.innerHTML='+';
+ e.ondblclick=function(e){
+ const ne=document.createElement('li');
+ const na=document.createElement('a');
+ ne.draggable=true;
+ ne.ondblclick=function(e){
+ edit(this);
+ }
+ ne.appendChild(na);
+ ui.cont.insertBefore(ne,ui.cont.lastChild);
+ edit(ne);
+ }
+ ui.cont.appendChild(e);
+ make_sortable(ui.cont,function(x){move(ui.cont,curo,x.contname);})
+}
+function edit(e)
+{
+ if(e==editinge)return;
+ if(editinge)finish_edit();
+ let clon=ui.templates.lnkmod;
+ if(e.isfolder||e.parentNode==ui.folderlist)
+ clon=ui.templates.fdrmod;
+ const cloned=clon.cloneNode(true);
+ cloned.id=cloned.id.substring(1);
+ for(let i=0;i<cloned.childNodes.length;++i)
+ if(cloned.childNodes[i].id)
+ {
+ cloned.childNodes[i].id=cloned.childNodes[i].id.substring(1);
+ }
+ e.firstChild.style.display='none';
+ e.appendChild(cloned);
+ if(e.isfolder||e.parentNode==ui.folderlist)
+ {
+ if(e.contname)document.getElementById('fdrname').value=e.contname;
+ document.getElementById('fdrname').focus();
+ }
+ else
+ {
+ if(e.contname)document.getElementById('lnkname').value=e.contname;
+ if(e.contname)document.getElementById('lnkhref').value=e.querySelector('a').href;
+ document.getElementById('lnkname').focus();
+ }
+ editinge=e;
+}
+function finish_edit()
+{
+ if(editinge)
+ {
+ const e=document.getElementById('lnkmod')||document.getElementById('fdrmod');
+ if(e.parentNode==editinge)
+ editinge.removeChild(e);
+ editinge.firstChild.style.display=null;
+ }
+ editinge=null;
+}
+function confirm_edit()
+{
+ if(!editinge)return;
+ if(editinge.parentNode==ui.folderlist)
+ {
+ if(editinge.contname)
+ {
+ for(let i=0;i<o.content.length;++i)
+ if(o.content[i].name==editinge.contname)
+ {
+ if(!document.getElementById('fdrname').value.length)
+ {
+ o.content.splice(i,1);
+ editinge.parentNode.removeChild(editinge);
+ editinge=null;
+ return;
+ }
+ o.content[i].name=document.getElementById('fdrname').value;
+ editinge.querySelector('a').innerHTML=editinge.contname=
+ o.content[i].name;
+ break;
+ }
+ }
+ else
+ {
+ if(!document.getElementById('fdrname').value.length)
+ {
+ finish_edit();
+ editinge.parentNode.removeChild(editinge);
+ }
+ let newo={};
+ newo.type='folder';
+ newo.name=document.getElementById('fdrname').value;
+ newo.content=[];
+ o.content.push(newo);
+ editinge.querySelector('a').innerHTML=editinge.contname=newo.name;
+ }
+ }
+ else
+ {
+ if(editinge.contname)
+ {
+ for(let i=0;i<curo.content.length;++i)
+ if(curo.content[i].name==editinge.contname)
+ {
+ curo.content[i].name=document.getElementById(editinge.isfolder?'fdrname':'lnkname').value;
+ if(!curo.content[i].name)
+ {
+ curo.content.splice(i,1);
+ editinge.parentNode.removeChild(editinge);
+ editinge=null;
+ return;
+ }
+ editinge.querySelector('a').innerHTML=editinge.contname=
+ curo.content[i].name;
+ if(!editinge.isfolder)
+ {
+ curo.content[i].href=document.getElementById('lnkhref').value;
+ editinge.querySelector('a').href=curo.content[i].href;
+ }
+ break;
+ }
+ }
+ else
+ {
+ if(!document.getElementById('lnkname').value.length)
+ {
+ finish_edit();
+ editinge.parentNode.removeChild(editinge);
+ }
+ let newo={};
+ newo.type=document.getElementById('lnkhref').value.length?'link':'folder';
+ newo.name=document.getElementById('lnkname').value;
+ editinge.querySelector('a').innerHTML=editinge.contname=newo.name;
+ if(newo.type=='folder')
+ {
+ newo.content=[];
+ editinge.style.backgroundColor='rgba(255,255,128,0.4)';
+ editinge.isfolder=true;
+ editinge.querySelector('a').onclick=function(e){
+ e.preventDefault();
+ switch_folder(curp+=this.innerHTML+'/');
+ }
+ }
+ else
+ {
+ newo.href=document.getElementById('lnkhref').value;
+ editinge.querySelector('a').href=newo.href;
+ }
+ curo.content.push(newo);
+ }
+ }
+ finish_edit();
+}
+function kp(e)
+{
+ e=e||window.event;
+ const keyCode=e.keyCode||e.which;
+ if(keyCode==13)confirm_edit();
+ if(keyCode==27)finish_edit();
+}
+function move(el,o,name)
+{
+ let origpos=-1,newpos=-1;
+ const qs=el.querySelectorAll('li');
+ for(let i=0;i<qs.length;++i)
+ if(qs[i].contname==name){newpos=i;break;}
+ for(let i=0;i<o.content.length;++i)
+ if(o.content[i].name==name){origpos=i;break;}
+ const ob=o.content.splice(origpos,1);
+ o.content.splice(newpos,0,ob[0]);
+}
+async function load_bookmarks()
+{
+ const qo={};
+ qo.op=1;
+ qo.token=getcookie("navigator_token");
+ const h=new Headers();h.append('Content-Type','application/json');
+ const resp=await fetch(new Request('/navigator/cgi-bin/navigator_api.cgi',{method:"POST",headers:h,body:JSON.stringify(qo)}));
+ const r=await resp.text();
+ const ro=JSON.parse(r);
+ if(ro.result)return -1;
+ else try{o=JSON.parse(ro.bookmarks);}catch(e){o={type:"folder",name:"",content:[]};}
+ return 0;
+}
+async function save_bookmarks()
+{
+ const qo={};
+ qo.op=2;
+ qo.token=getcookie("navigator_token");
+ qo.bookmarks=JSON.stringify(o);
+ const h=new Headers();h.append('Content-Type','application/json');
+ const resp=await fetch(new Request('/navigator/cgi-bin/navigator_api.cgi',{method:"POST",headers:h,body:JSON.stringify(qo)}));
+ const r=await resp.text();
+ const ro=JSON.parse(r);
+ if(ro.result)return -1;
+ return 0;
+}
+async function login_a()
+{
+ const o={};
+ o.op=0;
+ o.username=document.getElementById('usrname').value;
+ o.passwd=Sha256.hash(document.getElementById('passwd').value);
+ o.sessionname=document.getElementById('session').value;
+ const h=new Headers();h.append('Content-Type','application/json');
+ const resp=await fetch(new Request('/navigator/cgi-bin/navigator_api.cgi',{method:"POST",headers:h,body:JSON.stringify(o)}));
+ const r=await resp.text();
+ const ro=JSON.parse(r);
+ if(ro.result)return -1;
+ else document.cookie='navigator_token='+ro.token;
+ return 0;
+}
+function login()
+{
+ login_a().then(r=>{
+ if(r)
+ {
+ document.getElementById('loginerr').innerHTML='login failed:'
+ switch(r)
+ {
+ case 1:
+ document.getElementById('loginerr').innerHTML+='authentication failure';
+ break;
+ case 2:
+ document.getElementById('loginerr').innerHTML+='duplicate session';
+ break;
+ }
+ }
+ else
+ {
+ document.getElementById('loginerr').innerHTML='';
+ init();
+ }
+ }
+ );
+}
+function display_bookmarks()
+{
+ remove_children(ui.folderlist);
+ for(let i=0;i<o.content.length;++i)
+ {
+ const e=document.createElement('li');
+ const a=document.createElement('a');
+ a.classList.add('listitem');
+ e.draggable=true;
+ a.innerHTML=e.contname=o.content[i].name;
+ a.onclick=function(e){
+ e.preventDefault();
+ switch_folder(curp='/'+this.innerHTML+'/');
+ }
+ e.ondblclick=function(e){
+ edit(this);
+ }
+ e.appendChild(a);
+ ui.folderlist.appendChild(e);
+ }
+ const e=document.createElement('li');
+ const ea=document.createElement('a');
+ ea.innerHTML='+';e.appendChild(ea);
+ ea.classList.add('listitem');
+ e.ondblclick=function(e){
+ const ne=document.createElement('li');
+ const na=document.createElement('a');
+ na.classList.add('listitem');
+ ne.draggable=true;
+ na.onclick=function(e){
+ e.preventDefault();
+ switch_folder('/'+this.innerHTML+'/');
+ }
+ ne.ondblclick=function(e){
+ edit(this);
+ }
+ ne.appendChild(na);
+ ui.folderlist.insertBefore(ne,ui.folderlist.lastChild);
+ edit(ne);
+ }
+ make_sortable(ui.folderlist,function(x){move(ui.folderlist,o,x.contname);});
+ ui.folderlist.appendChild(e);
+}
+async function list_sessions()
+{
+ const o={};
+ o.op=3;o.token=getcookie("navigator_token");
+ const h=new Headers();h.append('Content-Type','application/json');
+ const resp=await fetch(new Request('/navigator/cgi-bin/navigator_api.cgi',{method:"POST",headers:h,body:JSON.stringify(o)}));
+ const r=await resp.text();
+ const ro=JSON.parse(r);
+ if(ro.result)return -1;
+ remove_children(ui.sessionlist);
+ for(let i=0;i<ro.sessions.length;++i)
+ {
+ const tr=document.createElement('tr');
+ const td1=document.createElement('td');
+ const td2=document.createElement('td');
+ const td3=document.createElement('td');
+ const a=document.createElement('a');
+ td1.innerHTML=ro.sessions[i].sessionname;
+ const d=new Date(ro.sessions[i].date*1000);
+ td2.innerHTML=d.toString();
+ a.innerHTML='x';
+ a.sessionname=ro.sessions[i].sessionname;
+ a.onclick=function(){remove_session(this.sessionname);}
+ a.href='javascript:void(0)';
+ td3.appendChild(a);
+ tr.appendChild(td1);tr.appendChild(td2);tr.appendChild(td3);
+ ui.sessionlist.appendChild(tr);
+ }
+}
+async function remove_session(s)
+{
+ const o={};
+ o.op=4;o.token=getcookie("navigator_token");o.session=s;
+ const h=new Headers();h.append('Content-Type','application/json');
+ const resp=await fetch(new Request('/navigator/cgi-bin/navigator_api.cgi',{method:"POST",headers:h,body:JSON.stringify(o)}));
+ const r=await resp.text();
+ const ro=JSON.parse(r);
+ if(ro.result)return -1;
+ return 0;
+}
+function showsettings()
+{
+ ui.main.style.display=null;
+ ui.settings.style.display='block';
+ list_sessions();
+}
+function init()
+{
+ ui.login=document.getElementById('login');
+ ui.main=document.getElementById('main');
+ ui.cont=document.getElementById('container');
+ ui.folderlist=document.getElementById('folderlist');
+ ui.sessionlist=document.getElementById('sessions');
+ ui.settings=document.getElementById('settings');
+ ui.templates={};
+ ui.templates.lnkmod=document.getElementById('_lnkmod');
+ ui.templates.fdrmod=document.getElementById('_fdrmod');
+ load_bookmarks().then(
+ r=>{
+ if(r)
+ {
+ ui.login.style.display='block';
+ ui.main.style.display=null;
+ }
+ else
+ {
+ ui.login.style.display=null;
+ display_bookmarks();
+ ui.main.style.display='block';
+ }
+ }
+ );
+}
diff --git a/xp/navigator/sha256.js b/xp/navigator/sha256.js
new file mode 100644
index 0000000..5ce6b7e
--- /dev/null
+++ b/xp/navigator/sha256.js
@@ -0,0 +1,173 @@
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* SHA-256 (FIPS 180-4) implementation in JavaScript (c) Chris Veness 2002-2017 */
+/* MIT Licence */
+/* www.movable-type.co.uk/scripts/sha256.html */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+'use strict';
+
+
+/**
+ * SHA-256 hash function reference implementation.
+ *
+ * This is an annotated direct implementation of FIPS 180-4, without any optimisations. It is
+ * intended to aid understanding of the algorithm rather than for production use.
+ *
+ * While it could be used where performance is not critical, I would recommend using the ‘Web
+ * Cryptography API’ (developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest) for the browser,
+ * or the ‘crypto’ library (nodejs.org/api/crypto.html#crypto_class_hash) in Node.js.
+ *
+ * See csrc.nist.gov/groups/ST/toolkit/secure_hashing.html
+ * csrc.nist.gov/groups/ST/toolkit/examples.html
+ */
+class Sha256 {
+
+ /**
+ * Generates SHA-256 hash of string.
+ *
+ * @param {string} msg - (Unicode) string to be hashed.
+ * @param {Object} [options]
+ * @param {string} [options.msgFormat=string] - Message format: 'string' for JavaScript string
+ * (gets converted to UTF-8 for hashing); 'hex-bytes' for string of hex bytes ('616263' ≡ 'abc') .
+ * @param {string} [options.outFormat=hex] - Output format: 'hex' for string of contiguous
+ * hex bytes; 'hex-w' for grouping hex bytes into groups of (4 byte / 8 character) words.
+ * @returns {string} Hash of msg as hex character string.
+ */
+ static hash(msg, options) {
+ const defaults = { msgFormat: 'string', outFormat: 'hex' };
+ const opt = Object.assign(defaults, options);
+
+ // note use throughout this routine of 'n >>> 0' to coerce Number 'n' to unsigned 32-bit integer
+
+ switch (opt.msgFormat) {
+ default: // default is to convert string to UTF-8, as SHA only deals with byte-streams
+ case 'string': msg = utf8Encode(msg); break;
+ case 'hex-bytes':msg = hexBytesToString(msg); break; // mostly for running tests
+ }
+
+ // constants [§4.2.2]
+ const K = [
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ];
+
+ // initial hash value [§5.3.3]
+ const H = [
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ];
+
+ // PREPROCESSING [§6.2.1]
+
+ msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1]
+
+ // convert string msg into 512-bit blocks (array of 16 32-bit integers) [§5.2.1]
+ const l = msg.length/4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length
+ const N = Math.ceil(l/16); // number of 16-integer (512-bit) blocks required to hold 'l' ints
+ const M = new Array(N); // message M is N×16 array of 32-bit integers
+
+ for (let i=0; i<N; i++) {
+ M[i] = new Array(16);
+ for (let j=0; j<16; j++) { // encode 4 chars per integer (64 per block), big-endian encoding
+ M[i][j] = (msg.charCodeAt(i*64+j*4+0)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16)
+ | (msg.charCodeAt(i*64+j*4+2)<< 8) | (msg.charCodeAt(i*64+j*4+3)<< 0);
+ } // note running off the end of msg is ok 'cos bitwise ops on NaN return 0
+ }
+ // add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1]
+ // note: most significant word would be (len-1)*8 >>> 32, but since JS converts
+ // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators
+ const lenHi = ((msg.length-1)*8) / Math.pow(2, 32);
+ const lenLo = ((msg.length-1)*8) >>> 0;
+ M[N-1][14] = Math.floor(lenHi);
+ M[N-1][15] = lenLo;
+
+
+ // HASH COMPUTATION [§6.2.2]
+
+ for (let i=0; i<N; i++) {
+ const W = new Array(64);
+
+ // 1 - prepare message schedule 'W'
+ for (let t=0; t<16; t++) W[t] = M[i][t];
+ for (let t=16; t<64; t++) {
+ W[t] = (Sha256.σ1(W[t-2]) + W[t-7] + Sha256.σ0(W[t-15]) + W[t-16]) >>> 0;
+ }
+
+ // 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value
+ let a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
+
+ // 3 - main loop (note '>>> 0' for 'addition modulo 2^32')
+ for (let t=0; t<64; t++) {
+ const T1 = h + Sha256.Σ1(e) + Sha256.Ch(e, f, g) + K[t] + W[t];
+ const T2 = Sha256.Σ0(a) + Sha256.Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = (d + T1) >>> 0;
+ d = c;
+ c = b;
+ b = a;
+ a = (T1 + T2) >>> 0;
+ }
+
+ // 4 - compute the new intermediate hash value (note '>>> 0' for 'addition modulo 2^32')
+ H[0] = (H[0]+a) >>> 0;
+ H[1] = (H[1]+b) >>> 0;
+ H[2] = (H[2]+c) >>> 0;
+ H[3] = (H[3]+d) >>> 0;
+ H[4] = (H[4]+e) >>> 0;
+ H[5] = (H[5]+f) >>> 0;
+ H[6] = (H[6]+g) >>> 0;
+ H[7] = (H[7]+h) >>> 0;
+ }
+
+ // convert H0..H7 to hex strings (with leading zeros)
+ for (let h=0; h<H.length; h++) H[h] = ('00000000'+H[h].toString(16)).slice(-8);
+
+ // concatenate H0..H7, with separator if required
+ const separator = opt.outFormat=='hex-w' ? ' ' : '';
+
+ return H.join(separator);
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+ function utf8Encode(str) {
+ try {
+ return new TextEncoder().encode(str, 'utf-8').reduce((prev, curr) => prev + String.fromCharCode(curr), '');
+ } catch (e) { // no TextEncoder available?
+ return unescape(encodeURIComponent(str)); // monsur.hossa.in/2012/07/20/utf-8-in-javascript.html
+ }
+ }
+
+ function hexBytesToString(hexStr) { // convert string of hex numbers to a string of chars (eg '616263' -> 'abc').
+ const str = hexStr.replace(' ', ''); // allow space-separated groups
+ return str=='' ? '' : str.match(/.{2}/g).map(byte => String.fromCharCode(parseInt(byte, 16))).join('');
+ }
+ }
+
+
+
+ /**
+ * Rotates right (circular right shift) value x by n positions [§3.2.4].
+ * @private
+ */
+ static ROTR(n, x) {
+ return (x >>> n) | (x << (32-n));
+ }
+
+
+ /**
+ * Logical functions [§4.1.2].
+ * @private
+ */
+ static Σ0(x) { return Sha256.ROTR(2, x) ^ Sha256.ROTR(13, x) ^ Sha256.ROTR(22, x); }
+ static Σ1(x) { return Sha256.ROTR(6, x) ^ Sha256.ROTR(11, x) ^ Sha256.ROTR(25, x); }
+ static σ0(x) { return Sha256.ROTR(7, x) ^ Sha256.ROTR(18, x) ^ (x>>>3); }
+ static σ1(x) { return Sha256.ROTR(17, x) ^ Sha256.ROTR(19, x) ^ (x>>>10); }
+ static Ch(x, y, z) { return (x & y) ^ (~x & z); } // 'choice'
+ static Maj(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); } // 'majority'
+
+} \ No newline at end of file
diff --git a/xp/navigator/shit.php b/xp/navigator/shit.php
new file mode 100644
index 0000000..8602a94
--- /dev/null
+++ b/xp/navigator/shit.php
@@ -0,0 +1,38 @@
+<?php
+ function do_login($pdo,$o)
+ {
+ $usrname=$o['username'];
+ $passwd=$o['passwd'];
+ $sessname=$o['sessionname'];
+ $qr=$pdo->query(sprintf('select passwd from navigator_user where username=\'%s\'',$usrname));
+ if($qr->rowCount()!=1){return array('result'=>1);}
+ if($passwd!=$qr->fetchColumn(0)){return array('result'=>1);}
+ $token=uniqid();
+ $pdo->query(sprintf('insert into navigator_session values(\'%s\',\'%s\',\'%s\',%d)',
+ $usrname,$passwd,$sessname,time()));
+ $pdo->commit();
+ {return array('result'=>0,'token'=>$token);}
+ }
+ $pdo=new PDO('mysql:host=localhost;dbname=chrisoft','chrisoft',null);
+ $o=json_decode(file_get_contents('php://stdin'));
+ $r=null;
+ switch($o['op'])
+ {
+ case 0:
+ $r=do_login($pdo,$o);
+ break;
+ case 1:
+ //get bookmarks
+ break;
+ case 2:
+ //set bookmarks
+ break;
+ case 3:
+ //list sessions
+ break;
+ case 4:
+ //remove session
+ break;
+ }
+ echo json_encode($r);
+?>