in the middle of rewriting it
This commit is contained in:
		@@ -1,245 +1,313 @@
 | 
			
		||||
/*! @file
 | 
			
		||||
 | 
			
		||||
    @id $Id$
 | 
			
		||||
   @id $Id$
 | 
			
		||||
 | 
			
		||||
    This is the main application as it is fully run in the user's browser.
 | 
			
		||||
   This is the main application as it is fully run in the user's browser.
 | 
			
		||||
 | 
			
		||||
    @dot
 | 
			
		||||
    digraph X {
 | 
			
		||||
   @dot
 | 
			
		||||
   digraph X {
 | 
			
		||||
 | 
			
		||||
    start [URL="\ref start()"];
 | 
			
		||||
    newuser [URL="\ref newuser()"];
 | 
			
		||||
    login [URL="\ref login()"];
 | 
			
		||||
    createkeypair [URL="\ref createkeypair()"];
 | 
			
		||||
    chat [URL="\ref chat()"];
 | 
			
		||||
    getpwd [URL="\ref getpwd()"];
 | 
			
		||||
    setpw [URL="\ref setpw()"];
 | 
			
		||||
    get [URL="\ref get()"];
 | 
			
		||||
    sendmessage [URL="\ref sendmessage()"];
 | 
			
		||||
    
 | 
			
		||||
    start -> newuser [label="if no keys exist"];
 | 
			
		||||
    start -> login [label="if keys exist"];
 | 
			
		||||
    newuser -> createkeypair [label="on submit"];
 | 
			
		||||
    createkeypair -> "openpgp.generateKeyPair";
 | 
			
		||||
    "openpgp.generateKeyPair" -> login [label="keys generated in local store"];
 | 
			
		||||
    login -> chat [label="user is valid on server"];
 | 
			
		||||
    chat -> getpwd [label="password not yet entered"];
 | 
			
		||||
    getpwd -> setpw [label="on input"];
 | 
			
		||||
    setpw -> chat [label="password is valid"];
 | 
			
		||||
    chat -> chat [label="remain in chat"];
 | 
			
		||||
    chat -> get [label="start timer"];
 | 
			
		||||
    get -> get [label="restart timer"];
 | 
			
		||||
    chat -> sendmessage [label="on submit"];
 | 
			
		||||
    sendmessage -> chat [label="remain in chat"];
 | 
			
		||||
    }
 | 
			
		||||
    @enddot
 | 
			
		||||
*/
 | 
			
		||||
   start [URL="\ref start()"];
 | 
			
		||||
   newuser [URL="\ref newuser()"];
 | 
			
		||||
   login [URL="\ref login()"];
 | 
			
		||||
   createkeypair [URL="\ref createkeypair()"];
 | 
			
		||||
   chat [URL="\ref chat()"];
 | 
			
		||||
   getpwd [URL="\ref getpwd()"];
 | 
			
		||||
   setpw [URL="\ref setpw()"];
 | 
			
		||||
   get [URL="\ref get()"];
 | 
			
		||||
   sendmessage [URL="\ref sendmessage()"];
 | 
			
		||||
   
 | 
			
		||||
   start -> newuser [label="if no keys exist"];
 | 
			
		||||
   start -> login [label="if keys exist"];
 | 
			
		||||
   newuser -> createkeypair [label="on submit"];
 | 
			
		||||
   createkeypair -> "openpgp.generateKey";
 | 
			
		||||
   "openpgp.generateKey" -> login [label="keys generated in local store"];
 | 
			
		||||
   login -> chat [label="user is valid on server"];
 | 
			
		||||
   chat -> getpwd [label="password not yet entered"];
 | 
			
		||||
   getpwd -> setpw [label="on input"];
 | 
			
		||||
   setpw -> chat [label="password is valid"];
 | 
			
		||||
   chat -> chat [label="remain in chat"];
 | 
			
		||||
   chat -> get [label="start timer"];
 | 
			
		||||
   get -> get [label="restart timer"];
 | 
			
		||||
   chat -> sendmessage [label="on submit"];
 | 
			
		||||
   sendmessage -> chat [label="remain in chat"];
 | 
			
		||||
   }
 | 
			
		||||
   @enddot
 | 
			
		||||
 */
 | 
			
		||||
//       1         2         3         4         5         6         7         8
 | 
			
		||||
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
 | 
			
		||||
 | 
			
		||||
