in the middle of rewriting it
This commit is contained in:
		@@ -3,7 +3,6 @@ module.exports = function(config) {
 | 
				
			|||||||
  var fs = require('fs');
 | 
					  var fs = require('fs');
 | 
				
			||||||
  config.multipleStatements = true;
 | 
					  config.multipleStatements = true;
 | 
				
			||||||
  var pool = mysql.createPool(config);
 | 
					  var pool = mysql.createPool(config);
 | 
				
			||||||
  console.log(__dirname+'/schema.sql')
 | 
					 | 
				
			||||||
  pool.query(fs.readFileSync(__dirname+'/schema.sql').toString());
 | 
					  pool.query(fs.readFileSync(__dirname+'/schema.sql').toString());
 | 
				
			||||||
  if (config.max_allowed_packet)
 | 
					  if (config.max_allowed_packet)
 | 
				
			||||||
    pool.query("set global max_allowed_packet=?", [config.max_allowed_packet]);
 | 
					    pool.query("set global max_allowed_packet=?", [config.max_allowed_packet]);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								nodejs/etc/systemd/system/safechat.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								nodejs/etc/systemd/system/safechat.service
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					[Unit]
 | 
				
			||||||
 | 
					Description=Secure and Encrypted Chat Server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Service]
 | 
				
			||||||
 | 
					ExecStart=/usr/bin/nodejs /usr/share/safechat/nodejs/safechat > /var/log/safechat.log
 | 
				
			||||||
 | 
					Restart=on-abort
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Install]
 | 
				
			||||||
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
@@ -1,245 +1,313 @@
 | 
				
			|||||||
/*! @file
 | 
					/*! @file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @id $Id$
 | 
					   @id $Id$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    This is the main application as it is fully run in the user's browser.
 | 
					   This is the main application as it is fully run in the user's browser.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @dot
 | 
					   @dot
 | 
				
			||||||
    digraph X {
 | 
					   digraph X {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    start [URL="\ref start()"];
 | 
					   start [URL="\ref start()"];
 | 
				
			||||||
    newuser [URL="\ref newuser()"];
 | 
					   newuser [URL="\ref newuser()"];
 | 
				
			||||||
    login [URL="\ref login()"];
 | 
					   login [URL="\ref login()"];
 | 
				
			||||||
    createkeypair [URL="\ref createkeypair()"];
 | 
					   createkeypair [URL="\ref createkeypair()"];
 | 
				
			||||||
    chat [URL="\ref chat()"];
 | 
					   chat [URL="\ref chat()"];
 | 
				
			||||||
    getpwd [URL="\ref getpwd()"];
 | 
					   getpwd [URL="\ref getpwd()"];
 | 
				
			||||||
    setpw [URL="\ref setpw()"];
 | 
					   setpw [URL="\ref setpw()"];
 | 
				
			||||||
    get [URL="\ref get()"];
 | 
					   get [URL="\ref get()"];
 | 
				
			||||||
    sendmessage [URL="\ref sendmessage()"];
 | 
					   sendmessage [URL="\ref sendmessage()"];
 | 
				
			||||||
    
 | 
					   
 | 
				
			||||||
    start -> newuser [label="if no keys exist"];
 | 
					   start -> newuser [label="if no keys exist"];
 | 
				
			||||||
    start -> login [label="if keys exist"];
 | 
					   start -> login [label="if keys exist"];
 | 
				
			||||||
    newuser -> createkeypair [label="on submit"];
 | 
					   newuser -> createkeypair [label="on submit"];
 | 
				
			||||||
    createkeypair -> "openpgp.generateKeyPair";
 | 
					   createkeypair -> "openpgp.generateKey";
 | 
				
			||||||
    "openpgp.generateKeyPair" -> login [label="keys generated in local store"];
 | 
					   "openpgp.generateKey" -> login [label="keys generated in local store"];
 | 
				
			||||||
    login -> chat [label="user is valid on server"];
 | 
					   login -> chat [label="user is valid on server"];
 | 
				
			||||||
    chat -> getpwd [label="password not yet entered"];
 | 
					   chat -> getpwd [label="password not yet entered"];
 | 
				
			||||||
    getpwd -> setpw [label="on input"];
 | 
					   getpwd -> setpw [label="on input"];
 | 
				
			||||||
    setpw -> chat [label="password is valid"];
 | 
					   setpw -> chat [label="password is valid"];
 | 
				
			||||||
    chat -> chat [label="remain in chat"];
 | 
					   chat -> chat [label="remain in chat"];
 | 
				
			||||||
    chat -> get [label="start timer"];
 | 
					   chat -> get [label="start timer"];
 | 
				
			||||||
    get -> get [label="restart timer"];
 | 
					   get -> get [label="restart timer"];
 | 
				
			||||||
    chat -> sendmessage [label="on submit"];
 | 
					   chat -> sendmessage [label="on submit"];
 | 
				
			||||||
    sendmessage -> chat [label="remain in chat"];
 | 
					   sendmessage -> chat [label="remain in chat"];
 | 
				
			||||||
    }
 | 
					   }
 | 
				
			||||||
    @enddot
 | 
					   @enddot
 | 
				
			||||||
*/
 | 
					 */
 | 
				
			||||||
//       1         2         3         4         5         6         7         8
 | 
					//       1         2         3         4         5         6         7         8
 | 
				
			||||||
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
 | 
					// 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 password = null; ///< password, only stored temporary, until reload
 | 
				
			||||||
var username = null; ///< username, only used during registration
 | 
					var username = null; ///< username, only used during registration
 | 
				
			||||||
var filecontent = new Array(); ///< temporary storage for attachments
 | 
					var filecontent = new Array(); ///< temporary storage for attachments
 | 
				
			||||||
var socket = io.connect();
 | 
					var socket = io.connect();
 | 
				
			||||||
 | 
					var hostname = window.location.hostname!='localhost'?window.location.hostname:'safechat.ch';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Padding for numbers in dates
 | 
					/// Padding for numbers in dates
 | 
				
			||||||
