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"];
|
||||
@@ -37,16 +37,84 @@
|
||||
// 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