From 6fb4351cafd1abdcc073580f29303f3a6df002f7 Mon Sep 17 00:00:00 2001 From: tomsmeding Date: Wed, 1 Jan 2020 10:57:19 +0100 Subject: Split up client/server in separate npm packages (1.0.2) --- client/client.js | 363 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100755 client/client.js (limited to 'client/client.js') diff --git a/client/client.js b/client/client.js new file mode 100755 index 0000000..efd721c --- /dev/null +++ b/client/client.js @@ -0,0 +1,363 @@ +#!/usr/bin/env node + +var fs=require("fs"), + path=require("path"), + https=require("https"), + crypto=require("crypto"), + read=require("read"), + toClipboard=require("to-clipboard"), + notifier=require("node-notifier"); + +var HOSTNAME=process.argv[2]||"tomsmeding.com",HTTPSPORT=443; +console.log("Using server "+HOSTNAME); + + +var lockfiledesc; +var lockfilename="/tmp/.lock_serverstore_client_"+HOSTNAME; +try {lockfiledesc=fs.openSync(lockfilename,"wx");} +catch(e){ + var lockedpid=+fs.readFileSync(lockfilename); + var canoverwrite=false; + if(isNaN(lockedpid))canoverwrite=true; + try {process.kill(lockedpid,0);} + catch(e){canoverwrite=true;} + if(!canoverwrite){ + console.log("Another copy of the serverstore client with the same hostname seems to be running."); + console.log("If this is not the case, or intentional, delete the file "+lockfilename+" and try again."); + process.exit(1); + } + lockfiledesc=fs.openSync(lockfilename,"w"); +} +fs.writeSync(lockfiledesc,process.pid+"\n"); +process.on("exit",function(){ + fs.closeSync(lockfiledesc); + fs.unlinkSync(lockfilename); +}); + + +var userid="0",password="-"; + + +var HOMEDIR=process.env.HOME||process.env.HOMEPATH||process.env.HOMEDIR||process.cwd(); +var WATCHDIR; +if(process.platform=="darwin"){ + WATCHDIR=HOMEDIR+"/Desktop"; +} else { + WATCHDIR=HOMEDIR+"/screenshots"; +} +var ignored=[]; + +var currentState=[]; + +function notify(message) { + notifier.notify({ + title: "serverstore", + message: message, + }); +} + +//lots of code taken/modified from tomsmeding/gvajnez +function collectDirState(dir){ + if(!dir)dir=directory; + //console.log("collectDirState("+dir+")"); + var list=fs.readdirSync(dir); + var result=[]; + var statinfo,i,j; + for(i=0;i60||(now-namedate)/1000>60)continue; //1 minute limit + setTimeout(function(n){ + return function(){sendfile(n);}; + }(changes[i].name),100); + } +} + +function authhash(challenge,password){ + var s=challenge+password; + var hasher=crypto.createHash("sha256"); + hasher.update(s); + return hasher.digest("hex"); +} + +function getchallenge(cb){ + console.log("Going to request challenge..."); + var req=https.request({ + hostname:HOSTNAME, + port:HTTPSPORT, + path:"/ss/challenge", + method:"GET", + keepAlive:true //speed up the next request + },function(res){ + var body=""; + res.on("data",function(data){ + body+=data; + }); + res.on("end",function(){ + if(res.statusCode!=200){ + notify("Could not request challenge! Is your internet connection alive?\n"+body); + return; + } + console.log("challenge = "+body); + cb(body); + }); + }); + req.on("error",function(err){ + console.log(err); + }); + req.end(); +} +function sendfile(fname,retries){ + retries=retries!=null?retries:3; + var barefname; + var idx=fname.lastIndexOf("/"); + if(idx==-1)barefname=fname; + else barefname=fname.slice(idx+1); + getchallenge(function(challenge){ + var req=https.request({ + hostname:HOSTNAME, + port:HTTPSPORT, + path:"/ss/image/"+userid+"/"+authhash(challenge,password)+"/"+escape(barefname), + method:"POST", + headers:{ + "Content-Type":"image/png" + } + },function(res){ + var body=""; + res.on("data",function(data){ + body+=data; + }); + res.on("end",function(){ + if(res.statusCode!=200){ + if(retries>0)sendfile(fname,retries-1); + else notify("Could not upload image! Are your credentials still okay?\n"+body); + return; + } + console.log("Successful upload"); + toClipboard.sync(body.trim()); + notify("Uploaded, link copied"); + try { + fs.unlinkSync(fname); + } catch(e){ + console.log(e); + } + }); + }); + req.on("error",function(err){ + console.log(err); + }); + try{req.end(fs.readFileSync(fname));} + catch(e){console.log(e);} + }); +} + + +function userExists(userid,cb){ + var req=https.request({ + hostname:HOSTNAME, + port:HTTPSPORT, + path:"/ss/exists/"+userid, + method:"GET" + },function(res){ + if(res.statusCode==200)cb(true); + else if(res.statusCode==404)cb(false); + else { + console.log("Server returned status code "+res.statusCode+" for exists query!"); + } + }); + req.on("error",function(err){ + console.log(err); + }); + req.end(); +} + +function checkLogin(userid,password,cb,retries){ + retries=retries!=null?retries:3; + getchallenge(function(challenge){ + var req=https.request({ + hostname:HOSTNAME, + port:HTTPSPORT, + path:"/ss/checklogin/"+userid+"/"+authhash(challenge,password), + method:"GET" + },function(res){ + if(res.statusCode==200)cb(true); + else if(res.statusCode==404||res.statusCode==403){ + if(retries>0)checkLogin(userid,password,cb,retries-1); + else cb(false); + } else { + console.log("Server returned status code "+res.statusCode+" for checklogin query!"); + } + }); + req.on("error",function(err){ + console.log(err); + }); + req.end(); + }); +} + +function registerUser(userid,password){ + var req=https.request({ + hostname:HOSTNAME, + port:HTTPSPORT, + path:"/ss/registerx/"+userid, + method:"POST", + headers:{ + "Content-Type":"text/plain" + } + },function(res){ + var body=""; + res.on("data",function(data){ + body+=data; + }); + res.on("end",function(){ + if(res.statusCode==200)console.log("Successfully registered user "+userid); + else if(res.statusCode==409)console.log("Conflict: "+body); + else console.log("Error: "+body); + }); + }); + req.on("error",function(err){ + console.log(err); + }); + req.end(password); +} + +var stdinFD = null; +if (!process.stdin.isTTY) { + stdinFD = fs.openSync("/dev/stdin", "r"); +} + +function readStdinLine(options, callback) { + console.log("Entering read with " + JSON.stringify(options)); + if (stdinFD) { + var input = "", buf = Buffer.alloc(1), ch; + while (true) { + if (fs.readSync(stdinFD, buf, 0, 1, null) < 1) { + console.log("Error reading from stdin"); + process.exit(1); + } + ch = buf.toString() + if (ch == "\n") break; + input += ch; + } + callback(input); + } else { + read(options, function(err, res) { + console.log("Returned from read with " + JSON.stringify(options)); + if (err) { + console.log("Error reading from stdin: " + err); + process.exit(1); + } + callback(res); + }); + } +} + +function readUserPass(callback) { + readStdinLine({prompt: "Username? "}, function(res) { + userid = res; + + readStdinLine({prompt: "Password? ", silent: true}, function(passinput) { + var hasher = crypto.createHash("sha256"); + hasher.update(passinput); + password = hasher.digest("hex"); + + callback(); + }); + }); +} + + + +readUserPass(function() { + console.log("\nChecking existence..."); + + userExists(userid,function(exists){ + if(exists){ + checkLogin(userid,password,function(ok){ + if(ok)console.log("User login ok."); + else { + console.log("Username or password incorrect!"); + process.exit(); + } + }); + return; + } + readStdinLine({prompt: "That username doesn't seem to exist. Register it? [y/N] "}, function(response) { + response = response[0]; + if(response=="y"||response=="Y")registerUser(userid,password); + else { + console.log("Not registered. Exiting."); + process.exit(); + } + }); + }); + + var timeout=null; + var watcher=fs.watch(WATCHDIR,{persistent:true,recursive:false},function(ev,fname){ + //console.log("change in directory "+WATCHDIR+" (fname "+fname+")"); + if(timeout)return; + timeout=setTimeout(function(){ + var newstate=collectDirState(WATCHDIR); + var changes=collectChanges(WATCHDIR,newstate).map(function(o){o.name=o.name.replace(/^\.\//,"");return o;}); + currentState=newstate; + if(changes.length!=0)handleChanges(changes); + timeout=null; + //console.log(currentState); + },500); + }); + currentState=collectDirState(WATCHDIR); + console.log("-- (Client ready.)"); +}); -- cgit v1.2.3-54-g00ecf