|
|
|
@ -1,245 +1,313 @@ |
|
|
|
|
/*! @file |
|
|
|
|
|
|
|
|
|
@id $Id$ |
|
|
|
|
|
|
|
|
|
This is the main application as it is fully run in the user's browser. |
|
|
|
|
|
|
|
|
|
@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 |
|
|
|
|
*/ |
|
|
|
|
@id $Id$ |
|
|
|
|
|
|
|
|
|
This is the main application as it is fully run in the user's browser. |
|
|
|
|
|
|
|
|
|
@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.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 ($("#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) 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 (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
|
|
|
|
|