Compare commits

...

22 Commits

Author SHA1 Message Date
Sai Raj
211b8402a1
Merge pull request #3 from sairajzero/master 2019-08-10 00:06:07 +05:30
Sai Raj
7e946b958d
Update README.md 2019-08-09 23:56:05 +05:30
Sai Raj
4b06885688
Create README.md 2019-08-09 23:54:26 +05:30
Sai Raj
a8f15adeec
bug fix 2019-08-09 20:35:44 +05:30
Sai Raj
6ab215b346
Adding Android apk for FloTwitterWSS 2019-08-09 20:13:08 +05:30
Sai Raj
a3125b8ed1
Create floTwitterWSS_Android.c 2019-08-09 20:11:43 +05:30
Sai Raj
9a9e0d0664
Rename websocket_chat.c to floTwitterWSS_linux.c 2019-08-09 20:09:58 +05:30
Sai Raj
72778f8c4a
bug fix 2019-08-09 15:40:09 +05:30
Sai Raj
29bccda9ae
bug fix 2019-08-09 15:39:10 +05:30
Sai Raj
196ae1c30e
Merge pull request #2 from sairajzero/master 2019-08-09 11:51:06 +05:30
sairajzero
24c4542d92 bug fix 2019-07-24 16:59:06 +05:30
sairajzero
ed9e88ad2c Bug fix 2019-07-24 16:19:08 +05:30
sairajzero
ee7776f877 Bug fix 2019-07-24 16:13:01 +05:30
sairajzero
059a5e38de Bug fix 2019-07-24 16:02:14 +05:30
sairajzero
35cbdf0ac1 Bug fix 2019-07-24 15:53:50 +05:30
sairajzero
e7e701cbad Automatically reconnect selfWebsocket and reloadAPI
SelfWebSocket will now automatically attempt to reconnect when its disconnected
Added setInterval to reloadAPIdata (only for superNodes)
2019-07-24 15:26:22 +05:30
sairajzero
3456d372f0 Bug fix : reloadInitData - add new data to sessionStorage too 2019-07-24 15:13:55 +05:30
sairajzero
e8c7680050 Merge branch 'master' of https://github.com/sairajzero/FLO_Twitter 2019-07-24 15:05:57 +05:30
sairajzero
20c8f124e5 Adding reload API data button
The reload API data button (top-right) refreshes the API for new data
The refreshed API data are added to memory as well as stored in IDB.
The kBucket is reloaded
2019-07-24 15:01:21 +05:30
Sai Raj
629f3fb089
Update .gitattributes 2019-07-24 03:56:36 +05:30
Sai Raj
2c405ec3d3
Create .gitattributes 2019-07-24 03:50:06 +05:30
sairajzero
174b9fddf6 Improved user login and stability of funtions
Old prompt based user login and server connect are replaced with login page
Improved stability and simplification of functions
Moved all superNode functions to app.js
Supernode mode will now work in all pages (home,profile,msg) instead of just home
2019-07-24 03:01:29 +05:30
15 changed files with 8140 additions and 7626 deletions

6
.gitattributes vendored Normal file
View 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
View 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

View File

@ -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);
});
}

View File

@ -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>

View File

@ -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) };
}

View File

@ -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

File diff suppressed because it is too large Load Diff

336
app/web/login.js Normal file
View 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';">&#10006;</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();">&#10004;</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;
}

View File

@ -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>

View File

@ -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();
};
}

View File

@ -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>

View File

@ -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

Binary file not shown.

View 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;
}