@ -41,9 +41,15 @@ function SafeChat() {
/// Create UID from a name by appending an E-Mail
/// Create UID from a name by appending an E-Mail
function uid ( name ) {
function uid ( name ) {
return name + ' <' + name + '@' + hostname + '>'
return name + ' <' + mail ( name ) + '>'
}
}
function mail ( name ) {
var hostname = window . location . hostname != 'localhost' ? window . location . hostname : 'safechat.ch'
return name + '@' + hostname
}
//==============================================================================
/// @class Crypto cryptographic functions
/// @class Crypto cryptographic functions
/** @param view is of class SafeChat.View */
/** @param view is of class SafeChat.View */
function Crypto ( controller ) {
function Crypto ( controller ) {
@ -51,42 +57,23 @@ function SafeChat() {
/// cache client's key from local strorage
/// cache client's key from local strorage
var k = null
var k = null
/// detect hosstname, default to safechat.ch
/// detect hostname, default to safechat.ch
var hostname = window . location . hostname != 'localhost' ? window . location . hostname : 'safechat.ch'
/// get user key
/// get user key
/ * * @ i n t e r n a l k e y i s t c a c h e d i n k
/ * * @ i n t e r n a l k e y i s t c a c h e d i n k
@ return key * /
@ return key * /
function key ( ) {
this . key = function ( ) {
if ( k ) return k
if ( k ) return k // cached key
if ( typeof localStorage . key == 'undefined' ) return null
if ( typeof localStorage . priv key = == 'undefined' ) return null
return k = openpgp . key . readArmored ( localStorage . key )
return k = openpgp . key . readArmored ( localStorage . priv key)
}
}
/// get own user name
/// get own user name
/** get user name as user id of first public key */
/** get user name as user id of first public key */
function user ( ) {
this . user = function ( ) {
if ( k || key ( ) ) return k . pub . keys [ 0 ] . getUserIds ( ) [ 0 ]
if ( k || key ( ) ) return k . pub . keys [ 0 ] . getUserIds ( ) [ 0 ]
return null
return null
}
}
/// create New User
function createuser ( user , email , pwd ) {
controller . notice ( "generating keys" )
openpgp . generateKey ( {
numBits : 4096 ,
userIds : [ { name : user , email : email } ] ,
passphrase : pwd
} ) . then ( function ( keyPair ) {
controller . success ( "keys generated" )
localStorage . key = keyPair . privateKeyArmored
k = keyPair . key
} ) . catch ( function ( e ) {
console . log ( e )
controller . fatal ( "generating key pairs failed" )
} )
}
/// open private key with password
/// open private key with password
/** @return @c true if password matches */
/** @return @c true if password matches */
function password ( pwd ) {
function password ( pwd ) {
@ -116,8 +103,15 @@ function SafeChat() {
return true
return true
}
}
//------------------------------------------------------------------------------
if ( openpgp . initWorker ( "openpgp.worker.min.js" ) )
console . log ( "asynchronous openpgp enabled" )
else
console . log ( "asynchronous openpgp failed" )
}
}
//==============================================================================
/// database that stores in indexed db
/// database that stores in indexed db
function DataBase ( ) {
function DataBase ( ) {
@ -126,6 +120,7 @@ function SafeChat() {
}
}
//==============================================================================
/// manage local copy of users
/// manage local copy of users
function Users ( ) {
function Users ( ) {
@ -142,6 +137,7 @@ function SafeChat() {
}
}
//------------------------------------------------------------------------------
/// manage local copy of messages
/// manage local copy of messages
function Messages ( ) {
function Messages ( ) {
@ -152,6 +148,7 @@ function SafeChat() {
}
}
//==============================================================================
/// @class Communication client socket communication
/// @class Communication client socket communication
/** @param view is of class SafeChat.View */
/** @param view is of class SafeChat.View */
function Communication ( controller ) {
function Communication ( controller ) {
@ -163,9 +160,13 @@ function SafeChat() {
socket . broadcast . emit ( signal , data )
socket . broadcast . emit ( signal , data )
}
}
function emit ( signal , data ) {
function emit ( signal , data , next ) {
console . log ( "<-snd " + signal )
console . log ( "<-snd " + signal )
socket . emit ( signal , data )
socket . emit ( signal , data , next )
}
this . lookup = function ( usr , next ) {
emit ( 'user' , usr , next )
}
}
socket
socket
@ -183,6 +184,7 @@ function SafeChat() {
}
}
//==============================================================================
/// @class View provides the glue to the GUI in the index.ejs file
/// @class View provides the glue to the GUI in the index.ejs file
/ * * V i e w p r o v i d e s t h e f o l l o w i n g c a l l b a c k s :
/ * * V i e w p r o v i d e s t h e f o l l o w i n g c a l l b a c k s :
- status updates :
- status updates :
@ -299,7 +301,7 @@ function SafeChat() {
@ param msg ( optional ) the success message text * /
@ param msg ( optional ) the success message text * /
function show ( id , msg ) {
function show ( id , msg ) {
console . log ( "state: " + id )
console . log ( "state: " + id )
if ( msg ) success ( msg ) else $ ( "#status" ) . hide ( )
if ( msg ) success ( msg ) ; else $ ( "#status" ) . hide ( ) ;
$ ( "#main" ) . children ( ":not(#" + id + ")" ) . hide ( )
$ ( "#main" ) . children ( ":not(#" + id + ")" ) . hide ( )
$ ( "#main #" + id ) . show ( )
$ ( "#main #" + id ) . show ( )
$ ( "#main #" + id + " form input:first-child" ) . focus ( )
$ ( "#main #" + id + " form input:first-child" ) . focus ( )
@ -327,21 +329,15 @@ function SafeChat() {
}
}
function checkFeature ( id , query ) {
function checkFeature ( id , query ) {
if ( query ) $ ( '#' + id + ':before' )
. css ( 'color' , 'green' )
. css ( 'content' , '✔' )
else $ ( '#' + id + ':before' )
. css ( 'color' , 'red' )
. css ( 'content' , '✘' )
if ( query ) $ ( '#' + id )
if ( query ) $ ( '#' + id )
. css ( 'color' , 'green' )
. css ( 'color' , 'green' )
. css ( 'text-decoration' , 'line-through ')
. prepend ( '<span>✔</span>' )
else $ ( '#' + id )
else $ ( '#' + id )
. css ( 'color' , 'red' )
. css ( 'color' , 'red' )
. css ( 'text-decoration' , 'none ')
. prepend ( '<span>✘</span>' )
}
}
function checkFeatures ( ) {
this . checkFeatures = function ( ) {
$ ( 'ul.features' ) . css ( 'list-style-type' , 'none' )
$ ( 'ul.features' ) . css ( 'list-style-type' , 'none' )
checkFeature ( "localstorage" , Storage )
checkFeature ( "localstorage" , Storage )
checkFeature ( "indexeddb" , window . indexedDB )
checkFeature ( "indexeddb" , window . indexedDB )
@ -350,6 +346,54 @@ function SafeChat() {
checkFeature ( "filereader" , window . FileReader )
checkFeature ( "filereader" , window . FileReader )
}
}
/// @name create new user
/// @{
this . newuser = function ( ) {
show ( 'newuser' )
}
var user = null
var pwd = false
function invalid ( usr ) {
return ! user || ! user . exists && user . name . length < 3
}
this . available = function ( usr ) {
user = usr
console . log ( "props:" , invalid ( user ) || ! pwd )
$ ( "#createuser" ) . prop ( ":disabled" , invalid ( user ) || ! pwd )
if ( user . length == 0 )
notice ( "please chose a user name" )
else if ( user . length < 3 )
notice ( "please chose a longer user name" )
else if ( user . exists )
notice ( "user name is already in use" )
else if ( ! pwd )
notice ( "please chose a password" )
else
success ( "user is ready to be created" )
}
this . passwords = function ( pwd1 , pwd2 ) {
pwd = pwd1 == pwd2 && pwd1 . length > 5
console . log ( "props:" , invalid ( user ) || ! pwd )
$ ( "#createuser" ) . prop ( ":disabled" , invalid ( user ) || ! pwd )
if ( pwd1 . length == 0 )
notice ( 'please chose a password' )
else if ( pwd1 . length < 6 )
notice ( 'please chose a longer password' )
else if ( pwd1 != pwd2 )
notice ( "passwords don't match" )
else if ( invalid ( user ) )
notice ( "please chose a user name" )
else
success ( "user is ready to be created" )
}
/// @}
function DataTransfer ( ) {
function DataTransfer ( ) {
var reboottimer = null
var reboottimer = null
@ -405,10 +449,11 @@ function SafeChat() {
}
}
//==============================================================================
/// @class Controller defines the programm flow
/// @class Controller defines the programm flow
function Controller ( view ) {
function Controller ( view ) {
var db = new Datab ase ( )
var db = new DataB ase ( )
var crypto = new Crypto ( this )
var crypto = new Crypto ( this )
var communication = new Communication ( this )
var communication = new Communication ( this )
var users = new Users ( )
var users = new Users ( )
@ -461,6 +506,33 @@ function SafeChat() {
// @}
// @}
/// @name signals from view
/// @{
/// @name new user registration
/// @{
this . lookup = function ( usr ) {
if ( usr . length > 2 ) communication . lookup ( uid ( usr ) , function ( res ) {
view . available ( res )
} )
}
this . checkpasswords = view . passwords
this . createuser = function ( name , pwd ) {
crypto . createuser ( name , name + '@' + hostname , pwd ) . then ( function ( ) {
if ( ! crypto . password ( pwd ) )
fatal ( "private key decryption failed" )
else
chat ( )
} )
}
/// @}
/// @}
function initBrowser ( ) {
function initBrowser ( ) {
window . indexedDB = window . indexedDB || window . mozIndexedDB || window . webkitIndexedDB || window . msIndexedDB
window . indexedDB = window . indexedDB || window . mozIndexedDB || window . webkitIndexedDB || window . msIndexedDB
window . IDBTransaction = window . IDBTransaction || window . webkitIDBTransaction || window . msIDBTransaction
window . IDBTransaction = window . IDBTransaction || window . webkitIDBTransaction || window . msIDBTransaction
@ -469,43 +541,48 @@ function SafeChat() {
return window . indexedDB && window . crypto . getRandomValues && Storage
return window . indexedDB && window . crypto . getRandomValues && Storage
}
}
function register ( ) {
var newuser = view . newuser
function chat ( ) {
}
function password ( ) {
}
}
function login ( ) {
function login ( ) {
if ( ! crypto . key ( ) ) register ( )
if ( ! crypto . key ( ) ) newuser ( ) ; else password ( ) ;
else password ( )
}
}
function ru n( ) {
this . run = functio n( ) {
login ( )
login ( )
}
}
function start ( ) {
this . start = function ( ) {
view . reboot = run
view . reboot = this . run
var compatible = initBrowser ( )
var compatible = initBrowser ( )
view . checkFeatures ( )
view . checkFeatures ( )
if ( ! compatible )
if ( ! compatible )
view . fatal ( "your browser is not supported" )
view . fatal ( "your browser is not supported" )
else
else
run ( )
this . run ( )
}
}
}
}
//==============================================================================
//==============================================================================
//------------------------------------------------------------------------------
return new Controller ( new View ( ) )
return new Controller ( new View ( ) )
}
}
//==============================================================================
//------------------------------------------------------------------------------
var filecontent = new Array ( ) ///< temporary storage for attachments
var filecontent = new Array ( ) ///< temporary storage for attachments
var reboottimer = null
var reboottimer = null
function connectionstatus ( ) {
function connectionstatus ( ) {
if ( socket . connected ) connected ( ) else disconnected ( )
if ( socket . connected ) connected ( ) ; else disconnected ( ) ;
}
}
/// Configure local groups
/// Configure local groups
@ -673,7 +750,8 @@ function SafeChat() {
if ( recorder ) {
if ( recorder ) {
$ ( "#videorecorder" ) . hide ( )
$ ( "#videorecorder" ) . hide ( )
recorder . release ( )
recorder . release ( )
delete recorder recorder = null
delete recorder
recorder = null
}
}
}
}
@ -852,10 +930,10 @@ function SafeChat() {
console . log ( "rcv-> messages(" + msgs . length + ")" )
console . log ( "rcv-> messages(" + msgs . length + ")" )
if ( ! password || ! privateKey ( ) )
if ( ! password || ! privateKey ( ) )
return setTimeout ( function ( ) { emit ( "messages" ) } , 1000 ) // try again later
return setTimeout ( function ( ) { emit ( "messages" ) } , 1000 ) // try again later
status ( "allmessages" )
show ( "allmessages" )
notice ( "load messages, please wait …" )
notice ( "load messages, please wait …" )
msgs . forEach ( function ( msg ) { message ( msg , true ) } )
msgs . forEach ( function ( msg ) { message ( msg , true ) } )
status ( "chat" )
show ( "chat" )
}
}
/// Received a message from server
/// Received a message from server
@ -905,6 +983,7 @@ function SafeChat() {
// not for me
// not for me
success ( )
success ( )
} )
} )
} )
}
}
/// Send Message To Server
/// Send Message To Server
@ -970,7 +1049,7 @@ function SafeChat() {
function getpwd ( ) {
function getpwd ( ) {
if ( password ) return
if ( password ) return
$ ( "#removeKey" ) . show ( )
$ ( "#removeKey" ) . show ( )
status ( "getpwd" )
show ( "getpwd" )
}
}
function deleteUser ( ) {
function deleteUser ( ) {
@ -983,7 +1062,7 @@ function SafeChat() {
function removeKey ( ) {
function removeKey ( ) {
togglemenu ( )
togglemenu ( )
$ ( "#removeKey" ) . hide ( )
$ ( "#removeKey" ) . hide ( )
status ( 'forgotpassword' )
show ( 'forgotpassword' )
}
}
/// Main Chat Window
/// Main Chat Window
@ -992,7 +1071,7 @@ function SafeChat() {
var firsttime = true
var firsttime = true
function chat ( ) {
function chat ( ) {
if ( ! password ) return getpwd ( )
if ( ! password ) return getpwd ( )
status ( "chat" )
show ( "chat" )
if ( firsttime && $ ( '#msgs' ) . is ( ':empty' ) ) {
if ( firsttime && $ ( '#msgs' ) . is ( ':empty' ) ) {
firsttime = false
firsttime = false
notice ( "getting previous messages, please wait …" )
notice ( "getting previous messages, please wait …" )
@ -1022,7 +1101,7 @@ function SafeChat() {
/ * * S h o w s u s e r c r e a t i o n f o r m . O n s u b m i t , a p r i v a t e k e y i s g e n e r a t e d i n
/ * * S h o w s u s e r c r e a t i o n f o r m . O n s u b m i t , a p r i v a t e k e y i s g e n e r a t e d i n
createkeypair ( ) , then login ( ) creates the user . * /
createkeypair ( ) , then login ( ) creates the user . * /
function newuser ( ) {
function newuser ( ) {
status ( "newuser" )
show ( "newuser" )
}
}
/// Check if local storage is available
/// Check if local storage is available
@ -1033,7 +1112,7 @@ function SafeChat() {
localStorage . removeItem ( test )
localStorage . removeItem ( test )
return true
return true
} catch ( e ) {
} catch ( e ) {
status ( "nolocalstorage" )
show ( "nolocalstorage" )
error ( "local storage not available" )
error ( "local storage not available" )
}
}
return false
return false
@ -1043,7 +1122,7 @@ function SafeChat() {
/** Decide whether to login or to create a new user */
/** Decide whether to login or to create a new user */
function start ( ) {
function start ( ) {
$ ( "#menu" ) . hide ( )
$ ( "#menu" ) . hide ( )
//status ("startup")
//show ("startup")
if ( checkLocalStorage ( ) )
if ( checkLocalStorage ( ) )
try {
try {
if ( ! userid ( ) ) {
if ( ! userid ( ) ) {
@ -1057,7 +1136,13 @@ function SafeChat() {
}
}
}
}
var safechat = new SafeChat ( )
function init ( ) {
function init ( ) {
safechat . start ( )
}
function old ( ) {
/// On Load, Call @ref start
/// On Load, Call @ref start
$ ( window . onbeforeunload = function ( ) {
$ ( window . onbeforeunload = function ( ) {
return "Are you sure you want to navigate away?"
return "Are you sure you want to navigate away?"