Compare commits
22 Commits
8bac60074a
...
211b8402a1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
211b8402a1 | ||
|
|
7e946b958d | ||
|
|
4b06885688 | ||
|
|
a8f15adeec | ||
|
|
6ab215b346 | ||
|
|
a3125b8ed1 | ||
|
|
9a9e0d0664 | ||
|
|
72778f8c4a | ||
|
|
29bccda9ae | ||
|
|
196ae1c30e | ||
|
|
24c4542d92 | ||
|
|
ed9e88ad2c | ||
|
|
ee7776f877 | ||
|
|
059a5e38de | ||
|
|
35cbdf0ac1 | ||
|
|
e7e701cbad | ||
|
|
3456d372f0 | ||
|
|
e8c7680050 | ||
|
|
20c8f124e5 | ||
|
|
629f3fb089 | ||
|
|
2c405ec3d3 | ||
|
|
174b9fddf6 |
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*.gitattributes linguist-vendored
|
||||
util/mongoose.c linguist-vendored
|
||||
util/mongoose.h linguist-vendored
|
||||
app/web/init.js linguist-vendored
|
||||
|
||||
.gitattributes export-ignore
|
||||
78
README.md
Normal file
78
README.md
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
# FLO_Twitter
|
||||
This is a peer-peer Twitter like application totally encrypted in transit without needing a central server.
|
||||
Current web based technologies have a inbound address problem namely ordinary web users do not have a fixed IP, so it is not easy to establish connection to them.
|
||||
We propose to solve that problem by using TOR addresses for ordinary users which can provide a fixed inbound routable address on Internet for everyone including for those on dynamic IPs.
|
||||
The TOR Project very easy to connect to a TOR based service. We believe it can form a stable architecture for peer to peer services.
|
||||
Another limitation of current web based technologies is ordinary users cannot allocate a fixed port using just their web browsers for inbound connections. So every user will need to run their own webservers on which they can receive chat messages. We could not find a way to eliminate webservers. But we have found a very simple webserver called Mongoose Webserver, where a user can invoke a fixed port based service on click of a single button.
|
||||
To facilitate globally unique identification of every peer, we propose to use FLO Blockchain IDs. Then user can then attach his TOR address onto his FLO id inside the FLO Blockchain.
|
||||
Since the blockchain data is immutable, it will provide a continous uniterruptable source of connection information based on user's FLO ID.
|
||||
|
||||
## Requirements
|
||||
1. Ubuntu OS or its derivatives (or) Andoid
|
||||
2. Onion Browser ([Brave]([https://brave.com/](https://brave.com/)) for Desktop or [Tor Browser](https://play.google.com/store/apps/details?id=org.torproject.torbrowser)/[Orbot](https://play.google.com/store/apps/details?id=org.torproject.android) for Android)
|
||||
3. FLO ID
|
||||
|
||||
## Installation
|
||||
The FLO_Twitter WSS can either run on Ubuntu OS or Android
|
||||
|
||||
#### Ubuntu
|
||||
1. Download or Clone this repo
|
||||
|
||||
git clone https://github.com/ranchimall/FLO_Twitter
|
||||
2. Open terminal in the directory
|
||||
|
||||
cd FLO_Twitter
|
||||
3. Run `autogen.sh` to install tor, configure tor and create the start file.
|
||||
|
||||
sudo ./autogen.sh
|
||||
#### Android
|
||||
1. Download and extract [FLO_Twitter](https://github.com/ranchimall/FLO_Twitter/archive/master.zip) to Internal Storage Home (`/storage/emulated/0/`) **Make sure it is extracted as FLO_Twitter** (if its extracted as FLO_Twitter-master.. rename it to FLO_Twitter). The extracted path should be `/storage/emulated/0/FLO_Twitter/`
|
||||
2. Install the app using `util/FLO_Twitter.apk`
|
||||
3. Install [Orbot](https://play.google.com/store/apps/details?id=org.torproject.android)
|
||||
4. Open Orbot
|
||||
* Open Options (`:`) -> Hidden Services -> Hosted Services
|
||||
* Add New Service with local port and onion port as `3232` and save it
|
||||
* Enable `VPN Mode` and open :gear:
|
||||
* add `FLO_Twitter` and the browser ([Orfox](https://play.google.com/store/apps/details?id=info.guardianproject.orfox) ll be enabled by default)
|
||||
|
||||
|
||||
## Usage
|
||||
The FLO_Twitter WSS should be started and then you can access it from any device.
|
||||
### Starting FLO_Twitter WSS
|
||||
#### Ubuntu
|
||||
1. run start in terminal (in the `FLO_Twitter` directory). Enter a strong `server-password` which will be used to connect in client.
|
||||
|
||||
./start <server-password>
|
||||
2. `FLO_Twitter WSS` and `Tor` will be started automatically. (Hidden_service will be created on the 1st time). Your hidden_service `onion address` will be displayed in information dialog box
|
||||
#### Android
|
||||
1. Open `FLO_Twitter` app
|
||||
2. Open `Orbot` app and `Start`
|
||||
* Open Options (`:`) -> Hidden Services -> Hosted Services and Copy the `onion address`
|
||||
|
||||
**Bookmark the onion address (`<url>.onion:3232`) in the browser**
|
||||
|
||||
### Accessing the client webpages
|
||||
1. Open the `onion address` (`<url>.onion:3232`) in Onion browser (`Brave` for desktop or `Tor Browser`/`Orbot`-enabled browsers for android) [not necessary to be on the same device]
|
||||
3. Enter the `server-password` and click `Connect`. Access will be granted when entered correct server-password
|
||||
4. Enter the `Username` and `FLO_ID privKey`. (FLO_ID privKey can be generated using [flo_webWallet](https://flo-webwallet.duckdns.org/) or [flo-core wallet](https://github.com/ranchimall/FLO-wallet-core)). **FLO_ID private key is important! DO NOT lose or share it!**
|
||||
5. Click `SignIn`. New users and users changing the onion address or username will require to register in the FLO blockchain. (The registration is automatic, just click on ok when prompted). A minimum amount will be required to register [Balance recharge can be done using https://international.bittrex.com/]. Upon successful registration the txid will be alerted
|
||||
* **Now your are logged on to FLO_Twitter.**
|
||||
### FLO_Twitter Features
|
||||
* Home
|
||||
* Enter Tweet and post.
|
||||
* Watch posted tweeted by users you follow
|
||||
* Profile
|
||||
* Click on the profile list in right-side column to navigate to the respective user profiles
|
||||
* Watch tweets posted by an user
|
||||
* Follow or Unfollow the user
|
||||
* Message
|
||||
* Click on the profile list to open the user chat
|
||||
* Send and Receive messages with the other users
|
||||
|
||||
**NOTE:**
|
||||
* All Tweets are Signed using the Tweeter's FLO_ID PrivKey
|
||||
* Hence all Tweets are verified by the Tweeter's PubKey
|
||||
* All Messages are signed by the Sender's Privkey and Encrypted with Reciver's PubKey
|
||||
* Only the Receiver can decrypt the message using their own PrivKey
|
||||
* Message's signature is verified using Sender's PubKey
|
||||
480
app/web/app.js
480
app/web/app.js
@ -7,6 +7,15 @@ if (!window.indexedDB) {
|
||||
window.alert("Your browser doesn't support a stable version of IndexedDB.")
|
||||
}
|
||||
|
||||
const adminID = "FMabh7gTSyKPAb2Wi9sK5CBhV8nVFk783i";
|
||||
|
||||
if(blockchain == "FLO")
|
||||
var api_url = `https://flosight.duckdns.org/`;
|
||||
else if(blockchain == "FLO_TEST")
|
||||
var api_url = `https://testnet-flosight.duckdns.org/`;
|
||||
|
||||
var supernodeKBucket;
|
||||
var superNodeList;
|
||||
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
||||
|
||||
var encrypt = {
|
||||
@ -14,7 +23,7 @@ var encrypt = {
|
||||
p: BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16),
|
||||
|
||||
exponent1: function () {
|
||||
return encrypt.p.add(BigInteger.ONE).divide(BigInteger("4"))
|
||||
return this.p.add(BigInteger.ONE).divide(BigInteger("4"))
|
||||
},
|
||||
|
||||
calculateY: function (x) {
|
||||
@ -147,7 +156,7 @@ var encrypt = {
|
||||
getPubKeyHex: function(privateKeyHex){
|
||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||
if(key.priv == null){
|
||||
alert("Invalid Private key");
|
||||
console.log("Invalid Private key");
|
||||
return;
|
||||
}
|
||||
key.setCompressed(true);
|
||||
@ -159,6 +168,34 @@ var encrypt = {
|
||||
var floID = key.getBitcoinAddress();
|
||||
return floID;
|
||||
},
|
||||
validateAddr: function (value) {
|
||||
try{
|
||||
var addr = new Bitcoin.Address(value);
|
||||
if (addr == value)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}catch(error){
|
||||
return false;
|
||||
}
|
||||
},
|
||||
verifyWIF: function (wif,addr){
|
||||
try {
|
||||
var key = new Bitcoin.ECKey(wif);
|
||||
if(key.priv == null){
|
||||
return false;
|
||||
}
|
||||
key.setCompressed(true);
|
||||
var bitcoinAddress = key.getBitcoinAddress();
|
||||
if (addr == bitcoinAddress)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
sign: function (msg, privateKeyHex) {
|
||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||
key.setCompressed(true);
|
||||
@ -255,14 +292,354 @@ function getTime(time){
|
||||
|
||||
function logout(){
|
||||
sessionStorage.clear();
|
||||
location.reload();
|
||||
window.location.href = "index.html";
|
||||
}
|
||||
|
||||
var supernodeKBucket;
|
||||
var superNodeList;
|
||||
/*Refresh profile and superNode data from API */
|
||||
function ajax(method, uri){
|
||||
var request = new XMLHttpRequest();
|
||||
var url = `${api_url}/${uri}`
|
||||
console.log(url)
|
||||
var result;
|
||||
request.open(method,url , false);
|
||||
request.onload = function () {
|
||||
if (request.readyState == 4 && request.status == 200)
|
||||
result = this.response;
|
||||
else {
|
||||
console.log('error');
|
||||
result = false;
|
||||
}
|
||||
};
|
||||
request.send();
|
||||
console.log(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function reloadInitData(){
|
||||
refreshAPIdata().then(result => {
|
||||
console.log(result);
|
||||
sessionStorage.profiles = JSON.stringify(profiles);
|
||||
sessionStorage.superNodeList = JSON.stringify(Array.from(superNodeList));
|
||||
kBucketObj.launchKBucket().then(result => {
|
||||
console.log(result)
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function refreshAPIdata(){
|
||||
return new Promise((resolve,reject) => {
|
||||
var addr = adminID;
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var lastTx = db.transaction('lastTx', "readwrite").objectStore('lastTx');
|
||||
console.log(addr);
|
||||
new Promise((res,rej) => {
|
||||
var lastTxReq = lastTx.get(addr);
|
||||
lastTxReq.onsuccess = (event) => {
|
||||
var lasttx = event.target.result;
|
||||
if(lasttx === undefined){
|
||||
lasttx = 0;
|
||||
}
|
||||
res(lasttx);
|
||||
}
|
||||
}).then(lasttx => {
|
||||
var response = ajax("GET",`api/addrs/${addr}/txs`);
|
||||
var nRequired = JSON.parse(response).totalItems - lasttx;
|
||||
console.log(nRequired);
|
||||
while(true && nRequired){
|
||||
var response = ajax("GET",`api/addrs/${addr}/txs?from=0&to=${nRequired}`);
|
||||
response = JSON.parse(response);
|
||||
if (nRequired + lasttx != response.totalItems ){
|
||||
nRequired = response.totalItems - lasttx;
|
||||
continue;
|
||||
}
|
||||
response.items.reverse().forEach(tx => {
|
||||
try {
|
||||
if (tx.vin[0].addr == addr){
|
||||
var data = JSON.parse(tx.floData).FLO_Tweet_SuperNode;
|
||||
if(data !== undefined){
|
||||
if(data.addNodes)
|
||||
for(var i=0; i<data.addNodes.length; i++)
|
||||
superNodeList.add(data.addNodes[i])
|
||||
if(data.removeNodes)
|
||||
for(var i=0; i<data.removeNodes.length; i++)
|
||||
superNodeList.delete(data.removeNodes[i])
|
||||
storeSuperNodeData(data).then(function (response) {
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}else{
|
||||
var data = JSON.parse(tx.floData).FLO_Tweet;
|
||||
if(data !== undefined){
|
||||
if(encrypt.getFLOIDfromPubkeyHex(data.pubKey)!=tx.vin[0].addr)
|
||||
throw("PublicKey doesnot match with floID")
|
||||
data = {floID : tx.vin[0].addr, onionAddr : data.onionAddr, name : data.name, pubKey:data.pubKey};
|
||||
profiles[data.floID] = {onionAddr : data.onionAddr, name : data.name, pubKey : data.pubKey};
|
||||
storeProfile(data).then(function (response) {
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
//console.log(error)
|
||||
}
|
||||
});
|
||||
var obs = db.transaction('lastTx', "readwrite").objectStore('lastTx');
|
||||
obs.put(response.totalItems,addr);
|
||||
break;
|
||||
}
|
||||
db.close();
|
||||
resolve('retrived data from API');
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/* Common IDB functions */
|
||||
function storeProfile(data){
|
||||
return new Promise((resolve,reject) => {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = (event) => { console.log("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction('profiles', "readwrite").objectStore('profiles');
|
||||
objectRequest = obs.put(data);
|
||||
objectRequest.onerror = (event) => { reject(Error('Error occured: Unable to store data'))};
|
||||
objectRequest.onsuccess = (event) => { resolve('Data saved OK') };
|
||||
db.close();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function storeSuperNodeData(data){
|
||||
return new Promise((resolve,reject) => {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!"); };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction('superNodes', "readwrite").objectStore('superNodes');
|
||||
if(data.addNodes)
|
||||
for(var i=0; i<data.addNodes.length; i++)
|
||||
obs.add(true,data.addNodes[i])
|
||||
if(data.removeNodes)
|
||||
for(var i=0; i<data.removeNodes.length; i++)
|
||||
obs.delete(data.removeNodes[i])
|
||||
db.close();
|
||||
resolve('Updated superNodes list in IDB');
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function storeTweet(data,tid){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = (event) => { console.log("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("tweets", "readwrite").objectStore("tweets");
|
||||
data.tweetID = `${data.time}_${data.floID}`;
|
||||
data.tid = tid;
|
||||
obs.add(data);
|
||||
var obsL = db.transaction("lastTweet", "readwrite").objectStore("lastTweet");
|
||||
obsL.put(tid,data.floID);
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
|
||||
function storeMsg(data){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = (event) => { console.log("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("messages", "readwrite").objectStore("messages");
|
||||
data.msgID = `${data.time}_${data.floID}`;
|
||||
obs.add(data);
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
|
||||
/* SuperNode functions */
|
||||
function sendDataToSuperNode(floID,data){
|
||||
kBucketObj.determineClosestSupernode(floID).then(result=>{
|
||||
var superNodeWS = new WebSocket("ws://"+profiles[result[0].floID].onionAddr+"/ws");
|
||||
superNodeWS.onopen = function(ev){
|
||||
console.log(`Connected to ${floID}'s SuperNode!`);
|
||||
superNodeWS.send(data);
|
||||
};
|
||||
superNodeWS.onerror = function(ev) {console.log(`${floid}'s SuperNode is offline!`);};
|
||||
superNodeWS.onclose = function(ev) {console.log(`Disconnected from ${floid}'s SuperNode!`);};
|
||||
}).catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
}
|
||||
|
||||
function superNodeMode(data){
|
||||
if(data.reqNewTweets){
|
||||
kBucketObj.determineClosestSupernode(data.floID).then(result => {
|
||||
if(result[0].floID == selfID)
|
||||
SuperNode_sendTweetsFromIDB(data.floID,data.tid,data.requestor);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}else if(data.newSuperNodeTweet){
|
||||
kBucketObj.determineClosestSupernode(data.floID).then(result => {
|
||||
if(result[0].floID == selfID)
|
||||
SuperNode_storeSuperNodeTweet(data.data,data.tid);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}else if(data.viaSuperNodeMsg){
|
||||
kBucketObj.determineClosestSupernode(data.to).then(result => {
|
||||
if(result[0].floID == selfID)
|
||||
SuperNode_storeViaSuperNodeMsg(data.from,data.to,data.data);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}else if(data.viaMsgreq){
|
||||
kBucketObj.determineClosestSupernode(data.floID).then(result => {
|
||||
if(result[0].floID == selfID)
|
||||
SuperNode_sendviaMsgFromIDB(data.floID);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function SuperNode_sendTweetsFromIDB(floID,tid,requestor){
|
||||
return new Promise((resolve,reject) => {
|
||||
var requestorWS = new WebSocket("ws://"+profiles[requestor].onionAddr+"/ws");
|
||||
requestorWS.onopen = (event) => {
|
||||
console.log(`sending ${floID} tweets to ${requestor} Server!`);
|
||||
var idb = indexedDB.open("FLO_Tweet",2);
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onupgradeneeded = (event) => {
|
||||
var objectStore1 = event.target.result.createObjectStore("superNodeTweet",{ keyPath: 'tweetID' });
|
||||
objectStore1.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore1.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore1.createIndex('data', 'data', { unique: false });
|
||||
var objectStore2 = event.target.result.createObjectStore("viaSuperNodeMsg",{ keyPath: 'id',autoIncrement:true });
|
||||
objectStore2.createIndex('from', 'from', { unique: false });
|
||||
objectStore2.createIndex('to', 'to', { unique: false });
|
||||
objectStore2.createIndex('data', 'data', { unique: false });
|
||||
}
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("superNodeTweet", "readwrite").objectStore("superNodeTweet");
|
||||
var curReq = obs.openCursor();
|
||||
curReq.onsuccess = (event) => {
|
||||
var cursor = event.target.result;
|
||||
if(cursor) {
|
||||
if(cursor.value.floID == floID && cursor.value.tid > tid){
|
||||
data = JSON.stringify({fromSuperNode:true, floID:cursor.value.floID,tid:cursor.value.tid,data:cursor.value.data})
|
||||
requestorWS.send(data);
|
||||
}
|
||||
cursor.continue();
|
||||
}else{
|
||||
resolve("Displayed Tweets from IDB!");
|
||||
}
|
||||
}
|
||||
curReq.onerror = (event) => { reject("Error in Reading tweets from IDB!") }
|
||||
db.close();
|
||||
};
|
||||
};
|
||||
requestorWS.onerror = (event) => { console.log(`${requestor} Server is offline!`) };
|
||||
requestorWS.onclose = (event) => { console.log(`Disconnected from ${requestor} Server!`) };
|
||||
});
|
||||
}
|
||||
|
||||
function SuperNode_storeSuperNodeTweet(data,tid){
|
||||
var idb = indexedDB.open("FLO_Tweet",2);
|
||||
idb.onerror = (event) => { console.log("Error in opening IndexedDB!") };
|
||||
idb.onupgradeneeded = (event) => {
|
||||
var objectStore1 = event.target.result.createObjectStore("superNodeTweet",{ keyPath: 'tweetID' });
|
||||
objectStore1.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore1.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore1.createIndex('data', 'data', { unique: false });
|
||||
var objectStore2 = event.target.result.createObjectStore("viaSuperNodeMsg",{ keyPath: 'id',autoIncrement:true });
|
||||
objectStore2.createIndex('from', 'from', { unique: false });
|
||||
objectStore2.createIndex('to', 'to', { unique: false });
|
||||
objectStore2.createIndex('data', 'data', { unique: false });
|
||||
}
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("superNodeTweet", "readwrite").objectStore("superNodeTweet");
|
||||
var parsedData = JSON.parse(data);
|
||||
var tweetID = ''+parsedData.floID+'_'+parsedData.time;
|
||||
obs.add({tweetID:tweetID,floID:parsedData.floID,tid:tid,data:data});
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
|
||||
function SuperNode_storeViaSuperNodeMsg(from,to,data){
|
||||
var idb = indexedDB.open("FLO_Tweet",2);
|
||||
idb.onerror = (event) => { console.log("Error in opening IndexedDB!") };
|
||||
idb.onupgradeneeded = (event) => {
|
||||
var objectStore1 = event.target.result.createObjectStore("superNodeTweet",{ keyPath: 'tweetID' });
|
||||
objectStore1.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore1.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore1.createIndex('data', 'data', { unique: false });
|
||||
var objectStore2 = event.target.result.createObjectStore("viaSuperNodeMsg",{ keyPath: 'id',autoIncrement :true });
|
||||
objectStore2.createIndex('from', 'from', { unique: false });
|
||||
objectStore2.createIndex('to', 'to', { unique: false });
|
||||
objectStore2.createIndex('data', 'data', { unique: false });
|
||||
}
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("viaSuperNodeMsg", "readwrite").objectStore("viaSuperNodeMsg");
|
||||
obs.add({from:from,to:to,data:data});
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
|
||||
function SuperNode_sendviaMsgFromIDB(floID){
|
||||
var receiverWS = new WebSocket("ws://"+profiles[floID].onionAddr+"/ws");
|
||||
receiverWS.onopen = (event) => {
|
||||
var idb = indexedDB.open("FLO_Tweet",2);
|
||||
idb.onerror = (event) => { console.log("Error in opening IndexedDB!") };
|
||||
idb.onupgradeneeded = (event) => {
|
||||
var objectStore1 = event.target.result.createObjectStore("superNodeTweet",{ keyPath: 'tweetID' });
|
||||
objectStore1.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore1.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore1.createIndex('data', 'data', { unique: false });
|
||||
var objectStore2 = event.target.result.createObjectStore("viaSuperNodeMsg",{ keyPath: 'id',autoIncrement:true });
|
||||
objectStore2.createIndex('from', 'from', { unique: false });
|
||||
objectStore2.createIndex('to', 'to', { unique: false });
|
||||
objectStore2.createIndex('data', 'data', { unique: false });
|
||||
};
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("viaSuperNodeMsg", "readwrite").objectStore("viaSuperNodeMsg");
|
||||
obs.openCursor().onsuccess = (event) => {
|
||||
var cursor = event.target.result;
|
||||
if(cursor) {
|
||||
if(cursor.value.to == floID){
|
||||
receiverWS.send(cursor.value.data);
|
||||
cursor.delete();
|
||||
}
|
||||
cursor.continue();
|
||||
}else{
|
||||
console.log('Sent All messages to '+floID)
|
||||
}
|
||||
}
|
||||
db.close();
|
||||
};
|
||||
};
|
||||
receiverWS.onerror = (event) => { console.log('Connection Error to '+floID) };
|
||||
receiverWS.onclose = (event) => { console.log('Disconnected from '+floID) };
|
||||
}
|
||||
|
||||
/* ---end of app.js --- */
|
||||
|
||||
/*Kademlia DHT K-bucket implementation as a binary tree.*/
|
||||
|
||||
if (typeof reactor == "undefined" || !reactor) {
|
||||
if (typeof reactor == "undefined" || !reactor) {
|
||||
(function () {
|
||||
|
||||
function Event(name) {
|
||||
@ -317,25 +694,6 @@ reactor.addEventListener('bucket_full', function (someObject) {
|
||||
console.log('Bucket full ' + someObject);
|
||||
});
|
||||
|
||||
/*
|
||||
//Sample Usage
|
||||
//Creating and defining the event
|
||||
reactor.registerEvent('big bang');
|
||||
reactor.addEventListener('big bang', function(someObject){
|
||||
console.log('This is big bang listener yo!'+ someObject.a);
|
||||
});
|
||||
|
||||
//Firing the event
|
||||
reactor.dispatchEvent('big bang');
|
||||
reactor.dispatchEvent('big bang',{a:1});
|
||||
reactor.dispatchEvent('big bang',{a:55});
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//Checking if existing NodeID can be used
|
||||
//This first block of if will initialize the configuration of KBucket
|
||||
//Add Events, Messaging between different K-Buckets, and attach relevant distributed data
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} array1
|
||||
@ -753,7 +1111,7 @@ reactor.dispatchEvent('big bang',{a:55});
|
||||
|
||||
kBucketObj = {
|
||||
|
||||
decodeBase58Address: function (blockchain, address) {
|
||||
decodeBase58Address: function (address) {
|
||||
let k = bitjs.Base58.decode(address)
|
||||
k.shift()
|
||||
k.splice(-4, 4)
|
||||
@ -764,12 +1122,12 @@ kBucketObj = {
|
||||
try {
|
||||
//const master_flo_pubKey = localbitcoinplusplus.master_configurations.masterFLOPubKey;
|
||||
const master_flo_addr = adminID;
|
||||
const SuKBucketId = this.floIdToKbucketId(crypto, master_flo_addr);
|
||||
const SuKBucketId = this.floIdToKbucketId(master_flo_addr);
|
||||
const SukbOptions = { localNodeId: SuKBucketId }
|
||||
supernodeKBucket = new BuildKBucket(SukbOptions);
|
||||
for(var i=0; i<superNodeList.length ; i++){
|
||||
this.addNewUserNodeInKbucket(crypto,superNodeList[i],supernodeKBucket)
|
||||
}
|
||||
var SNArray = Array.from(superNodeList);
|
||||
for(var i=0; i<SNArray.length ; i++)
|
||||
this.addNewUserNodeInKbucket(SNArray[i],supernodeKBucket)
|
||||
resolve('SuperNode KBucket formed');
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
@ -783,7 +1141,7 @@ kBucketObj = {
|
||||
try {
|
||||
let flo_id = bitjs.pubkey2address(pubKey);
|
||||
let kname = `SKBucket_${pubKey}`;
|
||||
const KBucketId = this.floIdToKbucketId(crypto, flo_id)
|
||||
const KBucketId = this.floIdToKbucketId(flo_id)
|
||||
const kbOptions = { localNodeId: KBucketId }
|
||||
window[kname] = new BuildKBucket(kbOptions);
|
||||
resolve(true);
|
||||
@ -800,18 +1158,18 @@ kBucketObj = {
|
||||
};
|
||||
KB.add(contact)
|
||||
},
|
||||
addNewUserNodeInKbucket: function(blockchain, address, KB=supernodeKBucket) {
|
||||
addNewUserNodeInKbucket: function(address, KB=supernodeKBucket) {
|
||||
let decodedId = address;
|
||||
try {
|
||||
decodedId = this.floIdToKbucketId(blockchain, address);
|
||||
decodedId = this.floIdToKbucketId(address);
|
||||
} catch(e) {
|
||||
decodedId = address;
|
||||
}
|
||||
const addNewUserNode = this.addContact(decodedId, address, KB);
|
||||
return {decodedId:decodedId, address:address};
|
||||
},
|
||||
floIdToKbucketId: function (blockchain, address) {
|
||||
const decodedId = this.decodeBase58Address(blockchain, address);
|
||||
floIdToKbucketId: function (address) {
|
||||
const decodedId = this.decodeBase58Address(address);
|
||||
const nodeIdBigInt = new BigInteger(decodedId, 16);
|
||||
const nodeIdBytes = nodeIdBigInt.toByteArrayUnsigned();
|
||||
const nodeIdNewInt8Array = new Uint8Array(nodeIdBytes);
|
||||
@ -835,7 +1193,7 @@ kBucketObj = {
|
||||
let pubKeyBytes = Crypto.util.hexToBytes(pubKey);
|
||||
return Crypto.util.bytesToBase64(pubKeyBytes);
|
||||
},
|
||||
restoreKbucket: function(flo_addr, blockchain="FLO_TEST", KB=KBucket) {
|
||||
restoreKbucket: function(flo_addr, KB=KBucket) {
|
||||
return new Promise((resolve, reject)=>{
|
||||
readAllDB('kBucketStore')
|
||||
.then(dbObject => {
|
||||
@ -846,7 +1204,7 @@ kBucketObj = {
|
||||
dbObject
|
||||
.filter(f=>!su_flo_addr_array.includes(f.data.id))
|
||||
.map(dbObj=>{
|
||||
this.addNewUserNodeInKbucket(blockchain, flo_addr, dbObj.data, KB);
|
||||
this.addNewUserNodeInKbucket(flo_addr, dbObj.data, KB);
|
||||
});
|
||||
} else {
|
||||
reject(`Failed to restore kBucket.`);
|
||||
@ -862,7 +1220,7 @@ kBucketObj = {
|
||||
let supernodeSeedsObj = JSON.parse(supernodeSeeds);
|
||||
|
||||
Object.entries(supernodeSeedsObj).map(seedObj=>{
|
||||
let kbuck = this.addNewUserNodeInKbucket(crypto, seedObj[1].kbucketId,
|
||||
let kbuck = this.addNewUserNodeInKbucket(seedObj[1].kbucketId,
|
||||
{ id: seedObj[1].kbucketId }, supernodeKBucket);
|
||||
});
|
||||
|
||||
@ -940,7 +1298,7 @@ kBucketObj = {
|
||||
}
|
||||
let isFloIdUint8 = flo_addr instanceof Uint8Array;
|
||||
if (!isFloIdUint8) {
|
||||
flo_addr = this.floIdToKbucketId(crypto, flo_addr);
|
||||
flo_addr = this.floIdToKbucketId(flo_addr);
|
||||
}
|
||||
const closestSupernode = supernodeKBucket.closest(flo_addr, n);
|
||||
resolve(closestSupernode);
|
||||
@ -951,49 +1309,5 @@ kBucketObj = {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
addClosestSupernodeInDB: function(flo_addr, KB=KBucket) {
|
||||
return new Promise(async (resolve, reject)=>{
|
||||
const supernodeSeeds = localbitcoinplusplus.master_configurations.supernodeSeeds;
|
||||
if (typeof supernodeSeeds !== "object") reject("Failed to get supernode seeds.");
|
||||
let supernodeSeedsObj = JSON.parse(supernodeSeeds);
|
||||
|
||||
Object.entries(supernodeSeedsObj).map(seedObj=>{
|
||||
console.log(seedObj);
|
||||
this.addNewUserNodeInKbucketAndDB(
|
||||
crypto, seedObj[1].kbucketId,
|
||||
{ id: seedObj[1].kbucketId });
|
||||
});
|
||||
|
||||
let primarySu = await this.determineClosestSupernode(flo_addr);
|
||||
let nearestSupernode = await this.determineClosestSupernode(flo_addr="", n=1, supernodeKBucket, primarySu[0].data.id);
|
||||
let nearestSupernodeIds = nearestSupernode.map(f=>f.data.id);
|
||||
let supernodeSeedsArray = Object.values(supernodeSeedsObj)
|
||||
.filter(seed=>nearestSupernodeIds.includes(seed.kbucketId))
|
||||
.sort(function(a, b){
|
||||
return nearestSupernodeIds.indexOf(a.kbucketId) - nearestSupernodeIds.indexOf(b.kbucketId);
|
||||
});
|
||||
|
||||
if (supernodeSeedsArray.length>0) {
|
||||
resolve(supernodeSeedsArray);
|
||||
} else {
|
||||
reject(false);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function sendDataToSuperNode(floID,data){
|
||||
kBucketObj.determineClosestSupernode(floID).then(result=>{
|
||||
var superNodeWS = new WebSocket("ws://"+profiles[result[0].floID].onionAddr+"/ws");
|
||||
superNodeWS.onopen = function(ev){
|
||||
console.log(`Connected to ${floID}'s SuperNode!`);
|
||||
superNodeWS.send(data);
|
||||
};
|
||||
superNodeWS.onerror = function(ev) {console.log(`${floid}'s SuperNode is offline!`);};
|
||||
superNodeWS.onclose = function(ev) {console.log(`Disconnected from ${floid}'s SuperNode!`);};
|
||||
}).catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
}
|
||||
@ -8,12 +8,12 @@
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<!-- Font Awesome File -->
|
||||
|
||||
<script type="text/javascript" src="registerID.js"></script>
|
||||
<script type="text/javascript" src="init.js"></script>
|
||||
<script type="text/javascript" src="app.js"></script>
|
||||
<script type="text/javascript" src="home.js"></script>
|
||||
</head>
|
||||
|
||||
<body onload = "userDataStartUp();">
|
||||
<body onload = "viewHome();">
|
||||
|
||||
<div class="navbar navbar-default navbar-static-top">
|
||||
<div class="container">
|
||||
@ -35,10 +35,13 @@
|
||||
<input type="text" class="form-control-nav" id="search" aria-describedby="search1">
|
||||
<span class="glyphicon glyphicon-search form-control-feedback" aria-hidden="true"></span>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-default btn-sm" onclick="reloadInitData()">
|
||||
<span class="glyphicon glyphicon-refresh"> </span>
|
||||
</button>
|
||||
<button class="btn btn-default btn-sm" onclick="logout()">
|
||||
<span class="glyphicon glyphicon-log-out"> </span> Log out
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
670
app/web/home.js
670
app/web/home.js
@ -1,317 +1,65 @@
|
||||
var profiles = []
|
||||
var selfID;
|
||||
var profiles, following;
|
||||
var selfWebsocket,followingWebSockets = [];
|
||||
var privKey;
|
||||
var following;
|
||||
var privKey, modSuperNode, selfID;
|
||||
|
||||
function userDataStartUp(){
|
||||
console.log("StartUp");
|
||||
|
||||
/*document.getElementById("sendMsgInput").addEventListener("keyup",function(event){
|
||||
if(event.keyCode === 13){
|
||||
event.preventDefault();
|
||||
sendMsg();
|
||||
}
|
||||
});*/
|
||||
|
||||
getDatafromAPI().then(function (result) {
|
||||
console.log(result);
|
||||
getProfilesfromIDB().then(function (result){
|
||||
profiles = arrayToObject(result);
|
||||
console.log(profiles);
|
||||
sessionStorage.profiles = JSON.stringify(profiles);
|
||||
getSuperNodeListfromIDB().then(function(result){
|
||||
console.log(result)
|
||||
superNodeList = result;
|
||||
sessionStorage.superNodeList = JSON.stringify(superNodeList);
|
||||
kBucketObj.launchKBucket().then(function(result){
|
||||
console.log(result)
|
||||
getuserID().then(function(result){
|
||||
console.log(result);
|
||||
selfID = result;
|
||||
if(superNodeList.includes(selfID))
|
||||
modSuperNode = true;
|
||||
sessionStorage.privKey = JSON.stringify(encrypt.createShamirsSecretShares(privKey,10,10));
|
||||
sessionStorage.selfID = selfID;
|
||||
alert(`${selfID}\nWelcome ${profiles[selfID].name}`)
|
||||
initselfWebSocket();
|
||||
listProfiles();
|
||||
pingSuperNodeforNewMsgs();
|
||||
getFollowinglistFromIDB().then(function(result){
|
||||
following = result;
|
||||
if(!following.includes(selfID))
|
||||
following.push(selfID);
|
||||
console.log(following);
|
||||
displayTweetsFromIDB().then(function(result){
|
||||
connectToAllFollowing();
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
})
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
})
|
||||
}).catch(function (error) {
|
||||
console.log(error.message);
|
||||
});
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
});
|
||||
}).catch(function (error) {
|
||||
console.log(error.message);
|
||||
});
|
||||
}).catch(function (error) {
|
||||
console.log(error.message);
|
||||
});
|
||||
}).catch(function (error) {
|
||||
console.log(error.message);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function storedata(data){
|
||||
return new Promise(
|
||||
function(resolve, reject) {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
console.log("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction('profiles', "readwrite").objectStore('profiles');
|
||||
objectRequest = obs.put(data);
|
||||
objectRequest.onerror = function(event) {
|
||||
reject(Error('Error occured: Unable to store data'));
|
||||
};
|
||||
|
||||
objectRequest.onsuccess = function(event) {
|
||||
resolve('Data saved OK');
|
||||
db.close();
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getDatafromAPI(){
|
||||
return new Promise(
|
||||
function(resolve, reject) {
|
||||
var addr = adminID;
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
reject("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onupgradeneeded = function(event) {
|
||||
var db = event.target.result;
|
||||
var objectStore0 = event.target.result.createObjectStore("superNodes");
|
||||
var objectStore1 = db.createObjectStore("profiles",{ keyPath: 'floID' });
|
||||
objectStore1.createIndex('onionAddr', 'onionAddr', { unique: false });
|
||||
objectStore1.createIndex('name', 'name', { unique: false });
|
||||
objectStore1.createIndex('pubKey', 'pubKey', { unique: false });
|
||||
var objectStore2 = db.createObjectStore("lastTx");
|
||||
var objectStore3 = db.createObjectStore("tweets",{ keyPath: 'tweetID' });
|
||||
objectStore3.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore3.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore3.createIndex('time', 'time', { unique: false });
|
||||
objectStore3.createIndex('data', 'data', { unique: false });
|
||||
var objectStore4 = db.createObjectStore("lastTweet");
|
||||
var objectStore5 = db.createObjectStore("followers");
|
||||
var objectStore6 = db.createObjectStore("following");
|
||||
var objectStore7 = event.target.result.createObjectStore("messages",{ keyPath: 'msgID' });
|
||||
objectStore7.createIndex('time', 'time', { unique: false });
|
||||
objectStore7.createIndex('text', 'text', { unique: false });
|
||||
objectStore7.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore7.createIndex('type', 'type', { unique: false });
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
//window["wait"] = addrList.length;
|
||||
var lastTx = db.transaction('lastTx', "readwrite").objectStore('lastTx');
|
||||
//addrList.forEach(function(addr){
|
||||
console.log(addr);
|
||||
new Promise(function(res,rej){
|
||||
var lastTxReq = lastTx.get(addr);
|
||||
lastTxReq.onsuccess = function(event){
|
||||
var lasttx = event.target.result;
|
||||
if(lasttx === undefined){
|
||||
lasttx = 0;
|
||||
}
|
||||
res(lasttx);
|
||||
}
|
||||
}).then(function(lasttx){
|
||||
var response = ajax("GET",`api/addrs/${addr}/txs`);
|
||||
var nRequired = JSON.parse(response).totalItems - lasttx;
|
||||
console.log(nRequired);
|
||||
while(true && nRequired){
|
||||
var response = ajax("GET",`api/addrs/${addr}/txs?from=0&to=${nRequired}`);
|
||||
response = JSON.parse(response);
|
||||
if (nRequired + lasttx != response.totalItems ){
|
||||
nRequired = response.totalItems - lasttx;
|
||||
continue;
|
||||
}
|
||||
response.items.reverse().forEach(function(tx){
|
||||
try {
|
||||
if (tx.vin[0].addr == addr){
|
||||
var data = JSON.parse(tx.floData).FLO_Tweet_SuperNode;
|
||||
if(data !== undefined){
|
||||
storeSuperNodeData(data).then(function (response) {
|
||||
}).catch(function (error) {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
}else{
|
||||
var data = JSON.parse(tx.floData).FLO_Tweet;
|
||||
if(data !== undefined){
|
||||
if(encrypt.getFLOIDfromPubkeyHex(data.pubKey)!=tx.vin[0].addr)
|
||||
throw("PublicKey doesnot match with floID")
|
||||
data = {floID : tx.vin[0].addr, onionAddr : data.onionAddr, name : data.name, pubKey:data.pubKey};
|
||||
storedata(data).then(function (response) {
|
||||
}).catch(function (error) {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
//console.log(e)
|
||||
}
|
||||
});
|
||||
|
||||
var obs = db.transaction('lastTx', "readwrite").objectStore('lastTx');
|
||||
obs.put(response.totalItems,addr);
|
||||
break;
|
||||
}
|
||||
db.close();
|
||||
resolve('retrived data from API');
|
||||
});
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function storeSuperNodeData(data){
|
||||
return new Promise(
|
||||
function(resolve, reject) {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
reject("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction('superNodes', "readwrite").objectStore('superNodes');
|
||||
if(data.addNodes)
|
||||
for(var i=0; i<data.addNodes.length; i++)
|
||||
obs.add(true,data.addNodes[i])
|
||||
if(data.removeNodes)
|
||||
for(var i=0; i<data.removeNodes.length; i++)
|
||||
obs.delete(data.removeNodes[i])
|
||||
db.close();
|
||||
resolve('Updated superNodes list in IDB');
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getSuperNodeListfromIDB(){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
reject("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("superNodes", "readwrite").objectStore("superNodes");
|
||||
var getReq = obs.getAllKeys();
|
||||
getReq.onsuccess = function(event){
|
||||
resolve(event.target.result);
|
||||
}
|
||||
getReq.onerror = function(event){
|
||||
reject('Unable to read superNode list!')
|
||||
}
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getuserID(){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
privKey = (sessionStorage.privKey !== undefined ? encrypt.retrieveShamirSecret(JSON.parse(sessionStorage.privKey)):prompt("Enter FLO Private Key : "));
|
||||
var key = new Bitcoin.ECKey(privKey);
|
||||
while(key.priv == null){
|
||||
privKey = prompt("Invalid FLO Private Key! Retry : ")
|
||||
key = Bitcoin.ECKey(privKey);
|
||||
}
|
||||
key.setCompressed(true);
|
||||
var userID = key.getBitcoinAddress();
|
||||
if (profiles[userID] === undefined)
|
||||
var reg = confirm(`${userID} is not registers to FLO Tweet!\nRegister FLO ID to this onion?`);
|
||||
else if (profiles[userID].onionAddr == window.location.host)
|
||||
resolve(userID)
|
||||
else
|
||||
var reg = confirm(`${userID} is registered to another onion!\nChange to this onion?`);
|
||||
|
||||
if(reg){
|
||||
var name = prompt("Enter your name :");
|
||||
var pubKey = key.getPubKeyHex();
|
||||
if(registerID(userID,window.location.host,privKey,pubKey,name)){
|
||||
profiles[userID] = {onionAddr : window.location.host, name : name, pubKey : pubKey};
|
||||
resolve(userID);
|
||||
}
|
||||
}
|
||||
reject(`Unable to bind ${userID} to this onionAddress!\nTry again later!`);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getProfilesfromIDB(){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
reject("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("profiles", "readwrite").objectStore("profiles");
|
||||
var getReq = obs.getAll();
|
||||
getReq.onsuccess = function(event){
|
||||
resolve(event.target.result);
|
||||
}
|
||||
getReq.onerror = function(event){
|
||||
reject('Unable to read profiles!')
|
||||
}
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
);
|
||||
function viewHome(){
|
||||
if( sessionStorage.profiles === undefined || sessionStorage.privKey === undefined || sessionStorage.selfID === undefined || sessionStorage.serverPass === undefined || sessionStorage.superNodeList === undefined ){
|
||||
alert("Login credentials failed! Returning to login page!");
|
||||
window.location.href = "index.html";
|
||||
return;
|
||||
}
|
||||
profiles = JSON.parse(sessionStorage.profiles);
|
||||
console.log(profiles);
|
||||
privKey = encrypt.retrieveShamirSecret(JSON.parse(sessionStorage.privKey));
|
||||
selfID = sessionStorage.selfID;
|
||||
superNodeList = new Set(JSON.parse(sessionStorage.superNodeList));
|
||||
if(superNodeList.has(selfID)){
|
||||
modSuperNode = true;
|
||||
setInterval(reloadInitData, 3600000);
|
||||
}
|
||||
kBucketObj.launchKBucket().then(result => {
|
||||
console.log(result);
|
||||
initselfWebSocket();
|
||||
listProfiles();
|
||||
pingSuperNodeforNewMsgs();
|
||||
getFollowinglistFromIDB().then(result => {
|
||||
following = result;
|
||||
if(!following.includes(selfID))
|
||||
following.push(selfID);
|
||||
console.log(following);
|
||||
displayTweetsFromIDB().then(result => {
|
||||
connectToAllFollowing();
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
})
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
})
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function initselfWebSocket(){
|
||||
selfWebsocket = new WebSocket("ws://"+location.host+"/ws");
|
||||
selfWebsocket.onopen = function(evt){
|
||||
selfWebsocket.onopen = (event) => {
|
||||
console.log("Connecting");
|
||||
var pass = (sessionStorage.serverPass !== undefined ? encrypt.retrieveShamirSecret(JSON.parse(sessionStorage.serverPass)): prompt("Enter server password :"));
|
||||
selfWebsocket.send("$"+pass);
|
||||
sessionStorage.serverPass = JSON.stringify(encrypt.createShamirsSecretShares(pass,5,5));
|
||||
var serverPass = encrypt.retrieveShamirSecret(JSON.parse(sessionStorage.serverPass));
|
||||
selfWebsocket.send("$"+serverPass);
|
||||
};
|
||||
selfWebsocket.onclose = function(evt){
|
||||
selfWebsocket.onclose = (event) => {
|
||||
console.log("DISCONNECTED");
|
||||
initselfWebSocket();
|
||||
};
|
||||
selfWebsocket.onmessage = function(evt){
|
||||
console.log(evt.data);
|
||||
if(evt.data == "$Access Denied!"){
|
||||
var pass = prompt("Access Denied! reEnter server password :");
|
||||
selfWebsocket.send("$"+pass);
|
||||
sessionStorage.serverPass = JSON.stringify(encrypt.createShamirsSecretShares(pass,5,5));
|
||||
}else if(evt.data == "$Access Granted!")
|
||||
alert("Access Granted!")
|
||||
else{
|
||||
try{
|
||||
data = JSON.parse(evt.data);
|
||||
selfWebsocket.onmessage = (event) => {
|
||||
console.log(event.data);
|
||||
if(event.data[0] == '$')
|
||||
return;
|
||||
try{
|
||||
data = JSON.parse(event.data);
|
||||
if(data.follow && encrypt.verify(selfID, data.sign, profiles[data.floID].pubKey)){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("followers", "readwrite").objectStore("followers");
|
||||
obs.add(data.sign,data.floID);
|
||||
@ -320,7 +68,7 @@ function initselfWebSocket(){
|
||||
selfWebsocket.send(`F${data.floID}`);
|
||||
}else if(data.unfollow && encrypt.verify(selfID, data.sign, profiles[data.floID].pubKey)){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("followers", "readwrite").objectStore("followers");
|
||||
obs.delete(data.floID);
|
||||
@ -339,44 +87,13 @@ function initselfWebSocket(){
|
||||
createTweetElement(data.floID,data.time,data.tweet);
|
||||
}
|
||||
}else if(modSuperNode){
|
||||
if(data.reqNewTweets){
|
||||
kBucketObj.determineClosestSupernode(data.floID).then(result=>{
|
||||
if(result[0].floID == selfID)
|
||||
SuperNode_sendTweetsFromIDB(data.floID,data.tid,data.requestor);
|
||||
}).catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
}else if(data.newSuperNodeTweet){
|
||||
kBucketObj.determineClosestSupernode(data.floID).then(result=>{
|
||||
if(result[0].floID == selfID)
|
||||
SuperNode_storeSuperNodeTweet(data.data,data.tid);
|
||||
}).catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
}else if(data.viaSuperNodeMsg){
|
||||
kBucketObj.determineClosestSupernode(data.to).then(result=>{
|
||||
if(result[0].floID == selfID)
|
||||
SuperNode_storeViaSuperNodeMsg(data.from,data.to,data.data);
|
||||
}).catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
}else if(data.viaMsgreq){
|
||||
kBucketObj.determineClosestSupernode(data.floID).then(result=>{
|
||||
if(result[0].floID == selfID)
|
||||
SuperNode_sendviaMsgFromIDB(data.floID);
|
||||
}).catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
}
|
||||
superNodeMode(data);
|
||||
}
|
||||
}catch(error){
|
||||
console.log(error.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
selfWebsocket.onerror = function(evt){
|
||||
console.log(evt);
|
||||
}catch(error){
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
selfWebsocket.onerror = (event) => { console.log(event) };
|
||||
}
|
||||
|
||||
function listProfiles(){
|
||||
@ -394,7 +111,6 @@ function listProfiles(){
|
||||
</div></a>`
|
||||
profileList.appendChild(element);
|
||||
}
|
||||
//document.getElementById("profileInfo").style.display = "none";
|
||||
}
|
||||
|
||||
function postTweet(){
|
||||
@ -406,98 +122,40 @@ function postTweet(){
|
||||
var data = JSON.stringify({floID:selfID,time:time,tweet:tweet,sign:sign});
|
||||
console.log(data);
|
||||
selfWebsocket.send(data);
|
||||
getLastTweetCount(selfID).then(function(result){
|
||||
sendTweetToSuperNode(data,result+1);
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
getLastTweetCount(selfID).then(result => {
|
||||
var SNdata = JSON.stringify({newSuperNodeTweet:true,floID:selfID,tid:result+1,data:data});
|
||||
sendDataToSuperNode(selfID,SNdata);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function storeTweet(data,tid){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
console.log("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("tweets", "readwrite").objectStore("tweets");
|
||||
data.tweetID = `${data.time}_${data.floID}`;
|
||||
data.tid = tid;
|
||||
obs.add(data);
|
||||
var obsL = db.transaction("lastTweet", "readwrite").objectStore("lastTweet");
|
||||
obsL.put(tid,data.floID);
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
|
||||
function storeMsg(data){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
console.log("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("messages", "readwrite").objectStore("messages");
|
||||
data.msgID = `${data.time}_${data.floID}`;
|
||||
obs.add(data);
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
|
||||
function sendTweetToSuperNode(data,tid){
|
||||
kBucketObj.determineClosestSupernode(selfID).then(result=>{
|
||||
var superNodeWS = new WebSocket("ws://"+profiles[result[0].floID].onionAddr+"/ws");
|
||||
superNodeWS.onopen = function(ev){
|
||||
console.log(`Connected to self SuperNode!`);
|
||||
data = JSON.stringify({newSuperNodeTweet:true,floID:selfID,tid:tid,data:data})
|
||||
console.log(data)
|
||||
superNodeWS.send(data);
|
||||
};
|
||||
superNodeWS.onerror = function(ev) {console.log(`self SuperNode is offline!`);};
|
||||
superNodeWS.onclose = function(ev) {console.log(`Disconnected from self SuperNode!`);};
|
||||
}).catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
}
|
||||
|
||||
function getFollowinglistFromIDB(){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
return new Promise((resolve,reject) => {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
reject("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("following", "readwrite").objectStore("following");
|
||||
var getReq = obs.getAllKeys();
|
||||
getReq.onsuccess = function(event){
|
||||
resolve(event.target.result);
|
||||
}
|
||||
getReq.onerror = function(event){
|
||||
reject('Unable to read following list!')
|
||||
}
|
||||
getReq.onsuccess = (event) => { resolve(event.target.result) }
|
||||
getReq.onerror = (event) => { reject('Unable to read following list!') }
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function displayTweetsFromIDB(){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
return new Promise((resolve,reject) => {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
reject("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("tweets", "readwrite").objectStore("tweets");
|
||||
var curReq = obs.openCursor();
|
||||
curReq.onsuccess = function(event) {
|
||||
curReq.onsuccess = (event) => {
|
||||
var cursor = event.target.result;
|
||||
if(cursor) {
|
||||
//console.log(cursor.value)
|
||||
if(cursor.value.floID == selfID || following.includes(cursor.value.floID))
|
||||
createTweetElement(cursor.value.floID,cursor.value.time,cursor.value.data);
|
||||
cursor.continue();
|
||||
@ -505,13 +163,10 @@ function displayTweetsFromIDB(){
|
||||
resolve("Displayed Tweets from IDB!");
|
||||
}
|
||||
}
|
||||
curReq.onerror = function(event){
|
||||
reject("Error in Reading tweets from IDB!");
|
||||
}
|
||||
curReq.onerror = (event) => { reject("Error in Reading tweets from IDB!") }
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function createTweetElement(floID,time,tweet){
|
||||
@ -539,27 +194,24 @@ function connectToAllFollowing(){
|
||||
console.log(floid)
|
||||
followingWebSockets[floid] = new WebSocket("ws://"+profiles[floid].onionAddr+"/ws");
|
||||
|
||||
followingWebSockets[floid].onopen = function(ev){
|
||||
followingWebSockets[floid].onopen = (event) => {
|
||||
console.log(`Connected to ${floid} Server!`);
|
||||
getLastTweetCount(floid).then(function(result){
|
||||
getLastTweetCount(floid).then(result => {
|
||||
followingWebSockets[floid].send(`>${result}`);
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
followingWebSockets[floid].onerror = function(ev) {
|
||||
followingWebSockets[floid].onerror = (event) => {
|
||||
console.log(`${floid} Server is offline!`);
|
||||
//Ping SuperNode for any new tweets
|
||||
pingSuperNodeforNewTweets(floid);
|
||||
|
||||
};
|
||||
followingWebSockets[floid].onclose = function(ev) {
|
||||
console.log(`Disconnected from ${floid} Server!`);
|
||||
};
|
||||
followingWebSockets[floid].onmessage = function(evt){
|
||||
console.log(evt.data);
|
||||
followingWebSockets[floid].onclose = (event) => { console.log(`Disconnected from ${floid} Server!`); };
|
||||
followingWebSockets[floid].onmessage = (event) => {
|
||||
console.log(event.data);
|
||||
try{
|
||||
var data = JSON.parse(evt.data);
|
||||
var data = JSON.parse(event.data);
|
||||
var id = data.id;
|
||||
data = data.data;
|
||||
if( floid!=data.floID || !encrypt.verify(data.tweet,data.sign,profiles[floid].pubKey))
|
||||
@ -567,21 +219,20 @@ function connectToAllFollowing(){
|
||||
storeTweet({floID:data.floID,time:data.time,data:data.tweet},id);
|
||||
createTweetElement(data.floID,data.time,data.tweet);
|
||||
}catch(error){
|
||||
console.log(error.message);
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getLastTweetCount(floid){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
return new Promise((resolve,reject) => {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var lastTweet = db.transaction('lastTweet', "readwrite").objectStore('lastTweet');
|
||||
var lastTweetReq = lastTweet.get(floid);
|
||||
lastTweetReq.onsuccess = function(event){
|
||||
lastTweetReq.onsuccess = (event) => {
|
||||
var result = event.target.result;
|
||||
if(result === undefined)
|
||||
result = 0;
|
||||
@ -589,8 +240,7 @@ function getLastTweetCount(floid){
|
||||
}
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function pingSuperNodeforNewMsgs(){
|
||||
@ -599,150 +249,10 @@ function pingSuperNodeforNewMsgs(){
|
||||
}
|
||||
|
||||
function pingSuperNodeforNewTweets(floID){
|
||||
getLastTweetCount(floID).then(function(result){
|
||||
getLastTweetCount(floID).then(result => {
|
||||
var data = JSON.stringify({reqNewTweets:true,floID:floID,tid:result,requestor:selfID})
|
||||
sendDataToSuperNode(floID,data);
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function SuperNode_storeSuperNodeTweet(data,tid){
|
||||
var idb = indexedDB.open("FLO_Tweet",2);
|
||||
idb.onerror = function(event) {
|
||||
console.log("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onupgradeneeded = function(event){
|
||||
var objectStore1 = event.target.result.createObjectStore("superNodeTweet",{ keyPath: 'tweetID' });
|
||||
objectStore1.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore1.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore1.createIndex('data', 'data', { unique: false });
|
||||
var objectStore2 = event.target.result.createObjectStore("viaSuperNodeMsg",{ keyPath: 'id',autoIncrement:true });
|
||||
objectStore2.createIndex('from', 'from', { unique: false });
|
||||
objectStore2.createIndex('to', 'to', { unique: false });
|
||||
objectStore2.createIndex('data', 'data', { unique: false });
|
||||
}
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("superNodeTweet", "readwrite").objectStore("superNodeTweet");
|
||||
var parsedData = JSON.parse(data);
|
||||
var tweetID = ''+parsedData.floID+'_'+parsedData.time;
|
||||
obs.add({tweetID:tweetID,floID:parsedData.floID,tid:tid,data:data});
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
|
||||
function SuperNode_storeViaSuperNodeMsg(from,to,data){
|
||||
var idb = indexedDB.open("FLO_Tweet",2);
|
||||
idb.onerror = function(event) {
|
||||
console.log("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onupgradeneeded = function(event){
|
||||
var objectStore1 = event.target.result.createObjectStore("superNodeTweet",{ keyPath: 'tweetID' });
|
||||
objectStore1.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore1.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore1.createIndex('data', 'data', { unique: false });
|
||||
var objectStore2 = event.target.result.createObjectStore("viaSuperNodeMsg",{ keyPath: 'id',autoIncrement :true });
|
||||
objectStore2.createIndex('from', 'from', { unique: false });
|
||||
objectStore2.createIndex('to', 'to', { unique: false });
|
||||
objectStore2.createIndex('data', 'data', { unique: false });
|
||||
}
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("viaSuperNodeMsg", "readwrite").objectStore("viaSuperNodeMsg");
|
||||
obs.add({from:from,to:to,data:data});
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
|
||||
function SuperNode_sendTweetsFromIDB(floID,tid,requestor){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
var requestorWS = new WebSocket("ws://"+profiles[requestor].onionAddr+"/ws");
|
||||
|
||||
requestorWS.onopen = function(ev){
|
||||
console.log(`sending ${floID} tweets to ${requestor} Server!`);
|
||||
var idb = indexedDB.open("FLO_Tweet",2);
|
||||
idb.onerror = function(event) {
|
||||
reject("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onupgradeneeded = function(event){
|
||||
var objectStore1 = event.target.result.createObjectStore("superNodeTweet",{ keyPath: 'tweetID' });
|
||||
objectStore1.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore1.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore1.createIndex('data', 'data', { unique: false });
|
||||
var objectStore2 = event.target.result.createObjectStore("viaSuperNodeMsg",{ keyPath: 'id',autoIncrement:true });
|
||||
objectStore2.createIndex('from', 'from', { unique: false });
|
||||
objectStore2.createIndex('to', 'to', { unique: false });
|
||||
objectStore2.createIndex('data', 'data', { unique: false });
|
||||
}
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("superNodeTweet", "readwrite").objectStore("superNodeTweet");
|
||||
var curReq = obs.openCursor();
|
||||
curReq.onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if(cursor) {
|
||||
if(cursor.value.floID == floID && cursor.value.tid > tid){
|
||||
data = JSON.stringify({fromSuperNode:true, floID:cursor.value.floID,tid:cursor.value.tid,data:cursor.value.data})
|
||||
requestorWS.send(data);
|
||||
}
|
||||
cursor.continue();
|
||||
}else{
|
||||
resolve("Displayed Tweets from IDB!");
|
||||
}
|
||||
}
|
||||
curReq.onerror = function(event){
|
||||
reject("Error in Reading tweets from IDB!");
|
||||
}
|
||||
db.close();
|
||||
};
|
||||
};
|
||||
requestorWS.onerror = function(ev) {
|
||||
console.log(`${requestor} Server is offline!`);
|
||||
};
|
||||
requestorWS.onclose = function(ev) {
|
||||
console.log(`Disconnected from ${requestor} Server!`);
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function SuperNode_sendviaMsgFromIDB(floID){
|
||||
var receiverWS = new WebSocket("ws://"+profiles[floID].onionAddr+"/ws");
|
||||
receiverWS.onopen = function(ev){
|
||||
var idb = indexedDB.open("FLO_Tweet",2);
|
||||
idb.onerror = function(event) {
|
||||
console.log("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onupgradeneeded = function(event) {
|
||||
var objectStore1 = event.target.result.createObjectStore("superNodeTweet",{ keyPath: 'tweetID' });
|
||||
objectStore1.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore1.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore1.createIndex('data', 'data', { unique: false });
|
||||
var objectStore2 = event.target.result.createObjectStore("viaSuperNodeMsg",{ keyPath: 'id',autoIncrement:true });
|
||||
objectStore2.createIndex('from', 'from', { unique: false });
|
||||
objectStore2.createIndex('to', 'to', { unique: false });
|
||||
objectStore2.createIndex('data', 'data', { unique: false });
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("viaSuperNodeMsg", "readwrite").objectStore("viaSuperNodeMsg");
|
||||
obs.openCursor().onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if(cursor) {
|
||||
if(cursor.value.to == floID){
|
||||
receiverWS.send(cursor.value.data);
|
||||
cursor.delete();
|
||||
}
|
||||
cursor.continue();
|
||||
}else{
|
||||
console.log('Sent All messages to '+floID)
|
||||
}
|
||||
}
|
||||
db.close();
|
||||
};
|
||||
};
|
||||
receiverWS.onerror = function(ev) { console.log('Connection Error to '+floID) };
|
||||
receiverWS.onclose = function(ev) { console.log('Disconnected from '+floID) };
|
||||
}
|
||||
@ -1,5 +1,146 @@
|
||||
<html>
|
||||
<script type="text/javascript">
|
||||
window.location.href = "home.html";
|
||||
</script>
|
||||
<head>
|
||||
<title>Login</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
||||
|
||||
<style>
|
||||
@charset "utf-8";
|
||||
/* CSS Document */
|
||||
|
||||
h1{
|
||||
margin: 0 auto;
|
||||
padding-left: 15%;
|
||||
text-transform:uppercase;
|
||||
font-size:30px;
|
||||
color:white;
|
||||
}
|
||||
|
||||
|
||||
h2{
|
||||
margin: 0 auto;
|
||||
padding-left: 15%;
|
||||
font-size:18px;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
font-family: Helvetica;
|
||||
margin:0 auto;
|
||||
min-height:100%;
|
||||
background: lightblue;
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.nav {
|
||||
margin: 0 auto;
|
||||
margin-top: 100px;
|
||||
margin-left: 35%;
|
||||
width: 300px;
|
||||
height: auto;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0, .5);
|
||||
|
||||
}
|
||||
|
||||
.input {
|
||||
border: 1px solid #a6a6a6;
|
||||
width: 230px;
|
||||
height: 40px;
|
||||
border-radius: 3px;
|
||||
margin-top: 5px;
|
||||
padding-left: 9px;
|
||||
color: #6c6c6c;
|
||||
background: #fcfcfc;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.input:hover:enabled{
|
||||
box-shadow: 0 0 10px #337AB7;
|
||||
border: 1px #337AB7 solid ;
|
||||
}
|
||||
|
||||
.input:focus, .button:focus, .button:hover:focus{
|
||||
box-shadow: 0 0 10px #337AB7;
|
||||
border: 1px #337AB7 solid ;
|
||||
}
|
||||
|
||||
.button {
|
||||
color:#fff;
|
||||
font-size:20px;
|
||||
border: 1px solid #a6a6a6;
|
||||
width: 243px;
|
||||
height: 43px;
|
||||
border-radius: 5px;
|
||||
margin-top: 5px;
|
||||
margin-right: 6px;
|
||||
outline: none;
|
||||
}
|
||||
.button:disabled,.input:disabled{
|
||||
cursor:not-allowed;
|
||||
}
|
||||
label {
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin-right: 115px;
|
||||
}
|
||||
label span {
|
||||
margin-left: 5px;
|
||||
font-size:14px;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
#alert-container{
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top:0;
|
||||
z-index: 2;
|
||||
}
|
||||
.alert {
|
||||
padding: 10px;
|
||||
color: white;
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.closebtn {
|
||||
margin-left: 15px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.closebtn:hover {
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="init.js"></script>
|
||||
<script type="text/javascript" src="app.js"></script>
|
||||
<script type="text/javascript" src="login.js"></script>
|
||||
</head>
|
||||
|
||||
<body onload="initAPIdata();">
|
||||
<div id="alert-container"></div>
|
||||
<div class="nav">
|
||||
<form id='serverConnect'>
|
||||
<input type="password" id="serverPass" class="input" placeholder="ServerPass" />
|
||||
<input type="button" value="Connect" class="button btn-primary" onclick="connect()"/></br>
|
||||
</form>
|
||||
<form id='userSignIn'>
|
||||
<input type="text" id="username" class="input" placeholder="Username" disabled/>
|
||||
<input type="password" id="privKey" class="input" placeholder="PrivKey" disabled/>
|
||||
<input type="button" value="Sign In" class="button btn-primary" onclick="signIn()" disabled/>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
13418
app/web/registerID.js → app/web/init.js
vendored
Executable file → Normal file
13418
app/web/registerID.js → app/web/init.js
vendored
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
336
app/web/login.js
Normal file
336
app/web/login.js
Normal file
@ -0,0 +1,336 @@
|
||||
if( sessionStorage.profiles && sessionStorage.privKey && sessionStorage.selfID && sessionStorage.serverPass && sessionStorage.superNodeList ){
|
||||
window.location.replace("home.html");
|
||||
}
|
||||
|
||||
const sendAmt = 0.001 ;
|
||||
const fee = 0.0005;
|
||||
var username,privKey,floID;
|
||||
var profiles;
|
||||
|
||||
function initAPIdata(){
|
||||
return new Promise((resolve,reject) => {
|
||||
console.log("initAPIdata");
|
||||
getDatafromAPI().then(result => {
|
||||
console.log(result);
|
||||
getProfilesfromIDB().then(result => {
|
||||
profiles = arrayToObject(result);
|
||||
console.log(profiles);
|
||||
sessionStorage.profiles = JSON.stringify(profiles);
|
||||
getSuperNodeListfromIDB().then(result => {
|
||||
console.log(result)
|
||||
superNodeList = new Set(result);
|
||||
sessionStorage.superNodeList = JSON.stringify(Array.from(superNodeList));
|
||||
resolve('Refresh API data Successful!')
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function customAlert(msg,type){
|
||||
color = {info:"#2196F3",danger:"#f44336",warning:"#ff9800",success:"#4CAF50"};
|
||||
var alertBox = document.createElement('div');
|
||||
alertBox.setAttribute('class','alert');
|
||||
alertBox.style.backgroundColor = color[type];
|
||||
alertBox.innerHTML = `<span class="closebtn" onclick="this.parentElement.style.display='none';">✖</span>
|
||||
${msg}`;
|
||||
document.getElementById("alert-container").appendChild(alertBox);
|
||||
}
|
||||
|
||||
function disableForm(formId, disableFlag) {
|
||||
var form = document.getElementById(formId);
|
||||
for(var i=0; i<form.length; i++)
|
||||
form[i].disabled = disableFlag;
|
||||
}
|
||||
|
||||
function connect(){
|
||||
document.getElementById("alert-container").innerHTML = '';
|
||||
var serverPass = document.getElementById('serverPass').value;
|
||||
initselfWebSocket(serverPass).then(result => {
|
||||
sessionStorage.serverPass = JSON.stringify(encrypt.createShamirsSecretShares(serverPass,10,10));
|
||||
customAlert(result,'success');
|
||||
disableForm('serverConnect',true);
|
||||
disableForm('userSignIn',false);
|
||||
}).catch(error => {
|
||||
customAlert(error,'danger');
|
||||
});
|
||||
}
|
||||
|
||||
function signIn(){
|
||||
document.getElementById("alert-container").innerHTML = '';
|
||||
username = document.getElementById('username').value;
|
||||
privKey = document.getElementById('privKey').value;
|
||||
var key = new Bitcoin.ECKey(privKey);
|
||||
if(key.priv == null){
|
||||
customAlert("Invalid FLO Private Key!",'danger');
|
||||
return false;
|
||||
}
|
||||
key.setCompressed(true);
|
||||
floID = key.getBitcoinAddress();
|
||||
try{
|
||||
console.log(floID)
|
||||
if (profiles[floID] === undefined)
|
||||
throw `${floID} is not registers to FLO Tweet!<br/>Register FLO ID to this onion?`;
|
||||
if (profiles[floID].onionAddr != window.location.host)
|
||||
throw `${floID} is registered to another onion!<br/>Change to this onion?`;
|
||||
if (profiles[floID].name != username)
|
||||
throw `${floID} is registered to another username!<br/>Change username?`;
|
||||
login();
|
||||
}catch(msg){
|
||||
console.log(msg)
|
||||
customAlert(`${msg}<span class="closebtn" onclick="this.parentElement.style.display='none'; signUp();">✔</span>`,'warning');
|
||||
}
|
||||
}
|
||||
|
||||
function login(){
|
||||
sessionStorage.privKey = JSON.stringify(encrypt.createShamirsSecretShares(privKey,10,10));
|
||||
sessionStorage.selfID = floID;
|
||||
disableForm('userSignIn',true);
|
||||
customAlert(`Welcome ${username}`,'info');
|
||||
setTimeout(window.location.replace("home.html"), 3000);
|
||||
}
|
||||
|
||||
function signUp(){
|
||||
registerID(floID,window.location.host,privKey,encrypt.getPubKeyHex(privKey),username).then(result =>{
|
||||
customAlert(`Registration Successful!<br/> txid : ${result}`,'success');
|
||||
refreshAPIdata().then(result => {
|
||||
console.log(result);
|
||||
sessionStorage.profiles = JSON.stringify(profiles);
|
||||
sessionStorage.superNodeList = JSON.stringify(Array.from(superNodeList));
|
||||
login();
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}).catch(error => {
|
||||
customAlert(error,'danger');
|
||||
});
|
||||
}
|
||||
|
||||
function ajax(method, uri){
|
||||
var request = new XMLHttpRequest();
|
||||
var url = `${api_url}/${uri}`
|
||||
console.log(url)
|
||||
var result;
|
||||
request.open(method,url , false);
|
||||
request.onload = function () {
|
||||
if (request.readyState == 4 && request.status == 200)
|
||||
result = this.response;
|
||||
else {
|
||||
console.log('error');
|
||||
result = false;
|
||||
}
|
||||
};
|
||||
request.send();
|
||||
console.log(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function getDatafromAPI(){
|
||||
return new Promise((resolve,reject) => {
|
||||
var addr = adminID;
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onupgradeneeded = (event) => {
|
||||
var db = event.target.result;
|
||||
var objectStore0 = event.target.result.createObjectStore("superNodes");
|
||||
var objectStore1 = db.createObjectStore("profiles",{ keyPath: 'floID' });
|
||||
objectStore1.createIndex('onionAddr', 'onionAddr', { unique: false });
|
||||
objectStore1.createIndex('name', 'name', { unique: false });
|
||||
objectStore1.createIndex('pubKey', 'pubKey', { unique: false });
|
||||
var objectStore2 = db.createObjectStore("lastTx");
|
||||
var objectStore3 = db.createObjectStore("tweets",{ keyPath: 'tweetID' });
|
||||
objectStore3.createIndex('tid', 'tid', { unique: false });
|
||||
objectStore3.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore3.createIndex('time', 'time', { unique: false });
|
||||
objectStore3.createIndex('data', 'data', { unique: false });
|
||||
var objectStore4 = db.createObjectStore("lastTweet");
|
||||
var objectStore5 = db.createObjectStore("followers");
|
||||
var objectStore6 = db.createObjectStore("following");
|
||||
var objectStore7 = event.target.result.createObjectStore("messages",{ keyPath: 'msgID' });
|
||||
objectStore7.createIndex('time', 'time', { unique: false });
|
||||
objectStore7.createIndex('text', 'text', { unique: false });
|
||||
objectStore7.createIndex('floID', 'floID', { unique: false });
|
||||
objectStore7.createIndex('type', 'type', { unique: false });
|
||||
};
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var lastTx = db.transaction('lastTx', "readwrite").objectStore('lastTx');
|
||||
console.log(addr);
|
||||
new Promise((res,rej) => {
|
||||
var lastTxReq = lastTx.get(addr);
|
||||
lastTxReq.onsuccess = (event) => {
|
||||
var lasttx = event.target.result;
|
||||
if(lasttx === undefined){
|
||||
lasttx = 0;
|
||||
}
|
||||
res(lasttx);
|
||||
}
|
||||
}).then(lasttx => {
|
||||
var response = ajax("GET",`api/addrs/${addr}/txs`);
|
||||
var nRequired = JSON.parse(response).totalItems - lasttx;
|
||||
console.log(nRequired);
|
||||
while(true && nRequired){
|
||||
var response = ajax("GET",`api/addrs/${addr}/txs?from=0&to=${nRequired}`);
|
||||
response = JSON.parse(response);
|
||||
if (nRequired + lasttx != response.totalItems ){
|
||||
nRequired = response.totalItems - lasttx;
|
||||
continue;
|
||||
}
|
||||
response.items.reverse().forEach(tx => {
|
||||
try {
|
||||
if (tx.vin[0].addr == addr){
|
||||
var data = JSON.parse(tx.floData).FLO_Tweet_SuperNode;
|
||||
if(data !== undefined){
|
||||
storeSuperNodeData(data).then(function (response) {
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}else{
|
||||
var data = JSON.parse(tx.floData).FLO_Tweet;
|
||||
if(data !== undefined){
|
||||
if(encrypt.getFLOIDfromPubkeyHex(data.pubKey)!=tx.vin[0].addr)
|
||||
throw("PublicKey doesnot match with floID")
|
||||
data = {floID : tx.vin[0].addr, onionAddr : data.onionAddr, name : data.name, pubKey:data.pubKey};
|
||||
storeProfile(data).then(function (response) {
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
//console.log(error)
|
||||
}
|
||||
});
|
||||
var obs = db.transaction('lastTx', "readwrite").objectStore('lastTx');
|
||||
obs.put(response.totalItems,addr);
|
||||
break;
|
||||
}
|
||||
db.close();
|
||||
resolve('retrived data from API');
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getProfilesfromIDB(){
|
||||
return new Promise((resolve,reject) => {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("profiles", "readwrite").objectStore("profiles");
|
||||
var getReq = obs.getAll();
|
||||
getReq.onsuccess = (event) => { resolve(event.target.result) }
|
||||
getReq.onerror = (event) => { reject('Unable to read profiles!') }
|
||||
db.close();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getSuperNodeListfromIDB(){
|
||||
return new Promise((resolve,reject) => {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("superNodes", "readwrite").objectStore("superNodes");
|
||||
var getReq = obs.getAllKeys();
|
||||
getReq.onsuccess = (event) => { resolve(event.target.result) }
|
||||
getReq.onerror = (event) => { reject('Unable to read superNode list!') }
|
||||
db.close();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function initselfWebSocket(serverPass){
|
||||
return new Promise((resolve,reject) => {
|
||||
selfWebsocket = new WebSocket("ws://"+location.host+"/ws");
|
||||
selfWebsocket.onopen = (event) => {
|
||||
console.log("Connecting");
|
||||
selfWebsocket.send("$"+serverPass);
|
||||
};
|
||||
selfWebsocket.onclose = (event) => { console.log("DISCONNECTED") };
|
||||
selfWebsocket.onmessage = (event) => {
|
||||
console.log(event.data);
|
||||
if(event.data == "$Access Denied!")
|
||||
reject("Access Denied!");
|
||||
else if(event.data == "$Access Granted!")
|
||||
resolve("Access Granted!");
|
||||
};
|
||||
selfWebsocket.onerror = (event) => { console.log(event) };
|
||||
});
|
||||
}
|
||||
|
||||
function registerID(sender,onionAddr,wif,pubkey,username) {
|
||||
return new Promise((resolve,reject) => {
|
||||
var trx = bitjs.transaction();
|
||||
var utxoAmt = 0.0;
|
||||
var x = sendAmt+fee;
|
||||
var response = ajax("GET",`api/addr/${sender}/utxo`);
|
||||
var utxos = JSON.parse(response);
|
||||
for(var x = utxos.length-1; x >= 0; x--){
|
||||
if(utxoAmt < sendAmt+fee){
|
||||
trx.addinput(utxos[x].txid, utxos[x].vout, utxos[x].scriptPubKey);
|
||||
utxoAmt += utxos[x].amount;
|
||||
}else
|
||||
break;
|
||||
}
|
||||
console.log(utxoAmt+":"+(sendAmt+fee));
|
||||
if(utxoAmt < sendAmt+fee)
|
||||
reject("Insufficient balance!");
|
||||
trx.addoutput(adminID, sendAmt);
|
||||
console.log(adminID+":"+ sendAmt);
|
||||
var change = utxoAmt-sendAmt-fee;
|
||||
if(change>0)
|
||||
trx.addoutput(sender, change);
|
||||
console.log(sender+":"+ change);
|
||||
var key = new Bitcoin.ECKey(wif);
|
||||
var sendFloData = JSON.stringify({FLO_Tweet:{onionAddr:onionAddr, name: username, pubKey: pubkey}});;
|
||||
trx.addflodata(sendFloData);
|
||||
console.log(sendFloData);
|
||||
var signedTxHash = trx.sign(wif, 1);
|
||||
console.log(signedTxHash);
|
||||
var txid = broadcastTx(signedTxHash);
|
||||
if (txid)
|
||||
resolve(txid);
|
||||
else
|
||||
reject('Registration Unsuccessful!')
|
||||
});
|
||||
}
|
||||
|
||||
function broadcastTx(signedTxHash) {
|
||||
var http = new XMLHttpRequest();
|
||||
var url = `${api_url}/api/tx/send`;
|
||||
if (signedTxHash.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var params = `{"rawtx":"${signedTxHash}"}`;
|
||||
var result;
|
||||
http.open('POST', url, false);
|
||||
|
||||
//Send the proper header information along with the request
|
||||
http.setRequestHeader('Content-type', 'application/json');
|
||||
|
||||
http.onreadystatechange = function () { //Call a function when the state changes.
|
||||
if (http.readyState == 4 && http.status == 200) {
|
||||
console.log(http.response);
|
||||
var txid = JSON.parse(http.response).txid.result;
|
||||
console.log("Transaction successful! txid : " + txid);
|
||||
result = txid;
|
||||
} else {
|
||||
console.log(http.responseText);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
http.send(params);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<!-- Font Awesome File -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
|
||||
<script type="text/javascript" src="registerID.js"></script>
|
||||
<script type="text/javascript" src="init.js"></script>
|
||||
<script type="text/javascript" src="app.js"></script>
|
||||
<script type="text/javascript" src="msg.js"></script>
|
||||
|
||||
|
||||
110
app/web/msg.js
110
app/web/msg.js
@ -1,33 +1,37 @@
|
||||
var profiles;
|
||||
var receiverID,selfID,privKey;
|
||||
var receiverID,selfID,privKey,modSuperNode;
|
||||
var selfwebsocket,receiverWebSocket,recStat;
|
||||
|
||||
function initMsgs(){
|
||||
if( sessionStorage.profiles === undefined || sessionStorage.privKey === undefined || sessionStorage.selfID === undefined || sessionStorage.serverPass === undefined){
|
||||
alert("Login credentials failed! Returning to home page!");
|
||||
window.location.href = "home.html";
|
||||
if( sessionStorage.profiles === undefined || sessionStorage.privKey === undefined || sessionStorage.selfID === undefined || sessionStorage.serverPass === undefined || sessionStorage.superNodeList === undefined ){
|
||||
alert("Login credentials failed! Returning to login page!");
|
||||
window.location.href = "index.html";
|
||||
return;
|
||||
}
|
||||
profiles = JSON.parse(sessionStorage.profiles);
|
||||
console.log(profiles);
|
||||
privKey = encrypt.retrieveShamirSecret(JSON.parse(sessionStorage.privKey));
|
||||
selfID = sessionStorage.selfID;
|
||||
superNodeList = JSON.parse(sessionStorage.superNodeList);
|
||||
kBucketObj.launchKBucket().then(function(result){
|
||||
superNodeList = new Set(JSON.parse(sessionStorage.superNodeList));
|
||||
if(superNodeList.has(selfID)){
|
||||
modSuperNode = true;
|
||||
setInterval(reloadInitData, 3600000);
|
||||
}
|
||||
kBucketObj.launchKBucket().then(result => {
|
||||
console.log(result)
|
||||
initselfWebSocket();
|
||||
readMsgfromIDB().then(function(result){
|
||||
readMsgfromIDB().then(result => {
|
||||
listProfiles();
|
||||
document.getElementById("sendMsgInput").addEventListener("keyup",function(event){
|
||||
document.getElementById("sendMsgInput").addEventListener("keyup",(event) => {
|
||||
if(event.keyCode === 13){
|
||||
event.preventDefault();
|
||||
sendMsg();
|
||||
}
|
||||
});
|
||||
}).catch(function(error){
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}).catch(function(error){
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
@ -42,32 +46,33 @@ function listProfiles(){
|
||||
var element = document.createElement("div");
|
||||
element.setAttribute("class", "media");
|
||||
element.innerHTML = `<a href="javascript:changeReceiver('${p}');"><div class="media-body">
|
||||
<h5 class="media-heading">${profiles[p].name}</h5>
|
||||
<small>@${p}</small>
|
||||
</div></a>`
|
||||
<h5 class="media-heading">${profiles[p].name}</h5>
|
||||
<small>@${p}</small>
|
||||
</div></a>`
|
||||
profileList.appendChild(element);
|
||||
}
|
||||
}
|
||||
|
||||
function initselfWebSocket(){
|
||||
selfWebsocket = new WebSocket("ws://"+location.host+"/ws");
|
||||
selfWebsocket.onopen = function(evt){
|
||||
selfWebsocket.onopen = (event) => {
|
||||
console.log("Connecting");
|
||||
var serverPass = encrypt.retrieveShamirSecret(JSON.parse(sessionStorage.serverPass));
|
||||
selfWebsocket.send("$"+serverPass);
|
||||
};
|
||||
selfWebsocket.onclose = function(evt){
|
||||
selfWebsocket.onclose = (event) => {
|
||||
console.log("DISCONNECTED");
|
||||
initselfWebSocket();
|
||||
};
|
||||
selfWebsocket.onmessage = function(evt){
|
||||
console.log(evt.data);
|
||||
if(evt.data[0] == '$')
|
||||
selfWebsocket.onmessage = (event) => {
|
||||
console.log(event.data);
|
||||
if(event.data[0] == '$')
|
||||
return;
|
||||
try{
|
||||
data = JSON.parse(evt.data);
|
||||
data = JSON.parse(event.data);
|
||||
if(data.follow && encrypt.verify(selfID, data.sign, profiles[data.floID].pubKey)){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("followers", "readwrite").objectStore("followers");
|
||||
obs.add(data.sign,data.floID);
|
||||
@ -76,7 +81,7 @@ function initselfWebSocket(){
|
||||
selfWebsocket.send(`F${data.floID}-${data.sign}`);
|
||||
}else if(data.unfollow && encrypt.verify(selfID, data.sign, profiles[data.floID].pubKey)){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("followers", "readwrite").objectStore("followers");
|
||||
obs.delete(data.floID);
|
||||
@ -89,19 +94,18 @@ function initselfWebSocket(){
|
||||
createMsgElement(data.from,data.time,msg,'R')
|
||||
storeMsg({time:data.time,floID:data.from,text:msg,type:'R'});
|
||||
}
|
||||
}else if(modSuperNode){
|
||||
superNodeMode(data);
|
||||
}
|
||||
}catch(error){
|
||||
console.log(error.message)
|
||||
console.log(error)
|
||||
}
|
||||
};
|
||||
selfWebsocket.onerror = function(evt){
|
||||
console.log(evt);
|
||||
};
|
||||
selfWebsocket.onerror = (event) => { console.log(event) };
|
||||
}
|
||||
|
||||
function readMsgfromIDB(){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
return new Promise((resolve,reject) => {
|
||||
var disp = document.getElementById("msgsContainer");
|
||||
for(floID in profiles){
|
||||
var element = document.createElement('div');
|
||||
@ -110,13 +114,11 @@ function readMsgfromIDB(){
|
||||
disp.appendChild(element);
|
||||
}
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
reject("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("messages", "readwrite").objectStore("messages");
|
||||
obs.openCursor().onsuccess = function(event) {
|
||||
obs.openCursor().onsuccess = (event) => {
|
||||
var cursor = event.target.result;
|
||||
if(cursor) {
|
||||
createMsgElement(cursor.value.floID,cursor.value.time,cursor.value.text,cursor.value.type);
|
||||
@ -128,8 +130,7 @@ function readMsgfromIDB(){
|
||||
};
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function changeReceiver(floID){
|
||||
@ -148,9 +149,9 @@ function changeReceiver(floID){
|
||||
document.getElementById(receiverID).style.display = 'block';
|
||||
try{
|
||||
receiverWebSocket = new WebSocket("ws://"+profiles[receiverID].onionAddr+"/ws");
|
||||
receiverWebSocket.onopen = function(ev){ recStat = true; };
|
||||
receiverWebSocket.onerror = function(ev) { recStat = false;};
|
||||
receiverWebSocket.onclose = function(ev) { recStat = false; };
|
||||
receiverWebSocket.onopen = (event) => { recStat = true; };
|
||||
receiverWebSocket.onerror = (event) => { recStat = false;};
|
||||
receiverWebSocket.onclose = (event) => { recStat = false; };
|
||||
}catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
@ -161,23 +162,22 @@ function sendMsg(){
|
||||
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 = encrypt.sign(msg,privKey)
|
||||
var msgEncrypt = encrypt.encryptMessage(msg,profiles[receiverID].pubKey)
|
||||
var inp = document.getElementById('sendMsgInput');
|
||||
var msg = inp.value;
|
||||
inp.value = "";
|
||||
console.log(msg);
|
||||
var time = Date.now();
|
||||
var sign = encrypt.sign(msg,privKey);
|
||||
var msgEncrypt = encrypt.encryptMessage(msg,profiles[receiverID].pubKey);
|
||||
var data = JSON.stringify({message:true,from:selfID,to:receiverID,time:time,secret:msgEncrypt.secret,sign:sign,pubVal:msgEncrypt.senderPublicKeyString});
|
||||
|
||||
if(recStat)
|
||||
receiverWebSocket.send(data);
|
||||
else{
|
||||
var SNdata = JSON.stringify({viaSuperNodeMsg:true,from:selfID,to:receiverID,data:data})
|
||||
sendDataToSuperNode(receiverID,SNdata)
|
||||
var SNdata = JSON.stringify({viaSuperNodeMsg:true,from:selfID,to:receiverID,data:data});
|
||||
sendDataToSuperNode(receiverID,SNdata);
|
||||
}
|
||||
console.log(`sentMsg : ${data}`);
|
||||
createMsgElement(receiverID,time,msg,'S')
|
||||
createMsgElement(receiverID,time,msg,'S');
|
||||
storeMsg({time:time,floID:receiverID,text:msg,type:'S'});
|
||||
}
|
||||
|
||||
@ -194,17 +194,3 @@ function createMsgElement(floID,time,msg,type){
|
||||
</div>`;
|
||||
msgField.appendChild(element);
|
||||
}
|
||||
|
||||
function storeMsg(data){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
console.log("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("messages", "readwrite").objectStore("messages");
|
||||
data.msgID = `${data.time}_${data.floID}`;
|
||||
obs.add(data);
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
@ -8,7 +8,7 @@
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<!-- Font Awesome File -->
|
||||
|
||||
<script type="text/javascript" src="registerID.js"></script>
|
||||
<script type="text/javascript" src="init.js"></script>
|
||||
<script type="text/javascript" src="app.js"></script>
|
||||
<script type="text/javascript" src="profile.js"></script>
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
|
||||
var profileWebsocket, selfWebsocket,profileServerStatus;
|
||||
var profiles;
|
||||
var profileID, privKey, selfID;
|
||||
var profileID, privKey, selfID, modSuperNode;
|
||||
|
||||
function viewProfile(){
|
||||
if( sessionStorage.profiles === undefined || sessionStorage.privKey === undefined || sessionStorage.selfID === undefined || sessionStorage.serverPass === undefined){
|
||||
alert("Login credentials failed! Returning to home page!");
|
||||
window.location.href = "home.html";
|
||||
if( sessionStorage.profiles === undefined || sessionStorage.privKey === undefined || sessionStorage.selfID === undefined || sessionStorage.serverPass === undefined || sessionStorage.superNodeList === undefined ){
|
||||
alert("Login credentials failed! Returning to login page!");
|
||||
window.location.href = "index.html";
|
||||
return;
|
||||
}
|
||||
profiles = JSON.parse(sessionStorage.profiles);
|
||||
@ -15,13 +15,17 @@ function viewProfile(){
|
||||
selfID = sessionStorage.selfID;
|
||||
var url = new URL(window.location.href);
|
||||
profileID = url.searchParams.get("floID");
|
||||
superNodeList = JSON.parse(sessionStorage.superNodeList);
|
||||
kBucketObj.launchKBucket().then(function(result){
|
||||
superNodeList = new Set(JSON.parse(sessionStorage.superNodeList));
|
||||
if(superNodeList.has(selfID)){
|
||||
modSuperNode = true;
|
||||
setInterval(reloadInitData, 3600000);
|
||||
}
|
||||
kBucketObj.launchKBucket().then(result => {
|
||||
console.log(result)
|
||||
listProfiles();
|
||||
displayProfile(profileID);
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
@ -30,7 +34,7 @@ function displayProfile(profileID){
|
||||
var errorMsg;
|
||||
if(!profileID)
|
||||
errorMsg = "Select a Profile to display";
|
||||
else if(!validateAddr(profileID))
|
||||
else if(!encrypt.validateAddr(profileID))
|
||||
errorMsg = "Invalid FLO ID";
|
||||
else if(!(profileID in profiles))
|
||||
errorMsg = "FLO ID not registered to FLO Tweet";
|
||||
@ -44,32 +48,29 @@ function displayProfile(profileID){
|
||||
document.getElementById("profileName").innerHTML=profiles[profileID].name;
|
||||
document.getElementById("profileFloID").innerHTML='@'+profileID;
|
||||
initselfWebSocket();
|
||||
displayTweetFromIDB(profileID).then(function(result){
|
||||
connectToX(profileID).then(function(result){
|
||||
displayTweetFromIDB(profileID).then(result => {
|
||||
connectToProfileServer(profileID).then(result => {
|
||||
console.log(result);
|
||||
profileServerStatus = true;
|
||||
getTweetsFromX(profileID);
|
||||
}).catch(function(error){
|
||||
pingProfileServerforNewTweets(profileID);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
pingSuperNodeforNewTweets(profileID);
|
||||
profileServerStatus = false;
|
||||
});
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function displayTweetFromIDB(floID){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
return new Promise((resolve,reject) => {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
reject("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obj = db.transaction("following", "readwrite").objectStore("following");
|
||||
obj.get(floID).onsuccess = function (event) {
|
||||
obj.get(floID).onsuccess = (event) => {
|
||||
var followBtn = document.getElementById("follow-button");
|
||||
if(event.target.result === undefined){
|
||||
followBtn.innerHTML = "+ Follow";
|
||||
@ -81,7 +82,7 @@ function displayTweetFromIDB(floID){
|
||||
followBtn.disabled = false;
|
||||
}
|
||||
var obs = db.transaction("tweets", "readwrite").objectStore("tweets");
|
||||
obs.openCursor().onsuccess = function(event) {
|
||||
obs.openCursor().onsuccess = (event) => {
|
||||
var cursor = event.target.result;
|
||||
if(cursor) {
|
||||
//console.log(cursor.value)
|
||||
@ -94,8 +95,7 @@ function displayTweetFromIDB(floID){
|
||||
}
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function listProfiles(){
|
||||
@ -111,7 +111,6 @@ function listProfiles(){
|
||||
</div></a>`
|
||||
profileList.appendChild(element);
|
||||
}
|
||||
//document.getElementById("profileInfo").style.display = "none";
|
||||
}
|
||||
|
||||
function createTweetElement(floID,time,tweet){
|
||||
@ -133,23 +132,16 @@ function createTweetElement(floID,time,tweet){
|
||||
tweetDisplay.insertBefore(element, tweetDisplay.firstChild);
|
||||
}
|
||||
|
||||
function connectToX(floID){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
function connectToProfileServer(floID){
|
||||
return new Promise((resolve,reject) => {
|
||||
profileWebsocket = new WebSocket("ws://"+profiles[floID].onionAddr+"/ws");
|
||||
profileWebsocket.onopen = function(ev){
|
||||
resolve("Connected to Profile Server!");
|
||||
};
|
||||
profileWebsocket.onerror = function(ev) {
|
||||
reject("Profile Server is offline!");
|
||||
};
|
||||
profileWebsocket.onclose = function(ev) {
|
||||
console.log("Disconnected from Profile Server!")
|
||||
};
|
||||
profileWebsocket.onmessage = function(evt){
|
||||
console.log(evt.data);
|
||||
profileWebsocket.onopen = (event) => { resolve("Connected to Profile Server!") };
|
||||
profileWebsocket.onerror = (event) => { reject("Profile Server is offline!") };
|
||||
profileWebsocket.onclose = (event) => { console.log("Disconnected from Profile Server!") };
|
||||
profileWebsocket.onmessage = (event) => {
|
||||
console.log(event.data);
|
||||
try{
|
||||
var data = JSON.parse(evt.data);
|
||||
var data = JSON.parse(event.data);
|
||||
var id = data.id;
|
||||
data = data.data;
|
||||
if( floID!=data.floID || !encrypt.verify(data.tweet,data.sign,profiles[floID].pubKey))
|
||||
@ -157,76 +149,41 @@ function connectToX(floID){
|
||||
storeTweet({floID:floID,time:data.time,data:data.tweet},id);
|
||||
createTweetElement(floID,data.time,data.tweet);
|
||||
}catch(error){
|
||||
console.log(error.message);
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function storeTweet(data,id){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
console.log("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("tweets", "readwrite").objectStore("tweets");
|
||||
data.tweetID = `${data.time}_${data.floID}`;
|
||||
obs.add(data);
|
||||
var obsL = db.transaction("lastTweet", "readwrite").objectStore("lastTweet");
|
||||
obsL.put(id,data.floID);
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
|
||||
function storeMsg(data){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onerror = function(event) {
|
||||
console.log("Error in opening IndexedDB!");
|
||||
};
|
||||
idb.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("messages", "readwrite").objectStore("messages");
|
||||
data.msgID = `${data.time}_${data.floID}`;
|
||||
obs.add(data);
|
||||
db.close();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function pingSuperNodeforNewTweets(floID){
|
||||
getLastTweetCount(floID).then(function(result){
|
||||
var data = JSON.stringify({reqNewTweets:true,floID:floID,tid:result,requestor:selfID})
|
||||
sendDataToSuperNode(floID,data);
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
});
|
||||
getLastTweetCount(floID).then(result => {
|
||||
var data = JSON.stringify({reqNewTweets:true,floID:floID,tid:result,requestor:selfID})
|
||||
sendDataToSuperNode(floID,data);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function getTweetsFromX(floID){
|
||||
return new Promise(
|
||||
function (resolve,reject){
|
||||
getLastTweetCount(floID).then(function(result){
|
||||
console.log(profileWebsocket);
|
||||
profileWebsocket.send(`>${result}`);
|
||||
console.log("sent");
|
||||
resolve('Sent New tweet request to user server!');
|
||||
}).catch(function(error){
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
);
|
||||
function pingProfileServerforNewTweets(floID){
|
||||
getLastTweetCount(floID).then(result => {
|
||||
console.log(profileWebsocket);
|
||||
profileWebsocket.send(`>${result}`);
|
||||
console.log("Sent New tweet request to user server!");
|
||||
//resolve('Sent New tweet request to user server!');
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function getLastTweetCount(floid){
|
||||
return new Promise(
|
||||
function(resolve,reject){
|
||||
return new Promise((resolve,reject) => {
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var lastTweet = db.transaction('lastTweet', "readwrite").objectStore('lastTweet');
|
||||
var lastTweetReq = lastTweet.get(floid);
|
||||
lastTweetReq.onsuccess = function(event){
|
||||
lastTweetReq.onsuccess = (event) => {
|
||||
var result = event.target.result;
|
||||
if(result === undefined)
|
||||
result = 0;
|
||||
@ -234,29 +191,29 @@ function getLastTweetCount(floid){
|
||||
}
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function initselfWebSocket(){
|
||||
selfWebsocket = new WebSocket("ws://"+location.host+"/ws");
|
||||
selfWebsocket.onopen = function(evt){
|
||||
selfWebsocket.onopen = (event) => {
|
||||
console.log("Connecting");
|
||||
var serverPass = encrypt.retrieveShamirSecret(JSON.parse(sessionStorage.serverPass));
|
||||
selfWebsocket.send("$"+serverPass);
|
||||
};
|
||||
selfWebsocket.onclose = function(evt){
|
||||
selfWebsocket.onclose = (event) => {
|
||||
console.log("DISCONNECTED");
|
||||
initselfWebSocket();
|
||||
};
|
||||
selfWebsocket.onmessage = function(evt){
|
||||
console.log(evt.data);
|
||||
if(evt.data[0] == '$')
|
||||
selfWebsocket.onmessage = (event) => {
|
||||
console.log(event.data);
|
||||
if(event.data[0] == '$')
|
||||
return;
|
||||
try{
|
||||
data = JSON.parse(evt.data);
|
||||
data = JSON.parse(event.data);
|
||||
if(data.follow && encrypt.verify(selfID, data.sign, profiles[data.floID].pubKey)){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("followers", "readwrite").objectStore("followers");
|
||||
obs.add(data.sign,data.floID);
|
||||
@ -265,7 +222,7 @@ function initselfWebSocket(){
|
||||
selfWebsocket.send(`F${data.floID}-${data.sign}`);
|
||||
}else if(data.unfollow && encrypt.verify(selfID, data.sign, profiles[data.floID].pubKey)){
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("followers", "readwrite").objectStore("followers");
|
||||
obs.delete(data.floID);
|
||||
@ -283,14 +240,14 @@ function initselfWebSocket(){
|
||||
storeTweet({floID:data.floID,time:data.time,data:data.tweet},tid);
|
||||
createTweetElement(data.floID,data.time,data.tweet);
|
||||
}
|
||||
}else if(modSuperNode){
|
||||
superNodeMode(data);
|
||||
}
|
||||
}catch(error){
|
||||
console.log(error.message)
|
||||
console.log(error)
|
||||
}
|
||||
};
|
||||
selfWebsocket.onerror = function(evt){
|
||||
console.log(evt);
|
||||
};
|
||||
selfWebsocket.onerror = (event) => { console.log(event) };
|
||||
}
|
||||
|
||||
function follow(){
|
||||
@ -301,12 +258,12 @@ function follow(){
|
||||
if(profileServerStatus)
|
||||
profileWebsocket.send(data);
|
||||
else{
|
||||
var SNdata = JSON.stringify({viaSuperNodeMsg:true,from:selfID,to:profileID,data:data})
|
||||
sendDataToSuperNode(profileID,SNdata)
|
||||
var SNdata = JSON.stringify({viaSuperNodeMsg:true,from:selfID,to:profileID,data:data});
|
||||
sendDataToSuperNode(profileID,SNdata);
|
||||
}
|
||||
selfWebsocket.send(`f${profileID}-${sign}`)
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("following", "readwrite").objectStore("following");
|
||||
obs.add(sign,profileID);
|
||||
@ -321,12 +278,12 @@ function follow(){
|
||||
if(profileServerStatus)
|
||||
profileWebsocket.send(data);
|
||||
else{
|
||||
var SNdata = JSON.stringify({viaSuperNodeMsg:true,from:selfID,to:profileID,data:data})
|
||||
sendDataToSuperNode(profileID,SNdata)
|
||||
var SNdata = JSON.stringify({viaSuperNodeMsg:true,from:selfID,to:profileID,data:data});
|
||||
sendDataToSuperNode(profileID,SNdata);
|
||||
}
|
||||
selfWebsocket.send(`u${profileID}`)
|
||||
var idb = indexedDB.open("FLO_Tweet");
|
||||
idb.onsuccess = function(event) {
|
||||
idb.onsuccess = (event) => {
|
||||
var db = event.target.result;
|
||||
var obs = db.transaction("following", "readwrite").objectStore("following");
|
||||
obs.delete(profileID);
|
||||
@ -336,4 +293,3 @@ function follow(){
|
||||
followBtn.innerHTML = "+ Follow";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
util/FLO_Twitter.apk
Normal file
BIN
util/FLO_Twitter.apk
Normal file
Binary file not shown.
316
util/floTwitterWSS_Android.c
Normal file
316
util/floTwitterWSS_Android.c
Normal file
@ -0,0 +1,316 @@
|
||||
#include "mongoose.h"
|
||||
static sig_atomic_t s_signal_received = 0;
|
||||
static const char *s_http_port = "3232";
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
static char serverpass[100];
|
||||
static struct mg_connection *selfClient = NULL;
|
||||
|
||||
struct tweetData{
|
||||
int id;
|
||||
char data[5000];
|
||||
};
|
||||
|
||||
struct followData{
|
||||
char floID[35];
|
||||
char sign[150];
|
||||
};
|
||||
|
||||
static void signal_handler(int sig_num) {
|
||||
signal(sig_num, signal_handler); // Reinstantiate signal handler
|
||||
s_signal_received = sig_num;
|
||||
}
|
||||
|
||||
static int is_websocket(const struct mg_connection *nc) {
|
||||
return nc->flags & MG_F_IS_WEBSOCKET;
|
||||
}
|
||||
|
||||
static void broadcast(struct mg_connection *nc, const struct mg_str msg) {
|
||||
struct mg_connection *c;
|
||||
char buf[5000];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%.*s", (int) msg.len, msg.p);
|
||||
printf("%s\n", buf); /* Local echo. */
|
||||
for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) {
|
||||
if (c == nc) continue; /* Don't send to the sender. */
|
||||
mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, buf, strlen(buf));
|
||||
}
|
||||
}
|
||||
|
||||
static void unicast(struct mg_connection *nc,const struct mg_str msg) {
|
||||
char buf[5000];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%.*s", (int) msg.len, msg.p);
|
||||
printf("%s\n", buf); /* Local echo. */
|
||||
if(nc != NULL)
|
||||
mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, buf, strlen(buf));
|
||||
else
|
||||
printf("No selfClient is connected!\n");
|
||||
|
||||
}
|
||||
|
||||
static void storeTweet(struct mg_connection *nc,const struct mg_str msg){
|
||||
struct tweetData tweet;
|
||||
snprintf(tweet.data, sizeof(tweet.data), "%.*s", (int) msg.len, msg.p);
|
||||
FILE *fptr;
|
||||
fptr = fopen("/storage/emulated/0/FLO_Twitter/tweet.bin","ab");
|
||||
if(fptr == NULL){
|
||||
printf("Error in opening tweet.bin\n");
|
||||
return;
|
||||
}
|
||||
fseek(fptr,0,SEEK_END);
|
||||
long int filesize = ftell(fptr);
|
||||
tweet.id = filesize/sizeof(tweet) + 1;
|
||||
fwrite(&tweet,sizeof(tweet),1,fptr);
|
||||
fclose(fptr);
|
||||
//broadcast
|
||||
struct mg_connection *c;
|
||||
char buf[5050];
|
||||
snprintf(buf, sizeof(buf), "{\"id\":%d,\"data\":%s}", tweet.id, tweet.data);
|
||||
printf("%s\n", buf); /* Local echo. */
|
||||
for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) {
|
||||
if (c == nc) continue; /* Don't send to the sender. */
|
||||
mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, buf, strlen(buf));
|
||||
}
|
||||
}
|
||||
|
||||
static void sendTweets(struct mg_connection *nc, const struct mg_str d){
|
||||
int i, n = 0;
|
||||
for(i=1; i < d.len; i++)
|
||||
n = (n*10) + (d.p[i] - '0');
|
||||
//printf("%d\n",n);
|
||||
struct tweetData tweet;
|
||||
char buf[5050];
|
||||
FILE *fptr;
|
||||
fptr = fopen("/storage/emulated/0/FLO_Twitter/tweet.bin","rb");
|
||||
if(fptr == NULL){
|
||||
printf("Error in opening tweet.bin\n");
|
||||
return;
|
||||
}
|
||||
fseek(fptr, n*sizeof(tweet), SEEK_SET);
|
||||
while(fread(&tweet,sizeof(tweet),1,fptr)){
|
||||
snprintf(buf, sizeof(buf), "{\"id\":%d,\"data\":%s}", tweet.id, tweet.data);
|
||||
printf("%s\n", buf);
|
||||
mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, buf, strlen(buf));
|
||||
}
|
||||
fclose(fptr);
|
||||
}
|
||||
|
||||
static void Follower(const struct mg_str d){
|
||||
struct followData data;
|
||||
snprintf(data.floID, sizeof(data.floID), "%.*s", (int) (34), &d.p[1]);
|
||||
snprintf(data.sign, sizeof(data.sign), "%.*s", (int) (d.len-36), &d.p[36]);
|
||||
printf("Follower : %s\n",data.floID);
|
||||
FILE *fptr;
|
||||
fptr = fopen("/storage/emulated/0/FLO_Twitter/followers.bin","ab");
|
||||
if(fptr == NULL){
|
||||
printf("Error in opening followers.bin\n");
|
||||
return;
|
||||
}
|
||||
fseek(fptr,0,SEEK_END);
|
||||
fwrite(&data,sizeof(data),1,fptr);
|
||||
fclose(fptr);
|
||||
}
|
||||
|
||||
static void Unfollower(const struct mg_str d){
|
||||
char floID[35];
|
||||
struct followData data;
|
||||
snprintf(floID, sizeof(floID), "%.*s", (int) (34), &d.p[1]);
|
||||
printf("Unfollower : %s\n",floID);
|
||||
FILE *fp;
|
||||
FILE *fp_tmp;
|
||||
fp = fopen("/storage/emulated/0/FLO_Twitter/followers.bin", "rb");
|
||||
if (!fp) {
|
||||
printf("Error in opening followers.bin\n");
|
||||
return;
|
||||
}
|
||||
fp_tmp = fopen("/storage/emulated/0/FLO_Twitter/tmp.bin", "wb");
|
||||
if (!fp) {
|
||||
printf("Error in opening tmp.bin\n");
|
||||
return;
|
||||
}
|
||||
while(fread(&data,sizeof(data),1,fp)){
|
||||
if(strcmp(data.floID,floID)) //floID != follower.floID
|
||||
fwrite(&data,sizeof(data),1,fp_tmp);
|
||||
}
|
||||
fclose(fp);
|
||||
fclose(fp_tmp);
|
||||
remove("/storage/emulated/0/FLO_Twitter/followers.bin");
|
||||
rename("/storage/emulated/0/FLO_Twitter/tmp.bin", "followers.bin");
|
||||
return;
|
||||
}
|
||||
|
||||
static void follow(const struct mg_str d){
|
||||
struct followData data;
|
||||
snprintf(data.floID, sizeof(data.floID), "%.*s", (int) (34), &d.p[1]);
|
||||
snprintf(data.sign, sizeof(data.sign), "%.*s", (int) (d.len-36), &d.p[36]);
|
||||
printf("follow : %s\n",data.floID);
|
||||
FILE *fptr;
|
||||
fptr = fopen("/storage/emulated/0/FLO_Twitter/following.bin","ab");
|
||||
if(fptr == NULL){
|
||||
printf("Error in opening following.bin\n");
|
||||
return;
|
||||
}
|
||||
fseek(fptr,0,SEEK_END);
|
||||
fwrite(&data,sizeof(data),1,fptr);
|
||||
fclose(fptr);
|
||||
}
|
||||
|
||||
static void unfollow(const struct mg_str d){
|
||||
char floID[35];
|
||||
struct followData data;
|
||||
snprintf(floID, sizeof(floID), "%.*s", (int) (34), &d.p[1]);
|
||||
printf("unfollow : %s\n",floID);
|
||||
FILE *fp;
|
||||
FILE *fp_tmp;
|
||||
fp = fopen("/storage/emulated/0/FLO_Twitter/following.bin", "rb");
|
||||
if (!fp) {
|
||||
printf("Error in opening following.bin\n");
|
||||
return;
|
||||
}
|
||||
fp_tmp = fopen("/storage/emulated/0/FLO_Twitter/tmp.bin", "wb");
|
||||
if (!fp) {
|
||||
printf("Error in opening tmp.bin\n");
|
||||
return;
|
||||
}
|
||||
while(fread(&data,sizeof(data),1,fp)){
|
||||
if(strcmp(data.floID,floID)) //floID != follower.floID
|
||||
fwrite(&data,sizeof(data),1,fp_tmp);
|
||||
}
|
||||
fclose(fp);
|
||||
fclose(fp_tmp);
|
||||
remove("/storage/emulated/0/FLO_Twitter/following.bin");
|
||||
rename("/storage/emulated/0/FLO_Twitter/tmp.bin", "following.bin");
|
||||
return;
|
||||
}
|
||||
|
||||
static void storeIncoming(const struct mg_str msg){
|
||||
char buf[5000];
|
||||
snprintf(buf, sizeof(buf), "%.*s", (int) msg.len, msg.p);
|
||||
FILE *fptr;
|
||||
fptr = fopen("/storage/emulated/0/FLO_Twitter/incoming.bin","ab");
|
||||
if(fptr == NULL){
|
||||
printf("Error in opening incoming.bin\n");
|
||||
return;
|
||||
}
|
||||
fseek(fptr,0,SEEK_END);
|
||||
fwrite(&buf,sizeof(buf),1,fptr);
|
||||
fclose(fptr);
|
||||
}
|
||||
|
||||
static void forwardIncomings(){
|
||||
char buf[5000];
|
||||
FILE *fptr;
|
||||
fptr = fopen("/storage/emulated/0/FLO_Twitter/incoming.bin","rb");
|
||||
if(fptr == NULL){
|
||||
printf("No new Incomings\n");
|
||||
return;
|
||||
}
|
||||
while(fread(&buf,sizeof(buf),1,fptr)){
|
||||
mg_send_websocket_frame(selfClient, WEBSOCKET_OP_TEXT, buf, strlen(buf));
|
||||
}
|
||||
fclose(fptr);
|
||||
remove("/storage/emulated/0/FLO_Twitter/incoming.bin");
|
||||
}
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
switch (ev) {
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
|
||||
/* New websocket connection. Tell everybody. */
|
||||
//broadcast(nc, mg_mk_str("++ joined"));
|
||||
break;
|
||||
}
|
||||
case MG_EV_WEBSOCKET_FRAME: {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
/* New websocket message. Tell everybody. */
|
||||
struct mg_str d = {(char *) wm->data, wm->size};
|
||||
if(selfClient == nc){
|
||||
if(d.p[0] == 'F')
|
||||
Follower(d);
|
||||
else if(d.p[0] == 'U')
|
||||
Unfollower(d);
|
||||
else if(d.p[0] == 'f')
|
||||
follow(d);
|
||||
else if(d.p[0] == 'u')
|
||||
unfollow(d);
|
||||
else
|
||||
storeTweet(nc, d);
|
||||
}else{
|
||||
if (d.p[0] == '$'){
|
||||
char pass[100];
|
||||
snprintf(pass, sizeof(pass), "%.*s",(int)d.len-1, &d.p[1]);
|
||||
if(!strcmp(pass,serverpass)){
|
||||
if(selfClient!=NULL)
|
||||
unicast(selfClient,mg_mk_str("$Another login is encountered! Please close/refresh this window"));
|
||||
selfClient = nc;
|
||||
unicast(selfClient,mg_mk_str("$Access Granted!"));
|
||||
forwardIncomings();
|
||||
}else
|
||||
unicast(nc,mg_mk_str("$Access Denied!"));
|
||||
}
|
||||
else if(d.p[0] == '#'){
|
||||
if(selfClient == NULL)
|
||||
unicast(nc,mg_mk_str("#-"));
|
||||
else
|
||||
unicast(nc,mg_mk_str("#+"));
|
||||
}
|
||||
else if(d.p[0] == '>'){
|
||||
sendTweets(nc, d);
|
||||
}
|
||||
else {
|
||||
if(selfClient==NULL)
|
||||
storeIncoming(d);
|
||||
else
|
||||
unicast(selfClient,d);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
mg_serve_http(nc, (struct http_message *) ev_data, s_http_server_opts);
|
||||
break;
|
||||
}
|
||||
case MG_EV_CLOSE: {
|
||||
/* Disconnect. Tell everybody. */
|
||||
if (is_websocket(nc)) {
|
||||
if(nc == selfClient){
|
||||
selfClient = NULL;
|
||||
broadcast(nc, mg_mk_str("#-"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
if(argc<=1){
|
||||
printf("Enter server password : ");
|
||||
scanf("%s",serverpass);
|
||||
}
|
||||
else
|
||||
strcpy(serverpass,argv[1]);
|
||||
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
setvbuf(stderr, NULL, _IOLBF, 0);
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = "/storage/emulated/0/FLO_Twitter/app/web/"; // Serve current directory
|
||||
s_http_server_opts.enable_directory_listing = "no";
|
||||
|
||||
printf("Started on port %s\n", s_http_port);
|
||||
while (s_signal_received == 0) {
|
||||
mg_mgr_poll(&mgr, 200);
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user