#!/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.)"); });