diff --git a/nodejs/public/javascripts/safechat.js b/nodejs/public/javascripts/safechat.js index b16abd3..f8586dc 100644 --- a/nodejs/public/javascripts/safechat.js +++ b/nodejs/public/javascripts/safechat.js @@ -37,870 +37,1026 @@ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 -function SafeChatClient(success, notice, error) { +function SafeChat() { - /// Cache Client's Key from local Strorage - var k = null; - - function browserSupported() { - window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB - window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction - window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange - return window.indexedDB && window.crypto.getRandomValues && Storage + /// Create UID from a name by appending an E-Mail + function uid(name) { + return name+' <'+name+'@'+hostname+'>' } - /// 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) - } + /// @class Crypto cryptographic functions + /** @param view is of class SafeChat.View */ + function Crypto(view) { + + /// cache client's key from local strorage + var k = null + + /// detect hosstname, default to safechat.ch + var hostname = window.location.hostname!='localhost'?window.location.hostname:'safechat.ch' + + /// 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 user() { + if (k || key()) return k.pub.keys[0].getUserIds()[0] + return null + } + + /// create New User + function createuser(user, email, pwd) { + view.notice("generating keys") + openpgp.generateKey({ + numBits: 4096, + userIds: [{name: user, email: email}], + passphrase: pwd + }).then(function(keyPair) { + view.success("keys generated") + localStorage.key = keyPair.privateKeyArmored + k = keyPair.key + }).catch(function(e) { + console.log(e) + view.fatal("generating key pairs failed") + }) + } + + /// open private key with password + /** @return @c true if password matches */ + function password(pwd) { + return (k || keys()) && k.keys[0].decrypt(pwd) + } + + /// Encrypt Message + function encrypt(message, targetkeys, done, failed) { + if (!k) return false + openpgp.encrypt({ + publicKeys: targetkeys.keys.concat(k.keys), + privateKeys: k, + data: message, + armor: false + }).then(done).else(failed) + return true + } + + /// Decrypt Message + function decrypt(message, sourcekeys, done, failed) { + if (!k) return false + openpgp.decrypt({ + privateKeys: k.keys, + publicKeys: sourcekeys.keys, + message: message + }).then(done).else(failed) + return true + } - /// 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") - }) + /// database that stores in indexed db + function DataBase() { + + function user(name, key) { + } + } - function password(pwd) { - return (k || keys()) && k.keys[0].decrypt(pwd) + /// manage local copy of users + function Users() { + + var users = new Map() + + function add(usr) { + if (!users[usr.name]) + users[usr.name].valid = true + else + users[usr.name].valid = users[usr.name].valid && users[usr.name].key == usr.key + users[usr.name].key = usr.key + users[usr.name].online = usr.online + } + } - /// 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 + /// manage local copy of messages + function Messages() { + + var messages = {}; + + function add() { + } + } - /// Decrypt Message - function decrypt(message) { - if (!k) return false + /// @class Communication client socket communication + /** @param view is of class SafeChat.View */ + function Communication(controller) { + + var socket = io.connect() + + function broadcast(signal, data) { + console.log("<=snd "+signal) + socket.broadcast.emit(signal, data) + } + + function emit(signal, data) { + console.log("<-snd "+signal) + socket.emit(signal, data) + } + + socket + .on("login", controller.loggedin) + .on("fail", controller.fail) + .on("user", controller.user) + .on("users", controller.users) + .on("message", controller.message) + .on("messages", controller.messages) + .io + .on("connect", controller.connected) + .on("reconnect", controller.connected) + .on("disconnect", controller.disconnected) + .on("error", controller.disconnected) - 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'; + /// @class View provides the glue to the GUI in the index.ejs file + /** View provides the following callbacks: + - status updates: + - @c notice(msg) to display information + - @c warning(msg) + - @c error(msg) + - @c fatal(msg) */ + function View() { -/// Padding for numbers in dates -function pad(n) { - return n<10 ? '0'+n : n -} + var nexttimer = null -function uid(name) { - return name+' <'+name+'@'+hostname+'>'; -} + /// Padding for numbers in dates + function pad(n) { + return n<10 ? '0'+n : n + } + + /// escape text to show in html @see htmldec + function htmlenc(html) { + return $('
').text(html).html() + } + + /// decode html encoded text @see htmlenc + function htmldec(data) { + return $('
').html(data).text() + } -/// 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"; + /// alert user accoustically or by vibration + /** alert user, e.g. that a new message has arrived. */ + function beep() { + if (navigator.vibrate) navigator.vibrate(1000) + (new Audio("sounds/beep.mp3")).play() + } + + /// show fatal error + /** something completely failed, abort + @param msg */ + function fatal(msg) { + if (nexttimer) clearTimeout(nexttimer) + if (msg) { + error(msg) + $('#fatal-msg').html(msg) + } + show('fatal') + } + + /// show error messsage + /** shows 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 next (optional) next function to call */ + function error(data, next) { + if (nexttimer) clearTimeout(nexttimer) + $("#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 { - return Math.round(num/1024/1024/1024)+"GB"; + $("#status").html('error') + console.log("error: "+JSON.stringify(data)) } } else { - return Math.round(num/1024/1024)+"MB"; + $("#status").html('error') + console.log("error") } - } else { - return Math.round(num/1024)+"kB"; + $("#status").show() + if (next) nexttimer = setTimeout(function() { + nexttimer = null + next() + }, 5000) } - } 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. */ -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)); + /// show notice messsage + /** shows an notice message and logs to console. + @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() } - } 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. */ -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(); -} + /// show success messsage + /** shows an success message and logs to console. + @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() + } -/// Show notice messsage -/** Fades in an success message and logs to console. - @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(); -} + /// show a specific screen given the element id + /** @param id html id to be shown. + @param msg (optional) the success message text */ + function show(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() + } -/// Show status message in the main screen area -/** @param id HTML id to be shown. - @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(); -} + /// show server connected status + function connected() { + console.log("server connected") + $("#connectionstatus #bad").hide() + $("#connectionstatus #good").show() + success("server connected") + } -function emit(signal, data) { - console.log("<-snd "+signal); - socket.emit(signal, data); -} + /// show server disconnected status + function disconnected() { + console.log("server disconnected") + $("#connectionstatus #good").hide() + $("#connectionstatus #bad").show() + error("server disconnected", true) + } + + /// toggle menu display + function togglemenu() { + $("#menu").toggle() + } -function connected() { - console.log("server connected"); - $("#connectionstatus #bad").hide(); - $("#connectionstatus #good").show(); - success("server connected"); -} + function checkFeature(id, query) { + if (query) $('#'+id+':before') + .css('color', 'green') + .css('content', '✔') + else $('#'+id+':before') + .css('color', 'red') + .css('content', '✘') + if (query) $('#'+id) + .css('color', 'green') + .css('text-decoration', 'line-through') + else $('#'+id) + .css('color', 'red') + .css('text-decoration', 'none') + } -function disconnected() { - console.log("server disconnected"); - $("#connectionstatus #good").hide(); - $("#connectionstatus #bad").show(); - error("server disconnected", true); -} + function checkFeatures() { + $('ul.features').css('list-style-type', 'none') + checkFeature("localstorage", Storage) + checkFeature("indexeddb", window.indexedDB) + checkFeature("randomvalues", window.crypto.getRandomValues) + checkFeature("vibrate", navigator.vibrate) + checkFeature("filereader", window.FileReader) + } -function connectionstatus() { - if (socket.connected) connected(); else disconnected(); -} + function DataTransfer() { + + var reboottimer = null + var data = new DataTransfer() + + /// 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()+"@"+hostname+".bak" + var clickEvent = new MouseEvent("click", { + "view": window, + "bubbles": true, + "cancelable": false + }) + download.dispatchEvent(clickEvent) + togglemenu() + } + + /// Upload Profile Backup + function restore(evt) { + 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") + 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 && reboot) reboottimer = setTimeout(function() { + reboottimer = null + }, 2000) + } + reader.readAsText(file) + } + } -function htmlenc(html) { - return $('
').text(html).html(); -} + if (!window.FileReader) { + $('restore-menu-item').hide() + error("your browser does not support file upload") + } -function htmldec(data) { - return $('
').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(); -} -/// Toggle Menu Display -function togglemenu() { - $("#menu").toggle(); -} + /// @class Controller defines the programm flow + function Controller(view) { + + var db = new Database() + var communication = new Communication(this) + var users = new Users() + + function fail(msg) { + console.log('rcv-> fail('+msg+')') + error(msg) + } -/// 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()+"@"+hostname+".bak"; - var clickEvent = new MouseEvent("click", { - "view": window, - "bubbles": true, - "cancelable": false - }); - download.dispatchEvent(clickEvent); - togglemenu(); -} + function loggedin() { + console.log("rcv-> login") + success("login successful") + chat() + } + + function user(usr) { + console.log("rcv-> user") + if (usr.exits) users.add(usr) + } -/// 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); - } -} + function users() { + console.log("rcv-> users") + } + + function message(msg) { + console.log("rcv-> message") + } + + function messages(msgs) { + console.log("rcv-> messages") + } -/// Configure local groups -/** … */ -function groups() { -} + this.connected = view.connected + this.reconnect = view.connected + this.disconnect = view.disconnected + this.error = view.disconnected -/// Check if password is set and matches the repeated password -/** Checks if both passwords are identical and valid and gives - feedback to the user. - - Called when user edits the password fields. - - 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. */ -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"); - - } -} + function login() { + } -/// 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. */ -function checkpartner(user) { - $("#chat").submit(function(event) { - return false; - }); - emit("user", uid(user)); -} + function user(usr) { + if (usr.exists) db.adduser + } -/// 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.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") - }); -} + function initBrowser() { + window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB + window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction + window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange + navigator.vibrate = navigator.vibrate || navigator.webkitVibrate || navigator.mozVibrate || navigator.msVibrate + return window.indexedDB && window.crypto.getRandomValues && Storage + } -/// 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); + function run() { } - } - 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); + function start() { + view.reboot = run + var compatible = initBrowser() + view.checkFeatures() + if (!compatible) + view.fatal("your browser is not supported") + else + run() } + } - 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]; -} + //============================================================================== -/// 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); -} + //------------------------------------------------------------------------------ + return new Controller(new View()) -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+"@"+hostname+'.'+ext; } + + var filecontent = new Array() ///< temporary storage for attachments + var reboottimer = null -/// 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); - }); -} + function connectionstatus() { + if (socket.connected) connected() else disconnected() + } + + /// Configure local groups + /** … */ + 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. -var recorder; + Called when user edits the password fields. -function done() { - if (recorder) { - recorder.stop(); - recorder.recording(function(data) { - previewfile(data, "video/webm"); - abort(); - }); + 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. */ + 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") + + } } -} -function abort() { - if (recorder) { - $("#videorecorder").hide(); - recorder.release(); - delete recorder; recorder = null; + /// 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. */ + function checkpartner(user) { + $("#chat").submit(function(event) { + return false + }) + emit("user", uid(user)) } -} -/// 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); + /// 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.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") + }) } -} -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; + /// 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) } - 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'); + } + 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) } - 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); + } + return openpgp.key.readArmored(localStorage.privkey) } -} -/// 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. - - By now, only images are supported. - - 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); + /// 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] } -} -/// Sets Receiver's Name -/** Called when clicked on a receiver's name. Sets focus to the - message text field. + /// 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) + } - @param name The receiver's name. */ -function setreceiver(name) { - $("#recv").val(name); - checkpartner(name); - $("#msg").focus(); -} + 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+"@"+hostname+'.'+ext + } -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('