function SafeChatClient(success, notice, error) {
 | 
			
		||||
 | 
			
		||||
  /// Cache Client's Key from local Strorage
 | 
			
		||||
  var k = null;
 | 
			
		||||
 | 
			
		||||
  /// Get User Key
 | 
			
		||||
  /** @internal key ist cached in k
 | 
			
		||||
      @return key */
 | 
			
		||||
  function key() {
 | 
			
		||||
    if (k) return k
 | 
			
		||||
    if (typeof localStorage.key == 'undefined') return null
 | 
			
		||||
    return k = openpgp.key.readArmored(localStorage.key)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Get Own User Name
 | 
			
		||||
  /** Get user name as user id of first public key */
 | 
			
		||||
  function uid() {
 | 
			
		||||
    if (k || key()) return k.pub.keys[0].getUserIds()[0]
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Create New User
 | 
			
		||||
  function createuser(user, email, pwd) {
 | 
			
		||||
    notice("generating keys")
 | 
			
		||||
    openpgp.generateKey({
 | 
			
		||||
      numBits: 4096,
 | 
			
		||||
      userIds: [{name: user, email: email}],
 | 
			
		||||
      passphrase: pwd
 | 
			
		||||
    }).then(function(keyPair) {
 | 
			
		||||
      success("keys generated")
 | 
			
		||||
      localStorage.key = keyPair.privateKeyArmored
 | 
			
		||||
      k = keyPair.key
 | 
			
		||||
    }).catch(function(e) {
 | 
			
		||||
      console.log(e)
 | 
			
		||||
      error("generating key pairs failed")
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function password(pwd) {
 | 
			
		||||
    return (k || keys()) && k.keys[0].decrypt(pwd)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Encrypt Message
 | 
			
		||||
  function encrypt(targetkeys, message, done, failed) {
 | 
			
		||||
    if (!k) return false
 | 
			
		||||
    openpgp.encrypt({
 | 
			
		||||
      publicKeys: targets.keys.concat(k.keys),
 | 
			
		||||
      privateKeys: k,
 | 
			
		||||
      data: message,
 | 
			
		||||
      armor: false})
 | 
			
		||||
           .then(done)
 | 
			
		||||
           .else(failed)
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Decrypt Message
 | 
			
		||||
  function decrypt(message) {
 | 
			
		||||
    if (!k) return false
 | 
			
		||||
    
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var password = null; ///< password, only stored temporary, until reload
 | 
			
		||||
var username = null; ///< username, only used during registration
 | 
			
		||||
var filecontent = new Array(); ///< temporary storage for attachments
 | 
			
		||||
var socket = io.connect();
 | 
			
		||||
var hostname = window.location.hostname!='localhost'?window.location.hostname:'safechat.ch';
 | 
			
		||||
 | 
			
		||||
/// Padding for numbers in dates
 | 
			
		||||
function pad(n) {
 | 
			
		||||
    return n<10 ? '0'+n : n
 | 
			
		||||
  return n<10 ? '0'+n : n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function uid(name) {
 | 
			
		||||
  return name+' <'+name+'@'+hostname+'>';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Convert number of bytes to readable text
 | 
			
		||||
function size(num) {
 | 
			
		||||
    if (num>0.6*1024) {
 | 
			
		||||
        if (num>0.6*1024*1024) {
 | 
			
		||||
            if (num>0.6*1024*1024*1024) {
 | 
			
		||||
                if (num>0.6*1024*1024*1024*1024) {
 | 
			
		||||
                    return Math.round(num/1024/1024/1024/1024)+"TB";
 | 
			
		||||
                } else {
 | 
			
		||||
                    return Math.round(num/1024/1024/1024)+"GB";
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                return Math.round(num/1024/1024)+"MB";
 | 
			
		||||
            }
 | 
			
		||||
  if (num>0.6*1024) {
 | 
			
		||||
    if (num>0.6*1024*1024) {
 | 
			
		||||
      if (num>0.6*1024*1024*1024) {
 | 
			
		||||
        if (num>0.6*1024*1024*1024*1024) {
 | 
			
		||||
          return Math.round(num/1024/1024/1024/1024)+"TB";
 | 
			
		||||
        } else {
 | 
			
		||||
            return Math.round(num/1024)+"kB";
 | 
			
		||||
          return Math.round(num/1024/1024/1024)+"GB";
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        return Math.round(num/1024/1024)+"MB";
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
        return num+"B";
 | 
			
		||||
      return Math.round(num/1024)+"kB";
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    return num+"B";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var reboottimer = null;
 | 
			
		||||
/// Show error messsage
 | 
			
		||||
/** Fades in an error message and logs to console.
 | 
			
		||||
    @param data (optional) The error can be a string or any structure.
 | 
			
		||||
                Strings are shown to the user, structures are logged only.
 | 
			
		||||
    @param stay (optional) If not given as @c true, reloads page after 5s. */
 | 
			
		||||
   @param data (optional) The error can be a string or any structure.
 | 
			
		||||
   Strings are shown to the user, structures are logged only.
 | 
			
		||||
   @param stay (optional) If not given as @c true, reloads page after 5s. */
 | 
			
		||||
function error(data, stay) {
 | 
			
		||||
    $("#status").hide();
 | 
			
		||||
    $("#status").addClass("error")
 | 
			
		||||
    $("#status").removeClass("notice")
 | 
			
		||||
    $("#status").removeClass("success")
 | 
			
		||||
    if (data) {
 | 
			
		||||
        if (typeof data == 'string') {
 | 
			
		||||
            $("#status").html(data);
 | 
			
		||||
            console.log("error: "+data);
 | 
			
		||||
        } else {
 | 
			
		||||
            $("#status").html('unknown error: '+JSON.stringify(data));
 | 
			
		||||
            console.log("error: "+JSON.stringify(data));
 | 
			
		||||
        }
 | 
			
		||||
  $("#status").hide();
 | 
			
		||||
  $("#status").addClass("error")
 | 
			
		||||
  $("#status").removeClass("notice")
 | 
			
		||||
  $("#status").removeClass("success")
 | 
			
		||||
  if (data) {
 | 
			
		||||
    if (typeof data == 'string') {
 | 
			
		||||
      $("#status").html(data);
 | 
			
		||||
      console.log("error: "+data);
 | 
			
		||||
    } else {
 | 
			
		||||
        $("#status").html('error');
 | 
			
		||||
        console.log("error");
 | 
			
		||||
    }
 | 
			
		||||
    $("#status").show();
 | 
			
		||||
    if (!stay) {
 | 
			
		||||
        console.log("reboot in 5s");
 | 
			
		||||
        console.log((new Error('stacktrace')).stack);
 | 
			
		||||
        if (!reboottimer) reboottimer = setTimeout(function() {
 | 
			
		||||
            reboottimer = null;
 | 
			
		||||
            start();
 | 
			
		||||
        }, 5000);
 | 
			
		||||
      $("#status").html('unknown error: '+JSON.stringify(data));
 | 
			
		||||
      console.log("error: "+JSON.stringify(data));
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    $("#status").html('error');
 | 
			
		||||
    console.log("error");
 | 
			
		||||
  }
 | 
			
		||||
  $("#status").show();
 | 
			
		||||
  if (!stay) {
 | 
			
		||||
    console.log("reboot in 5s");
 | 
			
		||||
    console.log((new Error('stacktrace')).stack);
 | 
			
		||||
    if (!reboottimer) reboottimer = setTimeout(function() {
 | 
			
		||||
      reboottimer = null;
 | 
			
		||||
      start();
 | 
			
		||||
    }, 5000);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Show notice messsage
 | 
			
		||||
/** Fades in an notice message and logs to console.
 | 
			
		||||
    @param text (optional) The data is a string. */
 | 
			
		||||
   @param text (optional) The data is a string. */
 | 
			
		||||
function notice(text) {
 | 
			
		||||
    $("#status").hide()
 | 
			
		||||
    $("#status").addClass("notice")
 | 
			
		||||
    $("#status").removeClass("error")
 | 
			
		||||
    $("#status").removeClass("success")
 | 
			
		||||
    if (text) {
 | 
			
		||||
        $("#status").html(text);
 | 
			
		||||
        console.log("notice: "+text);
 | 
			
		||||
    } else {
 | 
			
		||||
        $("#status").html('');
 | 
			
		||||
        console.log("notice");
 | 
			
		||||
    }
 | 
			
		||||
    $("#status").show();
 | 
			
		||||
  $("#status").hide()
 | 
			
		||||
  $("#status").addClass("notice")
 | 
			
		||||
  $("#status").removeClass("error")
 | 
			
		||||
  $("#status").removeClass("success")
 | 
			
		||||
  if (text) {
 | 
			
		||||
    $("#status").html(text);
 | 
			
		||||
    console.log("notice: "+text);
 | 
			
		||||
  } else {
 | 
			
		||||
    $("#status").html('');
 | 
			
		||||
    console.log("notice");
 | 
			
		||||
  }
 | 
			
		||||
  $("#status").show();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Show notice messsage
 | 
			
		||||
/** Fades in an success message and logs to console.
 | 
			
		||||
    @param text (optional) The data is a string. */
 | 
			
		||||
   @param text (optional) The data is a string. */
 | 
			
		||||
function success(text) {
 | 
			
		||||
    $("#status").hide();
 | 
			
		||||
    $("#status").addClass("success")
 | 
			
		||||
    $("#status").removeClass("error")
 | 
			
		||||
    $("#status").removeClass("notice")
 | 
			
		||||
    if (text) {
 | 
			
		||||
        $("#status").html(text);
 | 
			
		||||
        console.log("success: "+text);
 | 
			
		||||
    } else {
 | 
			
		||||
        $("#status").html('');
 | 
			
		||||
        console.log("success");
 | 
			
		||||
    }
 | 
			
		||||
    $("#status").show();
 | 
			
		||||
  $("#status").hide();
 | 
			
		||||
  $("#status").addClass("success")
 | 
			
		||||
  $("#status").removeClass("error")
 | 
			
		||||
  $("#status").removeClass("notice")
 | 
			
		||||
  if (text) {
 | 
			
		||||
    $("#status").html(text);
 | 
			
		||||
    console.log("success: "+text);
 | 
			
		||||
  } else {
 | 
			
		||||
    $("#status").html('');
 | 
			
		||||
    console.log("success");
 | 
			
		||||
  }
 | 
			
		||||
  $("#status").show();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Show status message in the main screen area
 | 
			
		||||
/** @param id HTML id to be shown.
 | 
			
		||||
    @param msg The success message text */
 | 
			
		||||
   @param msg The success message text */
 | 
			
		||||
function status(id, msg) {
 | 
			
		||||
    console.log("state: "+id);
 | 
			
		||||
    if (msg) success(msg); else $("#status").hide();
 | 
			
		||||
    $("#main").children(":not(#"+id+")").hide();
 | 
			
		||||
    $("#main #"+id).show();
 | 
			
		||||
    $("#main #"+id+" form input:first-child").focus();
 | 
			
		||||
  console.log("state: "+id);
 | 
			
		||||
  if (msg) success(msg); else $("#status").hide();
 | 
			
		||||
  $("#main").children(":not(#"+id+")").hide();
 | 
			
		||||
  $("#main #"+id).show();
 | 
			
		||||
  $("#main #"+id+" form input:first-child").focus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function emit(signal, data) {
 | 
			
		||||
    console.log("<-snd "+signal);
 | 
			
		||||
    socket.emit(signal, data);
 | 
			
		||||
  console.log("<-snd "+signal);
 | 
			
		||||
  socket.emit(signal, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function connected() {
 | 
			
		||||
    console.log("server connected");
 | 
			
		||||
    $("#connectionstatus #bad").hide();
 | 
			
		||||
    $("#connectionstatus #good").show();
 | 
			
		||||
    success("server connected");
 | 
			
		||||
  console.log("server connected");
 | 
			
		||||
  $("#connectionstatus #bad").hide();
 | 
			
		||||
  $("#connectionstatus #good").show();
 | 
			
		||||
  success("server connected");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function disconnected() {
 | 
			
		||||
    console.log("server disconnected");
 | 
			
		||||
    $("#connectionstatus #good").hide();
 | 
			
		||||
    $("#connectionstatus #bad").show();
 | 
			
		||||
    error("server disconnected", true);
 | 
			
		||||
  console.log("server disconnected");
 | 
			
		||||
  $("#connectionstatus #good").hide();
 | 
			
		||||
  $("#connectionstatus #bad").show();
 | 
			
		||||
  error("server disconnected", true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function connectionstatus() {
 | 
			
		||||
    if (socket.connected) connected(); else disconnected();
 | 
			
		||||
  if (socket.connected) connected(); else disconnected();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function htmlenc(html) {
 | 
			
		||||
    return $('<div/>').text(html).html();
 | 
			
		||||
  return $('<div/>').text(html).html();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function htmldec(data) {
 | 
			
		||||
    return $('<div/>').html(data).text();
 | 
			
		||||
  return $('<div/>').html(data).text();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Alert user
 | 
			
		||||
/** Alert user, e.g. that a new message has arrived. */
 | 
			
		||||
function beep(user) {
 | 
			
		||||
    if (user) success("message from "+htmlenc(user)+" received");
 | 
			
		||||
    navigator.vibrate =
 | 
			
		||||
        navigator.vibrate || navigator.webkitVibrate || navigator.mozVibrate || navigator.msVibrate;
 | 
			
		||||
    if (navigator.vibrate) {
 | 
			
		||||
	// vibration API supported
 | 
			
		||||
        navigator.vibrate(1000);
 | 
			
		||||
    }
 | 
			
		||||
    (new Audio("sounds/A-Tone-His_Self-1266414414.mp3")).play();
 | 
			
		||||
  if (user) success("message from "+htmlenc(user)+" received");
 | 
			
		||||
  navigator.vibrate =
 | 
			
		||||
  navigator.vibrate || navigator.webkitVibrate || navigator.mozVibrate || navigator.msVibrate;
 | 
			
		||||
  if (navigator.vibrate) {
 | 
			
		||||
    // vibration API supported
 | 
			
		||||
    navigator.vibrate(1000);
 | 
			
		||||
  }
 | 
			
		||||
  (new Audio("sounds/A-Tone-His_Self-1266414414.mp3")).play();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Toggle Menu Display
 | 
			
		||||
function togglemenu() {
 | 
			
		||||
    $("#menu").toggle();
 | 
			
		||||
  $("#menu").toggle();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Download Profile Backup
 | 
			
		||||
function backup() {
 | 
			
		||||
    var download = document.createElement('a');
 | 
			
		||||
    download.href = 'data:attachment/text,'+encodeURI(JSON.stringify(localStorage));
 | 
			
		||||
    download.target = '_blank';
 | 
			
		||||
    var now = new Date();
 | 
			
		||||
    download.download =
 | 
			
		||||
        pad(now.getFullYear())+pad(now.getMonth()+1)+pad(now.getDate())+
 | 
			
		||||
        "-"+userid()+"@"+window.location.hostname+".bak";
 | 
			
		||||
    var clickEvent = new MouseEvent("click", {
 | 
			
		||||
        "view": window,
 | 
			
		||||
        "bubbles": true,
 | 
			
		||||
        "cancelable": false
 | 
			
		||||
    });
 | 
			
		||||
    download.dispatchEvent(clickEvent);
 | 
			
		||||
    togglemenu();
 | 
			
		||||
  var download = document.createElement('a');
 | 
			
		||||
  download.href = 'data:attachment/text,'+encodeURI(JSON.stringify(localStorage));
 | 
			
		||||
  download.target = '_blank';
 | 
			
		||||
  var now = new Date();
 | 
			
		||||
  download.download =
 | 
			
		||||
  pad(now.getFullYear())+pad(now.getMonth()+1)+pad(now.getDate())+
 | 
			
		||||
  "-"+userid()+"@"+hostname+".bak";
 | 
			
		||||
  var clickEvent = new MouseEvent("click", {
 | 
			
		||||
    "view": window,
 | 
			
		||||
    "bubbles": true,
 | 
			
		||||
    "cancelable": false
 | 
			
		||||
  });
 | 
			
		||||
  download.dispatchEvent(clickEvent);
 | 
			
		||||
  togglemenu();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Upload Profile Backup
 | 
			
		||||
function restore(evt) {
 | 
			
		||||
    if (!window.FileReader)
 | 
			
		||||
        return error("your browser does not support file upload", true);
 | 
			
		||||
    for (var i=0, f; f=evt.target.files[i]; ++i) {
 | 
			
		||||
        var file = f;
 | 
			
		||||
        var reader = new FileReader();
 | 
			
		||||
        reader.onload = function(evt) {
 | 
			
		||||
            if (evt.target.error) return error("error reading file", true);
 | 
			
		||||
            if (evt.target.readyState==0) return notice("waiting for data …");
 | 
			
		||||
            if (evt.target.readyState==1) return notice("loading data …");
 | 
			
		||||
            var parsed=JSON.parse(evt.target.result);
 | 
			
		||||
            togglemenu();
 | 
			
		||||
            localStorage.pubkey = parsed.pubkey;
 | 
			
		||||
            localStorage.privkey = parsed.privkey;
 | 
			
		||||
            success("backup is restored");
 | 
			
		||||
            console.log("reboot after restore in 2s");
 | 
			
		||||
            if (!reboottimer) reboottimer = setTimeout(function() {
 | 
			
		||||
                reboottimer = null;
 | 
			
		||||
                start();
 | 
			
		||||
            }, 2000);
 | 
			
		||||
        }
 | 
			
		||||
        reader.readAsText(file);
 | 
			
		||||
  if (!window.FileReader)
 | 
			
		||||
    return error("your browser does not support file upload", true);
 | 
			
		||||
  for (var i=0, f; f=evt.target.files[i]; ++i) {
 | 
			
		||||
    var file = f;
 | 
			
		||||
    var reader = new FileReader();
 | 
			
		||||
    reader.onload = function(evt) {
 | 
			
		||||
      if (evt.target.error) return error("error reading file", true);
 | 
			
		||||
      if (evt.target.readyState==0) return notice("waiting for data …");
 | 
			
		||||
      if (evt.target.readyState==1) return notice("loading data …");
 | 
			
		||||
      var parsed=JSON.parse(evt.target.result);
 | 
			
		||||
      togglemenu();
 | 
			
		||||
      localStorage.pubkey = parsed.pubkey;
 | 
			
		||||
      localStorage.privkey = parsed.privkey;
 | 
			
		||||
      success("backup is restored");
 | 
			
		||||
      console.log("reboot after restore in 2s");
 | 
			
		||||
      if (!reboottimer) reboottimer = setTimeout(function() {
 | 
			
		||||
        reboottimer = null;
 | 
			
		||||
        start();
 | 
			
		||||
      }, 2000);
 | 
			
		||||
    }
 | 
			
		||||
    reader.readAsText(file);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Configure local groups
 | 
			
		||||
@@ -249,573 +317,582 @@ function groups() {
 | 
			
		||||
 | 
			
		||||
/// Check if password is set and matches the repeated password
 | 
			
		||||
/** Checks if both passwords are identical and valid and gives
 | 
			
		||||
    feedback to the user.
 | 
			
		||||
   feedback to the user.
 | 
			
		||||
 | 
			
		||||
    Called when user edits the password fields.
 | 
			
		||||
   Called when user edits the password fields.
 | 
			
		||||
 | 
			
		||||
    Sets @ref username and checks @ref password - if both are well
 | 
			
		||||
    defined, enables the submit button.
 | 
			
		||||
   Sets @ref username and checks @ref password - if both are well
 | 
			
		||||
   defined, enables the submit button.
 | 
			
		||||
 | 
			
		||||
    @param pwd The password.
 | 
			
		||||
    @param pwd2 The repeated password. */
 | 
			
		||||
   @param pwd The password.
 | 
			
		||||
   @param pwd2 The repeated password. */
 | 
			
		||||
function checkpwd(pwd, pwd2) {
 | 
			
		||||
    $("#register").submit(function(event) {
 | 
			
		||||
        return false;
 | 
			
		||||
    });
 | 
			
		||||
    if (pwd==pwd2) password=pwd;
 | 
			
		||||
    else password=null;
 | 
			
		||||
    if (!password||password.length<1) password=null;
 | 
			
		||||
    $("#createuser").prop("disabled", !(username && password));
 | 
			
		||||
    if (password) {
 | 
			
		||||
        if (username) success("user is ready to be created");
 | 
			
		||||
        else notice("password matches, please chose a valid user name");
 | 
			
		||||
    } else {
 | 
			
		||||
        if (username) notice("passwords don't match");
 | 
			
		||||
        else if ($('#user').val()) notice("user name is already in use");
 | 
			
		||||
        else notice("please chose a user name");
 | 
			
		||||
                
 | 
			
		||||
    }
 | 
			
		||||
  $("#register").submit(function(event) {
 | 
			
		||||
    return false;
 | 
			
		||||
  });
 | 
			
		||||
  if (pwd==pwd2) password=pwd;
 | 
			
		||||
  else password=null;
 | 
			
		||||
  if (!password||password.length<1) password=null;
 | 
			
		||||
  $("#createuser").prop("disabled", !(username && password));
 | 
			
		||||
  if (password) {
 | 
			
		||||
    if (username) success("user is ready to be created");
 | 
			
		||||
    else notice("password matches, please chose a valid user name");
 | 
			
		||||
  } else {
 | 
			
		||||
    if (username) notice("passwords don't match");
 | 
			
		||||
    else if ($('#user').val()) notice("user name is already in use");
 | 
			
		||||
    else notice("please chose a user name");
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Checks if the receiver of a message exists on server.
 | 
			
		||||
/** Calls checknewuser.php on server and enables the message submit
 | 
			
		||||
    button if the receiver of the message exists on the server. */
 | 
			
		||||
   button if the receiver of the message exists on the server. */
 | 
			
		||||
function checkpartner(user) {
 | 
			
		||||
    $("#chat").submit(function(event) {
 | 
			
		||||
        return false;
 | 
			
		||||
    });
 | 
			
		||||
    emit("user", user);
 | 
			
		||||
  $("#chat").submit(function(event) {
 | 
			
		||||
    return false;
 | 
			
		||||
  });
 | 
			
		||||
  emit("user", uid(user));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create Local Public-/Private-Key Pair
 | 
			
		||||
/** Called if user has not yet his keys, just generates a new key pair. */
 | 
			
		||||
function createkeypair(user, pwd) {
 | 
			
		||||
    notice("generating keys");
 | 
			
		||||
    openpgp.generateKeyPair({
 | 
			
		||||
        numBits: 4096,
 | 
			
		||||
        userId: user,
 | 
			
		||||
        passphrase: pwd
 | 
			
		||||
    }).then(function(keyPair) {
 | 
			
		||||
        success("keys generated");
 | 
			
		||||
        localStorage.pubkey = keyPair.publicKeyArmored;
 | 
			
		||||
        localStorage.privkey = keyPair.privateKeyArmored;
 | 
			
		||||
        login();
 | 
			
		||||
    }).catch(function(e) {
 | 
			
		||||
        error("generating key pairs failed");
 | 
			
		||||
    });
 | 
			
		||||
  notice("generating keys");
 | 
			
		||||
  openpgp.generateKey({
 | 
			
		||||
    numBits: 4096,
 | 
			
		||||
    userIds: [{name: user, email: user+'@'+hostname}],
 | 
			
		||||
    passphrase: pwd
 | 
			
		||||
  }).then(function(keyPair) {
 | 
			
		||||
    success("keys generated");
 | 
			
		||||
    localStorage.pubkey = keyPair.publicKeyArmored;
 | 
			
		||||
    localStorage.privkey = keyPair.privateKeyArmored;
 | 
			
		||||
    login();
 | 
			
		||||
  }).catch(function(e) {
 | 
			
		||||
    console.log(e)
 | 
			
		||||
    error("generating key pairs failed")
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get Own Public Key
 | 
			
		||||
/** @return public key object */
 | 
			
		||||
function publicKey() {
 | 
			
		||||
    if (typeof localStorage.pubkey == 'undefined') {
 | 
			
		||||
        if (typeof localStorage.pubKey == 'undefined') {
 | 
			
		||||
            return null;
 | 
			
		||||
        } else {
 | 
			
		||||
            localStorage.pubkey = localStorage.pubKey;
 | 
			
		||||
            localStorage.removeItem(pubKey);
 | 
			
		||||
        }
 | 
			
		||||
  if (typeof localStorage.pubkey == 'undefined') {
 | 
			
		||||
    if (typeof localStorage.pubKey == 'undefined') {
 | 
			
		||||
      return null;
 | 
			
		||||
    } else {
 | 
			
		||||
      localStorage.pubkey = localStorage.pubKey;
 | 
			
		||||
      localStorage.removeItem(pubKey);
 | 
			
		||||
    }
 | 
			
		||||
    return openpgp.key.readArmored(localStorage.pubkey);
 | 
			
		||||
  }
 | 
			
		||||
  return openpgp.key.readArmored(localStorage.pubkey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get Own Private Key
 | 
			
		||||
/** @return private key object */
 | 
			
		||||
function privateKey() {
 | 
			
		||||
    if (typeof localStorage.privkey == 'undefined') {
 | 
			
		||||
        if (typeof localStorage.privKey == 'undefined') {
 | 
			
		||||
            return null;
 | 
			
		||||
        } else {
 | 
			
		||||
            localStorage.privkey = localStorage.privKey;
 | 
			
		||||
            localStorage.removeItem(privKey);
 | 
			
		||||
        }
 | 
			
		||||
  if (typeof localStorage.privkey == 'undefined') {
 | 
			
		||||
    if (typeof localStorage.privKey == 'undefined') {
 | 
			
		||||
      return null;
 | 
			
		||||
    } else {
 | 
			
		||||
      localStorage.privkey = localStorage.privKey;
 | 
			
		||||
      localStorage.removeItem(privKey);
 | 
			
		||||
    }
 | 
			
		||||
    return openpgp.key.readArmored(localStorage.privkey);
 | 
			
		||||
  }
 | 
			
		||||
  return openpgp.key.readArmored(localStorage.privkey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get Own User Name
 | 
			
		||||
/** Get user name as user id of first public key */
 | 
			
		||||
function userid() {
 | 
			
		||||
    if (!publicKey() ||
 | 
			
		||||
        publicKey().keys.length < 1 ||
 | 
			
		||||
        publicKey().keys[0].getUserIds().length < 1) return null
 | 
			
		||||
    return publicKey().keys[0].getUserIds()[0];
 | 
			
		||||
  if (!publicKey() ||
 | 
			
		||||
      publicKey().keys.length < 1 ||
 | 
			
		||||
      publicKey().keys[0].getUserIds().length < 1) return null
 | 
			
		||||
  return publicKey().keys[0].getUserIds()[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Clear Message Text And Attachments
 | 
			
		||||
/** Does not remove the receiver's name */
 | 
			
		||||
function clearmessage() {
 | 
			
		||||
    $("#message").prop(":disabled", true);
 | 
			
		||||
    filecontent = new Array();
 | 
			
		||||
    $('#preview').empty();
 | 
			
		||||
    $("#msg").val("");
 | 
			
		||||
    $("#message").prop(":disabled", false);
 | 
			
		||||
  $("#message").prop(":disabled", true);
 | 
			
		||||
  filecontent = new Array();
 | 
			
		||||
  $('#preview').empty();
 | 
			
		||||
  $("#msg").val("");
 | 
			
		||||
  $("#message").prop(":disabled", false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function guessfilename(mimetype, user, date) {
 | 
			
		||||
    if (!user) user = userid();
 | 
			
		||||
    if (!date) date = new Date();
 | 
			
		||||
    var ext = mimetype.replace(/.*\/(x-)?/i, "");
 | 
			
		||||
    return pad(date.getFullYear())+pad(date.getMonth()+1)+pad(date.getDate())
 | 
			
		||||
        +"-"+ext+"-"+user+"@"+window.location.hostname+'.'+ext;
 | 
			
		||||
  if (!user) user = userid();
 | 
			
		||||
  if (!date) date = new Date();
 | 
			
		||||
  var ext = mimetype.replace(/.*\/(x-)?/i, "");
 | 
			
		||||
  return pad(date.getFullYear())+pad(date.getMonth()+1)+pad(date.getDate())
 | 
			
		||||
        +"-"+ext+"-"+user+"@"+hostname+'.'+ext;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Display Image Attachments
 | 
			
		||||
function attachments(files, id, from, date) {
 | 
			
		||||
    if (files) files.forEach(function(file) {
 | 
			
		||||
        console.log(file);
 | 
			
		||||
        if (!file.name) file.name = guessfilename(file.type, from, date);
 | 
			
		||||
        var a = document.createElement('a');
 | 
			
		||||
        a.href = file.content;
 | 
			
		||||
        a.download = file.name;
 | 
			
		||||
        a.target = '_blank';
 | 
			
		||||
        if (file.type.match('^image/')) {
 | 
			
		||||
            var img = document.createElement('img');
 | 
			
		||||
            img.title = file.name;
 | 
			
		||||
            img.src = file.content;
 | 
			
		||||
            a.appendChild(img);
 | 
			
		||||
        } else if (file.type.match('^video/')) {
 | 
			
		||||
            var video = document.createElement('video');
 | 
			
		||||
            video.controls = true;
 | 
			
		||||
            video.title = file.name;
 | 
			
		||||
            video.src = file.content;
 | 
			
		||||
            a.appendChild(video);
 | 
			
		||||
        } else {
 | 
			
		||||
            var img = document.createElement('img');
 | 
			
		||||
            img.title = file.name;
 | 
			
		||||
            img.src = "images/Document_sans_PICOL-PIctorial-COmmunication-Language.svg";
 | 
			
		||||
            a.appendChild(img);
 | 
			
		||||
        }
 | 
			
		||||
        $(id).append(a);            
 | 
			
		||||
    });
 | 
			
		||||
  if (files) files.forEach(function(file) {
 | 
			
		||||
    console.log(file);
 | 
			
		||||
    if (!file.name) file.name = guessfilename(file.type, from, date);
 | 
			
		||||
    var a = document.createElement('a');
 | 
			
		||||
    a.href = file.content;
 | 
			
		||||
    a.download = file.name;
 | 
			
		||||
    a.target = '_blank';
 | 
			
		||||
    if (file.type.match('^image/')) {
 | 
			
		||||
      var img = document.createElement('img');
 | 
			
		||||
      img.title = file.name;
 | 
			
		||||
      img.src = file.content;
 | 
			
		||||
      a.appendChild(img);
 | 
			
		||||
    } else if (file.type.match('^video/')) {
 | 
			
		||||
      var video = document.createElement('video');
 | 
			
		||||
      video.controls = true;
 | 
			
		||||
      video.title = file.name;
 | 
			
		||||
      video.src = file.content;
 | 
			
		||||
      a.appendChild(video);
 | 
			
		||||
    } else {
 | 
			
		||||
      var img = document.createElement('img');
 | 
			
		||||
      img.title = file.name;
 | 
			
		||||
      img.src = "images/Document_sans_PICOL-PIctorial-COmmunication-Language.svg";
 | 
			
		||||
      a.appendChild(img);
 | 
			
		||||
    }
 | 
			
		||||
    $(id).append(a);            
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var recorder;
 | 
			
		||||
 | 
			
		||||
function done() {
 | 
			
		||||
    if (recorder) {
 | 
			
		||||
        recorder.stop();
 | 
			
		||||
        recorder.recording(function(data) {
 | 
			
		||||
            previewfile(data, "video/webm");
 | 
			
		||||
            abort();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
  if (recorder) {
 | 
			
		||||
    recorder.stop();
 | 
			
		||||
    recorder.recording(function(data) {
 | 
			
		||||
      previewfile(data, "video/webm");
 | 
			
		||||
      abort();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function abort() {
 | 
			
		||||
    if (recorder) {
 | 
			
		||||
        $("#videorecorder").hide();
 | 
			
		||||
        recorder.release();
 | 
			
		||||
        delete recorder; recorder = null;
 | 
			
		||||
    }
 | 
			
		||||
  if (recorder) {
 | 
			
		||||
    $("#videorecorder").hide();
 | 
			
		||||
    recorder.release();
 | 
			
		||||
    delete recorder; recorder = null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Record Video from builtin camera
 | 
			
		||||
function recordvideo() {
 | 
			
		||||
    try {
 | 
			
		||||
        abort();
 | 
			
		||||
        $("#videorecorder").show();
 | 
			
		||||
        recorder = new MediaStreamRecorder({
 | 
			
		||||
            video: {
 | 
			
		||||
                width: {ideal: 180},
 | 
			
		||||
                height: {ideal: 160}
 | 
			
		||||
            },
 | 
			
		||||
            audio: true
 | 
			
		||||
        });
 | 
			
		||||
        recorder.on("ready", function() {
 | 
			
		||||
            $("#videorecorder video").attr("src", recorder.preview());
 | 
			
		||||
            $("#videorecorder video").css("width", 180);
 | 
			
		||||
            $("#videorecorder video").css("height", 160);
 | 
			
		||||
            $("#videorecorder video").attr("width", 180);
 | 
			
		||||
            $("#videorecorder video").attr("height", 160);
 | 
			
		||||
            recorder.start();
 | 
			
		||||
        });
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        console.log(e);
 | 
			
		||||
        error("cannot access camera", true);
 | 
			
		||||
    }
 | 
			
		||||
  try {
 | 
			
		||||
    abort();
 | 
			
		||||
    $("#videorecorder").show();
 | 
			
		||||
    recorder = new MediaStreamRecorder({
 | 
			
		||||
      video: {
 | 
			
		||||
        width: {ideal: 180},
 | 
			
		||||
        height: {ideal: 160}
 | 
			
		||||
      },
 | 
			
		||||
      audio: true
 | 
			
		||||
    });
 | 
			
		||||
    recorder.on("ready", function() {
 | 
			
		||||
      $("#videorecorder video").attr("src", recorder.preview());
 | 
			
		||||
      $("#videorecorder video").css("width", 180);
 | 
			
		||||
      $("#videorecorder video").css("height", 160);
 | 
			
		||||
      $("#videorecorder video").attr("width", 180);
 | 
			
		||||
      $("#videorecorder video").attr("height", 160);
 | 
			
		||||
      recorder.start();
 | 
			
		||||
    });
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    console.log(e);
 | 
			
		||||
    error("cannot access camera", true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function previewfile(content, type, name) {
 | 
			
		||||
    if (!name) name = guessfilename(type);
 | 
			
		||||
    if (type.match('^image/')) {
 | 
			
		||||
        var img = document.createElement("img");
 | 
			
		||||
        img.onload = function() { // resize image to maximum 400px
 | 
			
		||||
            var MAX = 400;
 | 
			
		||||
            var width = img.width;
 | 
			
		||||
            var height = img.height;
 | 
			
		||||
            if (width > MAX) {
 | 
			
		||||
                height *= MAX / width;
 | 
			
		||||
                width = MAX;
 | 
			
		||||
            }
 | 
			
		||||
            if (height > MAX) {
 | 
			
		||||
                width *= MAX / height;
 | 
			
		||||
                height = MAX;
 | 
			
		||||
            }
 | 
			
		||||
            var canvas = document.createElement("canvas");
 | 
			
		||||
            canvas.width = width;
 | 
			
		||||
            canvas.height = height;
 | 
			
		||||
            var ctx = canvas.getContext("2d");
 | 
			
		||||
            ctx.drawImage(img, 0, 0, width, height);
 | 
			
		||||
            img.onload = function() {
 | 
			
		||||
                filecontent.push({name: name, type: type, content: img.src});
 | 
			
		||||
                $("#preview").append(img);
 | 
			
		||||
                success('image is ready to be sent');
 | 
			
		||||
            }
 | 
			
		||||
            img.src = canvas.toDataURL(file.type);
 | 
			
		||||
            img.title = name+"\n"+size(img.src.length);
 | 
			
		||||
        }
 | 
			
		||||
        img.src=content;
 | 
			
		||||
    } else if (type.match('^video/')) {
 | 
			
		||||
        filecontent.push({name: name, type: type, content: content});
 | 
			
		||||
        var video = document.createElement("video");
 | 
			
		||||
        video.setAttribute("controls", "controls");
 | 
			
		||||
        video.setAttribute("loop", "loop");
 | 
			
		||||
        video.setAttribute("src", content);
 | 
			
		||||
        video.setAttribute("title", name+"\n"+size(content.length));
 | 
			
		||||
        $("#preview").append(video);
 | 
			
		||||
    } else {
 | 
			
		||||
        filecontent.push({name: name, type: type, content: content});
 | 
			
		||||
        var img = document.createElement("img");
 | 
			
		||||
        img.src = "images/Document_sans_PICOL-PIctorial-COmmunication-Language.svg";
 | 
			
		||||
        img.title = name+"\n"+size(content.length);
 | 
			
		||||
  if (!name) name = guessfilename(type);
 | 
			
		||||
  if (type.match('^image/')) {
 | 
			
		||||
    var img = document.createElement("img");
 | 
			
		||||
    img.onload = function() { // resize image to maximum 400px
 | 
			
		||||
      var MAX = 400;
 | 
			
		||||
      var width = img.width;
 | 
			
		||||
      var height = img.height;
 | 
			
		||||
      if (width > MAX) {
 | 
			
		||||
        height *= MAX / width;
 | 
			
		||||
        width = MAX;
 | 
			
		||||
      }
 | 
			
		||||
      if (height > MAX) {
 | 
			
		||||
        width *= MAX / height;
 | 
			
		||||
        height = MAX;
 | 
			
		||||
      }
 | 
			
		||||
      var canvas = document.createElement("canvas");
 | 
			
		||||
      canvas.width = width;
 | 
			
		||||
      canvas.height = height;
 | 
			
		||||
      var ctx = canvas.getContext("2d");
 | 
			
		||||
      ctx.drawImage(img, 0, 0, width, height);
 | 
			
		||||
      img.onload = function() {
 | 
			
		||||
        filecontent.push({name: name, type: type, content: img.src});
 | 
			
		||||
        $("#preview").append(img);
 | 
			
		||||
        success('image is ready to be sent');
 | 
			
		||||
      }
 | 
			
		||||
      img.src = canvas.toDataURL(file.type);
 | 
			
		||||
      img.title = name+"\n"+size(img.src.length);
 | 
			
		||||
    }
 | 
			
		||||
    img.src=content;
 | 
			
		||||
  } else if (type.match('^video/')) {
 | 
			
		||||
    filecontent.push({name: name, type: type, content: content});
 | 
			
		||||
    var video = document.createElement("video");
 | 
			
		||||
    video.setAttribute("controls", "controls");
 | 
			
		||||
    video.setAttribute("loop", "loop");
 | 
			
		||||
    video.setAttribute("src", content);
 | 
			
		||||
    video.setAttribute("title", name+"\n"+size(content.length));
 | 
			
		||||
    $("#preview").append(video);
 | 
			
		||||
  } else {
 | 
			
		||||
    filecontent.push({name: name, type: type, content: content});
 | 
			
		||||
    var img = document.createElement("img");
 | 
			
		||||
    img.src = "images/Document_sans_PICOL-PIctorial-COmmunication-Language.svg";
 | 
			
		||||
    img.title = name+"\n"+size(content.length);
 | 
			
		||||
    $("#preview").append(img);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
/// Upload Attachment
 | 
			
		||||
/** Prepares attachment to be sent in a message. If the attachment is
 | 
			
		||||
    an image, it resizes the image to 400px on the lager side.
 | 
			
		||||
   an image, it resizes the image to 400px on the lager side.
 | 
			
		||||
 | 
			
		||||
    By now, only images are supported.
 | 
			
		||||
   By now, only images are supported.
 | 
			
		||||
 | 
			
		||||
    Stores data in global variable @ref filecontent. */
 | 
			
		||||
   Stores data in global variable @ref filecontent. */
 | 
			
		||||
function fileupload(evt) {
 | 
			
		||||
    if (!window.FileReader)
 | 
			
		||||
        return error("your browser does not support file upload", true);
 | 
			
		||||
    for (var i=0, f; f=evt.target.files[i]; ++i) {
 | 
			
		||||
        var file = f;
 | 
			
		||||
        var reader = new FileReader();
 | 
			
		||||
        reader.onload = function(evt) {
 | 
			
		||||
            if (evt.target.error) return error("error reading file", true);
 | 
			
		||||
            if (evt.target.readyState==0) return notice("waiting for data …");
 | 
			
		||||
            if (evt.target.readyState==1) return notice("loading data …");
 | 
			
		||||
            previewfile(evt.target.result, file.type, file.name);
 | 
			
		||||
        }
 | 
			
		||||
        reader.readAsDataURL(file);
 | 
			
		||||
  if (!window.FileReader)
 | 
			
		||||
    return error("your browser does not support file upload", true);
 | 
			
		||||
  for (var i=0, f; f=evt.target.files[i]; ++i) {
 | 
			
		||||
    var file = f;
 | 
			
		||||
    var reader = new FileReader();
 | 
			
		||||
    reader.onload = function(evt) {
 | 
			
		||||
      if (evt.target.error) return error("error reading file", true);
 | 
			
		||||
      if (evt.target.readyState==0) return notice("waiting for data …");
 | 
			
		||||
      if (evt.target.readyState==1) return notice("loading data …");
 | 
			
		||||
      previewfile(evt.target.result, file.type, file.name);
 | 
			
		||||
    }
 | 
			
		||||
    reader.readAsDataURL(file);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Sets Receiver's Name
 | 
			
		||||
/** Called when clicked on a receiver's name. Sets focus to the
 | 
			
		||||
    message text field.
 | 
			
		||||
   message text field.
 | 
			
		||||
 | 
			
		||||
    @param name The receiver's name. */
 | 
			
		||||
   @param name The receiver's name. */
 | 
			
		||||
function setreceiver(name) {
 | 
			
		||||
    $("#recv").val(name);
 | 
			
		||||
    checkpartner(name);
 | 
			
		||||
    $("#msg").focus();
 | 
			
		||||
  $("#recv").val(name);
 | 
			
		||||
  checkpartner(name);
 | 
			
		||||
  $("#msg").focus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var userMap = null;
 | 
			
		||||
function users(userlist) {
 | 
			
		||||
    console.log("rcv-> users");
 | 
			
		||||
    userMap = new Array();
 | 
			
		||||
    $("#allusers").empty();
 | 
			
		||||
    userlist.forEach(function(usr) {
 | 
			
		||||
        userMap[usr.name] = usr.pubkey;
 | 
			
		||||
        $("#allusers").append('<option value="'+htmlenc(usr.name)+'">')
 | 
			
		||||
        $("#allusers").hide();
 | 
			
		||||
        console.log("   user: "+usr.name);
 | 
			
		||||
    });
 | 
			
		||||
    localStorage.userMap = JSON.stringify(userMap);
 | 
			
		||||
  console.log("rcv-> users");
 | 
			
		||||
  userMap = new Array();
 | 
			
		||||
  $("#allusers").empty();
 | 
			
		||||
  userlist.forEach(function(usr) {
 | 
			
		||||
    userMap[usr.name] = usr.pubkey;
 | 
			
		||||
    $("#allusers").append('<option value="'+htmlenc(usr.name)+'">')
 | 
			
		||||
    $("#allusers").hide();
 | 
			
		||||
    console.log("   user: "+usr.name);
 | 
			
		||||
  });
 | 
			
		||||
  localStorage.userMap = JSON.stringify(userMap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fail(msg) {
 | 
			
		||||
    console.log("rcv-> fail");
 | 
			
		||||
    error(msg);
 | 
			
		||||
  console.log("rcv-> fail");
 | 
			
		||||
  error(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function loggedin() {
 | 
			
		||||
    console.log("rcv-> login");
 | 
			
		||||
    success("login successful");
 | 
			
		||||
    chat();
 | 
			
		||||
  console.log("rcv-> login");
 | 
			
		||||
  success("login successful");
 | 
			
		||||
  chat();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function user(usr) {
 | 
			
		||||
    if (usr.exists) console.log("rcv-> user("+usr.name+")");
 | 
			
		||||
    else console.log("rcv-> user("+usr.name+"): name is available");
 | 
			
		||||
    if ($("#newuser").is(":visible") && usr.name==$('#user').val()) {
 | 
			
		||||
        // same username as in the create user form
 | 
			
		||||
        $("#createuser").prop("disabled", usr.exists); // todo: check password
 | 
			
		||||
        if (!usr.exists) {
 | 
			
		||||
            username = usr.name;
 | 
			
		||||
            success("user name "+usr.name+" is available");
 | 
			
		||||
        } else {
 | 
			
		||||
            username = null;
 | 
			
		||||
            error("user name "+usr.name+" is in use", true);
 | 
			
		||||
        }
 | 
			
		||||
  if (usr.exists) console.log("rcv-> user("+usr.name+")");
 | 
			
		||||
  else console.log("rcv-> user("+usr.name+"): name is available");
 | 
			
		||||
  if ($("#newuser").is(":visible") && usr.name==uid($('#user').val())) {
 | 
			
		||||
    // same username as in the create user form
 | 
			
		||||
    $("#createuser").prop("disabled", usr.exists); // todo: check password
 | 
			
		||||
    if (!usr.exists) {
 | 
			
		||||
      username = usr.name;
 | 
			
		||||
      success("user name "+usr.name+" is available");
 | 
			
		||||
    } else {
 | 
			
		||||
      username = null;
 | 
			
		||||
      error("user name "+usr.name+" is in use", true);
 | 
			
		||||
    }
 | 
			
		||||
    if ($("#chat").is(":visible") && usr.name==$("#recv").val()) { // same username as in receiver
 | 
			
		||||
        $('#send').prop("disabled", !usr.exists);
 | 
			
		||||
        $("label[for=send] img").css("opacity", usr.exists?"1.0":"0.4");
 | 
			
		||||
        $("label[for=send] img").css("filter", usr.exists?"alpha(opacity=100)":"alpha(opacity=40)");
 | 
			
		||||
        if (usr.exists) success("recipient exists");
 | 
			
		||||
        else error("unknown recipient", true);
 | 
			
		||||
    }
 | 
			
		||||
    if (userMap == null) {
 | 
			
		||||
        if (localStorage.userMap) {
 | 
			
		||||
            userMap = JSON.parse(localStorage.userMap);
 | 
			
		||||
        } else {
 | 
			
		||||
            userMap = new Array();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (usr.exists && usr.pubkey && userMap[usr.name] != usr.pubkey) {
 | 
			
		||||
        userMap[usr.name] = usr.pubkey;
 | 
			
		||||
        $("#allusers").append('option value="'+htmlenc(usr.name)+'"')
 | 
			
		||||
        localStorage.userMap = JSON.stringify(userMap);
 | 
			
		||||
  }
 | 
			
		||||
  if ($("#chat").is(":visible") && usr.name==uid($("#recv").val())) { // same username as in receiver
 | 
			
		||||
    $('#send').prop("disabled", !usr.exists);
 | 
			
		||||
    $("label[for=send] img").css("opacity", usr.exists?"1.0":"0.4");
 | 
			
		||||
    $("label[for=send] img").css("filter", usr.exists?"alpha(opacity=100)":"alpha(opacity=40)");
 | 
			
		||||
    if (usr.exists) success("recipient exists");
 | 
			
		||||
    else error("unknown recipient", true);
 | 
			
		||||
  }
 | 
			
		||||
  if (userMap == null) {
 | 
			
		||||
    if (localStorage.userMap) {
 | 
			
		||||
      userMap = JSON.parse(localStorage.userMap);
 | 
			
		||||
    } else {
 | 
			
		||||
      userMap = new Array();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (usr.exists && usr.pubkey && userMap[usr.name] != usr.pubkey) {
 | 
			
		||||
    userMap[usr.name] = usr.pubkey;
 | 
			
		||||
    $("#allusers").append('option value="'+htmlenc(usr.name)+'"')
 | 
			
		||||
    localStorage.userMap = JSON.stringify(userMap);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function queryuser(usr) {
 | 
			
		||||
  console.log("query user: "+uid(usr));
 | 
			
		||||
  socket.emit("user", uid(usr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get a user's public key.
 | 
			
		||||
/** The first time, gets it from the server, later from the cache. */
 | 
			
		||||
function getPublicKey(user) {
 | 
			
		||||
    var deferredObject = $.Deferred();
 | 
			
		||||
    if (userMap && userMap[user]) deferredObject.resolve(userMap[user]);
 | 
			
		||||
    else deferredObject.reject("unknown user");
 | 
			
		||||
    return deferredObject.promise();
 | 
			
		||||
  var deferredObject = $.Deferred();
 | 
			
		||||
  if (userMap && userMap[user]) deferredObject.resolve(userMap[user]);
 | 
			
		||||
  else deferredObject.reject("unknown user");
 | 
			
		||||
  return deferredObject.promise();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Received a list of messages from server
 | 
			
		||||
function messages(msgs) {
 | 
			
		||||
    console.log("rcv-> messages("+msgs.length+")");
 | 
			
		||||
    if (!password || !privateKey())
 | 
			
		||||
        return setTimeout(function() {emit("messages");}, 1000); // try again later
 | 
			
		||||
    status("allmessages");
 | 
			
		||||
    notice("load messages, please wait …");
 | 
			
		||||
    msgs.forEach(function(msg) {message(msg, true);});
 | 
			
		||||
    status("chat");
 | 
			
		||||
  console.log("rcv-> messages("+msgs.length+")");
 | 
			
		||||
  if (!password || !privateKey())
 | 
			
		||||
    return setTimeout(function() {emit("messages");}, 1000); // try again later
 | 
			
		||||
  status("allmessages");
 | 
			
		||||
  notice("load messages, please wait …");
 | 
			
		||||
  msgs.forEach(function(msg) {message(msg, true);});
 | 
			
		||||
  status("chat");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Received a message from server
 | 
			
		||||
function message(m, internal) {
 | 
			
		||||
    if (!internal) console.log("rcv-> message("+m.user+")");
 | 
			
		||||
    if (!password || !privateKey()) return;
 | 
			
		||||
    var key=openpgp.key.readArmored(m.pubkey);
 | 
			
		||||
    if (key.err) return error("key of sender unreadable", true);
 | 
			
		||||
    var message = openpgp.message.readArmored(m.msg);
 | 
			
		||||
    var privkey = privateKey().keys[0];
 | 
			
		||||
    if (privkey.decrypt(password)) // prepare own key
 | 
			
		||||
        openpgp.decryptAndVerifyMessage(privkey, key.keys, message)
 | 
			
		||||
        .then(function(msg) { // decryption succeded
 | 
			
		||||
            // prepend message to list of messages
 | 
			
		||||
            var message = JSON.parse(msg.text);
 | 
			
		||||
            $("#msgs") // todo: check msg.signatures[0].valid
 | 
			
		||||
                .prepend('<div id="id'+(m.id)+'" class="msg '+
 | 
			
		||||
                         (m.user==userid()?"me":"other")+
 | 
			
		||||
                         '"><div class="header">'+
 | 
			
		||||
                         '<span class="date">'+
 | 
			
		||||
                         (new Date(m.time)).toLocaleString()+
 | 
			
		||||
                         '</span><span class="sender">'+
 | 
			
		||||
                         '<a href="javascript:void(0)" '+
 | 
			
		||||
                         'onclick="setreceiver(this.innerHTML)">'+
 | 
			
		||||
                         htmlenc(m.user)+
 | 
			
		||||
                         '</a>'+(message.receiver?' → <a href="javascript:void(0)" '+
 | 
			
		||||
                                 'onclick="setreceiver(this.innerHTML)">'
 | 
			
		||||
                                 +htmlenc(message.receiver)+'</a>':"")+
 | 
			
		||||
                         '</span></div>'+
 | 
			
		||||
                         '<div class="text">'+
 | 
			
		||||
                         htmlenc(message.text)+
 | 
			
		||||
                         '</div></div><div class="clear"/>');
 | 
			
		||||
            // show attachments
 | 
			
		||||
            attachments(message.files, '#id'+m.id+' .text', m.user, new Date(m.time));
 | 
			
		||||
            // calculate and show emoticons
 | 
			
		||||
            $('#id'+m.id).emoticonize();
 | 
			
		||||
            if (!internal) beep(m.user);
 | 
			
		||||
        })
 | 
			
		||||
        .catch(function(e) {
 | 
			
		||||
            // not for me
 | 
			
		||||
            success();
 | 
			
		||||
        });
 | 
			
		||||
  if (!internal) console.log("rcv-> message("+m.user+")");
 | 
			
		||||
  if (!password || !privateKey()) return;
 | 
			
		||||
  var key=openpgp.key.readArmored(m.pubkey);
 | 
			
		||||
  if (key.err) return error("key of sender unreadable", true);
 | 
			
		||||
  var message = openpgp.message.readArmored(m.msg);
 | 
			
		||||
  var privkey = privateKey().keys[0];
 | 
			
		||||
  if (privkey.decrypt(password)) // prepare own key
 | 
			
		||||
    openpgp.decrypt({
 | 
			
		||||
      privateKeys: privkey,
 | 
			
		||||
      publicKeys: key.keys,
 | 
			
		||||
      message: message
 | 
			
		||||
    }).then(function(msg) { // decryption succeded
 | 
			
		||||
      // prepend message to list of messages
 | 
			
		||||
      var message = JSON.parse(msg.text);
 | 
			
		||||
      $("#msgs") // todo: check msg.signatures[0].valid
 | 
			
		||||
        .prepend('<div id="id'+(m.id)+'" class="msg '+
 | 
			
		||||
                           (m.user==userid()?"me":"other")+
 | 
			
		||||
                 '"><div class="header">'+
 | 
			
		||||
                 '<span class="date">'+
 | 
			
		||||
                           (new Date(m.time)).toLocaleString()+
 | 
			
		||||
                 '</span><span class="sender">'+
 | 
			
		||||
                 '<a href="javascript:void(0)" '+
 | 
			
		||||
                 'onclick="setreceiver(this.innerHTML)">'+
 | 
			
		||||
                 htmlenc(m.user)+
 | 
			
		||||
                 '</a>'+(message.receiver?' → <a href="javascript:void(0)" '+
 | 
			
		||||
                                          'onclick="setreceiver(this.innerHTML)">'
 | 
			
		||||
                                         +htmlenc(message.receiver)+'</a>':"")+
 | 
			
		||||
                 '</span></div>'+
 | 
			
		||||
                 '<div class="text">'+
 | 
			
		||||
                 htmlenc(message.text)+
 | 
			
		||||
                 '</div></div><div class="clear"/>');
 | 
			
		||||
      // show attachments
 | 
			
		||||
      attachments(message.files, '#id'+m.id+' .text', m.user, new Date(m.time));
 | 
			
		||||
      // calculate and show emoticons
 | 
			
		||||
      $('#id'+m.id).emoticonize();
 | 
			
		||||
      if (!internal) beep(m.user);
 | 
			
		||||
    }).catch(function(e) {
 | 
			
		||||
      // not for me
 | 
			
		||||
      success();
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Send Message To Server
 | 
			
		||||
/** User wants to send a message. Encrypt message with own private and
 | 
			
		||||
    the receiver's public key, then send it to the server. */
 | 
			
		||||
   the receiver's public key, then send it to the server. */
 | 
			
		||||
function sendmessage(recv, txt) {
 | 
			
		||||
    notice("1/3 preparing message …");
 | 
			
		||||
    $("#message").prop(":disabled", true);
 | 
			
		||||
    getPublicKey(recv) // get receiver's public key
 | 
			
		||||
        .done(function(pk) {
 | 
			
		||||
            var key=openpgp.key.readArmored(pk);
 | 
			
		||||
            if (!pk||key.err) {
 | 
			
		||||
                $("#message").prop(":disabled", false);
 | 
			
		||||
                error("receiver's key not found", true);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            var privkey = privateKey().keys[0];
 | 
			
		||||
            privkey.decrypt(password); // get own private key ready
 | 
			
		||||
            var message = JSON.stringify({receiver: recv, text: txt, files: filecontent});
 | 
			
		||||
            notice("2/3 encrypting message …");
 | 
			
		||||
            openpgp.signAndEncryptMessage(key.keys.concat(publicKey().keys),
 | 
			
		||||
                                          privkey,
 | 
			
		||||
                                          message)
 | 
			
		||||
                .then(function(msg) { // message is encrypted
 | 
			
		||||
                    notice("3/3 sending message …");
 | 
			
		||||
                    emit("message", {user: userid(), content: msg});
 | 
			
		||||
                    clearmessage();
 | 
			
		||||
                })
 | 
			
		||||
                .catch(function(e) {
 | 
			
		||||
                    $("#message").prop(":disabled", false);
 | 
			
		||||
                    error("encryption of message failed", true);
 | 
			
		||||
                });
 | 
			
		||||
        })
 | 
			
		||||
        .fail(function(e) {
 | 
			
		||||
            $("#message").prop(":disabled", false);
 | 
			
		||||
            error("user not found", true);
 | 
			
		||||
        });
 | 
			
		||||
  notice("1/3 preparing message …");
 | 
			
		||||
  $("#message").prop(":disabled", true);
 | 
			
		||||
  getPublicKey(recv) // get receiver's public key
 | 
			
		||||
    .done(function(pk) {
 | 
			
		||||
      var key=openpgp.key.readArmored(pk);
 | 
			
		||||
      if (!pk||key.err) {
 | 
			
		||||
        $("#message").prop(":disabled", false);
 | 
			
		||||
        error("receiver's key not found", true);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      var privkey = privateKey().keys[0];
 | 
			
		||||
      privkey.decrypt(password); // get own private key ready
 | 
			
		||||
      var message = JSON.stringify({receiver: recv, text: txt, files: filecontent});
 | 
			
		||||
      notice("2/3 encrypting message …");
 | 
			
		||||
      openpgp.encrypt({publicKeys: key.keys.concat(publicKey().keys),
 | 
			
		||||
                      privateKeys: privkey,
 | 
			
		||||
                      data: message,
 | 
			
		||||
                       armor: false})
 | 
			
		||||
             .then(function(msg) { // message is encrypted
 | 
			
		||||
               notice("3/3 sending message …");
 | 
			
		||||
               emit("message", {user: userid(), content: msg});
 | 
			
		||||
               clearmessage();
 | 
			
		||||
             })
 | 
			
		||||
             .catch(function(e) {
 | 
			
		||||
               $("#message").prop(":disabled", false);
 | 
			
		||||
               error("encryption of message failed", true);
 | 
			
		||||
             });
 | 
			
		||||
    })
 | 
			
		||||
    .fail(function(e) {
 | 
			
		||||
      $("#message").prop(":disabled", false);
 | 
			
		||||
      error("user not found", true);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check And Set Password
 | 
			
		||||
/** Check if given password matches to decrypt the private key. If so,
 | 
			
		||||
    store it in global temporary variable @ref password and start the
 | 
			
		||||
    chat. The password matches, when the private key can be decrypted.
 | 
			
		||||
   store it in global temporary variable @ref password and start the
 | 
			
		||||
   chat. The password matches, when the private key can be decrypted.
 | 
			
		||||
 | 
			
		||||
    @param pwd The password to check. */
 | 
			
		||||
   @param pwd The password to check. */
 | 
			
		||||
function setpw(pwd) {
 | 
			
		||||
    if (privateKey().keys[0].decrypt(pwd)) {
 | 
			
		||||
        success("password matches");
 | 
			
		||||
        $("#removeKey").hide();
 | 
			
		||||
        password = pwd;
 | 
			
		||||
        chat();
 | 
			
		||||
    } else {
 | 
			
		||||
        notice("password does not match");
 | 
			
		||||
    }
 | 
			
		||||
  if (privateKey().keys[0].decrypt(pwd)) {
 | 
			
		||||
    success("password matches");
 | 
			
		||||
    $("#removeKey").hide();
 | 
			
		||||
    password = pwd;
 | 
			
		||||
    chat();
 | 
			
		||||
  } else {
 | 
			
		||||
    notice("password does not match");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create Password Entry Field
 | 
			
		||||
/** Asks user for password. When user starts to enter it, it is
 | 
			
		||||
    permanentely checked in setpw(). As soon as the password matches,
 | 
			
		||||
    setpw() continues automatically. No submit is required by the
 | 
			
		||||
    user. */
 | 
			
		||||
   permanentely checked in setpw(). As soon as the password matches,
 | 
			
		||||
   setpw() continues automatically. No submit is required by the
 | 
			
		||||
   user. */
 | 
			
		||||
function getpwd() {
 | 
			
		||||
    if (password) return;
 | 
			
		||||
    $("#removeKey").show();
 | 
			
		||||
    status("getpwd");
 | 
			
		||||
  if (password) return;
 | 
			
		||||
  $("#removeKey").show();
 | 
			
		||||
  status("getpwd");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function deleteUser() {
 | 
			
		||||
    var uid = userid();
 | 
			
		||||
    localStorage.removeItem(pubkey);
 | 
			
		||||
    localStorage.removeItem(privkey);
 | 
			
		||||
    error("user "+uid+" permanentely lost");
 | 
			
		||||
  var uid = userid();
 | 
			
		||||
  localStorage.removeItem(pubkey);
 | 
			
		||||
  localStorage.removeItem(privkey);
 | 
			
		||||
  error("user "+uid+" permanentely lost");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function removeKey() {
 | 
			
		||||
    togglemenu();
 | 
			
		||||
    $("#removeKey").hide();
 | 
			
		||||
    status('forgotpassword');
 | 
			
		||||
  togglemenu();
 | 
			
		||||
  $("#removeKey").hide();
 | 
			
		||||
  status('forgotpassword');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Main Chat Window
 | 
			
		||||
/** Gets chat widgets from server and displays them. Starts timer for
 | 
			
		||||
    get() which polls for new messages. */
 | 
			
		||||
   get() which polls for new messages. */
 | 
			
		||||
var firsttime = true;
 | 
			
		||||
function chat() {
 | 
			
		||||
    if (!password) return getpwd();
 | 
			
		||||
    status("chat");
 | 
			
		||||
    if (firsttime && $('#msgs').is(':empty')) {
 | 
			
		||||
        firsttime = false;
 | 
			
		||||
        notice("getting previous messages, please wait …");
 | 
			
		||||
        emit("messages");
 | 
			
		||||
    }
 | 
			
		||||
  if (!password) return getpwd();
 | 
			
		||||
  status("chat");
 | 
			
		||||
  if (firsttime && $('#msgs').is(':empty')) {
 | 
			
		||||
    firsttime = false;
 | 
			
		||||
    notice("getting previous messages, please wait …");
 | 
			
		||||
    emit("messages");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Login User
 | 
			
		||||
/** This is not really a login, it is just some kind of validation.
 | 
			
		||||
    The server does not care if a user is online or not, it is only
 | 
			
		||||
    interesting to the client to make sure, everything is fine. User
 | 
			
		||||
    is logged in the following way: User name and public key are sent
 | 
			
		||||
    to the server. If the user name exists on the server and the
 | 
			
		||||
    public key is the same, the user is considered logged in, his
 | 
			
		||||
    credentials seem to be valid. If user does not yet exits on
 | 
			
		||||
    server, it is created now. If user exists, but public key is
 | 
			
		||||
    different, then this is a complete failure, something went
 | 
			
		||||
    terribly wrong. */
 | 
			
		||||
   The server does not care if a user is online or not, it is only
 | 
			
		||||
   interesting to the client to make sure, everything is fine. User
 | 
			
		||||
   is logged in the following way: User name and public key are sent
 | 
			
		||||
   to the server. If the user name exists on the server and the
 | 
			
		||||
   public key is the same, the user is considered logged in, his
 | 
			
		||||
   credentials seem to be valid. If user does not yet exits on
 | 
			
		||||
   server, it is created now. If user exists, but public key is
 | 
			
		||||
   different, then this is a complete failure, something went
 | 
			
		||||
   terribly wrong. */
 | 
			
		||||
function login() {
 | 
			
		||||
    $("#username").html(userid()+"@"+window.location.hostname);
 | 
			
		||||
    emit("login", {name: userid(),
 | 
			
		||||
                   pubkey: localStorage.pubkey});
 | 
			
		||||
    success("login sent to server");
 | 
			
		||||
  $("#username").html(userid()+"@"+hostname);
 | 
			
		||||
  emit("login", {name: userid(),
 | 
			
		||||
                 pubkey: localStorage.pubkey});
 | 
			
		||||
  success("login sent to server");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get And Display Form To Create New User
 | 
			
		||||
/** Shows user creation form. On submit, a private key is generated in
 | 
			
		||||
    createkeypair(), then login() creates the user. */
 | 
			
		||||
   createkeypair(), then login() creates the user. */
 | 
			
		||||
function newuser() {
 | 
			
		||||
    status("newuser");
 | 
			
		||||
  status("newuser");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check if local storage is available
 | 
			
		||||
function checkLocalStorage() {
 | 
			
		||||
    var test = 'test';
 | 
			
		||||
    try {
 | 
			
		||||
        localStorage.setItem(test, test);
 | 
			
		||||
        localStorage.removeItem(test);
 | 
			
		||||
        return true;
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        status("nolocalstorage");
 | 
			
		||||
        error("local storage not available");
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  var test = 'test';
 | 
			
		||||
  try {
 | 
			
		||||
    localStorage.setItem(test, test);
 | 
			
		||||
    localStorage.removeItem(test);
 | 
			
		||||
    return true;
 | 
			
		||||
  } catch(e) {
 | 
			
		||||
    status("nolocalstorage");
 | 
			
		||||
    error("local storage not available");
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Initial Function: Startup
 | 
			
		||||
/** Decide whether to login or to create a new user */
 | 
			
		||||
function start() {
 | 
			
		||||
    $("#menu").hide();
 | 
			
		||||
    //status("startup");
 | 
			
		||||
    if (checkLocalStorage())
 | 
			
		||||
        try {
 | 
			
		||||
            if (!userid()) {
 | 
			
		||||
                newuser();
 | 
			
		||||
            } else {
 | 
			
		||||
                login();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (m) {
 | 
			
		||||
            console.log(m.stack);
 | 
			
		||||
            error(m);
 | 
			
		||||
        }
 | 
			
		||||
  $("#menu").hide();
 | 
			
		||||
  //status("startup");
 | 
			
		||||
  if (checkLocalStorage())
 | 
			
		||||
    try {
 | 
			
		||||
      if (!userid()) {
 | 
			
		||||
        newuser();
 | 
			
		||||
      } else {
 | 
			
		||||
        login();
 | 
			
		||||
      }
 | 
			
		||||
    } catch (m) {
 | 
			
		||||
      console.log(m.stack);
 | 
			
		||||
      error(m);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    /// On Load, Call @ref start
 | 
			
		||||
    $(window.onbeforeunload = function() {
 | 
			
		||||
        return "Are you sure you want to navigate away?";
 | 
			
		||||
    });
 | 
			
		||||
    /// Allow Running in Background on Android
 | 
			
		||||
    document.addEventListener('deviceready', function () {
 | 
			
		||||
        if (cordova && cordova.plugins.backgroundMode) {
 | 
			
		||||
            cordova.plugins.backgroundMode.enable();
 | 
			
		||||
        }
 | 
			
		||||
    }, false);
 | 
			
		||||
    socket.io.on("connect", connected);
 | 
			
		||||
    socket.io.on("reconnect", connected);
 | 
			
		||||
    socket.io.on("disconnect", disconnected);
 | 
			
		||||
    socket.io.on("error", disconnected);
 | 
			
		||||
    socket.on("login", loggedin);
 | 
			
		||||
    socket.on("fail", fail);
 | 
			
		||||
    socket.on("user", user);
 | 
			
		||||
    socket.on("users", users);
 | 
			
		||||
    socket.on("message", message);
 | 
			
		||||
    socket.on("messages", messages);
 | 
			
		||||
    connectionstatus();
 | 
			
		||||
    if (openpgp.initWorker("javascripts/openpgp.worker.js"))
 | 
			
		||||
        console.log("asynchronous openpgp enabled");
 | 
			
		||||
    else
 | 
			
		||||
        console.log("asynchronous openpgp failed");
 | 
			
		||||
    emit('users');
 | 
			
		||||
    start();
 | 
			
		||||
  /// On Load, Call @ref start
 | 
			
		||||
  $(window.onbeforeunload = function() {
 | 
			
		||||
    return "Are you sure you want to navigate away?";
 | 
			
		||||
  });
 | 
			
		||||
  /// Allow Running in Background on Android
 | 
			
		||||
  document.addEventListener('deviceready', function () {
 | 
			
		||||
    if (cordova && cordova.plugins.backgroundMode) {
 | 
			
		||||
      cordova.plugins.backgroundMode.enable();
 | 
			
		||||
    }
 | 
			
		||||
  }, false);
 | 
			
		||||
  socket.io.on("connect", connected);
 | 
			
		||||
  socket.io.on("reconnect", connected);
 | 
			
		||||
  socket.io.on("disconnect", disconnected);
 | 
			
		||||
  socket.io.on("error", disconnected);
 | 
			
		||||
  socket.on("login", loggedin);
 | 
			
		||||
  socket.on("fail", fail);
 | 
			
		||||
  socket.on("user", user);
 | 
			
		||||
  socket.on("users", users);
 | 
			
		||||
  socket.on("message", message);
 | 
			
		||||
  socket.on("messages", messages);
 | 
			
		||||
  connectionstatus();
 | 
			
		||||
  if (openpgp.initWorker("openpgp.worker.min.js"))
 | 
			
		||||
    console.log("asynchronous openpgp enabled");
 | 
			
		||||
  else
 | 
			
		||||
    console.log("asynchronous openpgp failed");
 | 
			
		||||
  emit('users');
 | 
			
		||||
  start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Start Main Loop
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user