diff --git a/index.html b/index.html index 9a5325e..aeac7e9 100644 --- a/index.html +++ b/index.html @@ -61,35 +61,21 @@ //load messages from IDB and render them console.log(`Loading Data! Please Wait...`) - messenger.initUserDB().then(result => { - console.log(result) - //Check for availble bg image - setBgImage() - messenger.loadDataFromIDB().then(data => { - console.log(data) - floGlobals.appendix = data.appendix; - floGlobals.groups = data.groups; - floGlobals.chats = data.chats - floGlobals['marked'] = data.marked - renderChatList() - renderMailList(data.mails, false) - renderMarked(data.marked) - messenger.setUIcallbacks(renderDirectUI, renderGroupUI) - messenger.requestDirectInbox() - .then(r => console.log("DirectConn:", r)) - .catch(e => console.error("Request error:", e)) - messenger.requestGroupInbox() - .then(r => console.log(r)) - console.log(`Load Successful!`) - if(isPinSet){ - loadPage() - } - }).catch(error => { - //console.error(`Failed to load data`) - notify(error, "error") - }) - }) - + //Check for availble bg image + setBgImage(); + //Set UI render functions + messenger.renderUI.chats = renderChatList; + messenger.renderUI.directChat = renderDirectUI; + messenger.renderUI.groupChat = renderGroupUI; + messenger.renderUI.mails = m => renderMailList(m, false); + messenger.renderUI.marked = renderMarked; + //init messenger + messenger.init().then(result => { + console.log(result); + if(isPinSet){ + loadPage() + } + }).catch(error => notify(error, "error")); }).catch(error => notify(error, "error")) } @@ -1143,7 +1129,7 @@ getRef('add_as_contact_option').classList.remove('hide-completely') } - const markUnread = floGlobals.marked[clickedContact.floID]?.includes('unread') + const markUnread = messenger.marked[clickedContact.floID]?.includes('unread') if (markUnread) { getRef('mark_read_option').classList.remove('hide-completely') getRef('mark_unread_option').classList.add('hide-completely') @@ -1162,7 +1148,7 @@ getRef("contact_initial").innerHTML = ` ` - if (floGlobals.groups[clickedContact['floID']].admin === myFloID) + if (messenger.groups[clickedContact['floID']].admin === myFloID) getRef('contact_name').disabled = false else getRef('contact_name').disabled = true @@ -1180,7 +1166,7 @@ case 'contacts_popup': const contacts = [] for (contact in floGlobals.contacts) { - if (!floGlobals.groups[activeChat.floID].members.includes(contact) && contact in floGlobals.pubKeys) { + if (!messenger.groups[activeChat.floID].members.includes(contact) && contact in floGlobals.pubKeys) { contacts.push(contact) } } @@ -1382,8 +1368,8 @@ let name if (floGlobals.contacts[floID]) name = floGlobals.contacts[floID] - else if (floGlobals.groups[floID]) - name = floGlobals.groups[floID].name + else if (messenger.groups[floID]) + name = messenger.groups[floID].name else if (floID === myFloID) name = 'You' else @@ -1678,7 +1664,7 @@ initial.innerHTML = ` ` - cardContainer.querySelector('.name').textContent = floGlobals.groups[floID].name + cardContainer.querySelector('.name').textContent = messenger.groups[floID].name } else { cardContainer.querySelector('.name').textContent = name !== 'Unknown' ? name : floID @@ -1713,7 +1699,7 @@ } } if (type === 'group' && lastMessage.time === 0) { - lastMessage.time = floGlobals.groups[floID].created + lastMessage.time = messenger.groups[floID].created } let lastText = document.createElement('p') if ((type === 'chat' && lastMessage.category === 'sent') || (type === 'group' && lastMessage.sender === myFloID)) { @@ -1856,7 +1842,7 @@ else if (admin) { if (newMembers.length) { const cards = document.createDocumentFragment() - const { admin } = floGlobals.groups[groupID] + const { admin } = messenger.groups[groupID] newMembers.forEach(member => { let eventCard = document.createElement('p') eventCard.classList.add('group-event-card') @@ -1872,7 +1858,7 @@ } else if (rmMembers.length) { const cards = document.createDocumentFragment() - const { admin } = floGlobals.groups[groupID] + const { admin } = messenger.groups[groupID] rmMembers.forEach(member => { let eventCard = document.createElement('p') eventCard.classList.add('group-event-card') @@ -2134,7 +2120,7 @@ clickedContact['chatCard'] = contact clickedContact['floID'] = contact.getAttribute("flo-id") clickedContact['name'] = contact.getAttribute("name") - clickedContact['isGroup'] = floGlobals.groups.hasOwnProperty(clickedContact['floID']) + clickedContact['isGroup'] = messenger.groups.hasOwnProperty(clickedContact['floID']) showPopup('contact_details_popup') }, 500) getRef('chat_page').addEventListener('touchmove', handleTouchMove) @@ -2171,7 +2157,7 @@ clickedContact['chatCard'] = contact clickedContact['floID'] = contact.getAttribute("flo-id") clickedContact['name'] = contact.getAttribute("name") - clickedContact['isGroup'] = floGlobals.groups.hasOwnProperty(clickedContact['floID']) + clickedContact['isGroup'] = messenger.groups.hasOwnProperty(clickedContact['floID']) if (clickedContact['floID'] === myFloID) return @@ -2744,15 +2730,15 @@ getRef('contacts_container').append(frag) } - async function renderChatList() { + async function renderChatList(chatOrder) { const frag = document.createDocumentFragment() getRef('chat_container').innerHTML = '' - for (floID of messenger.getChatOrder().mixed) { - const markUnread = floGlobals.marked[floID]?.includes('unread') + for (floID of chatOrder) { + const markUnread = messenger.marked[floID]?.includes('unread') let type - if (floGlobals.chats[floID]) + if (messenger.chats[floID]) type = 'chat' - else if (floGlobals.groups[floID]) + else if (messenger.groups[floID]) type = 'group' frag.append(render.contactCard(floID, { type, markUnread })) } @@ -2927,7 +2913,7 @@ backgroundColor = contact.getAttribute('background-color') activeChat['floID'] = floID - activeChat['isGroup'] = floGlobals.groups[floID] ? true : false + activeChat['isGroup'] = messenger.groups[floID] ? true : false getRef("chat_dp").setAttribute('style', `color: ${textColor}; background-color: ${backgroundColor};`) getRef("receiver_name").textContent = getContactName(floID); if (activeChat.isGroup) { @@ -3235,12 +3221,12 @@ getRef("chat_dp").innerHTML = ` ` - getRef("last_interaction_time").textContent = `Created ${getFormatedTime(floGlobals.groups[floID].created)}`; + getRef("last_interaction_time").textContent = `Created ${getFormatedTime(messenger.groups[floID].created)}`; getRef("chat_type").textContent = `Group FLO ID`; getRef('group_members_list').innerHTML = '' - floGlobals.groups[floID].members.forEach(member => { - let isAdmin = floGlobals.groups[floID].admin === member ? true : false + messenger.groups[floID].members.forEach(member => { + let isAdmin = messenger.groups[floID].admin === member ? true : false frag.append(render.contactCard(member, { type: 'contact', contactOnly: true, isAdmin })) }) getRef('group_members_list').append(frag) @@ -3250,9 +3236,9 @@ getRef('delete_chat_button').classList.add('hide-completely') - getRef("group_description").value = floGlobals.groups[floID].description === '' ? 'Add group description' : floGlobals.groups[floID].description; + getRef("group_description").value = messenger.groups[floID].description === '' ? 'Add group description' : messenger.groups[floID].description; - if (floGlobals.groups[activeChat['floID']].admin === myFloID) { + if (messenger.groups[activeChat['floID']].admin === myFloID) { getRef("chat_name").disabled = false getRef('group_description').disabled = false getRef('edit_group_button').classList.remove('hide-completely') diff --git a/scripts/messenger.js b/scripts/messenger.js index 7546c37..fc8c837 100644 --- a/scripts/messenger.js +++ b/scripts/messenger.js @@ -1,6 +1,62 @@ (function() { const messenger = window.messenger = {}; + const expiredKeys = {}; + + const UI = { + group: (d, e) => console.log(d, e), + direct: (d, e) => console.log(d, e), + chats: (c) => console.log(c), + mails: (m) => console.log(m), + marked: (r) => console.log(r) + }; + messenger.renderUI = {}; + Object.defineProperties(messenger.renderUI, { + chats: { + set: ui_fn => UI.chats = ui_fn + }, + directChat: { + set: ui_fn => UI.direct = ui_fn + }, + groupChat: { + set: ui_fn => UI.group = ui_fn + }, + mails: { + set: ui_fn => UI.mails = ui_fn + }, + marked: { + set: ui_fn => UI.marked = ui_fn + }, + }); + + const _loaded = {}; + Object.defineProperties(messenger, { + chats: { + get: _loaded.chats + }, + groups: { + get: _loaded.groups + }, + blocked: { + get: _loaded.blocked + }, + marked: { + get: _loaded.marked + } + }); + + var directConnID, groupConnID = {}; + messenger.conn = {}; + Object.defineProperties(messenger.conn, { + direct: { + get: directConnID + }, + group: { + get: Object.assign({}, groupConnID), + value: g_id => groupConnID[g_id] + } + }); + function sendRaw(message, recipient, type, encrypt = null, comment = undefined) { return new Promise((resolve, reject) => { if (!floCrypto.validateAddr(recipient)) @@ -21,11 +77,11 @@ }) } - function encrypt(value, key = floGlobals.appendix.AESKey) { + function encrypt(value, key = _loaded.appendix.AESKey) { return Crypto.AES.encrypt(value, key) } - function decrypt(value, key = floGlobals.appendix.AESKey) { + function decrypt(value, key = _loaded.appendix.AESKey) { return Crypto.AES.decrypt(value, key) } @@ -51,8 +107,7 @@ if (!result || !result.includes(mark)) return resolve("Mark doesnot exist") else { - result.splice(result.indexOf(mark), - 1); //remove the mark from the list of marks + result.splice(result.indexOf(mark), 1); //remove the mark from the list of marks compactIDB.writeData("marked", result, key) .then(result => resolve("Mark removed")) .catch(error => reject(error)) @@ -61,13 +116,12 @@ }) } - const UIcallback = { - group: (d, e) => console.log(d, e), - direct: (d, e) => console.log(d, e) - } - - function groupConn(groupID) { - console.debug(UIcallback); + function requestGroupInbox(groupID) { + console.debug(UI); + if (groupConnID[groupID]) { //close existing request connection (if any) + floCloudAPI.closeRequest(groupConnID[groupID]); + delete groupConnID[groupID]; + } let callbackFn = function(dataSet, error) { if (error) return console.error(error) @@ -78,7 +132,7 @@ let infoChange = false; for (let vc in dataSet) { if (groupID !== dataSet[vc].receiverID || - !floGlobals.groups[groupID].members.includes(dataSet[vc].senderID)) + !_loaded.groups[groupID].members.includes(dataSet[vc].senderID)) continue; try { let data = { @@ -86,33 +140,33 @@ sender: dataSet[vc].senderID, groupID: dataSet[vc].receiverID } - let k = floGlobals.groups[groupID].eKey; - if (floGlobals.expiredKeys[groupID]) { - var ex = Object.keys(floGlobals.expiredKeys[groupID]).sort() + let k = _loaded.groups[groupID].eKey; + if (expiredKeys[groupID]) { + var ex = Object.keys(expiredKeys[groupID]).sort() while (ex.lenght && vc > ex[0]) ex.shift() if (ex.length) - k = floGlobals.expiredKeys[groupID][ex.shift()] + k = expiredKeys[groupID][ex.shift()] } dataSet[vc].message = decrypt(dataSet[vc].message, k) //store the pubKey if not stored already floDapps.storePubKey(dataSet[vc].senderID, dataSet[vc].pubKey) if (dataSet[vc].type === "GROUP_MSG") data.message = encrypt(dataSet[vc].message); - else if (data.sender === floGlobals.groups[groupID].admin) { - let groupInfo = floGlobals.groups[groupID] + else if (data.sender === _loaded.groups[groupID].admin) { + let groupInfo = _loaded.groups[groupID] data.admin = true; if (dataSet[vc].type === "ADD_MEMBERS") { data.newMembers = dataSet[vc].message.split("|") data.note = dataSet[vc].comment - groupInfo.members = [...new Set(groupInfo.members.concat(data - .newMembers))] + groupInfo.members = [...new Set(groupInfo.members.concat(data.newMembers))] } else if (dataSet[vc].type === "RM_MEMBERS") { data.rmMembers = dataSet[vc].message.split("|") data.note = dataSet[vc].comment - groupInfo.members = groupInfo.members.filter(m => !data.rmMembers - .includes(m)) - if (data.rmMembers.includes(myFloID)) - groupInfo.status = false + groupInfo.members = groupInfo.members.filter(m => !data.rmMembers.includes(m)) + if (data.rmMembers.includes(myFloID)) { + disableGroup(groupID); + return; + } } else if (dataSet[vc].type === "UP_DESCRIPTION") { data.description = dataSet[vc].message groupInfo.description = data.description @@ -131,40 +185,35 @@ console.log(data) if (data.sender !== myFloID) addMark(data.groupID, "unread") - if (!floGlobals.appendix[`lastReceived_${groupID}`] || - floGlobals.appendix[`lastReceived_${groupID}`] < vc) - floGlobals.appendix[`lastReceived_${groupID}`] = vc; + if (!_loaded.appendix[`lastReceived_${groupID}`] || + _loaded.appendix[`lastReceived_${groupID}`] < vc) + _loaded.appendix[`lastReceived_${groupID}`] = vc; } catch (error) { console.log(error) } } - compactIDB.writeData("appendix", floGlobals.appendix[`lastReceived_${groupID}`], + compactIDB.writeData("appendix", _loaded.appendix[`lastReceived_${groupID}`], `lastReceived_${groupID}`); if (infoChange) { let newInfo = { - ...floGlobals.groups[groupID] + ..._loaded.groups[groupID] } newInfo.eKey = encrypt(newInfo.eKey) compactIDB.writeData("groups", newInfo, groupID) } console.debug(newInbox); - UIcallback.group(newInbox); + UI.group(newInbox); } - return floCloudAPI.requestApplicationData(null, { - receiverID: groupID, - lowerVectorClock: floGlobals.appendix[`lastReceived_${groupID}`] + 1, - callback: callbackFn - }) + floCloudAPI.requestApplicationData(null, { + receiverID: groupID, + lowerVectorClock: _loaded.appendix[`lastReceived_${groupID}`] + 1, + callback: callbackFn + }).then(conn_id => groupConnID[groupID] = conn_id) + .catch(error => console.error(`request-group(${groupID}):`, error)) + } - messenger.setUIcallbacks = function(directUI = null, groupUI = null) { - if (directUI instanceof Function) - UIcallback["direct"] = directUI - if (groupUI instanceof Function) - UIcallback["group"] = groupUI - } - - messenger.initUserDB = function() { + const initUserDB = function() { return new Promise((resolve, reject) => { var obj = { messages: {}, @@ -173,6 +222,7 @@ chats: {}, groups: {}, gkeys: {}, + blocked: {}, appendix: {}, userSettings: {} } @@ -184,8 +234,30 @@ }) } + messenger.blockUser = function(floID) { + return new Promise((resolve, reject) => { + if (_loaded.blocked.has(floID)) + return resolve("User is already blocked"); + compactIDB.addData("blocked", true, floID).then(result => { + _loaded.blocked.add(floID); + resolve("Blocked User: " + floID); + }).catch(error => reject(error)) + }) + } + + messenger.unblockUser = function(floID) { + return new Promise((resolve, reject) => { + if (!_loaded.blocked.has(floID)) + return resolve("User is not blocked"); + compactIDB.removeData("blocked", floID).then(result => { + _loaded.blocked.delete(floID); + resolve("Unblocked User: " + floID); + }).catch(error => reject(error)) + }) + } + messenger.sendMessage = function(message, receiver) { - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { sendRaw(message, receiver, "MESSAGE").then(result => { let vc = result.vectorClock; let data = { @@ -194,7 +266,7 @@ category: 'sent', message: encrypt(message) } - floGlobals.chats[receiver] = parseInt(vc) + _loaded.chats[receiver] = parseInt(vc) compactIDB.writeData("chats", parseInt(vc), receiver) compactIDB.addData("messages", { ...data @@ -208,7 +280,7 @@ } messenger.sendMail = function(subject, content, recipients, prev = null) { - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { if (!Array.isArray(recipients)) recipients = [recipients] let mail = { @@ -240,112 +312,114 @@ }) } - messenger.requestDirectInbox = function() { - return new Promise((resolve, reject) => { - let callbackFn = function(dataSet, error) { - if (error) - return console.error(error) - let newInbox = { - messages: {}, - mails: {}, - newgroups: [], - keyrevoke: [] - } - console.log(dataSet) - for (let vc in dataSet) { - try { - //store the pubKey if not stored already - floDapps.storePubKey(dataSet[vc].senderID, dataSet[vc].pubKey) - if (dataSet[vc].message instanceof Object && "secret" in dataSet[vc].message) - dataSet[vc].message = floCrypto.decryptData(dataSet[vc].message, myPrivKey) - if (dataSet[vc].type === "MESSAGE") { - //process as message - let dm = { - time: dataSet[vc].time, - floID: dataSet[vc].senderID, - category: "received", - message: encrypt(dataSet[vc].message) - } - compactIDB.addData("messages", { - ...dm - }, `${dm.floID}|${vc}`) - floGlobals.chats[dm.floID] = parseInt(vc) - compactIDB.writeData("chats", parseInt(vc), dm.floID) - dm.message = dataSet[vc].message; - newInbox.messages[vc] = dm; - addMark(dm.floID, "unread") - } else if (dataSet[vc].type === "MAIL") { - //process as mail - let data = JSON.parse(dataSet[vc].message); - let mail = { - time: dataSet[vc].time, - from: dataSet[vc].senderID, - to: [myFloID], - subject: data.subject, - content: encrypt(data.content), - ref: data.ref, - prev: data.prev - } - compactIDB.addData("mails", { - ...mail - }, mail.ref); - mail.content = data.content; - newInbox.mails[mail.ref] = mail; - addMark(mail.ref, "unread") - } else if (dataSet[vc].type === "CREATE_GROUP") { - //process create group - let groupInfo = JSON.parse(dataSet[vc].message); - let h = ["groupID", "created", "admin"].map(x => groupInfo[x]).join('|') - if (groupInfo.admin === dataSet[vc].senderID && - floCrypto.verifySign(h, groupInfo.hash, groupInfo.pubKey) && - floCrypto.getFloID(groupInfo.pubKey) === groupInfo.groupID) { - let eKey = groupInfo.eKey - groupInfo.eKey = encrypt(eKey) - compactIDB.writeData("groups", { - ...groupInfo - }, groupInfo.groupID) - groupInfo.eKey = eKey - floGlobals.groups[groupInfo.groupID] = groupInfo - groupConn(groupInfo.groupID) - newInbox.newgroups.push(groupInfo.groupID) - } - } else if (dataSet[vc].type === "REVOKE_KEY") { - let r = JSON.parse(dataSet[vc].message); - let groupInfo = floGlobals.groups[r.groupID] - if (dataSet[vc].senderID === groupInfo.admin) { - if (typeof floGlobals.expiredKeys[r.groupID] !== "object") - floGlobals.expiredKeys[r.groupID] = {} - floGlobals.expiredKeys[r.groupID][vc] = groupInfo.eKey - let eKey = r.newKey - groupInfo.eKey = encrypt(eKey); - compactIDB.writeData("groups", { - ...groupInfo - }, groupInfo.groupID) - groupInfo.eKey = eKey - newInbox.keyrevoke.push(groupInfo.groupID) - } + const requestDirectInbox = function() { + if (directInboxConn) { //close existing request connection (if any) + floCloudAPI.closeRequest(directConnID); + directConnID = undefined; + } + let callbackFn = function(dataSet, error) { + if (error) + return console.error(error) + let newInbox = { + messages: {}, + mails: {}, + newgroups: [], + keyrevoke: [] + } + console.debug("Check if key is vc", dataSet); + for (let vc in dataSet) { + try { + //store the pubKey if not stored already + floDapps.storePubKey(dataSet[vc].senderID, dataSet[vc].pubKey); + if (_loaded.blocked.has(dataSet[vc].senderID) && dataSet[vc].type !== "REVOKE_KEY") + throw "blocked-user"; + if (dataSet[vc].message instanceof Object && "secret" in dataSet[vc].message) + dataSet[vc].message = floCrypto.decryptData(dataSet[vc].message, myPrivKey) + if (dataSet[vc].type === "MESSAGE") { + //process as message + let dm = { + time: dataSet[vc].time, + floID: dataSet[vc].senderID, + category: "received", + message: encrypt(dataSet[vc].message) + } + compactIDB.addData("messages", { + ...dm + }, `${dm.floID}|${vc}`) + _loaded.chats[dm.floID] = parseInt(vc) + compactIDB.writeData("chats", parseInt(vc), dm.floID) + dm.message = dataSet[vc].message; + newInbox.messages[vc] = dm; + addMark(dm.floID, "unread") + } else if (dataSet[vc].type === "MAIL") { + //process as mail + let data = JSON.parse(dataSet[vc].message); + let mail = { + time: dataSet[vc].time, + from: dataSet[vc].senderID, + to: [myFloID], + subject: data.subject, + content: encrypt(data.content), + ref: data.ref, + prev: data.prev + } + compactIDB.addData("mails", { + ...mail + }, mail.ref); + mail.content = data.content; + newInbox.mails[mail.ref] = mail; + addMark(mail.ref, "unread") + } else if (dataSet[vc].type === "CREATE_GROUP") { + //process create group + let groupInfo = JSON.parse(dataSet[vc].message); + let h = ["groupID", "created", "admin"].map(x => groupInfo[x]).join('|') + if (groupInfo.admin === dataSet[vc].senderID && + floCrypto.verifySign(h, groupInfo.hash, groupInfo.pubKey) && + floCrypto.getFloID(groupInfo.pubKey) === groupInfo.groupID) { + let eKey = groupInfo.eKey + groupInfo.eKey = encrypt(eKey) + compactIDB.writeData("groups", { + ...groupInfo + }, groupInfo.groupID) + groupInfo.eKey = eKey + _loaded.groups[groupInfo.groupID] = groupInfo + requestGroupInbox(groupInfo.groupID) + newInbox.newgroups.push(groupInfo.groupID) + } + } else if (dataSet[vc].type === "REVOKE_KEY") { + let r = JSON.parse(dataSet[vc].message); + let groupInfo = _loaded.groups[r.groupID] + if (dataSet[vc].senderID === groupInfo.admin) { + if (typeof expiredKeys[r.groupID] !== "object") + expiredKeys[r.groupID] = {} + expiredKeys[r.groupID][vc] = groupInfo.eKey + let eKey = r.newKey + groupInfo.eKey = encrypt(eKey); + compactIDB.writeData("groups", { + ...groupInfo + }, groupInfo.groupID) + groupInfo.eKey = eKey + newInbox.keyrevoke.push(groupInfo.groupID) } - } catch (error) { - console.log(error) - } finally { - if (floGlobals.appendix.lastReceived < vc) - floGlobals.appendix.lastReceived = vc; } + } catch (error) { + if (error !== "blocked-user") + console.log(error); + } finally { + if (_loaded.appendix.lastReceived < vc) + _loaded.appendix.lastReceived = vc; } - compactIDB.writeData("appendix", floGlobals.appendix.lastReceived, "lastReceived"); - console.debug(newInbox); - UIcallback.direct(newInbox) } - - var options = { + compactIDB.writeData("appendix", _loaded.appendix.lastReceived, "lastReceived"); + console.debug(newInbox); + UI.direct(newInbox) + } + floCloudAPI.requestApplicationData(null, { receiverID: myFloID, - lowerVectorClock: floGlobals.appendix.lastReceived + 1, + lowerVectorClock: _loaded.appendix.lastReceived + 1, callback: callbackFn - } - floCloudAPI.requestApplicationData(null, options) - .then(result => resolve(result)) - .catch(error => reject(error)) - }) + }).then(conn_id => directConnID = conn_id) + .catch(error => console.error("request-direct:", error)); } messenger.getMail = function(mailRef) { @@ -357,35 +431,32 @@ }); } - messenger.getChatOrder = function(type = ["direct", "group", "mixed"]) { - if (typeof type === "string") - type = type.split('|') - let result = {} - if (type.includes("direct")) - result.direct = Object.keys(floGlobals.chats).map(a => [floGlobals.chats[a], a]) - .sort((a, b) => b[0] - a[0]).map(a => a[1]) - if (type.includes("group")) - result.group = Object.keys(floGlobals.groups).map(a => [parseInt(floGlobals.appendix[`lastReceived_${a}`]), a]) - .sort((a, b) => b[0] - a[0]).map(a => a[1]) - if (type.includes("mixed")) - result.mixed = Object.keys(floGlobals.chats).map(a => [floGlobals.chats[a], a]) - .concat(Object.keys(floGlobals.groups).map(a => [parseInt(floGlobals.appendix[`lastReceived_${a}`]), a])) - .sort((a, b) => b[0] - a[0]).map(a => a[1]) - if (type.length === 1) - result = result[type[0]] - return result + const getChatOrder = messenger.getChatOrder = function(seperate = false) { + let result; + if (seperate) { + result = {}; + result.direct = Object.keys(_loaded.chats).map(a => [_loaded.chats[a], a]) + .sort((a, b) => b[0] - a[0]).map(a => a[1]); + result.group = Object.keys(_loaded.groups).map(a => [parseInt(_loaded.appendix[`lastReceived_${a}`]), a]) + .sort((a, b) => b[0] - a[0]).map(a => a[1]); + } else { + result = Object.keys(_loaded.chats).map(a => [_loaded.chats[a], a]) + .concat(Object.keys(_loaded.groups).map(a => [parseInt(_loaded.appendix[`lastReceived_${a}`]), a])) + .sort((a, b) => b[0] - a[0]).map(a => a[1]) + } + return result; } messenger.storeContact = function(floID, name) { return floDapps.storeContact(floID, name) } - const loadDataFromIDB = messenger.loadDataFromIDB = function(dataList = 'default') { + const loadDataFromIDB = function(defaultList = true) { return new Promise((resolve, reject) => { - if (dataList === 'default') - dataList = ["mails", "marked", "groups", "chats", "appendix"] - else if (dataList === 'all') - dataList = ["messages", "mails", "marked", "chats", "groups", "gkeys", "appendix"] + if (defaultList) + dataList = ["mails", "marked", "groups", "chats", "blocked", "appendix"] + else + dataList = ["messages", "mails", "marked", "chats", "groups", "gkeys", "blocked", "appendix"] let promises = [] for (var i = 0; i < dataList.length; i++) promises[i] = compactIDB.readAllData(dataList[i]) @@ -393,7 +464,7 @@ let data = {} for (var i = 0; i < dataList.length; i++) data[dataList[i]] = results[i] - data.appendix.lastReceived = data.appendix.lastReceived || '0' + data.appendix.lastReceived = data.appendix.lastReceived || '0'; if (data.appendix.AESKey) { try { let AESKey = floCrypto.decryptData(data.appendix.AESKey, myPrivKey); @@ -401,16 +472,16 @@ if (dataList.includes("messages")) for (let m in data.messages) if (data.messages[m].message) - data.messages[m].message = decrypt(data.messages[m].message, AESKey) + data.messages[m].message = decrypt(data.messages[m].message, AESKey); if (dataList.includes("mails")) for (let m in data.mails) - data.mails[m].content = decrypt(data.mails[m].content, AESKey) + data.mails[m].content = decrypt(data.mails[m].content, AESKey); if (dataList.includes("groups")) for (let g in data.groups) - data.groups[g].eKey = decrypt(data.groups[g].eKey, AESKey) + data.groups[g].eKey = decrypt(data.groups[g].eKey, AESKey); if (dataList.includes("gkeys")) for (let k in data.gkeys) - data.gkeys[k] = decrypt(data.gkeys[k], AESKey) + data.gkeys[k] = decrypt(data.gkeys[k], AESKey); resolve(data) } catch (error) { reject("Corrupted AES Key"); @@ -418,7 +489,7 @@ } else { if (Object.keys(data.mails).length) return reject("AES Key not Found") - let AESKey = floCrypto.randString(32); + let AESKey = floCrypto.randString(32, false); let encryptedKey = floCrypto.encryptData(AESKey, myPubKey); compactIDB.addData("appendix", encryptedKey, "AESKey").then(result => { data.appendix.AESKey = AESKey; @@ -430,14 +501,14 @@ } messenger.addMark = function(key, mark) { - if (floGlobals.marked.hasOwnProperty(key) && !floGlobals.marked[key].includes(mark)) - floGlobals.marked[key].push(mark) + if (_loaded.marked.hasOwnProperty(key) && !_loaded.marked[key].includes(mark)) + _loaded.marked[key].push(mark) return addMark(key, mark) } messenger.removeMark = function(key, mark) { - if (floGlobals.marked.hasOwnProperty(key)) - floGlobals.marked[key] = floGlobals.marked[key].filter(v => v !== mark) + if (_loaded.marked.hasOwnProperty(key)) + _loaded.marked[key] = _loaded.marked[key].filter(v => v !== mark) return removeMark(key, mark) } @@ -491,7 +562,7 @@ messenger.backupData = function() { return new Promise((resolve, reject) => { - loadDataFromIDB("all").then(data => { + loadDataFromIDB(false).then(data => { delete data.appendix.AESKey; data.contacts = floGlobals.contacts; data.pubKeys = floGlobals.pubKeys; @@ -556,14 +627,14 @@ for (let g in data.groups) data.groups[g].eKey = encrypt(data.groups[g].eKey) for (let c in data.chats) - if (data.chats[c] <= floGlobals.chats[c]) + if (data.chats[c] <= _loaded.chats[c]) delete data.chats[c] for (let l in data.appendix) - if (l.startsWith('lastReceived') && data.appendix[l] <= floGlobals.appendix[l]) + if (l.startsWith('lastReceived') && data.appendix[l] <= _loaded.appendix[l]) delete data.appendix[l] for (let c in data.contacts) if (c in floGlobals.contacts) - delete data.contact[c] + delete data.contacts[c] for (let p in data.pubKeys) if (p in floGlobals.pubKeys) delete data.pubKeys[p] @@ -623,12 +694,12 @@ groupInfo.hash = floCrypto.signData(h, id.privKey) let eKey = floCrypto.randString(16, false) groupInfo.eKey = encrypt(eKey) - p1 = compactIDB.addData("groups", groupInfo, id.floID) - p2 = compactIDB.addData("gkeys", encrypt(id.privKey), id.floID) + let p1 = compactIDB.addData("groups", groupInfo, id.floID) + let p2 = compactIDB.addData("gkeys", encrypt(id.privKey), id.floID) Promise.all([p1, p2]).then(r => { groupInfo.eKey = eKey - floGlobals.groups[id.floID] = groupInfo; - groupConn(id.floID) + _loaded.groups[id.floID] = groupInfo; + requestGroupInbox(id.floID) resolve(groupInfo) }).catch(e => reject(e)) }) @@ -636,7 +707,7 @@ messenger.changeGroupName = function(groupID, name) { return new Promise((resolve, reject) => { - let groupInfo = floGlobals.groups[groupID] + let groupInfo = _loaded.groups[groupID] if (myFloID !== groupInfo.admin) return reject("Access denied: Admin only!") let message = encrypt(name, groupInfo.eKey) @@ -648,7 +719,7 @@ messenger.changeGroupDescription = function(groupID, description) { return new Promise((resolve, reject) => { - let groupInfo = floGlobals.groups[groupID] + let groupInfo = _loaded.groups[groupID] if (myFloID !== groupInfo.admin) return reject("Access denied: Admin only!") let message = encrypt(description, groupInfo.eKey) @@ -674,11 +745,10 @@ else if (imem2.length) return reject(`Invalid Members (pubKey not available): ${imem2}`) //send new newMem list to existing members - let groupInfo = floGlobals.groups[groupID] + let groupInfo = _loaded.groups[groupID] if (myFloID !== groupInfo.admin) return reject("Access denied: Admin only!") //send groupInfo to new newMem - let k = groupInfo.eKey groupInfo = JSON.stringify(groupInfo) let promises = newMem.map(m => sendRaw(groupInfo, m, "CREATE_GROUP", true)); Promise.allSettled(promises).then(results => { @@ -689,8 +759,8 @@ success.push(newMem[i]) else if (results[i].status === "rejected") failed.push(newMem[i]) - console.log(success.join("|"), k) - let message = encrypt(success.join("|"), k) + console.log(success.join("|")) + let message = encrypt(success.join("|"), groupInfo.eKey) sendRaw(message, groupID, "ADD_MEMBERS", false, note) .then(r => resolve(`Members added: ${success}`)) .catch(e => reject(e)) @@ -702,16 +772,16 @@ return new Promise((resolve, reject) => { if (!Array.isArray(rmMem) && typeof rmMem === "string") rmMem = [rmMem] - let groupInfo = floGlobals.groups[groupID] + let groupInfo = _loaded.groups[groupID] let imem = rmMem.filter(m => !groupInfo.members.includes(m)) if (imem.length) return reject(`Invalid members: ${imem}`) if (myFloID !== groupInfo.admin) return reject("Access denied: Admin only!") let message = encrypt(rmMem.join("|"), groupInfo.eKey) - p1 = sendRaw(message, groupID, "RM_MEMBERS", false, note) + let p1 = sendRaw(message, groupID, "RM_MEMBERS", false, note) groupInfo.members = groupInfo.members.filter(m => !rmMem.includes(m)) - p2 = revokeKey(groupID) + let p2 = revokeKey(groupID) Promise.all([p1, p2]) .then(r => resolve(`Members removed: ${rmMem}`)) .catch(e => reject(e)) @@ -720,7 +790,7 @@ const revokeKey = messenger.revokeKey = function(groupID) { return new Promise((resolve, reject) => { - let groupInfo = floGlobals.groups[groupID] + let groupInfo = _loaded.groups[groupID] if (myFloID !== groupInfo.admin) return reject("Access denied: Admin only!") let newKey = floCrypto.randString(16, false); @@ -734,8 +804,8 @@ } messenger.sendGroupMessage = function(message, groupID) { - return new Promise(async (resolve, reject) => { - let k = floGlobals.groups[groupID].eKey + return new Promise((resolve, reject) => { + let k = _loaded.groups[groupID].eKey message = encrypt(message, k) sendRaw(message, groupID, "GROUP_MSG", false) .then(result => resolve(`${groupID}: ${message}`)) @@ -743,32 +813,46 @@ }) } - messenger.requestGroupInbox = function() { - return new Promise((resolve) => { - let promises = [] - let reqFn = (g) => new Promise((res, rej) => { - groupConn(g) - .then(r => res([g, r])) - .catch(e => rej([g, e])) - }) - for (let g in floGlobals.groups) - if (floGlobals.groups[g].status !== false) - promises.push(reqFn(g)) - Promise.allSettled(promises).then(result => { - let ret = {}; - result.forEach(r => { - if (r.status === 'fulfilled') - ret[r.value[0]] = { - status: r.status, - value: r.value[1] - } - else if (r.status === "rejected") - ret[r.reason[0]] = { - status: r.status, - reason: r.reason[1] - } - }) - resolve(ret) + const disableGroup = messenger.disableGroup = function(groupID) { + return new Promise((resolve, reject) => { + if (!_loaded.groups[groupID]) + return reject("Group not found"); + let groupInfo = { + ..._loaded.groups[groupID] + }; + if (groupInfo.disabled) + return resolve("Group already diabled"); + groupInfo.disabled = true; + groupInfo.eKey = encrypt(groupInfo.eKey) + compactIDB.writeData("groups", groupInfo, groupID).then(result => { + floCloudAPI.closeRequest(groupConnID[groupID]); + delete groupConnID[groupID]; + resolve("Group diabled"); + }).catch(error => reject(error)) + }) + } + + messenger.init = function() { + return new Promise((resolve, reject) => { + initUserDB().then(result => { + console.debug(result); + loadDataFromIDB().then(data => { + console.debug(data); + //load data to memory + _loaded.appendix = data.appendix; + _loaded.groups = data.groups; + _loaded.chats = data.chats; + _loaded.marked = data.marked; + _loaded.blocked = new Set(Object.keys(data.blocked)); + //call UI render functions + UI.chats(getChatOrder()); + UI.mails(data.mail); + UI.marked(data.marked); + //request data from cloud + requestDirectInbox(); + data.groups.map(g => requestGroupInbox(g)) + resolve("Messenger initiated"); + }).catch(error => reject(error)); }) }) }