message this user

Name

upload

Global

#import jquery
#import getscript
#import nestlinks

function pbkdf2salt() {
 return '9fd89c8cee52923790bcbaa585bb5fa817dcdd5ae01725b45c226f23724051c5';
}

function pbkdf2iterations() {
 return 1000;
}

function keyfrompassword(pass,cb) {
 //We are going to later encrypt the file with AES-GCM
 //This requires a significant sized key.
 //To get that key we derive one from a password 
 //We could just pad the bytes or repeat it but that would be easy to dictionary attack
 //We are going to use the password plus a salt and pbkdf2 it a few times
 var te = new TextEncoder();
 crypto.subtle.importKey("raw",te.encode(pass),{name:'PBKDF2'},false,['deriveKey']).then(bk=>{
  crypto.subtle.deriveKey({
   name:'PBKDF2',
   salt:te.encode('upload'),
   iterations:pbkdf2iterations(),
   hash:{name:'SHA-512'}
  },bk,{name:'AES-GCM',length:128},true,['encrypt','decrypt']).then(cb);
 });
}

function encryptwithpass(pass,source,cb) {
 var iv = crypto.getRandomValues(new Uint8Array(12));
 window.iv = iv;
 console.log('iv',iv);
 keyfrompassword(pass,key=>{
  crypto.subtle.encrypt({name:'AES-GCM',iv},key,source).then(r=>{
   console.log('data',r);
   cb(joinIvAndData(iv,r));
  });
 });
}

function decryptwithpass(pass,source,cb) {
 var [iv,data] = separateIvFromData(source);
 console.log('iv',iv);
 console.log('data',data);
 keyfrompassword(pass,key=>{
  crypto.subtle.decrypt({name:'AES-GCM',iv},key,data).then(cb);
 });
}

function joinIvAndData(iv, data) {
  var buf = new Uint8Array(iv.length + data.byteLength);
  data = new Uint8Array(data);
  Array.prototype.forEach.call(iv, function (byte, i) {
    buf[i] = byte;
  });
  Array.prototype.forEach.call(data, function (byte, i) {
    buf[iv.length + i] = byte;
  });
  return buf;
}

function separateIvFromData(buf) {
  console.log('buf length',buf.length);
  console.log('buf',buf);
  var iv = new Uint8Array(12);
  var data = new Uint8Array(buf.length - iv.length);
  Array.prototype.forEach.call(buf, function (byte, i) {
    if (i < iv.length) {
      iv[i] = byte;
    } else {
      data[i - iv.length] = byte;
    }
  });
  return [iv,data];
}



function uploadEncrypted(pass,e,cb) {
 var fd = new FormData($('form')[0])
 var fr = new FileReader();
 fr.onloadend=(e)=>{
  encryptwithpass(pass,e.target.result,encdata=>{
   fd.delete('file');
   fd.append('file',new Blob([encdata],{type:file.type}),'enc_'+file.name);
   console.log(fd);
   cb(e,fd);
  });
 }
 fr.readAsArrayBuffer(fd.get('file'));
}

function randomString(n) {
 var text = '';
 var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
 for(var i=0;i<n;++i) {
  text += possible.charAt(Math.floor(Math.random() * possible.length));
 }
 return text;
}

function uploadUrl(e) {
 var url = $('#url').val();
 var name = $('#location').val();
 if(!name) {
  name = randomString(10);
  var maybeappend = url.split('.').pop();
  if(maybeappend.length<=4) {
   name+='.'+maybeappend;
  }
 }
 var keytype = $('#vis').val();
 var uploadurl = '/'+keytype+'/fseturl/upload/'+name;
 var downloadurl = uploadurl.replace('fseturl','fget');
 if(keytype!='skey') {
  downloadurl=downloadurl.replace('/fget/','/fget/'+window.user+'/');
 }
 var expandedurl = location.protocol+'//'+location.host+downloadurl;
 $.post({
   xhr:()=>{
    var xhr=$.ajaxSettings.xhr();
    $('progress').attr('value',0).css('display','inline');
    xhr.upload.addEventListener('progress',e=>{
     $('progress').attr('value',e.loaded/e.total);
    });
    return xhr;
   },
   url:uploadurl,
   data:url,
  },()=>{
   $('progress').css('display','none').attr('value',0);
   addUploadToHistory({name,url:expandedurl,time:Date.now()});
  }).fail(()=>{
   $('p').prepend('upload failed');
  });
}

