Added File-Share feature

Users can now share files
Click on the file-share button next to message input to attach file
Files are encrypted and sign along with messages
Note: file size is not restricted, yet its advised to share only files with size <1.5MB
This commit is contained in:
sairajzero 2019-09-16 19:34:21 +05:30
parent f083b8c313
commit 2a642f5c29
3 changed files with 200 additions and 174 deletions

View File

@ -161,13 +161,13 @@ var floOpt = {
var floID = key.getBitcoinAddress();
return floID;
},
signData: function (msg, privateKeyHex) {
signData: function (data, privateKeyHex) {
var key = new Bitcoin.ECKey(privateKeyHex);
key.setCompressed(true);
var privateKeyArr = key.getBitcoinPrivateKeyByteArray();
privateKey = BigInteger.fromByteArrayUnsigned(privateKeyArr);
var messageHash = Crypto.SHA256(msg);
var messageHash = Crypto.SHA256(data);
var messageHashBigInteger = new BigInteger(messageHash);
var messageSign = Bitcoin.ECDSA.sign(messageHashBigInteger, key.priv);
@ -175,8 +175,8 @@ var floOpt = {
var sighex = Crypto.util.bytesToHex(messageSign);
return sighex;
},
verifyData: function (msg, signatureHex, publicKeyHex) {
var msgHash = Crypto.SHA256(msg);
verifyData: function (data, signatureHex, publicKeyHex) {
var msgHash = Crypto.SHA256(data);
var messageHashBigInteger = new BigInteger(msgHash);
var sigBytes = Crypto.util.hexToBytes(signatureHex);
@ -326,10 +326,19 @@ function broadcastTx(signedTxHash) {
return result;
}
function resetForm(formID) {
var formEl = document.getElementById(formID);
formEl.reset()
var labelSpans = formEl.querySelectorAll('span');
for (var i = 0; i < labelSpans.length; i++)
labelSpans[i].textContent = '';
}
function userDataStartUp() {
console.log("StartUp");
document.getElementById("sendMsgInput").addEventListener("keydown", (event) => {
resetForm("replyForm");
//Initiate Event Handling
document.getElementById("msgInput").addEventListener("keydown", (event) => {
if (event.keyCode === 13 && !event.shiftKey) {
event.preventDefault();
sendMsg();
@ -337,6 +346,12 @@ function userDataStartUp() {
});
document.getElementById("searchContact").addEventListener("input", searchContact, true);
document.getElementById("searchList").addEventListener("input", searchChecklist, true);
document.getElementById('fileInput').onchange = function () {
var fileName = this.value.split("\\").pop();
this.nextSibling.textContent = fileName;
};
//Start Program
getDatafromAPI().then(result => {
console.log(result);
getContactsfromIDB().then(result => {
@ -453,7 +468,7 @@ function getDatafromAPI() {
var objectStore3 = db.createObjectStore("messages", {
keyPath: 'time'
});
objectStore3.createIndex('text', 'text', {
objectStore3.createIndex('msgData', 'msgData', {
unique: false
});
objectStore3.createIndex('floID', 'floID', {
@ -813,13 +828,13 @@ function initselfWebSocket() {
function processIncomingData(data) {
if (data.directMsg !== undefined) {
var msg = floOpt.decryptData(data.directMsg.msgCipher.secret, data.directMsg.msgCipher.pubVal, privKey)
if (!floOpt.verifyData(msg, data.directMsg.sign, contacts[data.from].pubKey))
var msgData = floOpt.decryptData(data.directMsg.msgCipher.secret, data.directMsg.msgCipher.pubVal, privKey)
if (!floOpt.verifyData(msgData, data.directMsg.sign, contacts[data.from].pubKey))
return
var msgInfo = {
time: Date.now(),
floID: data.from,
text: msg,
msgData: msgData,
type: "R"
}
createMsgElement(msgInfo);
@ -827,14 +842,14 @@ function processIncomingData(data) {
} else if (data.groupMsg !== undefined && data.groupMsg.group in groups) {
if (!(groups[data.groupMsg.group].members.includes(data.from)))
return
var msg = floOpt.decryptData(data.groupMsg.msgCipher.secret, data.groupMsg.msgCipher.pubVal, groups[data.groupMsg.group].privKey);
if (!floOpt.verifyData(msg, data.groupMsg.sign, contacts[data.from].pubKey))
var msgData = floOpt.decryptData(data.groupMsg.msgCipher.secret, data.groupMsg.msgCipher.pubVal, groups[data.groupMsg.group].privKey);
if (!floOpt.verifyData(msgData, data.groupMsg.sign, contacts[data.from].pubKey))
return
var msgInfo = {
time: Date.now(),
groupID: data.groupMsg.group,
sender: data.from,
text: msg,
msgData: msgData,
type: "R"
}
createMsgElement(msgInfo);
@ -898,28 +913,36 @@ function createMsgElement(msgInfo) {
R: 'receiver'
};
if (!msgInfo.groupID) {
var msgEl = document.getElementById(msgInfo.floID);
var msgCon = document.getElementById(msgInfo.floID);
var msghd = '';
} else {
var msgEl = document.getElementById(msgInfo.groupID);
var msghd = `<b>${msgInfo.sender}</b><br/>`;
var msgCon = document.getElementById(msgInfo.groupID);
var msghd = `<b>${msgInfo.sender}</b>`;
}
if (!msgEl)
if (!msgCon)
return;
var msgdiv = document.createElement('div');
msgdiv.setAttribute("class", "row message-body");
msgdiv.innerHTML = `<div class="col-sm-12 message-main-${type[msgInfo.type]}">
var msgData = JSON.parse(msgInfo.msgData);
var msgEl = document.createElement('div');
msgEl.setAttribute("class", "row message-body");
msgEl.innerHTML = `<div class="col-sm-12 message-main-${type[msgInfo.type]}">
<div class="${type[msgInfo.type]}">
<span class="message-text">
${msghd}<pre></pre>
<span class="message-text">${msghd}
<i class="hidden" aria-hidden="true"></i>
<pre></pre>
</span>
<span class="message-time pull-right">
${getTime(msgInfo.time)}
</span>
</div>
</div>`;
msgdiv.querySelector("pre").textContent = msgInfo.text;
msgEl.appendChild(msgdiv);
msgEl.querySelector("pre").textContent = msgData.text;
if (msgData.file) {
var fileEl = msgEl.querySelector("i");
fileEl.textContent = ` ${msgData.file.name} (${getFileSize(msgData.file.size)})`;
fileEl.setAttribute("onclick", `downloadFile(${msgInfo.time})`);
fileEl.setAttribute("class", "fa fa-arrow-circle-down");
}
msgCon.appendChild(msgEl);
} catch (e) {
console.log(e);
}
@ -1057,30 +1080,83 @@ function getTime(time) {
return tmp;
}
function getFileSize(size){
var filesizeUnits = ['B','kB','MB','GB','TB'];
for(var i = 0;i<filesizeUnits.length;i++){
if(size/1024 < 1)
return `${Number((size).toFixed(1))}${filesizeUnits[i]}`;
else
size = size/1024;
}
}
function sendMsg() {
if (receiverID === undefined) {
alert("Select a contact and send message");
return;
}
var inp = document.getElementById('sendMsgInput')
var msg = inp.value;
inp.value = "";
console.log(msg);
var time = Date.now();
var sign = floOpt.signData(msg, privKey);
if (msgType === 'direct')
sendDirectMsg(msg, time, sign);
else if (msgType === 'group')
sendGroupMsg(msg, time, sign);
getReplyInputs().then(msgData => {
console.log(msgData);
var time = Date.now();
var sign = floOpt.signData(msgData, privKey);
if (msgType === 'direct')
sendDirectMsg(msgData, time, sign);
else if (msgType === 'group')
sendGroupMsg(msgData, time, sign);
}).catch(error => {
console.log(error);
})
}
function sendDirectMsg(msg, time, sign) {
function getfileData(fileInput) {
return new Promise((resolve, reject) => {
try {
var files = document.getElementById(fileInput).files;
if (files.length == 0)
resolve(null);
else {
var reader = new FileReader();
reader.onload = (event) => {
var fileBytes = Crypto.charenc.Binary.stringToBytes(event.target.result);
resolve({
name: files[0].name,
size: files[0].size,
content: Crypto.util.bytesToBase64(fileBytes)
});
};
reader.onerror = (event) => {
reject("File could not be read! Code " + event.target.error.code);
};
reader.readAsBinaryString(files[0]);
}
} catch (e) {
reject(e);
}
});
}
function getReplyInputs() {
return new Promise((resolve, reject) => {
getfileData('fileInput').then(fileData => {
var msgData = {
text: document.getElementById('msgInput').value,
file: fileData
};
resetForm('replyForm');
resolve(JSON.stringify(msgData));
}).catch(error => {
reject(error);
});
});
}
function sendDirectMsg(msgData, time, sign) {
var data = JSON.stringify({
from: selfID,
to: receiverID,
directMsg: {
time: time,
msgCipher: floOpt.encryptData(msg, contacts[receiverID].pubKey),
msgCipher: floOpt.encryptData(msgData, contacts[receiverID].pubKey),
sign: sign
}
});
@ -1092,20 +1168,20 @@ function sendDirectMsg(msg, time, sign) {
var msgInfo = {
time: time,
floID: receiverID,
text: msg,
msgData: msgData,
type: "S"
}
createMsgElement(msgInfo);
storeMsg(msgInfo);
}
function sendGroupMsg(msg, time, sign) {
function sendGroupMsg(msgData, time, sign) {
var data = {
from: selfID,
groupMsg: {
group: receiverID,
time: time,
msgCipher: floOpt.encryptData(msg, groups[receiverID].pubKey),
msgCipher: floOpt.encryptData(msgData, groups[receiverID].pubKey),
sign: sign
}
};
@ -1121,7 +1197,7 @@ function sendGroupMsg(msg, time, sign) {
time: time,
sender: selfID,
groupID: receiverID,
text: msg,
msgData: msgData,
type: "S"
}
createMsgElement(msgInfo);
@ -1468,6 +1544,7 @@ function customCheckList(userList, ignoreList, okBtnVal = "Ok", okBtnType = "suc
grpNameInput.style.display = (okBtnVal === "Create Group" ? "block" : "none");
grpNameInput.value = '';
var userChecklist = document.getElementById('userChecklist');
userChecklist.innerHTML = '';
for (var i = 0; i < userList.length; i++) {
if (ignoreList.includes(userList[i]))
continue;
@ -1528,4 +1605,25 @@ function searchChecklist() {
} catch (e) {
console.log(e);
}
}
function downloadFile(msgID) {
var idb = indexedDB.open("FLO_Chat");
idb.onerror = (event) => {
console.log("Error in opening IndexedDB!");
};
idb.onsuccess = (event) => {
var db = event.target.result;
var msgReq = db.transaction('messages', "readwrite").objectStore('messages').get(msgID);
msgReq.onsuccess = (event) => {
var file = JSON.parse(event.target.result.msgData).file;
var tmpEl = document.createElement('a');
tmpEl.setAttribute('href', 'data:application/octet-stream;charset=utf-8;base64,' + file.content);
tmpEl.setAttribute('download', file.name);
tmpEl.style.display = 'none';
document.body.appendChild(tmpEl);
tmpEl.click();
document.body.removeChild(tmpEl);
}
}
}

View File

@ -105,23 +105,32 @@
<!-- Reply Box -->
<div class="row reply">
<!--
<div class="col-sm-1 col-xs-1 reply-icon">
<i class="fa fa-smile-o fa-2x"></i>
</div>
-->
<div class="col-sm-9 col-xs-9 reply-main">
<textarea class="form-control" rows="1" id="sendMsgInput" placeholder="Type message"
autocomplete="off"></textarea>
</div>
<!--
<div class="col-sm-1 col-xs-1 reply-icon">
<i class="fa fa-microphone fa-2x" aria-hidden="true"></i>
</div>
-->
<div class="col-sm-1 col-xs-1 reply-icon" onclick="sendMsg()">
<i class="fa fa-send fa-2x" aria-hidden="true"></i>
</div>
<form id="replyForm">
<!--
<div class="col-sm-1 col-xs-1 reply-icon">
<i class="fa fa-smile-o fa-2x"></i>
</div>
-->
<div class="col-sm-9 col-xs-9 reply-main">
<textarea class="form-control" rows="1" id="msgInput" placeholder="Type message"
autocomplete="off"></textarea>
</div>
<div class="col-sm-1 col-xs-1 reply-icon">
<label>
<i class="fa fa-file fa-2x" aria-hidden="true"></i>
<input class="attach-file" type="file" id="fileInput"><span></span>
</label>
</div>
<!--
<div class="col-sm-1 col-xs-1 reply-icon">
<i class="fa fa-microphone fa-2x" aria-hidden="true"></i>
</div>
-->
<div class="col-sm-1 col-xs-1 reply-icon" onclick="sendMsg()">
<i class="fa fa-send fa-2x" aria-hidden="true"></i>
</div>
</form>
</div>
<!-- Reply Box End -->
</div>

View File

@ -89,7 +89,8 @@ body {
height: 60px;
width: 100%;
background-color: #eee;
z-index: 1000;
z-index: 1;
overflow: visible;
}
.heading-name {
@ -174,17 +175,6 @@ body {
background-color: #f2f2f2;
}
.sideBar-avatar {
text-align: center;
padding: 0 !important;
}
.avatar-icon img {
border-radius: 50%;
height: 49px;
width: 49px;
}
.sideBar-main {
padding: 0 !important;
}
@ -321,7 +311,7 @@ body {
}
.message-body {
margin: 0 !important;
margin: 5px 0px !important;
padding: 0 !important;
width: auto;
height: auto;
@ -349,6 +339,15 @@ body {
word-break: break-word;
}
.message-text i {
border: 0.5px solid rgba(0, 0, 0, 0.3);
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.1);
padding: 5px;
display: block;
cursor: pointer;
}
.message-time {
margin: 0 !important;
font-size: 12px;
@ -360,7 +359,7 @@ body {
.receiver {
float: left;
width: auto !important;
padding: 4px 10px 7px !important;
padding: 5px 10px 2px !important;
border-radius: 10px 10px 10px 0;
background: #ffffff;
font-size: 12px;
@ -373,7 +372,7 @@ body {
height: auto;
background: #dcf8c6;
border-radius: 10px 10px 0 10px;
padding: 4px 10px 7px !important;
padding: 5px 10px 2px !important;
font-size: 12px;
}
@ -390,6 +389,7 @@ body {
.reply-icon {
padding: 5px !important;
overflow: visible;
}
.reply-icon i {
@ -414,7 +414,6 @@ body {
box-shadow: none;
height: 100%;
font-size: 16px;
overflow: hidden;
}
.reply-main textarea:focus {
@ -424,103 +423,6 @@ body {
box-shadow: none;
}
@media screen and (max-width: 700px) {
.app {
top: 0;
height: 100%;
}
.heading {
height: 70px;
background-color: #009688;
}
.fa-2x {
font-size: 2.3em !important;
}
.heading-icon {
padding: 5px !important;
}
.heading-icon i {
color: #fff;
cursor: pointer;
}
.sideBar {
height: calc(100% - 130px);
}
.sideBar-body {
height: 80px;
}
.sideBar-main {
padding: 0 !important;
}
.sideBar-main .row {
padding: 0 !important;
margin: 0 !important;
}
.sideBar-name {
padding: 10px 5px !important;
}
.name-meta {
font-size: 16px;
padding: 5% !important;
}
.sideBar-time {
padding: 10px !important;
}
.time-meta {
text-align: right;
font-size: 14px;
padding: 4% !important;
color: rgba(0, 0, 0, .4);
vertical-align: baseline;
}
/*Conversation*/
.conversation {
padding: 0 !important;
margin: 0 !important;
height: 100%;
border-left: 1px solid rgba(0, 0, 0, .08);
}
.message {
height: calc(100% - 140px);
}
.reply {
height: 70px;
}
.reply-icon {
padding: 5px 0 !important;
}
.reply-icon i {
padding: 5px 2px !important;
font-size: 1.8em !important;
}
.reply-main {
padding: 2px 8px !important;
}
.reply-main input {
padding: 8px !important;
font-size: 18px;
}
}
.badgebox {
opacity: 0;
}
@ -562,15 +464,17 @@ body {
.userChecklist-container {
margin-top: 25px;
width: 300px;
height: 400px;
padding: 5px;
width: 325px;
height: auto;
background-color: #eee;
display: inline-block;
}
#userChecklist {
height: 275px;
overflow-y: auto;
overflow-y: scroll;
}
.dropdown-content {
@ -579,7 +483,8 @@ body {
min-width: 150px;
z-index: 1;
cursor: pointer;
margin-left: -75px;
margin-left: -100px;
overflow: inherit;
}
.dropdown-content li {
@ -601,4 +506,18 @@ body {
#groupOptions {
display: none;
overflow: visible;
}
input.attach-file {
display: none;
}
input.attach-file+span {
color: #93918f;
font-size: 10px;
}
.hidden {
display: none;
}