in the middle of rewriting it
This commit is contained in:
		@@ -3,7 +3,6 @@ module.exports = function(config) {
 | 
			
		||||
  var fs = require('fs');
 | 
			
		||||
  config.multipleStatements = true;
 | 
			
		||||
  var pool = mysql.createPool(config);
 | 
			
		||||
  console.log(__dirname+'/schema.sql')
 | 
			
		||||
  pool.query(fs.readFileSync(__dirname+'/schema.sql').toString());
 | 
			
		||||
  if (config.max_allowed_packet)
 | 
			
		||||
    pool.query("set global max_allowed_packet=?", [config.max_allowed_packet]);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								nodejs/etc/systemd/system/safechat.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								nodejs/etc/systemd/system/safechat.service
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Secure and Encrypted Chat Server
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
ExecStart=/usr/bin/nodejs /usr/share/safechat/nodejs/safechat > /var/log/safechat.log
 | 
			
		||||
Restart=on-abort
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
@@ -20,8 +20,8 @@
 | 
			
		||||
   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"];
 | 
			
		||||
   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"];
 | 
			
		||||
@@ -33,20 +33,88 @@
 | 
			
		||||
   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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function uid(name) {
 | 
			
		||||
  return name+' <'+name+'@'+hostname+'>';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Convert number of bytes to readable text
 | 
			
		||||
function size(num) {
 | 
			
		||||
  if (num>0.6*1024) {
 | 
			
		||||
@@ -206,7 +274,7 @@ function backup() {
 | 
			
		||||
  var now = new Date();
 | 
			
		||||
  download.download =
 | 
			
		||||
  pad(now.getFullYear())+pad(now.getMonth()+1)+pad(now.getDate())+
 | 
			
		||||
        "-"+userid()+"@"+window.location.hostname+".bak";
 | 
			
		||||
  "-"+userid()+"@"+hostname+".bak";
 | 
			
		||||
  var clickEvent = new MouseEvent("click", {
 | 
			
		||||
    "view": window,
 | 
			
		||||
    "bubbles": true,
 | 
			
		||||
@@ -284,16 +352,16 @@ function checkpartner(user) {
 | 
			
		||||
  $("#chat").submit(function(event) {
 | 
			
		||||
    return false;
 | 
			
		||||
  });
 | 
			
		||||
    emit("user", user);
 | 
			
		||||
  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({
 | 
			
		||||
  openpgp.generateKey({
 | 
			
		||||
    numBits: 4096,
 | 
			
		||||
        userId: user,
 | 
			
		||||
    userIds: [{name: user, email: user+'@'+hostname}],
 | 
			
		||||
    passphrase: pwd
 | 
			
		||||
  }).then(function(keyPair) {
 | 
			
		||||
    success("keys generated");
 | 
			
		||||
@@ -301,7 +369,8 @@ function createkeypair(user, pwd) {
 | 
			
		||||
    localStorage.privkey = keyPair.privateKeyArmored;
 | 
			
		||||
    login();
 | 
			
		||||
  }).catch(function(e) {
 | 
			
		||||
        error("generating key pairs failed");
 | 
			
		||||
    console.log(e)
 | 
			
		||||
    error("generating key pairs failed")
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -357,7 +426,7 @@ function guessfilename(mimetype, user, date) {
 | 
			
		||||
  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;
 | 
			
		||||
        +"-"+ext+"-"+user+"@"+hostname+'.'+ext;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Display Image Attachments
 | 
			
		||||
@@ -545,7 +614,7 @@ function loggedin() {
 | 
			
		||||
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()) {
 | 
			
		||||
  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) {
 | 
			
		||||
@@ -556,7 +625,7 @@ function user(usr) {
 | 
			
		||||
      error("user name "+usr.name+" is in use", true);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
    if ($("#chat").is(":visible") && usr.name==$("#recv").val()) { // same username as in receiver
 | 
			
		||||
  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)");
 | 
			
		||||
@@ -577,6 +646,11 @@ function user(usr) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
@@ -606,8 +680,11 @@ function message(m, internal) {
 | 
			
		||||
  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
 | 
			
		||||
    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
 | 
			
		||||
@@ -632,8 +709,7 @@ function message(m, internal) {
 | 
			
		||||
      // calculate and show emoticons
 | 
			
		||||
      $('#id'+m.id).emoticonize();
 | 
			
		||||
      if (!internal) beep(m.user);
 | 
			
		||||
        })
 | 
			
		||||
        .catch(function(e) {
 | 
			
		||||
    }).catch(function(e) {
 | 
			
		||||
      // not for me
 | 
			
		||||
      success();
 | 
			
		||||
    });
 | 
			
		||||
@@ -657,9 +733,10 @@ function sendmessage(recv, txt) {
 | 
			
		||||
      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)
 | 
			
		||||
      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});
 | 
			
		||||
@@ -743,7 +820,7 @@ function chat() {
 | 
			
		||||
   different, then this is a complete failure, something went
 | 
			
		||||
   terribly wrong. */
 | 
			
		||||
function login() {
 | 
			
		||||
    $("#username").html(userid()+"@"+window.location.hostname);
 | 
			
		||||
  $("#username").html(userid()+"@"+hostname);
 | 
			
		||||
  emit("login", {name: userid(),
 | 
			
		||||
                 pubkey: localStorage.pubkey});
 | 
			
		||||
  success("login sent to server");
 | 
			
		||||
@@ -810,7 +887,7 @@ function init() {
 | 
			
		||||
  socket.on("message", message);
 | 
			
		||||
  socket.on("messages", messages);
 | 
			
		||||
  connectionstatus();
 | 
			
		||||
    if (openpgp.initWorker("javascripts/openpgp.worker.js"))
 | 
			
		||||
  if (openpgp.initWorker("openpgp.worker.min.js"))
 | 
			
		||||
    console.log("asynchronous openpgp enabled");
 | 
			
		||||
  else
 | 
			
		||||
    console.log("asynchronous openpgp failed");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,14 @@
 | 
			
		||||
module.exports = function(app, package) {
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * GET home page.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
var package = require(__dirname+"/../package.json");
 | 
			
		||||
 | 
			
		||||
exports.index = function(req, res) {
 | 
			
		||||
  res.render(path, {
 | 
			
		||||
    projecturl: package.documentation,
 | 
			
		||||
    packagename: package.name,
 | 
			
		||||
    packageversion: package.version
 | 
			
		||||
  [
 | 
			
		||||
    '',
 | 
			
		||||
    'webrtc'
 | 
			
		||||
  ].forEach(function(p) {
 | 
			
		||||
    app.get('/'+p, function(req, res) {
 | 
			
		||||
      res.render(p?p:'index', {
 | 
			
		||||
        package: package
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.webrtc = function(req, res) {
 | 
			
		||||
  res.render('webrtc', {
 | 
			
		||||
    projecturl: package.documentation,
 | 
			
		||||
    packagename: package.name,
 | 
			
		||||
    packageversion: package.version
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,8 @@
 | 
			
		||||
var package = require(__dirname+'/package.json');
 | 
			
		||||
var config = require(package.path.config);
 | 
			
		||||
var express = require('express');
 | 
			
		||||
var routes = require(__dirname+'/routes');
 | 
			
		||||
var app = module.exports = express.createServer();
 | 
			
		||||
var routes = require(__dirname+'/routes')(app, package);
 | 
			
		||||
var io = require('socket.io').listen(app);
 | 
			
		||||
var sql = require(__dirname+'/database')(config.mysql);
 | 
			
		||||
var sockets = require(__dirname+'/sockets')(sql);
 | 
			
		||||
@@ -34,11 +34,17 @@ app.configure(function() {
 | 
			
		||||
  app.use(app.router);
 | 
			
		||||
  app.use(express.static(__dirname + '/public'));
 | 
			
		||||
  [
 | 
			
		||||
    'jquery/dist',
 | 
			
		||||
    'openpgp/dist'
 | 
			
		||||
    'jquery/dist/jquery.min.js',
 | 
			
		||||
    'jquery/dist/jquery.js',
 | 
			
		||||
    'openpgp/dist/openpgp.min.js',
 | 
			
		||||
    'openpgp/dist/openpgp.js',
 | 
			
		||||
    'openpgp/dist/openpgp.worker.min.js',
 | 
			
		||||
    'openpgp/dist/openpgp.worker.js'
 | 
			
		||||
  ].forEach(function(file) {
 | 
			
		||||
    app.use('/'+file.replace(/\/.*/g, ''),
 | 
			
		||||
            express.static(__dirname + '/node_modules/'+file));
 | 
			
		||||
    app.get('/'+file.replace(/.*\//g, ''),
 | 
			
		||||
            function(req, res) {
 | 
			
		||||
              res.sendfile('/node_modules/'+file, {root: __dirname})
 | 
			
		||||
            })
 | 
			
		||||
  })
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -54,11 +60,6 @@ app.configure('production', function(){
 | 
			
		||||
 | 
			
		||||
io.sockets.on('connection', sockets.connection);
 | 
			
		||||
 | 
			
		||||
// Routes
 | 
			
		||||
 | 
			
		||||
app.get('/', routes.index);
 | 
			
		||||
app.get('/webrtc', routes.webrtc);
 | 
			
		||||
 | 
			
		||||
app.listen(config.port, function(){
 | 
			
		||||
  console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,7 @@ module.exports = function(sql) {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    socket.on("login", function(user) {
 | 
			
		||||
      console.log('-> signal: login('+user.name+')');
 | 
			
		||||
      if (!user.name || !user.pubkey) return emit("fail", "wrong login format");
 | 
			
		||||
      console.log("-> signal: login("+user.name+")");
 | 
			
		||||
      if (user.name=="safechat") return emit("fail", "user name safechat is reserved");
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,8 @@
 | 
			
		||||
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width initial-scale=1" />
 | 
			
		||||
    <link href="stylesheets/safechat.css" rel="stylesheet" type="text/css" />
 | 
			
		||||
    <script type="text/javascript" src="jquery/jquery.min.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="openpgp/openpgp.min.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="openpgp/openpgp.worker.min.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="jquery.min.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="openpgp.min.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="socket.io/socket.io.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="javascripts/mediarecorder.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="javascripts/safechat.js"></script>
 | 
			
		||||
@@ -18,7 +17,7 @@
 | 
			
		||||
  <body>
 | 
			
		||||
    
 | 
			
		||||
    <div id="header" class="header">
 | 
			
		||||
      <h1>Safe Chat <%= packageversion %></h1>
 | 
			
		||||
      <h1>Safe Chat <%= package.version %></h1>
 | 
			
		||||
      <div id="togglemenu">
 | 
			
		||||
        <span id="username">[unknown]</span>
 | 
			
		||||
        <span id="connectionstatus">
 | 
			
		||||
@@ -35,7 +34,7 @@
 | 
			
		||||
      <li id="groups" onclick="groups()">Edit Groups</li>
 | 
			
		||||
      <li id="removeKey" style="display: none" onclick="removeKey()">Password Forgotten</li>
 | 
			
		||||
      <li id="android-download" href="safechat.apk"><a href="safechat.apk">Download Android-App</a></li>
 | 
			
		||||
      <li href="<%= projecturl %>" target="_blank"><a href="<%= projecturl %>" target="_blank">About Safe Chat</a></li>
 | 
			
		||||
      <li href="<%= package.documentation %>" target="_blank"><a href="<%= package.documentation %>" target="_blank">About Safe Chat</a></li>
 | 
			
		||||
    </ul>
 | 
			
		||||
    <script type="text/javascript">
 | 
			
		||||
      $(function() { // on load: without cordova, remove andoid-download
 | 
			
		||||
@@ -56,16 +55,14 @@
 | 
			
		||||
          <input placeholder="username" autocomplete="off" type="text" id="user"/>
 | 
			
		||||
          <input placeholder="password" autocomplete="off" type="password" id="pwd" oninput="checkpwd(this.value, document.getElementById('pwd2').value)"/>
 | 
			
		||||
          <input placeholder="repeat password" autocomplete="off" type="password" id="pwd2" oninput="checkpwd(document.getElementById('pwd').value, this.value)"/>
 | 
			
		||||
          <input id="createuser" type="submit" disabled/>
 | 
			
		||||
          <button id="createuser" disabled>register</button>
 | 
			
		||||
        </form>
 | 
			
		||||
        <script>
 | 
			
		||||
          $("#user").on("input", function() {
 | 
			
		||||
            console.log("query user: "+$('#user').val());
 | 
			
		||||
            socket.emit("user", $('#user').val());
 | 
			
		||||
            queryuser($('#user').val());
 | 
			
		||||
          });
 | 
			
		||||
          $("#register").submit(function(event) {
 | 
			
		||||
            createkeypair(event.target.elements['user'].value,
 | 
			
		||||
                          event.target.elements['pwd'].value);
 | 
			
		||||
          $("#createuser").on("click", function(event) {
 | 
			
		||||
            createkeypair($('#user').val(), $('#pwd').val());
 | 
			
		||||
            return false;
 | 
			
		||||
          });
 | 
			
		||||
        </script>
 | 
			
		||||
@@ -193,7 +190,7 @@
 | 
			
		||||
          server. Your password and your secret key are fully under
 | 
			
		||||
          your control. That's why you must enable javascript and
 | 
			
		||||
          local storage for this application.</p>
 | 
			
		||||
        <p><a href="<%= projecturl %>" target="_blank">more information</a></p>
 | 
			
		||||
        <p><a href="<%= package.documentation %>" target="_blank">more information</a></p>
 | 
			
		||||
      </noscript>
 | 
			
		||||
 | 
			
		||||
      <!--                                                  Error: Missing LocalStorage -->
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user