function uploadClick(e,fd) {
 console.log('uploadclick',fd);
 e.preventDefault();
 if($('#url').val()) {
  return uploadUrl(e);
 }
 var pass = $('#pass').val();
 if(pass && !fd) {
  return uploadEncrypted(pass,e,uploadClick);
 }
 fd = fd || new FormData($('form')[0]);

 var keytype = $('#vis').val();
 var name = $('#location').val();
 if(!name) {
  name = randomString(10)+'.'+fd.get('file').name.split('.').pop();
 }
 var uploadurl='/'+keytype+'/fset/upload/'+name;
 var downloadurl=uploadurl.replace('fset','fget')
 if(keytype!='skey') {
  downloadurl=downloadurl.replace('/fget/','/fget/'+window.user+'/');
 }
 if(pass) {
  downloadurl='/app/encdownload'+downloadurl.replace('/fget/','/')+'#'+pass;
 }
 var expandedurl = location.protocol+'//'+location.host+downloadurl;
 $.post({
  xhr:()=>{
   var xhr=$.ajaxSettings.xhr();
   $('progress').attr('value',0).css('display','inline');
   xhr.upload.addEventListener('progress',e=>{
    $('progress').attr('value',e.loaded/e.total);
   });
   return xhr;
  },
  url:uploadurl,
  data:fd,
  processData:false,
  cache: false,
  contentType: false,
 },()=>{
  //$('p').prepend('<br>').prepend($('<a>').attr('href',downloadurl).text(expandedurl))
  $('progress').css('display','none').attr('value',0);
  addUploadToHistory({name,url:expandedurl,time:Date.now()});
 }).fail(()=>{
  $('p').prepend('upload failed');
 });
}



function getSCSS() {
 return `
 body {
  font-family:sans;
  text-align:center;
 }
 a {
  color:black;
  text-decoration:none;
 }
 a:hover {
  text-decoration:underline;
 }
 #uploadform {
  text-align:center;
 }
 form {
  background: #CCE8ED;
  text-align: center;
  padding: 24px;
  border: 2px solid #396B84;
  border-radius: 4px;
  display:inline-block;
 } 
 form > * {
  width:224px;
  box-sizing:border-box;
 }
 #history > * {
  margin:auto;
  margin-top: 5px;
  margin-bottom: 7px;
  border: 1px solid #396B84;
  border-radius: 4px;
  padding-top: 2px;
  padding-bottom: 2px;
  background-color: #CCE8ED;
  display:block;
  width:80vw;
 }
 td {
  padding-right:16px;
 }
 progress {
  display:none;
 }
 `;
}

function getSassLib(cb) {
 getscript('https://cdnjs.cloudflare.com/ajax/libs/sass.js/0.10.13/sass.sync.min.js',()=>{
  cb(Sass);
 });
}

function getCSS(cb) {
 getSassSync();
 Sass.compile(getSCSS(),obj=>{
  cb(obj.text);
 });
}


function getHTML() {
 return `
  <div id="uploadform">
   <form method="POST" enctype="multipart/form-data">
    <input id="location" placeholder="Name (optional)">
    <br>
    <input type="file" name="file" id="file">
    <br>
    <input name="url" id="url" placeholder="Url (optional)">
    <br>
    <select id='vis'>
     <option value='skey'>Private</option>
     <option value='pkey'>Logged in users</option>
     <option value='ppkey' selected>Public</option>
    </select>
    <br>
    <input id="pass" placeholder="Pass (optional)">
    <br>
    <button>Upload</button>
    <br>
    <progress max="1" value="0"></progress>
   </form>
   <p></p>
  </div>
  <div id="history"></div>
  <h3>Enjoy unlimited upload!</h3>
  <p>*<a href="/contentpolicy" style="color:blue">Content policy</a></p>
  <p>Accounts with offending content will have all content removed.</p>
 `;
}

function getUploadHistory(cb) {
 $.post('/skey/get/upload/uploads',uploads=>{
  window.uploads = JSON.parse(uploads||'[]');
  cb(uploads);
 });
}

function saveUploadHistory(history,cb) {
 $.post('/skey/set/upload/uploads',JSON.stringify(history),cb||function(){});
}

function addUploadToHistoryUI(item) {
 console.log('item',item);
 var tr = $('<a>').attr('href',item.url).attr('target','_blank');
 //var name = $('<td>').text(item.name);
 var link = $('<td>').text(item.url);
 var time = $('<td>').text((new Date(item.time)).toLocaleString());
 tr.append(link,time);
 $('#history').first().prepend(tr);
}
function addUploadToHistory(item) {
 window.uploads = window.uploads||[];
 window.uploads.unshift(item);
 addUploadToHistoryUI(item);
 saveUploadHistory(window.uploads);
}

function initHistoryUI() {
 getUploadHistory(()=>{
  for(var c=window.uploads.length-1;c>=0;--c) {
   addUploadToHistoryUI(window.uploads[c]);
  }
 });
}

function startApp() {
 add_style(getSCSS());
 add_style(nestcss());
 getjquery(()=>{
  $('body').html(getHTML());
  $('body').css('display','flow').css('flex-flow','row');
  $('form').submit(uploadClick);
  initHistoryUI();
  $('body').append(nestlinkselem());
 });
}

Init

console.log('Fuck off');
startApp();

Build

upload