function pad(n) {
 | 
					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
 | 
					/// Convert number of bytes to readable text
 | 
				
			||||||
function size(num) {
 | 
					function size(num) {
 | 
				
			||||||
    if (num>0.6*1024) {
 | 
					  if (num>0.6*1024) {
 | 
				
			||||||
        if (num>0.6*1024*1024) {
 | 
					    if (num>0.6*1024*1024) {
 | 
				
			||||||
            if (num>0.6*1024*1024*1024) {
 | 
					      if (num>0.6*1024*1024*1024) {
 | 
				
			||||||
                if (num>0.6*1024*1024*1024*1024) {
 | 
					        if (num>0.6*1024*1024*1024*1024) {
 | 
				
			||||||
                    return Math.round(num/1024/1024/1024/1024)+"TB";
 | 
					          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";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            return Math.round(num/1024)+"kB";
 | 
					          return Math.round(num/1024/1024/1024)+"GB";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return Math.round(num/1024/1024)+"MB";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        return num+"B";
 | 
					      return Math.round(num/1024)+"kB";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return num+"B";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var reboottimer = null;
 | 
					var reboottimer = null;
 | 
				
			||||||
/// Show error messsage
 | 
					/// Show error messsage
 | 
				
			||||||
/** Fades in an error message and logs to console.
 | 
					/** Fades in an error message and logs to console.
 | 
				
			||||||
    @param data (optional) The error can be a string or any structure.
 | 
					   @param data (optional) The error can be a string or any structure.
 | 
				
			||||||
                Strings are shown to the user, structures are logged only.
 | 
					   Strings are shown to the user, structures are logged only.
 | 
				
			||||||
    @param stay (optional) If not given as @c true, reloads page after 5s. */
 | 
					   @param stay (optional) If not given as @c true, reloads page after 5s. */
 | 
				
			||||||
function error(data, stay) {
 | 
					function error(data, stay) {
 | 
				
			||||||
    $("#status").hide();
 | 
					  $("#status").hide();
 | 
				
			||||||
    $("#status").addClass("error")
 | 
					  $("#status").addClass("error")
 | 
				
			||||||
    $("#status").removeClass("notice")
 | 
					  $("#status").removeClass("notice")
 | 
				
			||||||
    $("#status").removeClass("success")
 | 
					  $("#status").removeClass("success")
 | 
				
			||||||
    if (data) {
 | 
					  if (data) {
 | 
				
			||||||
        if (typeof data == 'string') {
 | 
					    if (typeof data == 'string') {
 | 
				
			||||||
            $("#status").html(data);
 | 
					      $("#status").html(data);
 | 
				
			||||||
            console.log("error: "+data);
 | 
					      console.log("error: "+data);
 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            $("#status").html('unknown error: '+JSON.stringify(data));
 | 
					 | 
				
			||||||
            console.log("error: "+JSON.stringify(data));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        $("#status").html('error');
 | 
					      $("#status").html('unknown error: '+JSON.stringify(data));
 | 
				
			||||||
        console.log("error");
 | 
					      console.log("error: "+JSON.stringify(data));
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    $("#status").show();
 | 
					 | 
				
			||||||
    if (!stay) {
 | 
					 | 
				
			||||||
        console.log("reboot in 5s");
 | 
					 | 
				
			||||||
        console.log((new Error('stacktrace')).stack);
 | 
					 | 
				
			||||||
        if (!reboottimer) reboottimer = setTimeout(function() {
 | 
					 | 
				
			||||||
            reboottimer = null;
 | 
					 | 
				
			||||||
            start();
 | 
					 | 
				
			||||||
        }, 5000);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  } 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
 | 
					/// Show notice messsage
 | 
				
			||||||
/** Fades in an notice message and logs to console.
 | 
					/** 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) {
 | 
					function notice(text) {
 | 
				
			||||||
    $("#status").hide()
 | 
					  $("#status").hide()
 | 
				
			||||||
    $("#status").addClass("notice")
 | 
					  $("#status").addClass("notice")
 | 
				
			||||||
    $("#status").removeClass("error")
 | 
					  $("#status").removeClass("error")
 | 
				
			||||||
    $("#status").removeClass("success")
 | 
					  $("#status").removeClass("success")
 | 
				
			||||||
    if (text) {
 | 
					  if (text) {
 | 
				
			||||||
        $("#status").html(text);
 | 
					    $("#status").html(text);
 | 
				
			||||||
        console.log("notice: "+text);
 | 
					    console.log("notice: "+text);
 | 
				
			||||||
    } else {
 | 
					  } else {
 | 
				
			||||||
        $("#status").html('');
 | 
					    $("#status").html('');
 | 
				
			||||||
        console.log("notice");
 | 
					    console.log("notice");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    $("#status").show();
 | 
					  $("#status").show();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Show notice messsage
 | 
					/// Show notice messsage
 | 
				
			||||||
/** Fades in an success message and logs to console.
 | 
					/** 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) {
 | 
					function success(text) {
 | 
				
			||||||
    $("#status").hide();
 | 
					  $("#status").hide();
 | 
				
			||||||
    $("#status").addClass("success")
 | 
					  $("#status").addClass("success")
 | 
				
			||||||
    $("#status").removeClass("error")
 | 
					  $("#status").removeClass("error")
 | 
				
			||||||
    $("#status").removeClass("notice")
 | 
					  $("#status").removeClass("notice")
 | 
				
			||||||
    if (text) {
 | 
					  if (text) {
 | 
				
			||||||
        $("#status").html(text);
 | 
					    $("#status").html(text);
 | 
				
			||||||
        console.log("success: "+text);
 | 
					    console.log("success: "+text);
 | 
				
			||||||
    } else {
 | 
					  } else {
 | 
				
			||||||
        $("#status").html('');
 | 
					    $("#status").html('');
 | 
				
			||||||
        console.log("success");
 | 
					    console.log("success");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    $("#status").show();
 | 
					  $("#status").show();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Show status message in the main screen area
 | 
					/// Show status message in the main screen area
 | 
				
			||||||
/** @param id HTML id to be shown.
 | 
					/** @param id HTML id to be shown.
 | 
				
			||||||
    @param msg The success message text */
 | 
					   @param msg The success message text */
 | 
				
			||||||
function status(id, msg) {
 | 
					function status(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();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function emit(signal, data) {
 | 
					function emit(signal, data) {
 | 
				
			||||||
    console.log("<-snd "+signal);
 | 
					  console.log("<-snd "+signal);
 | 
				
			||||||
    socket.emit(signal, data);
 | 
					  socket.emit(signal, data);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function connected() {
 | 
					function connected() {
 | 
				
			||||||
    console.log("server connected");
 | 
					  console.log("server connected");
 | 
				
			||||||
    $("#connectionstatus #bad").hide();
 | 
					  $("#connectionstatus #bad").hide();
 | 
				
			||||||
    $("#connectionstatus #good").show();
 | 
					  $("#connectionstatus #good").show();
 | 
				
			||||||
    success("server connected");
 | 
					  success("server connected");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function disconnected() {
 | 
					function disconnected() {
 | 
				
			||||||
    console.log("server disconnected");
 | 
					  console.log("server disconnected");
 | 
				
			||||||
    $("#connectionstatus #good").hide();
 | 
					  $("#connectionstatus #good").hide();
 | 
				
			||||||
    $("#connectionstatus #bad").show();
 | 
					  $("#connectionstatus #bad").show();
 | 
				
			||||||
    error("server disconnected", true);
 | 
					  error("server disconnected", true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function connectionstatus() {
 | 
					function connectionstatus() {
 | 
				
			||||||
    if (socket.connected) connected(); else disconnected();
 | 
					  if (socket.connected) connected(); else disconnected();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function htmlenc(html) {
 | 
					function htmlenc(html) {
 | 
				
			||||||
    return $('<div/>').text(html).html();
 | 
					  return $('<div/>').text(html).html();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function htmldec(data) {
 | 
					function htmldec(data) {
 | 
				
			||||||
    return $('<div/>').html(data).text();
 | 
					  return $('<div/>').html(data).text();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Alert user
 | 
					/// Alert user
 | 
				
			||||||
/** Alert user, e.g. that a new message has arrived. */
 | 
					/** Alert user, e.g. that a new message has arrived. */
 | 
				
			||||||
function beep(user) {
 | 
					function beep(user) {
 | 
				
			||||||
    if (user) success("message from "+htmlenc(user)+" received");
 | 
					  if (user) success("message from "+htmlenc(user)+" received");
 | 
				
			||||||
    navigator.vibrate =
 | 
					  navigator.vibrate =
 | 
				
			||||||
        navigator.vibrate || navigator.webkitVibrate || navigator.mozVibrate || navigator.msVibrate;
 | 
					  navigator.vibrate || navigator.webkitVibrate || navigator.mozVibrate || navigator.msVibrate;
 | 
				
			||||||
    if (navigator.vibrate) {
 | 
					  if (navigator.vibrate) {
 | 
				
			||||||
	// vibration API supported
 | 
					    // vibration API supported
 | 
				
			||||||
        navigator.vibrate(1000);
 | 
					    navigator.vibrate(1000);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    (new Audio("sounds/A-Tone-His_Self-1266414414.mp3")).play();
 | 
					  (new Audio("sounds/A-Tone-His_Self-1266414414.mp3")).play();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Toggle Menu Display
 | 
					/// Toggle Menu Display
 | 
				
			||||||
function togglemenu() {
 | 
					function togglemenu() {
 | 
				
			||||||
    $("#menu").toggle();
 | 
					  $("#menu").toggle();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Download Profile Backup
 | 
					/// Download Profile Backup
 | 
				
			||||||
function backup() {
 | 
					function backup() {
 | 
				
			||||||
    var download = document.createElement('a');
 | 
					  var download = document.createElement('a');
 | 
				
			||||||
    download.href = 'data:attachment/text,'+encodeURI(JSON.stringify(localStorage));
 | 
					  download.href = 'data:attachment/text,'+encodeURI(JSON.stringify(localStorage));
 | 
				
			||||||
    download.target = '_blank';
 | 
					  download.target = '_blank';
 | 
				
			||||||
    var now = new Date();
 | 
					  var now = new Date();
 | 
				
			||||||
    download.download =
 | 
					  download.download =
 | 
				
			||||||
        pad(now.getFullYear())+pad(now.getMonth()+1)+pad(now.getDate())+
 | 
					  pad(now.getFullYear())+pad(now.getMonth()+1)+pad(now.getDate())+
 | 
				
			||||||
        "-"+userid()+"@"+window.location.hostname+".bak";
 | 
					  "-"+userid()+"@"+hostname+".bak";
 | 
				
			||||||
    var clickEvent = new MouseEvent("click", {
 | 
					  var clickEvent = new MouseEvent("click", {
 | 
				
			||||||
        "view": window,
 | 
					    "view": window,
 | 
				
			||||||
        "bubbles": true,
 | 
					    "bubbles": true,
 | 
				
			||||||
        "cancelable": false
 | 
					    "cancelable": false
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
    download.dispatchEvent(clickEvent);
 | 
					  download.dispatchEvent(clickEvent);
 | 
				
			||||||
    togglemenu();
 | 
					  togglemenu();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Upload Profile Backup
 | 
					/// Upload Profile Backup
 | 
				
			||||||
function restore(evt) {
 | 
					function restore(evt) {
 | 
				
			||||||
    if (!window.FileReader)
 | 
					  if (!window.FileReader)
 | 
				
			||||||
        return error("your browser does not support file upload", true);
 | 
					    return error("your browser does not support file upload", true);
 | 
				
			||||||
    for (var i=0, f; f=evt.target.files[i]; ++i) {
 | 
					  for (var i=0, f; f=evt.target.files[i]; ++i) {
 | 
				
			||||||
        var file = f;
 | 
					    var file = f;
 | 
				
			||||||
        var reader = new FileReader();
 | 
					    var reader = new FileReader();
 | 
				
			||||||
        reader.onload = function(evt) {
 | 
					    reader.onload = function(evt) {
 | 
				
			||||||
            if (evt.target.error) return error("error reading file", true);
 | 
					      if (evt.target.error) return error("error reading file", true);
 | 
				
			||||||
            if (evt.target.readyState==0) return notice("waiting for data …");
 | 
					      if (evt.target.readyState==0) return notice("waiting for data …");
 | 
				
			||||||
            if (evt.target.readyState==1) return notice("loading data …");
 | 
					      if (evt.target.readyState==1) return notice("loading data …");
 | 
				
			||||||
            var parsed=JSON.parse(evt.target.result);
 | 
					      var parsed=JSON.parse(evt.target.result);
 | 
				
			||||||
            togglemenu();
 | 
					      togglemenu();
 | 
				
			||||||
            localStorage.pubkey = parsed.pubkey;
 | 
					      localStorage.pubkey = parsed.pubkey;
 | 
				
			||||||
            localStorage.privkey = parsed.privkey;
 | 
					      localStorage.privkey = parsed.privkey;
 | 
				
			||||||
            success("backup is restored");
 | 
					      success("backup is restored");
 | 
				
			||||||
            console.log("reboot after restore in 2s");
 | 
					      console.log("reboot after restore in 2s");
 | 
				
			||||||
            if (!reboottimer) reboottimer = setTimeout(function() {
 | 
					      if (!reboottimer) reboottimer = setTimeout(function() {
 | 
				
			||||||
                reboottimer = null;
 | 
					        reboottimer = null;
 | 
				
			||||||
                start();
 | 
					        start();
 | 
				
			||||||
            }, 2000);
 | 
					      }, 2000);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        reader.readAsText(file);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    reader.readAsText(file);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Configure local groups
 | 
					/// Configure local groups
 | 
				
			||||||
@@ -249,573 +317,582 @@ function groups() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// Check if password is set and matches the repeated password
 | 
					/// Check if password is set and matches the repeated password
 | 
				
			||||||
/** Checks if both passwords are identical and valid and gives
 | 
					/** 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
 | 
					   Sets @ref username and checks @ref password - if both are well
 | 
				
			||||||
    defined, enables the submit button.
 | 
					   defined, enables the submit button.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @param pwd The password.
 | 
					   @param pwd The password.
 | 
				
			||||||
    @param pwd2 The repeated password. */
 | 
					   @param pwd2 The repeated password. */
 | 
				
			||||||
function checkpwd(pwd, pwd2) {
 | 
					function checkpwd(pwd, pwd2) {
 | 
				
			||||||
    $("#register").submit(function(event) {
 | 
					  $("#register").submit(function(event) {
 | 
				
			||||||
        return false;
 | 
					    return false;
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
    if (pwd==pwd2) password=pwd;
 | 
					  if (pwd==pwd2) password=pwd;
 | 
				
			||||||
    else password=null;
 | 
					  else password=null;
 | 
				
			||||||
    if (!password||password.length<1) password=null;
 | 
					  if (!password||password.length<1) password=null;
 | 
				
			||||||
    $("#createuser").prop("disabled", !(username && password));
 | 
					  $("#createuser").prop("disabled", !(username && password));
 | 
				
			||||||
    if (password) {
 | 
					  if (password) {
 | 
				
			||||||
        if (username) success("user is ready to be created");
 | 
					    if (username) success("user is ready to be created");
 | 
				
			||||||
        else notice("password matches, please chose a valid user name");
 | 
					    else notice("password matches, please chose a valid user name");
 | 
				
			||||||
    } else {
 | 
					  } else {
 | 
				
			||||||
        if (username) notice("passwords don't match");
 | 
					    if (username) notice("passwords don't match");
 | 
				
			||||||
        else if ($('#user').val()) notice("user name is already in use");
 | 
					    else if ($('#user').val()) notice("user name is already in use");
 | 
				
			||||||
        else notice("please chose a user name");
 | 
					    else notice("please chose a user name");
 | 
				
			||||||
                
 | 
					    
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Checks if the receiver of a message exists on server.
 | 
					/// Checks if the receiver of a message exists on server.
 | 
				
			||||||
/** Calls checknewuser.php on server and enables the message submit
 | 
					/** 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) {
 | 
					function checkpartner(user) {
 | 
				
			||||||
    $("#chat").submit(function(event) {
 | 
					  $("#chat").submit(function(event) {
 | 
				
			||||||
        return false;
 | 
					    return false;
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
    emit("user", user);
 | 
					  emit("user", uid(user));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Create Local Public-/Private-Key Pair
 | 
					/// Create Local Public-/Private-Key Pair
 | 
				
			||||||
/** Called if user has not yet his keys, just generates a new key pair. */
 | 
					/** Called if user has not yet his keys, just generates a new key pair. */
 | 
				
			||||||
function createkeypair(user, pwd) {
 | 
					function createkeypair(user, pwd) {
 | 
				
			||||||
    notice("generating keys");
 | 
					  notice("generating keys");
 | 
				
			||||||
    openpgp.generateKeyPair({
 | 
					  openpgp.generateKey({
 | 
				
			||||||
        numBits: 4096,
 | 
					    numBits: 4096,
 | 
				
			||||||
        userId: user,
 | 
					    userIds: [{name: user, email: user+'@'+hostname}],
 | 
				
			||||||
        passphrase: pwd
 | 
					    passphrase: pwd
 | 
				
			||||||
    }).then(function(keyPair) {
 | 
					  }).then(function(keyPair) {
 | 
				
			||||||
        success("keys generated");
 | 
					    success("keys generated");
 | 
				
			||||||
        localStorage.pubkey = keyPair.publicKeyArmored;
 | 
					    localStorage.pubkey = keyPair.publicKeyArmored;
 | 
				
			||||||
        localStorage.privkey = keyPair.privateKeyArmored;
 | 
					    localStorage.privkey = keyPair.privateKeyArmored;
 | 
				
			||||||
        login();
 | 
					    login();
 | 
				
			||||||
    }).catch(function(e) {
 | 
					  }).catch(function(e) {
 | 
				
			||||||
        error("generating key pairs failed");
 | 
					    console.log(e)
 | 
				
			||||||
    });
 | 
					    error("generating key pairs failed")
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Get Own Public Key
 | 
					/// Get Own Public Key
 | 
				
			||||||
/** @return public key object */
 | 
					/** @return public key object */
 | 
				
			||||||
function publicKey() {
 | 
					function publicKey() {
 | 
				
			||||||
    if (typeof localStorage.pubkey == 'undefined') {
 | 
					  if (typeof localStorage.pubkey == 'undefined') {
 | 
				
			||||||
        if (typeof localStorage.pubKey == 'undefined') {
 | 
					    if (typeof localStorage.pubKey == 'undefined') {
 | 
				
			||||||
            return null;
 | 
					      return null;
 | 
				
			||||||
        } else {
 | 
					    } else {
 | 
				
			||||||
            localStorage.pubkey = localStorage.pubKey;
 | 
					      localStorage.pubkey = localStorage.pubKey;
 | 
				
			||||||
            localStorage.removeItem(pubKey);
 | 
					      localStorage.removeItem(pubKey);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return openpgp.key.readArmored(localStorage.pubkey);
 | 
					  }
 | 
				
			||||||
 | 
					  return openpgp.key.readArmored(localStorage.pubkey);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Get Own Private Key
 | 
					/// Get Own Private Key
 | 
				
			||||||
/** @return private key object */
 | 
					/** @return private key object */
 | 
				
			||||||
function privateKey() {
 | 
					function privateKey() {
 | 
				
			||||||
    if (typeof localStorage.privkey == 'undefined') {
 | 
					  if (typeof localStorage.privkey == 'undefined') {
 | 
				
			||||||
        if (typeof localStorage.privKey == 'undefined') {
 | 
					    if (typeof localStorage.privKey == 'undefined') {
 | 
				
			||||||
            return null;
 | 
					      return null;
 | 
				
			||||||
        } else {
 | 
					    } else {
 | 
				
			||||||
            localStorage.privkey = localStorage.privKey;
 | 
					      localStorage.privkey = localStorage.privKey;
 | 
				
			||||||
            localStorage.removeItem(privKey);
 | 
					      localStorage.removeItem(privKey);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return openpgp.key.readArmored(localStorage.privkey);
 | 
					  }
 | 
				
			||||||
 | 
					  return openpgp.key.readArmored(localStorage.privkey);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// 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 userid() {
 | 
					function userid() {
 | 
				
			||||||
    if (!publicKey() ||
 | 
					  if (!publicKey() ||
 | 
				
			||||||
        publicKey().keys.length < 1 ||
 | 
					      publicKey().keys.length < 1 ||
 | 
				
			||||||
        publicKey().keys[0].getUserIds().length < 1) return null
 | 
					      publicKey().keys[0].getUserIds().length < 1) return null
 | 
				
			||||||
    return publicKey().keys[0].getUserIds()[0];
 | 
					  return publicKey().keys[0].getUserIds()[0];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Clear Message Text And Attachments
 | 
					/// Clear Message Text And Attachments
 | 
				
			||||||
/** Does not remove the receiver's name */
 | 
					/** Does not remove the receiver's name */
 | 
				
			||||||
function clearmessage() {
 | 
					function clearmessage() {
 | 
				
			||||||
    $("#message").prop(":disabled", true);
 | 
					  $("#message").prop(":disabled", true);
 | 
				
			||||||
    filecontent = new Array();
 | 
					  filecontent = new Array();
 | 
				
			||||||
    $('#preview').empty();
 | 
					  $('#preview').empty();
 | 
				
			||||||
    $("#msg").val("");
 | 
					  $("#msg").val("");
 | 
				
			||||||
    $("#message").prop(":disabled", false);
 | 
					  $("#message").prop(":disabled", false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function guessfilename(mimetype, user, date) {
 | 
					function guessfilename(mimetype, user, date) {
 | 
				
			||||||
    if (!user) user = userid();
 | 
					  if (!user) user = userid();
 | 
				
			||||||
    if (!date) date = new Date();
 | 
					  if (!date) date = new Date();
 | 
				
			||||||
    var ext = mimetype.replace(/.*\/(x-)?/i, "");
 | 
					  var ext = mimetype.replace(/.*\/(x-)?/i, "");
 | 
				
			||||||
    return pad(date.getFullYear())+pad(date.getMonth()+1)+pad(date.getDate())
 | 
					  return pad(date.getFullYear())+pad(date.getMonth()+1)+pad(date.getDate())
 | 
				
			||||||
        +"-"+ext+"-"+user+"@"+window.location.hostname+'.'+ext;
 | 
					        +"-"+ext+"-"+user+"@"+hostname+'.'+ext;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Display Image Attachments
 | 
					/// Display Image Attachments
 | 
				
			||||||
function attachments(files, id, from, date) {
 | 
					function attachments(files, id, from, date) {
 | 
				
			||||||
    if (files) files.forEach(function(file) {
 | 
					  if (files) files.forEach(function(file) {
 | 
				
			||||||
        console.log(file);
 | 
					    console.log(file);
 | 
				
			||||||
        if (!file.name) file.name = guessfilename(file.type, from, date);
 | 
					    if (!file.name) file.name = guessfilename(file.type, from, date);
 | 
				
			||||||
        var a = document.createElement('a');
 | 
					    var a = document.createElement('a');
 | 
				
			||||||
        a.href = file.content;
 | 
					    a.href = file.content;
 | 
				
			||||||
        a.download = file.name;
 | 
					    a.download = file.name;
 | 
				
			||||||
        a.target = '_blank';
 | 
					    a.target = '_blank';
 | 
				
			||||||
        if (file.type.match('^image/')) {
 | 
					    if (file.type.match('^image/')) {
 | 
				
			||||||
            var img = document.createElement('img');
 | 
					      var img = document.createElement('img');
 | 
				
			||||||
            img.title = file.name;
 | 
					      img.title = file.name;
 | 
				
			||||||
            img.src = file.content;
 | 
					      img.src = file.content;
 | 
				
			||||||
            a.appendChild(img);
 | 
					      a.appendChild(img);
 | 
				
			||||||
        } else if (file.type.match('^video/')) {
 | 
					    } else if (file.type.match('^video/')) {
 | 
				
			||||||
            var video = document.createElement('video');
 | 
					      var video = document.createElement('video');
 | 
				
			||||||
            video.controls = true;
 | 
					      video.controls = true;
 | 
				
			||||||
            video.title = file.name;
 | 
					      video.title = file.name;
 | 
				
			||||||
            video.src = file.content;
 | 
					      video.src = file.content;
 | 
				
			||||||
            a.appendChild(video);
 | 
					      a.appendChild(video);
 | 
				
			||||||
        } else {
 | 
					    } else {
 | 
				
			||||||
            var img = document.createElement('img');
 | 
					      var img = document.createElement('img');
 | 
				
			||||||
            img.title = file.name;
 | 
					      img.title = file.name;
 | 
				
			||||||
            img.src = "images/Document_sans_PICOL-PIctorial-COmmunication-Language.svg";
 | 
					      img.src = "images/Document_sans_PICOL-PIctorial-COmmunication-Language.svg";
 | 
				
			||||||
            a.appendChild(img);
 | 
					      a.appendChild(img);
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
        $(id).append(a);            
 | 
					    $(id).append(a);            
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var recorder;
 | 
					var recorder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function done() {
 | 
					function done() {
 | 
				
			||||||
    if (recorder) {
 | 
					  if (recorder) {
 | 
				
			||||||
        recorder.stop();
 | 
					    recorder.stop();
 | 
				
			||||||
        recorder.recording(function(data) {
 | 
					    recorder.recording(function(data) {
 | 
				
			||||||
            previewfile(data, "video/webm");
 | 
					      previewfile(data, "video/webm");
 | 
				
			||||||
            abort();
 | 
					      abort();
 | 
				
			||||||
        });
 | 
					    });
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function abort() {
 | 
					function abort() {
 | 
				
			||||||
    if (recorder) {
 | 
					  if (recorder) {
 | 
				
			||||||
        $("#videorecorder").hide();
 | 
					    $("#videorecorder").hide();
 | 
				
			||||||
        recorder.release();
 | 
					    recorder.release();
 | 
				
			||||||
        delete recorder; recorder = null;
 | 
					    delete recorder; recorder = null;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Record Video from builtin camera
 | 
					/// Record Video from builtin camera
 | 
				
			||||||
function recordvideo() {
 | 
					function recordvideo() {
 | 
				
			||||||
    try {
 | 
					  try {
 | 
				
			||||||
        abort();
 | 
					    abort();
 | 
				
			||||||
        $("#videorecorder").show();
 | 
					    $("#videorecorder").show();
 | 
				
			||||||
        recorder = new MediaStreamRecorder({
 | 
					    recorder = new MediaStreamRecorder({
 | 
				
			||||||
            video: {
 | 
					      video: {
 | 
				
			||||||
                width: {ideal: 180},
 | 
					        width: {ideal: 180},
 | 
				
			||||||
                height: {ideal: 160}
 | 
					        height: {ideal: 160}
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
            audio: true
 | 
					      audio: true
 | 
				
			||||||
        });
 | 
					    });
 | 
				
			||||||
        recorder.on("ready", function() {
 | 
					    recorder.on("ready", function() {
 | 
				
			||||||
            $("#videorecorder video").attr("src", recorder.preview());
 | 
					      $("#videorecorder video").attr("src", recorder.preview());
 | 
				
			||||||
            $("#videorecorder video").css("width", 180);
 | 
					      $("#videorecorder video").css("width", 180);
 | 
				
			||||||
            $("#videorecorder video").css("height", 160);
 | 
					      $("#videorecorder video").css("height", 160);
 | 
				
			||||||
            $("#videorecorder video").attr("width", 180);
 | 
					      $("#videorecorder video").attr("width", 180);
 | 
				
			||||||
            $("#videorecorder video").attr("height", 160);
 | 
					      $("#videorecorder video").attr("height", 160);
 | 
				
			||||||
            recorder.start();
 | 
					      recorder.start();
 | 
				
			||||||
        });
 | 
					    });
 | 
				
			||||||
    } catch (e) {
 | 
					  } catch (e) {
 | 
				
			||||||
        console.log(e);
 | 
					    console.log(e);
 | 
				
			||||||
        error("cannot access camera", true);
 | 
					    error("cannot access camera", true);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function previewfile(content, type, name) {
 | 
					function previewfile(content, type, name) {
 | 
				
			||||||
    if (!name) name = guessfilename(type);
 | 
					  if (!name) name = guessfilename(type);
 | 
				
			||||||
    if (type.match('^image/')) {
 | 
					  if (type.match('^image/')) {
 | 
				
			||||||
        var img = document.createElement("img");
 | 
					    var img = document.createElement("img");
 | 
				
			||||||
        img.onload = function() { // resize image to maximum 400px
 | 
					    img.onload = function() { // resize image to maximum 400px
 | 
				
			||||||
            var MAX = 400;
 | 
					      var MAX = 400;
 | 
				
			||||||
            var width = img.width;
 | 
					      var width = img.width;
 | 
				
			||||||
            var height = img.height;
 | 
					      var height = img.height;
 | 
				
			||||||
            if (width > MAX) {
 | 
					      if (width > MAX) {
 | 
				
			||||||
                height *= MAX / width;
 | 
					        height *= MAX / width;
 | 
				
			||||||
                width = MAX;
 | 
					        width = MAX;
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
            if (height > MAX) {
 | 
					      if (height > MAX) {
 | 
				
			||||||
                width *= MAX / height;
 | 
					        width *= MAX / height;
 | 
				
			||||||
                height = MAX;
 | 
					        height = MAX;
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
            var canvas = document.createElement("canvas");
 | 
					      var canvas = document.createElement("canvas");
 | 
				
			||||||
            canvas.width = width;
 | 
					      canvas.width = width;
 | 
				
			||||||
            canvas.height = height;
 | 
					      canvas.height = height;
 | 
				
			||||||
            var ctx = canvas.getContext("2d");
 | 
					      var ctx = canvas.getContext("2d");
 | 
				
			||||||
            ctx.drawImage(img, 0, 0, width, height);
 | 
					      ctx.drawImage(img, 0, 0, width, height);
 | 
				
			||||||
            img.onload = function() {
 | 
					      img.onload = function() {
 | 
				
			||||||
                filecontent.push({name: name, type: type, content: img.src});
 | 
					        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);
 | 
					        $("#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
 | 
					/// Upload Attachment
 | 
				
			||||||
/** Prepares attachment to be sent in a message. If the attachment is
 | 
					/** 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) {
 | 
					function fileupload(evt) {
 | 
				
			||||||
    if (!window.FileReader)
 | 
					  if (!window.FileReader)
 | 
				
			||||||
        return error("your browser does not support file upload", true);
 | 
					    return error("your browser does not support file upload", true);
 | 
				
			||||||
    for (var i=0, f; f=evt.target.files[i]; ++i) {
 | 
					  for (var i=0, f; f=evt.target.files[i]; ++i) {
 | 
				
			||||||
        var file = f;
 | 
					    var file = f;
 | 
				
			||||||
        var reader = new FileReader();
 | 
					    var reader = new FileReader();
 | 
				
			||||||
        reader.onload = function(evt) {
 | 
					    reader.onload = function(evt) {
 | 
				
			||||||
            if (evt.target.error) return error("error reading file", true);
 | 
					      if (evt.target.error) return error("error reading file", true);
 | 
				
			||||||
            if (evt.target.readyState==0) return notice("waiting for data …");
 | 
					      if (evt.target.readyState==0) return notice("waiting for data …");
 | 
				
			||||||
            if (evt.target.readyState==1) return notice("loading data …");
 | 
					      if (evt.target.readyState==1) return notice("loading data …");
 | 
				
			||||||
            previewfile(evt.target.result, file.type, file.name);
 | 
					      previewfile(evt.target.result, file.type, file.name);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        reader.readAsDataURL(file);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    reader.readAsDataURL(file);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Sets Receiver's Name
 | 
					/// Sets Receiver's Name
 | 
				
			||||||
/** Called when clicked on a receiver's name. Sets focus to the
 | 
					/** 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) {
 | 
					function setreceiver(name) {
 | 
				
			||||||
    $("#recv").val(name);
 | 
					  $("#recv").val(name);
 | 
				
			||||||
    checkpartner(name);
 | 
					  checkpartner(name);
 | 
				
			||||||
    $("#msg").focus();
 | 
					  $("#msg").focus();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var userMap = null;
 | 
					var userMap = null;
 | 
				
			||||||
function users(userlist) {
 | 
					function users(userlist) {
 | 
				
			||||||
    console.log("rcv-> users");
 | 
					  console.log("rcv-> users");
 | 
				
			||||||
    userMap = new Array();
 | 
					  userMap = new Array();
 | 
				
			||||||
    $("#allusers").empty();
 | 
					  $("#allusers").empty();
 | 
				
			||||||
    userlist.forEach(function(usr) {
 | 
					  userlist.forEach(function(usr) {
 | 
				
			||||||
        userMap[usr.name] = usr.pubkey;
 | 
					    userMap[usr.name] = usr.pubkey;
 | 
				
			||||||
        $("#allusers").append('<option value="'+htmlenc(usr.name)+'">')
 | 
					    $("#allusers").append('<option value="'+htmlenc(usr.name)+'">')
 | 
				
			||||||
        $("#allusers").hide();
 | 
					    $("#allusers").hide();
 | 
				
			||||||
        console.log("   user: "+usr.name);
 | 
					    console.log("   user: "+usr.name);
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
    localStorage.userMap = JSON.stringify(userMap);
 | 
					  localStorage.userMap = JSON.stringify(userMap);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function fail(msg) {
 | 
					function fail(msg) {
 | 
				
			||||||
    console.log("rcv-> fail");
 | 
					  console.log("rcv-> fail");
 | 
				
			||||||
    error(msg);
 | 
					  error(msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function loggedin() {
 | 
					function loggedin() {
 | 
				
			||||||
    console.log("rcv-> login");
 | 
					  console.log("rcv-> login");
 | 
				
			||||||
    success("login successful");
 | 
					  success("login successful");
 | 
				
			||||||
    chat();
 | 
					  chat();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function user(usr) {
 | 
					function user(usr) {
 | 
				
			||||||
    if (usr.exists) console.log("rcv-> user("+usr.name+")");
 | 
					  if (usr.exists) console.log("rcv-> user("+usr.name+")");
 | 
				
			||||||
    else console.log("rcv-> user("+usr.name+"): name is available");
 | 
					  else console.log("rcv-> user("+usr.name+"): name is available");
 | 
				
			||||||
    if ($("#newuser").is(":visible") && usr.name==$('#user').val()) {
 | 
					  if ($("#newuser").is(":visible") && usr.name==uid($('#user').val())) {
 | 
				
			||||||
        // same username as in the create user form
 | 
					    // same username as in the create user form
 | 
				
			||||||
        $("#createuser").prop("disabled", usr.exists); // todo: check password
 | 
					    $("#createuser").prop("disabled", usr.exists); // todo: check password
 | 
				
			||||||
        if (!usr.exists) {
 | 
					    if (!usr.exists) {
 | 
				
			||||||
            username = usr.name;
 | 
					      username = usr.name;
 | 
				
			||||||
            success("user name "+usr.name+" is available");
 | 
					      success("user name "+usr.name+" is available");
 | 
				
			||||||
        } else {
 | 
					    } else {
 | 
				
			||||||
            username = null;
 | 
					      username = null;
 | 
				
			||||||
            error("user name "+usr.name+" is in use", true);
 | 
					      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);
 | 
					  if ($("#chat").is(":visible") && usr.name==uid($("#recv").val())) { // same username as in receiver
 | 
				
			||||||
        $("label[for=send] img").css("opacity", usr.exists?"1.0":"0.4");
 | 
					    $('#send').prop("disabled", !usr.exists);
 | 
				
			||||||
        $("label[for=send] img").css("filter", usr.exists?"alpha(opacity=100)":"alpha(opacity=40)");
 | 
					    $("label[for=send] img").css("opacity", usr.exists?"1.0":"0.4");
 | 
				
			||||||
        if (usr.exists) success("recipient exists");
 | 
					    $("label[for=send] img").css("filter", usr.exists?"alpha(opacity=100)":"alpha(opacity=40)");
 | 
				
			||||||
        else error("unknown recipient", true);
 | 
					    if (usr.exists) success("recipient exists");
 | 
				
			||||||
    }
 | 
					    else error("unknown recipient", true);
 | 
				
			||||||
    if (userMap == null) {
 | 
					  }
 | 
				
			||||||
        if (localStorage.userMap) {
 | 
					  if (userMap == null) {
 | 
				
			||||||
            userMap = JSON.parse(localStorage.userMap);
 | 
					    if (localStorage.userMap) {
 | 
				
			||||||
        } else {
 | 
					      userMap = JSON.parse(localStorage.userMap);
 | 
				
			||||||
            userMap = new Array();
 | 
					    } 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);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  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.
 | 
					/// Get a user's public key.
 | 
				
			||||||
/** The first time, gets it from the server, later from the cache. */
 | 
					/** The first time, gets it from the server, later from the cache. */
 | 
				
			||||||
function getPublicKey(user) {
 | 
					function getPublicKey(user) {
 | 
				
			||||||
    var deferredObject = $.Deferred();
 | 
					  var deferredObject = $.Deferred();
 | 
				
			||||||
    if (userMap && userMap[user]) deferredObject.resolve(userMap[user]);
 | 
					  if (userMap && userMap[user]) deferredObject.resolve(userMap[user]);
 | 
				
			||||||
    else deferredObject.reject("unknown user");
 | 
					  else deferredObject.reject("unknown user");
 | 
				
			||||||
    return deferredObject.promise();
 | 
					  return deferredObject.promise();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Received a list of messages from server
 | 
					/// Received a list of messages from server
 | 
				
			||||||
function messages(msgs) {
 | 
					function messages(msgs) {
 | 
				
			||||||
    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");
 | 
					  status("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");
 | 
					  status("chat");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Received a message from server
 | 
					/// Received a message from server
 | 
				
			||||||
function message(m, internal) {
 | 
					function message(m, internal) {
 | 
				
			||||||
    if (!internal) console.log("rcv-> message("+m.user+")");
 | 
					  if (!internal) console.log("rcv-> message("+m.user+")");
 | 
				
			||||||
    if (!password || !privateKey()) return;
 | 
					  if (!password || !privateKey()) return;
 | 
				
			||||||
    var key=openpgp.key.readArmored(m.pubkey);
 | 
					  var key=openpgp.key.readArmored(m.pubkey);
 | 
				
			||||||
    if (key.err) return error("key of sender unreadable", true);
 | 
					  if (key.err) return error("key of sender unreadable", true);
 | 
				
			||||||
    var message = openpgp.message.readArmored(m.msg);
 | 
					  var message = openpgp.message.readArmored(m.msg);
 | 
				
			||||||
    var privkey = privateKey().keys[0];
 | 
					  var privkey = privateKey().keys[0];
 | 
				
			||||||
    if (privkey.decrypt(password)) // prepare own key
 | 
					  if (privkey.decrypt(password)) // prepare own key
 | 
				
			||||||
        openpgp.decryptAndVerifyMessage(privkey, key.keys, message)
 | 
					    openpgp.decrypt({
 | 
				
			||||||
        .then(function(msg) { // decryption succeded
 | 
					      privateKeys: privkey,
 | 
				
			||||||
            // prepend message to list of messages
 | 
					      publicKeys: key.keys,
 | 
				
			||||||
            var message = JSON.parse(msg.text);
 | 
					      message: message
 | 
				
			||||||
            $("#msgs") // todo: check msg.signatures[0].valid
 | 
					    }).then(function(msg) { // decryption succeded
 | 
				
			||||||
                .prepend('<div id="id'+(m.id)+'" class="msg '+
 | 
					      // prepend message to list of messages
 | 
				
			||||||
                         (m.user==userid()?"me":"other")+
 | 
					      var message = JSON.parse(msg.text);
 | 
				
			||||||
                         '"><div class="header">'+
 | 
					      $("#msgs") // todo: check msg.signatures[0].valid
 | 
				
			||||||
                         '<span class="date">'+
 | 
					        .prepend('<div id="id'+(m.id)+'" class="msg '+
 | 
				
			||||||
                         (new Date(m.time)).toLocaleString()+
 | 
					                           (m.user==userid()?"me":"other")+
 | 
				
			||||||
                         '</span><span class="sender">'+
 | 
					                 '"><div class="header">'+
 | 
				
			||||||
                         '<a href="javascript:void(0)" '+
 | 
					                 '<span class="date">'+
 | 
				
			||||||
                         'onclick="setreceiver(this.innerHTML)">'+
 | 
					                           (new Date(m.time)).toLocaleString()+
 | 
				
			||||||
                         htmlenc(m.user)+
 | 
					                 '</span><span class="sender">'+
 | 
				
			||||||
                         '</a>'+(message.receiver?' → <a href="javascript:void(0)" '+
 | 
					                 '<a href="javascript:void(0)" '+
 | 
				
			||||||
                                 'onclick="setreceiver(this.innerHTML)">'
 | 
					                 'onclick="setreceiver(this.innerHTML)">'+
 | 
				
			||||||
                                 +htmlenc(message.receiver)+'</a>':"")+
 | 
					                 htmlenc(m.user)+
 | 
				
			||||||
                         '</span></div>'+
 | 
					                 '</a>'+(message.receiver?' → <a href="javascript:void(0)" '+
 | 
				
			||||||
                         '<div class="text">'+
 | 
					                                          'onclick="setreceiver(this.innerHTML)">'
 | 
				
			||||||
                         htmlenc(message.text)+
 | 
					                                         +htmlenc(message.receiver)+'</a>':"")+
 | 
				
			||||||
                         '</div></div><div class="clear"/>');
 | 
					                 '</span></div>'+
 | 
				
			||||||
            // show attachments
 | 
					                 '<div class="text">'+
 | 
				
			||||||
            attachments(message.files, '#id'+m.id+' .text', m.user, new Date(m.time));
 | 
					                 htmlenc(message.text)+
 | 
				
			||||||
            // calculate and show emoticons
 | 
					                 '</div></div><div class="clear"/>');
 | 
				
			||||||
            $('#id'+m.id).emoticonize();
 | 
					      // show attachments
 | 
				
			||||||
            if (!internal) beep(m.user);
 | 
					      attachments(message.files, '#id'+m.id+' .text', m.user, new Date(m.time));
 | 
				
			||||||
        })
 | 
					      // calculate and show emoticons
 | 
				
			||||||
        .catch(function(e) {
 | 
					      $('#id'+m.id).emoticonize();
 | 
				
			||||||
            // not for me
 | 
					      if (!internal) beep(m.user);
 | 
				
			||||||
            success();
 | 
					    }).catch(function(e) {
 | 
				
			||||||
        });
 | 
					      // not for me
 | 
				
			||||||
 | 
					      success();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Send Message To Server
 | 
					/// Send Message To Server
 | 
				
			||||||
/** User wants to send a message. Encrypt message with own private and
 | 
					/** 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) {
 | 
					function sendmessage(recv, txt) {
 | 
				
			||||||
    notice("1/3 preparing message …");
 | 
					  notice("1/3 preparing message …");
 | 
				
			||||||
    $("#message").prop(":disabled", true);
 | 
					  $("#message").prop(":disabled", true);
 | 
				
			||||||
    getPublicKey(recv) // get receiver's public key
 | 
					  getPublicKey(recv) // get receiver's public key
 | 
				
			||||||
        .done(function(pk) {
 | 
					    .done(function(pk) {
 | 
				
			||||||
            var key=openpgp.key.readArmored(pk);
 | 
					      var key=openpgp.key.readArmored(pk);
 | 
				
			||||||
            if (!pk||key.err) {
 | 
					      if (!pk||key.err) {
 | 
				
			||||||
                $("#message").prop(":disabled", false);
 | 
					        $("#message").prop(":disabled", false);
 | 
				
			||||||
                error("receiver's key not found", true);
 | 
					        error("receiver's key not found", true);
 | 
				
			||||||
                return;
 | 
					        return;
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
            var privkey = privateKey().keys[0];
 | 
					      var privkey = privateKey().keys[0];
 | 
				
			||||||
            privkey.decrypt(password); // get own private key ready
 | 
					      privkey.decrypt(password); // get own private key ready
 | 
				
			||||||
            var message = JSON.stringify({receiver: recv, text: txt, files: filecontent});
 | 
					      var message = JSON.stringify({receiver: recv, text: txt, files: filecontent});
 | 
				
			||||||
            notice("2/3 encrypting message …");
 | 
					      notice("2/3 encrypting message …");
 | 
				
			||||||
            openpgp.signAndEncryptMessage(key.keys.concat(publicKey().keys),
 | 
					      openpgp.encrypt({publicKeys: key.keys.concat(publicKey().keys),
 | 
				
			||||||
                                          privkey,
 | 
					                      privateKeys: privkey,
 | 
				
			||||||
                                          message)
 | 
					                      data: message,
 | 
				
			||||||
                .then(function(msg) { // message is encrypted
 | 
					                       armor: false})
 | 
				
			||||||
                    notice("3/3 sending message …");
 | 
					             .then(function(msg) { // message is encrypted
 | 
				
			||||||
                    emit("message", {user: userid(), content: msg});
 | 
					               notice("3/3 sending message …");
 | 
				
			||||||
                    clearmessage();
 | 
					               emit("message", {user: userid(), content: msg});
 | 
				
			||||||
                })
 | 
					               clearmessage();
 | 
				
			||||||
                .catch(function(e) {
 | 
					             })
 | 
				
			||||||
                    $("#message").prop(":disabled", false);
 | 
					             .catch(function(e) {
 | 
				
			||||||
                    error("encryption of message failed", true);
 | 
					               $("#message").prop(":disabled", false);
 | 
				
			||||||
                });
 | 
					               error("encryption of message failed", true);
 | 
				
			||||||
        })
 | 
					             });
 | 
				
			||||||
        .fail(function(e) {
 | 
					    })
 | 
				
			||||||
            $("#message").prop(":disabled", false);
 | 
					    .fail(function(e) {
 | 
				
			||||||
            error("user not found", true);
 | 
					      $("#message").prop(":disabled", false);
 | 
				
			||||||
        });
 | 
					      error("user not found", true);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Check And Set Password
 | 
					/// Check And Set Password
 | 
				
			||||||
/** Check if given password matches to decrypt the private key. If so,
 | 
					/** Check if given password matches to decrypt the private key. If so,
 | 
				
			||||||
    store it in global temporary variable @ref password and start the
 | 
					   store it in global temporary variable @ref password and start the
 | 
				
			||||||
    chat. The password matches, when the private key can be decrypted.
 | 
					   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) {
 | 
					function setpw(pwd) {
 | 
				
			||||||
    if (privateKey().keys[0].decrypt(pwd)) {
 | 
					  if (privateKey().keys[0].decrypt(pwd)) {
 | 
				
			||||||
        success("password matches");
 | 
					    success("password matches");
 | 
				
			||||||
        $("#removeKey").hide();
 | 
					    $("#removeKey").hide();
 | 
				
			||||||
        password = pwd;
 | 
					    password = pwd;
 | 
				
			||||||
        chat();
 | 
					    chat();
 | 
				
			||||||
    } else {
 | 
					  } else {
 | 
				
			||||||
        notice("password does not match");
 | 
					    notice("password does not match");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Create Password Entry Field
 | 
					/// Create Password Entry Field
 | 
				
			||||||
/** Asks user for password. When user starts to enter it, it is
 | 
					/** Asks user for password. When user starts to enter it, it is
 | 
				
			||||||
    permanentely checked in setpw(). As soon as the password matches,
 | 
					   permanentely checked in setpw(). As soon as the password matches,
 | 
				
			||||||
    setpw() continues automatically. No submit is required by the
 | 
					   setpw() continues automatically. No submit is required by the
 | 
				
			||||||
    user. */
 | 
					   user. */
 | 
				
			||||||
function getpwd() {
 | 
					function getpwd() {
 | 
				
			||||||
    if (password) return;
 | 
					  if (password) return;
 | 
				
			||||||
    $("#removeKey").show();
 | 
					  $("#removeKey").show();
 | 
				
			||||||
    status("getpwd");
 | 
					  status("getpwd");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function deleteUser() {
 | 
					function deleteUser() {
 | 
				
			||||||
    var uid = userid();
 | 
					  var uid = userid();
 | 
				
			||||||
    localStorage.removeItem(pubkey);
 | 
					  localStorage.removeItem(pubkey);
 | 
				
			||||||
    localStorage.removeItem(privkey);
 | 
					  localStorage.removeItem(privkey);
 | 
				
			||||||
    error("user "+uid+" permanentely lost");
 | 
					  error("user "+uid+" permanentely lost");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function removeKey() {
 | 
					function removeKey() {
 | 
				
			||||||
    togglemenu();
 | 
					  togglemenu();
 | 
				
			||||||
    $("#removeKey").hide();
 | 
					  $("#removeKey").hide();
 | 
				
			||||||
    status('forgotpassword');
 | 
					  status('forgotpassword');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Main Chat Window
 | 
					/// Main Chat Window
 | 
				
			||||||
/** Gets chat widgets from server and displays them. Starts timer for
 | 
					/** 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;
 | 
					var firsttime = true;
 | 
				
			||||||
function chat() {
 | 
					function chat() {
 | 
				
			||||||
    if (!password) return getpwd();
 | 
					  if (!password) return getpwd();
 | 
				
			||||||
    status("chat");
 | 
					  status("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 …");
 | 
				
			||||||
        emit("messages");
 | 
					    emit("messages");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Login User
 | 
					/// Login User
 | 
				
			||||||
/** This is not really a login, it is just some kind of validation.
 | 
					/** 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
 | 
					   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
 | 
					   interesting to the client to make sure, everything is fine. User
 | 
				
			||||||
    is logged in the following way: User name and public key are sent
 | 
					   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
 | 
					   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
 | 
					   public key is the same, the user is considered logged in, his
 | 
				
			||||||
    credentials seem to be valid. If user does not yet exits on
 | 
					   credentials seem to be valid. If user does not yet exits on
 | 
				
			||||||
    server, it is created now. If user exists, but public key is
 | 
					   server, it is created now. If user exists, but public key is
 | 
				
			||||||
    different, then this is a complete failure, something went
 | 
					   different, then this is a complete failure, something went
 | 
				
			||||||
    terribly wrong. */
 | 
					   terribly wrong. */
 | 
				
			||||||
function login() {
 | 
					function login() {
 | 
				
			||||||
    $("#username").html(userid()+"@"+window.location.hostname);
 | 
					  $("#username").html(userid()+"@"+hostname);
 | 
				
			||||||
    emit("login", {name: userid(),
 | 
					  emit("login", {name: userid(),
 | 
				
			||||||
                   pubkey: localStorage.pubkey});
 | 
					                 pubkey: localStorage.pubkey});
 | 
				
			||||||
    success("login sent to server");
 | 
					  success("login sent to server");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Get And Display Form To Create New User
 | 
					/// Get And Display Form To Create New User
 | 
				
			||||||
/** Shows user creation form. On submit, a private key is generated in
 | 
					/** 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() {
 | 
					function newuser() {
 | 
				
			||||||
    status("newuser");
 | 
					  status("newuser");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Check if local storage is available
 | 
					/// Check if local storage is available
 | 
				
			||||||
function checkLocalStorage() {
 | 
					function checkLocalStorage() {
 | 
				
			||||||
    var test = 'test';
 | 
					  var test = 'test';
 | 
				
			||||||
    try {
 | 
					  try {
 | 
				
			||||||
        localStorage.setItem(test, test);
 | 
					    localStorage.setItem(test, test);
 | 
				
			||||||
        localStorage.removeItem(test);
 | 
					    localStorage.removeItem(test);
 | 
				
			||||||
        return true;
 | 
					    return true;
 | 
				
			||||||
    } catch(e) {
 | 
					  } catch(e) {
 | 
				
			||||||
        status("nolocalstorage");
 | 
					    status("nolocalstorage");
 | 
				
			||||||
        error("local storage not available");
 | 
					    error("local storage not available");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    return false;
 | 
					  return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Initial Function: Startup
 | 
					/// Initial Function: Startup
 | 
				
			||||||
/** 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");
 | 
					  //status("startup");
 | 
				
			||||||
    if (checkLocalStorage())
 | 
					  if (checkLocalStorage())
 | 
				
			||||||
        try {
 | 
					    try {
 | 
				
			||||||
            if (!userid()) {
 | 
					      if (!userid()) {
 | 
				
			||||||
                newuser();
 | 
					        newuser();
 | 
				
			||||||
            } else {
 | 
					      } else {
 | 
				
			||||||
                login();
 | 
					        login();
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
        } catch (m) {
 | 
					    } catch (m) {
 | 
				
			||||||
            console.log(m.stack);
 | 
					      console.log(m.stack);
 | 
				
			||||||
            error(m);
 | 
					      error(m);
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function init() {
 | 
					function init() {
 | 
				
			||||||
    /// 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?";
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
    /// Allow Running in Background on Android
 | 
					  /// Allow Running in Background on Android
 | 
				
			||||||
    document.addEventListener('deviceready', function () {
 | 
					  document.addEventListener('deviceready', function () {
 | 
				
			||||||
        if (cordova && cordova.plugins.backgroundMode) {
 | 
					    if (cordova && cordova.plugins.backgroundMode) {
 | 
				
			||||||
            cordova.plugins.backgroundMode.enable();
 | 
					      cordova.plugins.backgroundMode.enable();
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
    }, false);
 | 
					  }, false);
 | 
				
			||||||
    socket.io.on("connect", connected);
 | 
					  socket.io.on("connect", connected);
 | 
				
			||||||
    socket.io.on("reconnect", connected);
 | 
					  socket.io.on("reconnect", connected);
 | 
				
			||||||
    socket.io.on("disconnect", disconnected);
 | 
					  socket.io.on("disconnect", disconnected);
 | 
				
			||||||
    socket.io.on("error", disconnected);
 | 
					  socket.io.on("error", disconnected);
 | 
				
			||||||
    socket.on("login", loggedin);
 | 
					  socket.on("login", loggedin);
 | 
				
			||||||
    socket.on("fail", fail);
 | 
					  socket.on("fail", fail);
 | 
				
			||||||
    socket.on("user", user);
 | 
					  socket.on("user", user);
 | 
				
			||||||
    socket.on("users", users);
 | 
					  socket.on("users", users);
 | 
				
			||||||
    socket.on("message", message);
 | 
					  socket.on("message", message);
 | 
				
			||||||
    socket.on("messages", messages);
 | 
					  socket.on("messages", messages);
 | 
				
			||||||
    connectionstatus();
 | 
					  connectionstatus();
 | 
				
			||||||
    if (openpgp.initWorker("javascripts/openpgp.worker.js"))
 | 
					  if (openpgp.initWorker("openpgp.worker.min.js"))
 | 
				
			||||||
        console.log("asynchronous openpgp enabled");
 | 
					    console.log("asynchronous openpgp enabled");
 | 
				
			||||||
    else
 | 
					  else
 | 
				
			||||||
        console.log("asynchronous openpgp failed");
 | 
					    console.log("asynchronous openpgp failed");
 | 
				
			||||||
    emit('users');
 | 
					  emit('users');
 | 
				
			||||||
    start();
 | 
					  start();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Start Main Loop
 | 
					/// Start Main Loop
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,14 @@
 | 
				
			|||||||
 | 
					module.exports = function(app, package) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					  [
 | 
				
			||||||
 * GET home page.
 | 
					    '',
 | 
				
			||||||
 */
 | 
					    'webrtc'
 | 
				
			||||||
 | 
					  ].forEach(function(p) {
 | 
				
			||||||
var package = require(__dirname+"/../package.json");
 | 
					    app.get('/'+p, function(req, res) {
 | 
				
			||||||
 | 
					      res.render(p?p:'index', {
 | 
				
			||||||
exports.index = function(req, res) {
 | 
					        package: package
 | 
				
			||||||
  res.render(path, {
 | 
					      })
 | 
				
			||||||
    projecturl: package.documentation,
 | 
					    })
 | 
				
			||||||
    packagename: package.name,
 | 
					 | 
				
			||||||
    packageversion: package.version
 | 
					 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.webrtc = function(req, res) {
 | 
					 | 
				
			||||||
  res.render('webrtc', {
 | 
					 | 
				
			||||||
    projecturl: package.documentation,
 | 
					 | 
				
			||||||
    packagename: package.name,
 | 
					 | 
				
			||||||
    packageversion: package.version
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,9 +5,9 @@
 | 
				
			|||||||
var package = require(__dirname+'/package.json');
 | 
					var package = require(__dirname+'/package.json');
 | 
				
			||||||
var config = require(package.path.config);
 | 
					var config = require(package.path.config);
 | 
				
			||||||
var express = require('express');
 | 
					var express = require('express');
 | 
				
			||||||
var routes = require(__dirname+'/routes');
 | 
					 | 
				
			||||||
var app = module.exports = express.createServer();
 | 
					var app = module.exports = express.createServer();
 | 
				
			||||||
var io  = require('socket.io').listen(app);
 | 
					var routes = require(__dirname+'/routes')(app, package);
 | 
				
			||||||
 | 
					var io = require('socket.io').listen(app);
 | 
				
			||||||
var sql = require(__dirname+'/database')(config.mysql);
 | 
					var sql = require(__dirname+'/database')(config.mysql);
 | 
				
			||||||
var sockets = require(__dirname+'/sockets')(sql);
 | 
					var sockets = require(__dirname+'/sockets')(sql);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -34,11 +34,17 @@ app.configure(function() {
 | 
				
			|||||||
  app.use(app.router);
 | 
					  app.use(app.router);
 | 
				
			||||||
  app.use(express.static(__dirname + '/public'));
 | 
					  app.use(express.static(__dirname + '/public'));
 | 
				
			||||||
  [
 | 
					  [
 | 
				
			||||||
    'jquery/dist',
 | 
					    'jquery/dist/jquery.min.js',
 | 
				
			||||||
    'openpgp/dist'
 | 
					    'jquery/dist/jquery.js',
 | 
				
			||||||
 | 
					    'openpgp/dist/openpgp.min.js',
 | 
				
			||||||
 | 
					    'openpgp/dist/openpgp.js',
 | 
				
			||||||
 | 
					    'openpgp/dist/openpgp.worker.min.js',
 | 
				
			||||||
 | 
					    'openpgp/dist/openpgp.worker.js'
 | 
				
			||||||
  ].forEach(function(file) {
 | 
					  ].forEach(function(file) {
 | 
				
			||||||
    app.use('/'+file.replace(/\/.*/g, ''),
 | 
					    app.get('/'+file.replace(/.*\//g, ''),
 | 
				
			||||||
            express.static(__dirname + '/node_modules/'+file));
 | 
					            function(req, res) {
 | 
				
			||||||
 | 
					              res.sendfile('/node_modules/'+file, {root: __dirname})
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,11 +60,6 @@ app.configure('production', function(){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
io.sockets.on('connection', sockets.connection);
 | 
					io.sockets.on('connection', sockets.connection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Routes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
app.get('/', routes.index);
 | 
					 | 
				
			||||||
app.get('/webrtc', routes.webrtc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
app.listen(config.port, function(){
 | 
					app.listen(config.port, function(){
 | 
				
			||||||
  console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
 | 
					  console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,103 +1,104 @@
 | 
				
			|||||||
module.exports = function(sql) {
 | 
					module.exports = function(sql) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var module={};
 | 
					  var module={};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    module.connection = function(socket) {
 | 
					  module.connection = function(socket) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        console.log("new client");
 | 
					    console.log("new client");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function emit(signal, data, info) {
 | 
					    function emit(signal, data, info) {
 | 
				
			||||||
            if (typeof data == 'string') {
 | 
					      if (typeof data == 'string') {
 | 
				
			||||||
                console.log("<- signal: "+signal+"("+data+")");
 | 
					        console.log("<- signal: "+signal+"("+data+")");
 | 
				
			||||||
            } else {
 | 
					      } else {
 | 
				
			||||||
                console.log("<- signal: "+signal);
 | 
					        console.log("<- signal: "+signal);
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
            if (info) console.log(info);
 | 
					      if (info) console.log(info);
 | 
				
			||||||
            socket.emit(signal, data);
 | 
					      socket.emit(signal, data);
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function broadcast(signal, data) {
 | 
					    function broadcast(signal, data) {
 | 
				
			||||||
            console.log("<= signal: "+signal);
 | 
					      console.log("<= signal: "+signal);
 | 
				
			||||||
            socket.broadcast.emit(signal, data);
 | 
					      socket.broadcast.emit(signal, data);
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        socket.on("message", function(msg) {
 | 
					    socket.on("message", function(msg) {
 | 
				
			||||||
            console.log("-> signal: message");
 | 
					      console.log("-> signal: message");
 | 
				
			||||||
            if (!msg || !msg.user || !msg.content) return emit("fail", "wrong message format");
 | 
					      if (!msg || !msg.user || !msg.content) return emit("fail", "wrong message format");
 | 
				
			||||||
            sql.query("select pubkey from user where name = ?", [msg.user],
 | 
					      sql.query("select pubkey from user where name = ?", [msg.user],
 | 
				
			||||||
                      function(err, res, flds) {
 | 
					                function(err, res, flds) {
 | 
				
			||||||
                          if (err || !res || !res.length) return emit("fail", "unknown sender");
 | 
					                  if (err || !res || !res.length) return emit("fail", "unknown sender");
 | 
				
			||||||
                          sql.query("insert into message set ?", {user: msg.user, msg: msg.content},
 | 
					                  sql.query("insert into message set ?", {user: msg.user, msg: msg.content},
 | 
				
			||||||
                                    function(err, result) {
 | 
					                            function(err, result) {
 | 
				
			||||||
                                        if (err) {
 | 
					                              if (err) {
 | 
				
			||||||
                                            if (err.code=='ER_NET_PACKET_TOO_LARGE')
 | 
					                                if (err.code=='ER_NET_PACKET_TOO_LARGE')
 | 
				
			||||||
                                                return emit('fail', "message too large", err);
 | 
					                                  return emit('fail', "message too large", err);
 | 
				
			||||||
                                            else
 | 
					                                else
 | 
				
			||||||
                                                return emit("fail", "cannot store message", err);
 | 
					                                  return emit("fail", "cannot store message", err);
 | 
				
			||||||
                                        }
 | 
					                              }
 | 
				
			||||||
                                        sql.query("select * from message, user"+
 | 
					                              sql.query("select * from message, user"+
 | 
				
			||||||
                                                  " where message.id = ? and"+
 | 
					                                        " where message.id = ? and"+
 | 
				
			||||||
                                                  " message.user = user.name",
 | 
					                                        " message.user = user.name",
 | 
				
			||||||
                                                  [result.insertId],
 | 
					                                        [result.insertId],
 | 
				
			||||||
                                                  function(err, res, flds) {
 | 
					                                        function(err, res, flds) {
 | 
				
			||||||
                                                      broadcast('message', res[0]);
 | 
					                                          broadcast('message', res[0]);
 | 
				
			||||||
                                                      emit('message', res[0]);
 | 
					                                          emit('message', res[0]);
 | 
				
			||||||
                                                  });
 | 
					 | 
				
			||||||
                                    });
 | 
					 | 
				
			||||||
                      });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        socket.on("messages", function(msg) {
 | 
					 | 
				
			||||||
            console.log("-> signal: messages");
 | 
					 | 
				
			||||||
            sql.query("select * from message, user where message.user = user.name order by message.id", [],
 | 
					 | 
				
			||||||
                      function(err, res, flds) {
 | 
					 | 
				
			||||||
                          emit('messages', res);
 | 
					 | 
				
			||||||
                      });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        socket.on("login", function(user) {
 | 
					 | 
				
			||||||
            if (!user.name || !user.pubkey) return emit("fail", "wrong login format");
 | 
					 | 
				
			||||||
            console.log("-> signal: login("+user.name+")");
 | 
					 | 
				
			||||||
            if (user.name=="safechat") return emit("fail", "user name safechat is reserved");
 | 
					 | 
				
			||||||
            sql.query("select name, pubkey from user where name = ?", [user.name],
 | 
					 | 
				
			||||||
                      function(err, res, flds) {
 | 
					 | 
				
			||||||
                          if (err) return emit('fail', "login failed (db access)", err);
 | 
					 | 
				
			||||||
                          if (!res || res.length==0) {
 | 
					 | 
				
			||||||
                              sql.query("insert into user (name, pubkey) values (?,?)",
 | 
					 | 
				
			||||||
                                        [user.name, user.pubkey], function(err, res, flds) {
 | 
					 | 
				
			||||||
                                            if (err) return emit('fail', "create user failed", err);
 | 
					 | 
				
			||||||
                                            broadcast("user", {
 | 
					 | 
				
			||||||
                                                name: user.name, exists: false, pubkey: user.pubkey
 | 
					 | 
				
			||||||
                                            });
 | 
					 | 
				
			||||||
                                            emit('login');
 | 
					 | 
				
			||||||
                                        });
 | 
					                                        });
 | 
				
			||||||
                          } else {
 | 
					                            });
 | 
				
			||||||
                              if (res[0].pubkey==user.pubkey) return emit('login');
 | 
					                });
 | 
				
			||||||
                              emit('fail', "login failed - wrong credentials");
 | 
					    });
 | 
				
			||||||
                          }
 | 
					 | 
				
			||||||
                      });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        socket.on("user", function(name) {
 | 
					    socket.on("messages", function(msg) {
 | 
				
			||||||
            console.log("-> signal: user("+name+")");
 | 
					      console.log("-> signal: messages");
 | 
				
			||||||
            var result = {name: name, exists: false, pubkey: null};
 | 
					      sql.query("select * from message, user where message.user = user.name order by message.id", [],
 | 
				
			||||||
            sql.query("select pubkey from user where name = ?", [name], function(err, res, flds) {
 | 
					                function(err, res, flds) {
 | 
				
			||||||
                if (!err && res && res.length) {
 | 
					                  emit('messages', res);
 | 
				
			||||||
                    result.exists = true;
 | 
					                });
 | 
				
			||||||
                    result.pubkey = res[0].pubkey;
 | 
					    });
 | 
				
			||||||
                }
 | 
					
 | 
				
			||||||
                emit('user', result);
 | 
					    socket.on("login", function(user) {
 | 
				
			||||||
            });
 | 
					      console.log('-> signal: login('+user.name+')');
 | 
				
			||||||
        });
 | 
					      if (!user.name || !user.pubkey) return emit("fail", "wrong login format");
 | 
				
			||||||
        
 | 
					      console.log("-> signal: login("+user.name+")");
 | 
				
			||||||
        socket.on("users", function(name) {
 | 
					      if (user.name=="safechat") return emit("fail", "user name safechat is reserved");
 | 
				
			||||||
            console.log("-> signal: users");
 | 
					      sql.query("select name, pubkey from user where name = ?", [user.name],
 | 
				
			||||||
            sql.query("select name, pubkey from user", [name], function(err, res, flds) {
 | 
					                function(err, res, flds) {
 | 
				
			||||||
                if (!err && res && res.length) emit('users', res);
 | 
					                  if (err) return emit('fail', "login failed (db access)", err);
 | 
				
			||||||
            });
 | 
					                  if (!res || res.length==0) {
 | 
				
			||||||
        });
 | 
					                    sql.query("insert into user (name, pubkey) values (?,?)",
 | 
				
			||||||
        
 | 
					                              [user.name, user.pubkey], function(err, res, flds) {
 | 
				
			||||||
    };
 | 
					                                if (err) return emit('fail', "create user failed", err);
 | 
				
			||||||
 | 
					                                broadcast("user", {
 | 
				
			||||||
 | 
					                                  name: user.name, exists: false, pubkey: user.pubkey
 | 
				
			||||||
 | 
					                                });
 | 
				
			||||||
 | 
					                                emit('login');
 | 
				
			||||||
 | 
					                              });
 | 
				
			||||||
 | 
					                  } else {
 | 
				
			||||||
 | 
					                    if (res[0].pubkey==user.pubkey) return emit('login');
 | 
				
			||||||
 | 
					                    emit('fail', "login failed - wrong credentials");
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return module;
 | 
					    socket.on("user", function(name) {
 | 
				
			||||||
 | 
					      console.log("-> signal: user("+name+")");
 | 
				
			||||||
 | 
					      var result = {name: name, exists: false, pubkey: null};
 | 
				
			||||||
 | 
					      sql.query("select pubkey from user where name = ?", [name], function(err, res, flds) {
 | 
				
			||||||
 | 
					        if (!err && res && res.length) {
 | 
				
			||||||
 | 
					          result.exists = true;
 | 
				
			||||||
 | 
					          result.pubkey = res[0].pubkey;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        emit('user', result);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    socket.on("users", function(name) {
 | 
				
			||||||
 | 
					      console.log("-> signal: users");
 | 
				
			||||||
 | 
					      sql.query("select name, pubkey from user", [name], function(err, res, flds) {
 | 
				
			||||||
 | 
					        if (!err && res && res.length) emit('users', res);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return module;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,9 +4,8 @@
 | 
				
			|||||||
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 | 
					    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 | 
				
			||||||
    <meta name="viewport" content="width=device-width initial-scale=1" />
 | 
					    <meta name="viewport" content="width=device-width initial-scale=1" />
 | 
				
			||||||
    <link href="stylesheets/safechat.css" rel="stylesheet" type="text/css" />
 | 
					    <link href="stylesheets/safechat.css" rel="stylesheet" type="text/css" />
 | 
				
			||||||
    <script type="text/javascript" src="jquery/jquery.min.js"></script>
 | 
					    <script type="text/javascript" src="jquery.min.js"></script>
 | 
				
			||||||
    <script type="text/javascript" src="openpgp/openpgp.min.js"></script>
 | 
					    <script type="text/javascript" src="openpgp.min.js"></script>
 | 
				
			||||||
    <script type="text/javascript" src="openpgp/openpgp.worker.min.js"></script>
 | 
					 | 
				
			||||||
    <script type="text/javascript" src="socket.io/socket.io.js"></script>
 | 
					    <script type="text/javascript" src="socket.io/socket.io.js"></script>
 | 
				
			||||||
    <script type="text/javascript" src="javascripts/mediarecorder.js"></script>
 | 
					    <script type="text/javascript" src="javascripts/mediarecorder.js"></script>
 | 
				
			||||||
    <script type="text/javascript" src="javascripts/safechat.js"></script>
 | 
					    <script type="text/javascript" src="javascripts/safechat.js"></script>
 | 
				
			||||||
@@ -18,7 +17,7 @@
 | 
				
			|||||||
  <body>
 | 
					  <body>
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    <div id="header" class="header">
 | 
					    <div id="header" class="header">
 | 
				
			||||||
      <h1>Safe Chat <%= packageversion %></h1>
 | 
					      <h1>Safe Chat <%= package.version %></h1>
 | 
				
			||||||
      <div id="togglemenu">
 | 
					      <div id="togglemenu">
 | 
				
			||||||
        <span id="username">[unknown]</span>
 | 
					        <span id="username">[unknown]</span>
 | 
				
			||||||
        <span id="connectionstatus">
 | 
					        <span id="connectionstatus">
 | 
				
			||||||
@@ -35,7 +34,7 @@
 | 
				
			|||||||
      <li id="groups" onclick="groups()">Edit Groups</li>
 | 
					      <li id="groups" onclick="groups()">Edit Groups</li>
 | 
				
			||||||
      <li id="removeKey" style="display: none" onclick="removeKey()">Password Forgotten</li>
 | 
					      <li id="removeKey" style="display: none" onclick="removeKey()">Password Forgotten</li>
 | 
				
			||||||
      <li id="android-download" href="safechat.apk"><a href="safechat.apk">Download Android-App</a></li>
 | 
					      <li id="android-download" href="safechat.apk"><a href="safechat.apk">Download Android-App</a></li>
 | 
				
			||||||
      <li href="<%= projecturl %>" target="_blank"><a href="<%= projecturl %>" target="_blank">About Safe Chat</a></li>
 | 
					      <li href="<%= package.documentation %>" target="_blank"><a href="<%= package.documentation %>" target="_blank">About Safe Chat</a></li>
 | 
				
			||||||
    </ul>
 | 
					    </ul>
 | 
				
			||||||
    <script type="text/javascript">
 | 
					    <script type="text/javascript">
 | 
				
			||||||
      $(function() { // on load: without cordova, remove andoid-download
 | 
					      $(function() { // on load: without cordova, remove andoid-download
 | 
				
			||||||
@@ -56,16 +55,14 @@
 | 
				
			|||||||
          <input placeholder="username" autocomplete="off" type="text" id="user"/>
 | 
					          <input placeholder="username" autocomplete="off" type="text" id="user"/>
 | 
				
			||||||
          <input placeholder="password" autocomplete="off" type="password" id="pwd" oninput="checkpwd(this.value, document.getElementById('pwd2').value)"/>
 | 
					          <input placeholder="password" autocomplete="off" type="password" id="pwd" oninput="checkpwd(this.value, document.getElementById('pwd2').value)"/>
 | 
				
			||||||
          <input placeholder="repeat password" autocomplete="off" type="password" id="pwd2" oninput="checkpwd(document.getElementById('pwd').value, this.value)"/>
 | 
					          <input placeholder="repeat password" autocomplete="off" type="password" id="pwd2" oninput="checkpwd(document.getElementById('pwd').value, this.value)"/>
 | 
				
			||||||
          <input id="createuser" type="submit" disabled/>
 | 
					          <button id="createuser" disabled>register</button>
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
        <script>
 | 
					        <script>
 | 
				
			||||||
          $("#user").on("input", function() {
 | 
					          $("#user").on("input", function() {
 | 
				
			||||||
            console.log("query user: "+$('#user').val());
 | 
					            queryuser($('#user').val());
 | 
				
			||||||
            socket.emit("user", $('#user').val());
 | 
					 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
          $("#register").submit(function(event) {
 | 
					          $("#createuser").on("click", function(event) {
 | 
				
			||||||
            createkeypair(event.target.elements['user'].value,
 | 
					            createkeypair($('#user').val(), $('#pwd').val());
 | 
				
			||||||
                          event.target.elements['pwd'].value);
 | 
					 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        </script>
 | 
					        </script>
 | 
				
			||||||
@@ -193,7 +190,7 @@
 | 
				
			|||||||
          server. Your password and your secret key are fully under
 | 
					          server. Your password and your secret key are fully under
 | 
				
			||||||
          your control. That's why you must enable javascript and
 | 
					          your control. That's why you must enable javascript and
 | 
				
			||||||
          local storage for this application.</p>
 | 
					          local storage for this application.</p>
 | 
				
			||||||
        <p><a href="<%= projecturl %>" target="_blank">more information</a></p>
 | 
					        <p><a href="<%= package.documentation %>" target="_blank">more information</a></p>
 | 
				
			||||||
      </noscript>
 | 
					      </noscript>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <!--                                                  Error: Missing LocalStorage -->
 | 
					      <!--                                                  Error: Missing LocalStorage -->
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user