"use strict"; var cmn=require("../$common.js"), persist=require("node-persist"), crypto=require("crypto"), http=require("http"), https=require("https"), URL=require("url"), child_process=require("child_process"); var moddir=null; persist=persist.create({ dir:"persist/changes", continuous:false, interval:false }); persist.initSync(); //urls: map(url => URLobject) //url: String //URLobject: {url, timeline: [[Date, hash, contents]]} var urls=persist.getItemSync("urls"); if(!urls){ urls={}; persist.setItemSync("urls",urls); } else { (function(){ var url,i; for(url in urls){ for(i=0;i"); nwaiting++; fetch("GET",url,function(url,status,body){ nwaiting--; console.log("Got <"+url+">; status = "+status); if(status==-1){ hashes[url]=[new Date(),null,null]; } else { var hash=crypto.createHash("sha256"); hash.update(body); hashes[url]=[new Date(),hash.digest("hex"),body]; } if(!urls[url]){ console.log("WARNING: url <"+url+"> from hashes not found in urls!"); } else { //var last=urls[url].timeline[urls[url].timeline.length-1]; //if(last==undefined||hashes[url][1]==null||hashes[url][1]!=last[1]){ urls[url].timeline.push(hashes[url]); //} } if(nwaiting==0&&cb)process.nextTick(cb); if(refresh_persist_timeout!=null)clearTimeout(refresh_persist_timeout); refresh_persist_timeout=setTimeout(function(){ persist.setItemSync("urls",urls); console.log("(persisted after refresh)") refresh_persist_timeout=null; },2000); }.bind(null,url)); } } function collectLastHashes(){ var lasthashes={}; for(url in urls){ if(urls[url].timeline.length==0){ lasthashes[url]=null; } else { lasthashes[url]=urls[url].timeline[urls[url].timeline.length-1][1]; } } return lasthashes; } function sendMail(tomail,body,cb){ var proc=child_process.spawn("sendmail",[tomail],{stdio:["pipe",process.stdout,process.stderr]}); proc.stdin.error(function(e){ console.log(e); cb(e); }); proc.stdin.write(body,function(){ proc.stdin.end(); cb(); }); } function constructMailBody(tomail,diffs){ var body="To: "+tomail+"\n"+ "From: changes \n"+ "Subject: changes notification mail\n\n"+ "The changes module recorded "+ "some changes in watched webpages.\n"+ "The URLs and their old and new content hashes are shown below.\n\n"; var i; for(i=0;i"+diffs[i].url+"\n"+ " was: "+diffs[i].oldhash+"\n"+ " now: "+diffs[i].newhash+"\n"; } body+="\n\n"+ "- webserver\n"; return body; } function performNotify(settings,cb){ var oldhashes=collectLastHashes(); refreshURLs(function(){ var newhashes=collectLastHashes(); var diffs=[]; // [{url, oldhash, newhash}] for(url in newhashes){ if(newhashes[url]==oldhashes[url])continue; diffs.push({ url: url, oldhash: oldhashes[url], newhash: newhashes[url] }); } sendMail(settings.to,constructMailBody(settings.to,diffs),cb); }); } function performCleanup(){ //keep only the last hash and the last different hash intact //(if there is no different hash, delete everything but the last) for(url in urls){ if(urls[url].timeline.length<=2)continue; var i,lasthash,prevhash=null,prevhashidx=-1,tllen; tllen=urls[url].timeline.length; lasthash=urls[url].timeline[tllen-1][1]; for(i=tllen-2;i>=0;i--){ if(urls[url].timeline[i][1]!=lasthash){ prevhash=urls[url].timeline[i][1]; prevhashidx=i; break; } } if(prevhash==null){ urls[url].timeline.splice(0,tllen-1); } else { urls[url].timeline.splice(prevhashidx+1,tllen-prevhashidx-2); urls[url].timeline.splice(0,prevhashidx); } } } module.exports=function(app,io,_moddir){ moddir=_moddir; app.all(["/changes","/changes/*"],cmn.authgen()); app.get("/changes",function(req,res){ res.sendFile(moddir+"/changes.html"); }); app.get("/changes/urls",function(req,res){ var list=[]; var url; for(url in urls)list.push(url); res.json(list); }); app.get("/changes/url",function(req,res){ var url=req.query.url; if(!urls[url]){ res.status(404); res.send("URL not found in watch list"); return; } res.json(urls[url]); }); app.post("/changes/url",function(req,res){ var url=req.body; urls[url]=new URLobject(url); persist.setItemSync("urls",urls); // refreshURLs(); res.send(); }); app.delete("/changes/url",function(req,res){ var url=req.body; if(!urls[url]){ res.status(404); res.send("URL not found in watch list"); return; } delete urls[url]; persist.setItemSync("urls",urls); res.send(); }); app.delete("/changes/url/history",function(req,res){ var param; try {param=JSON.parse(req.body);} catch(e){ res.status(400); res.send("Invalid JSON sent"); } var url=param.url; var todate=new Date(param.todate); if(!urls[url]){ res.status(404); res.send("URL not found in watch list"); return; } if(todate.getTime()==NaN){ res.status(400); res.send("Invalid time sent"); } var tl=urls[url].timeline; var i; for(i=0;i