Security Fix and minor UI changes

Verify privKey of groupID when creating new group
Names and Messages are now added in textContent instead of innerHTML to prevent HTML injection
Both direct message and group message use the same IDB objectStore
Converted send message input to textarea : now users can send multi-line messages
Minor UI changes and fixes
Improved Enter Key Press :
Shift+Enter key will now insert a new line
(Enter key pressed without shift key will send message as before)
Enter key event to send message will now tigger on keydown instead of keyup
This commit is contained in:
sairajzero 2019-09-15 19:05:50 +05:30
parent ebd532b608
commit eaa3470cf4
3 changed files with 91 additions and 217 deletions

View File

@ -214,6 +214,20 @@ var floOpt = {
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
},
verifyPrivKey: function (privateKeyHex, floID) {
try {
var key = new Bitcoin.ECKey(privateKeyHex);
if (key.priv == null)
return false;
key.setCompressed(true);
if (floID == key.getBitcoinAddress())
return true;
else
return false;
} catch (e) {
console.log(e);
}
} }
} }
//Script for AJAX, and register functions //Script for AJAX, and register functions
@ -315,8 +329,8 @@ function broadcastTx(signedTxHash) {
function userDataStartUp() { function userDataStartUp() {
console.log("StartUp"); console.log("StartUp");
document.getElementById("sendMsgInput").addEventListener("keyup", (event) => { document.getElementById("sendMsgInput").addEventListener("keydown", (event) => {
if (event.keyCode === 13) { if (event.keyCode === 13 && !event.shiftKey) {
event.preventDefault(); event.preventDefault();
sendMsg(); sendMsg();
} }
@ -342,15 +356,10 @@ function userDataStartUp() {
groups = result; groups = result;
readMsgfromIDB().then(result => { readMsgfromIDB().then(result => {
console.log(result); console.log(result);
readGroupMsgfromIDB().then(result => { initselfWebSocket();
console.log(result); pingSuperNodeForAwayMessages();
initselfWebSocket(); displayContacts();
pingSuperNodeForAwayMessages(); const createClock = setInterval(checkStatusInterval, 30000);
displayContacts();
const createClock = setInterval(checkStatusInterval, 30000);
}).catch(error => {
console.log(error);
});
}).catch(error => { }).catch(error => {
console.log(error); console.log(error);
}); });
@ -450,6 +459,12 @@ function getDatafromAPI() {
objectStore3.createIndex('floID', 'floID', { objectStore3.createIndex('floID', 'floID', {
unique: false unique: false
}); });
objectStore3.createIndex('groupID', 'groupID', {
unique: false
});
objectStore3.createIndex('sender', 'sender', {
unique: false
});
objectStore3.createIndex('type', 'type', { objectStore3.createIndex('type', 'type', {
unique: false unique: false
}); });
@ -459,21 +474,6 @@ function getDatafromAPI() {
objectStore4.createIndex('groupInfo', 'groupInfo', { objectStore4.createIndex('groupInfo', 'groupInfo', {
unique: false unique: false
}); });
var objectStore5 = db.createObjectStore("groupMsg", {
keyPath: 'time'
});
objectStore5.createIndex('text', 'text', {
unique: false
});
objectStore5.createIndex('groupID', 'groupID', {
unique: false
});
objectStore5.createIndex('type', 'type', {
unique: false
});
objectStore5.createIndex('sender', 'sender', {
unique: false
});
}; };
idb.onsuccess = (event) => { idb.onsuccess = (event) => {
var db = event.target.result; var db = event.target.result;
@ -657,6 +657,13 @@ function readMsgfromIDB() {
createLi.style.display = 'none'; createLi.style.display = 'none';
disp.appendChild(createLi); disp.appendChild(createLi);
} }
for (floID in groups) {
var createLi = document.createElement('div');
createLi.setAttribute("id", floID);
createLi.setAttribute("class", "message-inner");
createLi.style.display = 'none';
disp.appendChild(createLi);
}
var idb = indexedDB.open("FLO_Chat"); var idb = indexedDB.open("FLO_Chat");
idb.onerror = (event) => { idb.onerror = (event) => {
reject("Error in opening IndexedDB!"); reject("Error in opening IndexedDB!");
@ -678,37 +685,6 @@ function readMsgfromIDB() {
}); });
} }
function readGroupMsgfromIDB() {
return new Promise((resolve, reject) => {
var disp = document.getElementById("conversation");
for (floID in groups) {
var createLi = document.createElement('div');
createLi.setAttribute("id", floID);
createLi.setAttribute("class", "message-inner");
createLi.style.display = 'none';
disp.appendChild(createLi);
}
var idb = indexedDB.open("FLO_Chat");
idb.onerror = (event) => {
reject("Error in opening IndexedDB!");
};
idb.onsuccess = (event) => {
var db = event.target.result;
var obs = db.transaction("groupMsg", "readwrite").objectStore("groupMsg");
obs.openCursor().onsuccess = (event) => {
var cursor = event.target.result;
if (cursor) {
createMsgElement(cursor.value);
cursor.continue();
} else {
resolve("Read Group Msgs from IDB");
}
};
db.close();
};
});
}
function storeMsg(data) { function storeMsg(data) {
var idb = indexedDB.open("FLO_Chat"); var idb = indexedDB.open("FLO_Chat");
idb.onerror = (event) => { idb.onerror = (event) => {
@ -722,19 +698,6 @@ function storeMsg(data) {
}; };
} }
function storeGroupMsg(data) {
var idb = indexedDB.open("FLO_Chat");
idb.onerror = (event) => {
console.log("Error in opening IndexedDB!");
};
idb.onsuccess = (event) => {
var db = event.target.result;
var obs = db.transaction("groupMsg", "readwrite").objectStore("groupMsg");
obs.add(data);
db.close();
};
}
function storeSuperNodeMsg(data) { function storeSuperNodeMsg(data) {
var idb = indexedDB.open("FLO_Chat", 2); var idb = indexedDB.open("FLO_Chat", 2);
idb.onerror = (event) => { idb.onerror = (event) => {
@ -780,11 +743,12 @@ function displayContacts() {
createLi.innerHTML = `<div class="col-sm-11 col-xs-11 sideBar-main"> createLi.innerHTML = `<div class="col-sm-11 col-xs-11 sideBar-main">
<div class="row"> <div class="row">
<div class="col-sm-12 col-xs-12 sideBar-name"> <div class="col-sm-12 col-xs-12 sideBar-name">
<span class="name-meta">${contacts[floID].name}</span><br/> <span class="name-meta"></span><br/>
<span class="time-meta">@${floID}</span> <span class="time-meta">@${floID}</span>
</div> </div>
</div> </div>
</div>` </div>`
createLi.querySelector("span.name-meta").textContent = contacts[floID].name;
listElement.appendChild(createLi); listElement.appendChild(createLi);
} }
for (floID in groups) { for (floID in groups) {
@ -795,11 +759,12 @@ function displayContacts() {
createLi.innerHTML = `<div class="col-sm-11 col-xs-11 sideBar-main"> createLi.innerHTML = `<div class="col-sm-11 col-xs-11 sideBar-main">
<div class="row"> <div class="row">
<div class="col-sm-12 col-xs-12 sideBar-name"> <div class="col-sm-12 col-xs-12 sideBar-name">
<span class="name-meta">${groups[floID].name}</span><br/> <span class="name-meta"></span><br/>
<span class="time-meta">#${floID}</span> <span class="time-meta">#${floID}</span>
</div> </div>
</div> </div>
</div>` </div>`
createLi.querySelector("span.name-meta").textContent = groups[floID].name;
listElement.appendChild(createLi); listElement.appendChild(createLi);
} }
} }
@ -858,7 +823,7 @@ function processIncomingData(data) {
type: "R" type: "R"
} }
createMsgElement(msgInfo); createMsgElement(msgInfo);
storeGroupMsg(msgInfo); storeMsg(msgInfo);
} else if (data.groupMsg !== undefined && data.groupMsg.group in groups) { } else if (data.groupMsg !== undefined && data.groupMsg.group in groups) {
if (!(groups[data.groupMsg.group].members.includes(data.from))) if (!(groups[data.groupMsg.group].members.includes(data.from)))
return return
@ -873,11 +838,11 @@ function processIncomingData(data) {
type: "R" type: "R"
} }
createMsgElement(msgInfo); createMsgElement(msgInfo);
storeGroupMsg(msgInfo); storeMsg(msgInfo)
} else if (data.newGroup !== undefined) { } else if (data.newGroup !== undefined) {
var groupInfoStr = floOpt.decryptData(data.newGroup.groupInfo.secret, data.newGroup.groupInfo.pubVal, privKey) var groupInfoStr = floOpt.decryptData(data.newGroup.groupInfo.secret, data.newGroup.groupInfo.pubVal, privKey)
var groupInfo = JSON.parse(groupInfoStr); var groupInfo = JSON.parse(groupInfoStr);
if (floOpt.verifyData(groupInfoStr, data.newGroup.sign, contacts[groupInfo.creator].pubKey)) { if (floOpt.verifyData(groupInfoStr, data.newGroup.sign, contacts[groupInfo.creator].pubKey) && floOpt.verifyPrivKey(groupInfo.privKey, groupInfo.floID)) {
groups[groupInfo.floID] = groupInfo; groups[groupInfo.floID] = groupInfo;
searchIndex.add(groupInfo.floID, groupInfo.name + ' #' + groupInfo.floID); searchIndex.add(groupInfo.floID, groupInfo.name + ' #' + groupInfo.floID);
storeGroup(groupInfoStr, groupInfo.floID); storeGroup(groupInfoStr, groupInfo.floID);
@ -937,7 +902,7 @@ function createMsgElement(msgInfo) {
var msghd = ''; var msghd = '';
} else { } else {
var msgEl = document.getElementById(msgInfo.groupID); var msgEl = document.getElementById(msgInfo.groupID);
var msghd = `<b>${msgInfo.sender}</b>`; var msghd = `<b>${msgInfo.sender}</b><br/>`;
} }
if (!msgEl) if (!msgEl)
return; return;
@ -946,13 +911,14 @@ function createMsgElement(msgInfo) {
msgdiv.innerHTML = `<div class="col-sm-12 message-main-${type[msgInfo.type]}"> msgdiv.innerHTML = `<div class="col-sm-12 message-main-${type[msgInfo.type]}">
<div class="${type[msgInfo.type]}"> <div class="${type[msgInfo.type]}">
<span class="message-text"> <span class="message-text">
${msghd}<br/>${msgInfo.text} ${msghd}<pre></pre>
</span> </span>
<span class="message-time pull-right"> <span class="message-time pull-right">
${getTime(msgInfo.time)} ${getTime(msgInfo.time)}
</span> </span>
</div> </div>
</div>`; </div>`;
msgdiv.querySelector("pre").textContent = msgInfo.text;
msgEl.appendChild(msgdiv); msgEl.appendChild(msgdiv);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
@ -1014,7 +980,7 @@ function changeReceiver(param) {
document.getElementById(receiverID).style.display = 'none'; document.getElementById(receiverID).style.display = 'none';
//console.log(param.getAttribute("name")); //console.log(param.getAttribute("name"));
receiverID = param.getAttribute("name"); receiverID = param.getAttribute("name");
document.getElementById('recipient-floID').innerHTML = receiverID; document.getElementById('recipient-floID').textContent = receiverID;
receiverStatus(false) receiverStatus(false)
document.getElementById(receiverID).style.display = 'block'; document.getElementById(receiverID).style.display = 'block';
document.getElementById("groupOptions").style.display = 'none'; document.getElementById("groupOptions").style.display = 'none';
@ -1159,7 +1125,7 @@ function sendGroupMsg(msg, time, sign) {
type: "S" type: "S"
} }
createMsgElement(msgInfo); createMsgElement(msgInfo);
storeGroupMsg(msgInfo); storeMsg(msgInfo);
} }
function sendStoredSuperNodeMsgs(floID) { function sendStoredSuperNodeMsgs(floID) {
@ -1261,11 +1227,12 @@ function createGroupDisplay(groupInfo) {
createLi.innerHTML = `<div class="col-sm-11 col-xs-11 sideBar-main"> createLi.innerHTML = `<div class="col-sm-11 col-xs-11 sideBar-main">
<div class="row"> <div class="row">
<div class="col-sm-12 col-xs-12 sideBar-name"> <div class="col-sm-12 col-xs-12 sideBar-name">
<span class="name-meta">${groupInfo.name}</span><br/> <span class="name-meta"></span><br/>
<span class="time-meta">#${groupInfo.floID}</span> <span class="time-meta">#${groupInfo.floID}</span>
</div> </div>
</div> </div>
</div>`; </div>`;
createLi.querySelector("span.name-meta").textContent = groupInfo.name;
document.getElementById('contact-display').appendChild(createLi); document.getElementById('contact-display').appendChild(createLi);
var createEl = document.createElement('div'); var createEl = document.createElement('div');
@ -1496,7 +1463,7 @@ function customCheckList(userList, ignoreList, okBtnVal = "Ok", okBtnType = "suc
var okButton = dialog.querySelector('button.ok'); var okButton = dialog.querySelector('button.ok');
var cancelButton = dialog.querySelector('button.cancel'); var cancelButton = dialog.querySelector('button.cancel');
okButton.setAttribute("class", `ok btn btn-${okBtnType}`); okButton.setAttribute("class", `ok btn btn-${okBtnType}`);
okButton.innerHTML = okBtnVal; okButton.textContent = okBtnVal;
var grpNameInput = dialog.querySelector('input.grpName') var grpNameInput = dialog.querySelector('input.grpName')
grpNameInput.style.display = (okBtnVal === "Create Group" ? "block" : "none"); grpNameInput.style.display = (okBtnVal === "Create Group" ? "block" : "none");
grpNameInput.value = ''; grpNameInput.value = '';
@ -1508,11 +1475,11 @@ function customCheckList(userList, ignoreList, okBtnVal = "Ok", okBtnType = "suc
listEl.setAttribute('class', "btn btn-default listLabel"); listEl.setAttribute('class', "btn btn-default listLabel");
listEl.setAttribute('name', userList[i]); listEl.setAttribute('name', userList[i]);
listEl.innerHTML = ` listEl.innerHTML = `
<span>${contacts[userList[i]].name}<br/> <span></span><br/>
<sub>@${userList[i]}</sub> <sub>@${userList[i]}</sub>
</span>
<input type="checkbox" class="badgebox" value="${userList[i]}"> <input type="checkbox" class="badgebox" value="${userList[i]}">
<span class="badge">&check;</span>`; <span class="badge">&check;</span>`;
listEl.querySelector("span").textContent = contacts[userList[i]].name;
userChecklist.appendChild(listEl); userChecklist.appendChild(listEl);
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -43,12 +43,12 @@
<div class="row heading"> <div class="row heading">
<span class="name-meta"><b>FLO Whatsapp </b> </span> <span class="name-meta"><b>FLO Whatsapp </b> </span>
<!-- <!--
<div class="col-sm-1 col-xs-1 heading-dot pull-right"> <div class="col-sm-1 col-xs-1 heading-icon pull-right">
<i class="fa fa-ellipsis-v fa-2x pull-right" aria-hidden="true" onclick="min();"></i> <i class="fa fa-ellipsis-v fa-2x pull-right" aria-hidden="true" onclick="min();"></i>
</div> </div>
--> -->
<div class="col-sm-2 col-xs-2 heading-compose pull-right"> <div class="col-sm-2 col-xs-2 heading-icon pull-right">
<i class="fa fa-comments fa-2x pull-right" aria-hidden="true" onclick="createGroup();"></i> <i class="fa fa-comments fa-2x pull-right" aria-hidden="true" onclick="createGroup();"></i>
</div> </div>
<br /> <br />
@ -81,11 +81,11 @@
<!-- Heading --> <!-- Heading -->
<div class="row heading"> <div class="row heading">
<div class="col-sm-8 col-xs-8 heading-name"> <div class="col-sm-8 col-xs-8 heading-name">
<span class="heading-name-meta"><span id="recipient-status">O</span> &nbsp;&nbsp;<span <span class="heading-name-meta"><span id="recipient-status">O</span> &nbsp;&nbsp;
id="recipient-floID">Select Contact</span> <span id="recipient-floID">Select Contact</span>
</span> </span>
</div> </div>
<div class="col-sm-1 col-xs-1 heading-dot pull-right dropdown" id="groupOptions"> <div class="col-sm-1 col-xs-1 heading-icon pull-right dropdown" id="groupOptions">
<i class="fa fa-ellipsis-v fa-2x pull-right" aria-hidden="true"></i> <i class="fa fa-ellipsis-v fa-2x pull-right" aria-hidden="true"></i>
<div class="dropdown-content"> <div class="dropdown-content">
<li onclick="addGroupMembers()">Add Members</li> <li onclick="addGroupMembers()">Add Members</li>
@ -106,20 +106,20 @@
<!-- Reply Box --> <!-- Reply Box -->
<div class="row reply"> <div class="row reply">
<!-- <!--
<div class="col-sm-1 col-xs-1 reply-emojis"> <div class="col-sm-1 col-xs-1 reply-icon">
<i class="fa fa-smile-o fa-2x"></i> <i class="fa fa-smile-o fa-2x"></i>
</div> </div>
--> -->
<div class="col-sm-9 col-xs-9 reply-main"> <div class="col-sm-9 col-xs-9 reply-main">
<input class="form-control" rows="1" id="sendMsgInput" placeholder="Type message" <textarea class="form-control" rows="1" id="sendMsgInput" placeholder="Type message"
autocomplete="off"></input> autocomplete="off"></textarea>
</div> </div>
<!-- <!--
<div class="col-sm-1 col-xs-1 reply-recording"> <div class="col-sm-1 col-xs-1 reply-icon">
<i class="fa fa-microphone fa-2x" aria-hidden="true"></i> <i class="fa fa-microphone fa-2x" aria-hidden="true"></i>
</div> </div>
--> -->
<div class="col-sm-1 col-xs-1 reply-send" onclick="sendMsg()"> <div class="col-sm-1 col-xs-1 reply-icon" onclick="sendMsg()">
<i class="fa fa-send fa-2x" aria-hidden="true"></i> <i class="fa fa-send fa-2x" aria-hidden="true"></i>
</div> </div>
</div> </div>

View File

@ -10,6 +10,20 @@ span {
box-sizing: border-box; box-sizing: border-box;
} }
pre {
color: inherit;
padding: 0 !important;
margin: 0 !important;
font: inherit;
background-color: transparent;
border: 0;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
body { body {
background: no-repeat fixed center; background: no-repeat fixed center;
background-size: cover; background-size: cover;
@ -78,18 +92,6 @@ body {
z-index: 1000; z-index: 1000;
} }
.heading-avatar {
padding: 0 !important;
cursor: pointer;
}
.heading-avatar-icon img {
border-radius: 50%;
height: 40px;
width: 40px;
}
.heading-name { .heading-name {
padding: 0 !important; padding: 0 !important;
cursor: pointer; cursor: pointer;
@ -115,36 +117,17 @@ body {
color: white; color: white;
} }
.heading-online { .heading-icon {
display: none;
padding: 0 5px;
font-size: 12px;
color: #93918f;
}
.heading-compose {
padding: 0; padding: 0;
} }
.heading-compose i { .heading-icon i {
text-align: center; text-align: center;
padding: 5px; padding: 5px;
color: #93918f; color: #93918f;
cursor: pointer; cursor: pointer;
} }
.heading-dot {
padding: 0;
margin-left: 10px;
}
.heading-dot i {
text-align: right;
padding: 5px;
color: #93918f;
cursor: pointer;
}
.searchBox { .searchBox {
padding: 0 !important; padding: 0 !important;
margin: 0 !important; margin: 0 !important;
@ -346,30 +329,28 @@ body {
.message-main-receiver { .message-main-receiver {
/*padding: 10px 20px;*/ /*padding: 10px 20px;*/
max-width: 60%; max-width: 80%;
height: auto; height: auto;
} }
.message-main-sender { .message-main-sender {
padding: 3px 20px !important; padding: 3px 20px !important;
margin-left: 40% !important; margin-left: 20% !important;
max-width: 60%; max-width: 80%;
height: auto; height: auto;
} }
.message-text { .message-text {
margin: 0 !important; margin: 0 !important;
padding: 5px !important; padding: 0px !important;
word-wrap: break-word;
font-weight: 200; font-weight: 200;
font-size: 14px; font-size: 14px;
padding-bottom: 0 !important;
height: auto; height: auto;
word-break: break-word;
} }
.message-time { .message-time {
margin: 0 !important; margin: 0 !important;
margin-left: 50px !important;
font-size: 12px; font-size: 12px;
text-align: right; text-align: right;
color: #9a9a9a; color: #9a9a9a;
@ -383,8 +364,6 @@ body {
border-radius: 10px 10px 10px 0; border-radius: 10px 10px 10px 0;
background: #ffffff; background: #ffffff;
font-size: 12px; font-size: 12px;
word-wrap: break-word;
display: inline-block;
height: auto; height: auto;
} }
@ -396,8 +375,6 @@ body {
border-radius: 10px 10px 0 10px; border-radius: 10px 10px 0 10px;
padding: 4px 10px 7px !important; padding: 4px 10px 7px !important;
font-size: 12px; font-size: 12px;
display: inline-block;
word-wrap: break-word;
} }
/*Reply*/ /*Reply*/
@ -411,33 +388,11 @@ body {
z-index: 1000; z-index: 1000;
} }
.reply-emojis { .reply-icon {
padding: 5px !important; padding: 5px !important;
} }
.reply-emojis i { .reply-icon i {
text-align: center;
padding: 5px 5px 5px 5px !important;
color: #93918f;
cursor: pointer;
}
.reply-recording {
padding: 5px !important;
}
.reply-recording i {
text-align: center;
padding: 5px !important;
color: #93918f;
cursor: pointer;
}
.reply-send {
padding: 5px !important;
}
.reply-send i {
text-align: center; text-align: center;
padding: 5px !important; padding: 5px !important;
color: #93918f; color: #93918f;
@ -448,7 +403,7 @@ body {
padding: 2px 5px !important; padding: 2px 5px !important;
} }
.reply-main input { .reply-main textarea {
width: 100%; width: 100%;
resize: none; resize: none;
overflow: hidden; overflow: hidden;
@ -459,9 +414,10 @@ body {
box-shadow: none; box-shadow: none;
height: 100%; height: 100%;
font-size: 16px; font-size: 16px;
overflow: hidden;
} }
.reply-main input:focus { .reply-main textarea:focus {
outline: none; outline: none;
border: none; border: none;
text-indent: 5px; text-indent: 5px;
@ -483,30 +439,11 @@ body {
font-size: 2.3em !important; font-size: 2.3em !important;
} }
.heading-avatar { .heading-icon {
padding: 0 !important;
}
.heading-avatar-icon img {
height: 50px;
width: 50px;
}
.heading-compose {
padding: 5px !important; padding: 5px !important;
} }
.heading-compose i { .heading-icon i {
color: #fff;
cursor: pointer;
}
.heading-dot {
padding: 5px !important;
margin-left: 10px !important;
}
.heading-dot i {
color: #fff; color: #fff;
cursor: pointer; cursor: pointer;
} }
@ -519,16 +456,6 @@ body {
height: 80px; height: 80px;
} }
.sideBar-avatar {
text-align: left;
padding: 0 8px !important;
}
.avatar-icon img {
height: 55px;
width: 55px;
}
.sideBar-main { .sideBar-main {
padding: 0 !important; padding: 0 !important;
} }
@ -564,9 +491,7 @@ body {
padding: 0 !important; padding: 0 !important;
margin: 0 !important; margin: 0 !important;
height: 100%; height: 100%;
/*width: 100%;*/
border-left: 1px solid rgba(0, 0, 0, .08); border-left: 1px solid rgba(0, 0, 0, .08);
/*overflow-y: auto;*/
} }
.message { .message {
@ -577,11 +502,11 @@ body {
height: 70px; height: 70px;
} }
.reply-emojis { .reply-icon {
padding: 5px 0 !important; padding: 5px 0 !important;
} }
.reply-emojis i { .reply-icon i {
padding: 5px 2px !important; padding: 5px 2px !important;
font-size: 1.8em !important; font-size: 1.8em !important;
} }
@ -594,24 +519,6 @@ body {
padding: 8px !important; padding: 8px !important;
font-size: 18px; font-size: 18px;
} }
.reply-recording {
padding: 5px 0 !important;
}
.reply-recording i {
padding: 5px 0 !important;
font-size: 1.8em !important;
}
.reply-send {
padding: 5px 0 !important;
}
.reply-send i {
padding: 5px 2px 5px 0 !important;
font-size: 1.8em !important;
}
} }
.badgebox { .badgebox {