498 lines
10 KiB
JavaScript
498 lines
10 KiB
JavaScript
|
|
// Load the settings
|
|
var settings = require("./settings.json");
|
|
|
|
// Create an rsa key
|
|
var rsa_key;
|
|
|
|
function toBuffer(bytes)
|
|
{
|
|
// String
|
|
if(typeof(bytes) == 'string')
|
|
{
|
|
// Create a buffer
|
|
var buffer = new Buffer.alloc(bytes.length);
|
|
|
|
// Loop over the bytes
|
|
for(var i=0;i<bytes.length;i++)
|
|
{
|
|
// Save to the buffer
|
|
buffer[i] = bytes[i].charCodeAt(0);
|
|
}
|
|
|
|
// Return the buffer
|
|
return buffer;
|
|
}
|
|
|
|
// Return if this is anything else
|
|
return bytes;
|
|
}
|
|
|
|
function toBytes(buffer)
|
|
{
|
|
// Make sure the buffer is a buffer
|
|
buffer = toBuffer(buffer);
|
|
|
|
// Create some bytes
|
|
var bytes = "";
|
|
|
|
// Loop over the buffer
|
|
for(var i=0;i<buffer.length;i++)
|
|
{
|
|
// Add to the bytes string
|
|
bytes += String.fromCharCode(buffer[i]);
|
|
}
|
|
|
|
// Return the bytes
|
|
return bytes;
|
|
}
|
|
|
|
// Recieve helper global varibles
|
|
var recieve_buffer = new Buffer.alloc(0);
|
|
var recieve_get = false;
|
|
var recieve_size = 0;
|
|
|
|
function recieve_ordered(data, callback)
|
|
{
|
|
// Convert the data into a buffer
|
|
data = toBuffer(data);
|
|
|
|
// Loop over the data
|
|
for(var i=0;i<data.length;i++)
|
|
{
|
|
// Add the data to the buffer
|
|
recieve_buffer = new Buffer.concat([recieve_buffer, Buffer.from([data[i]])]);
|
|
|
|
// Is the buffer getting data
|
|
if(!recieve_get)
|
|
{
|
|
// Does the buffer contain a number
|
|
if(recieve_buffer.length >= 4)
|
|
{
|
|
// Get the number
|
|
recieve_size = recieve_buffer.readUInt32BE(0);
|
|
|
|
// Reset the buffer
|
|
recieve_buffer = new Buffer.alloc(0);
|
|
|
|
// Set the get data mode to true
|
|
recieve_get = true;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
// Is the recieve buffer as big as the size
|
|
if(recieve_buffer.length == recieve_size)
|
|
{
|
|
// Call the callback
|
|
callback(recieve_buffer);
|
|
|
|
// Reset the buffer and the size
|
|
recieve_buffer = new Buffer.alloc(0);
|
|
recieve_size = 0;
|
|
|
|
// Set the get data mode to false
|
|
recieve_get = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function send_ordered(data)
|
|
{
|
|
// Convert the data into a buffer
|
|
data = toBuffer(data);
|
|
|
|
// Get the size of the data
|
|
var size = data.length;
|
|
|
|
// Turn it into a 32 bit buffer
|
|
var bytes = new Buffer.alloc(4);
|
|
bytes.writeUInt32BE(size, 0);
|
|
|
|
// Return the buffer and the data
|
|
return new Buffer.concat([bytes, data]);
|
|
}
|
|
|
|
function string_decrypt(key, string)
|
|
{
|
|
// Convert the string to a buffer
|
|
var buff = toBuffer(string);
|
|
|
|
// Create some new data
|
|
var decrypted = new Buffer.alloc(buff.length);
|
|
|
|
// Iterate over the string
|
|
for(var i=0;i<string.length;i++)
|
|
{
|
|
// Convert the string item to a number
|
|
var d = buff[i]-key.str[key.at.rx];
|
|
while(d < 0) d += 256;
|
|
decrypted[i] = d;
|
|
|
|
// Add 1 to the key counter
|
|
key.at.rx += 1;
|
|
|
|
// Is the key counter out of range
|
|
if(key.at.rx >= key.str.length)
|
|
{
|
|
// Set to zero
|
|
key.at.rx = 0;
|
|
}
|
|
}
|
|
|
|
// Return the encrypted data
|
|
return decrypted;
|
|
}
|
|
|
|
function string_encrypt(key, string)
|
|
{
|
|
// Convert the string to a buffer
|
|
var buff = toBuffer(string);
|
|
|
|
// Create some new data
|
|
var encrypted = new Buffer.alloc(buff.length);
|
|
|
|
// Iterate over the string
|
|
for(var i=0;i<string.length;i++)
|
|
{
|
|
// Convert the string item to a number
|
|
var e = buff[i]+key.str[key.at.tx];
|
|
while(e > 255) e -= 256;
|
|
encrypted[i] = e;
|
|
|
|
// Add 1 to the key counter
|
|
key.at.tx += 1;
|
|
|
|
// Is the key counter out of range
|
|
if(key.at.tx >= key.str.length)
|
|
{
|
|
// Set to zero
|
|
key.at.tx = 0;
|
|
}
|
|
}
|
|
|
|
// Return the encrypted data
|
|
return encrypted;
|
|
}
|
|
|
|
function make_encryption_key(string)
|
|
{
|
|
// Make a new key
|
|
var key = new Object();
|
|
|
|
// Set the varibles
|
|
key.str = toBuffer(string);
|
|
key.at = new Object();
|
|
key.at.rx = 0;
|
|
key.at.tx = 0;
|
|
|
|
//console.log("make_encryption_key:", key.str);
|
|
|
|
// Return the key
|
|
return key;
|
|
}
|
|
|
|
function socket_write(socket, encryption, data)
|
|
{
|
|
//console.log("socket_write() b4:", data);
|
|
|
|
// Convert the data to JSON
|
|
data = JSON.stringify(data);
|
|
|
|
//console.log("socket_write() stringify:", data);
|
|
|
|
// Encrypt the data
|
|
data = string_encrypt(encryption, data);
|
|
|
|
//console.log("socket_write() encrypt:", data);
|
|
|
|
// Order the request
|
|
data = send_ordered(data);
|
|
|
|
//console.log("socket_write() ordered:", data);
|
|
|
|
// Send the data
|
|
socket.write(data);
|
|
}
|
|
|
|
function connect(profile, connection_id)
|
|
{
|
|
// Setup the port and hostname varibles
|
|
var port = profile.port;
|
|
var hostname = profile.hostname;
|
|
|
|
// Make an accessible global object
|
|
var g = new Object();
|
|
|
|
// Setup some global varibles
|
|
g.chats = [];
|
|
g.users = {};
|
|
|
|
// RSA child task
|
|
g.rsa_task = child_process.fork(
|
|
path.resolve('rsa.js'), [],
|
|
{
|
|
stdio: [ 'pipe', 'pipe', 'pipe', 'ipc' ],
|
|
silent: false
|
|
}
|
|
);
|
|
|
|
g.rsa_task.stdout.on('data', function(data)
|
|
{
|
|
//console.log("child stdout:", data.toString());
|
|
});
|
|
|
|
g.rsa_task.stderr.on('data', function(data)
|
|
{
|
|
console.log("child stderr:", data.toString());
|
|
});
|
|
|
|
g.rsa_task.on('error', function(error)
|
|
{
|
|
console.log("Error on child process:", error);
|
|
})
|
|
|
|
g.rsa_task.on('exit', function(code, signal)
|
|
{
|
|
console.log("Child process closed:", code, signal);
|
|
});
|
|
|
|
// Encryption varibles
|
|
g.encryption;
|
|
|
|
// Set new
|
|
g.sock_new = 2;
|
|
|
|
// Set a temporary encryption varible
|
|
g.raw_encryption_data = "";
|
|
g.raw_encryption_data_upto = 0;
|
|
g.raw_encryption_data_size = 0;
|
|
|
|
// Create a client
|
|
var client = new net.Socket();
|
|
g.client = client;
|
|
|
|
function rsa_task_send(data)
|
|
{
|
|
//console.log("Send to child:", JSON.stringify(data));
|
|
g.rsa_task.send(JSON.stringify(data));
|
|
}
|
|
|
|
// Connect to the server
|
|
client.connect(port, hostname, function()
|
|
{
|
|
// Load the RSA key
|
|
rsa_task_send({
|
|
mode: "load"
|
|
});
|
|
});
|
|
|
|
// Wait for data
|
|
client.on('data', function(data)
|
|
{
|
|
// Recieve data in order
|
|
recieve_ordered(data, function(data)
|
|
{
|
|
/*console.log("RAW: ", {
|
|
input: data,
|
|
sock_new:g.sock_new,
|
|
raw_encryption_data_upto: g.raw_encryption_data_upto,
|
|
bsplit_length: bsplit(data, Buffer.from("\n")).length
|
|
});*/
|
|
|
|
if(g.sock_new != 0)
|
|
{
|
|
// Parse the string
|
|
data = JSON.parse(toBytes(data));
|
|
|
|
// Is this the key
|
|
if(data.mode == "encryption_key")
|
|
{
|
|
// Send the key to be decrypted
|
|
g.rsa_task.send(JSON.stringify({
|
|
mode: "decrypt",
|
|
data: data.key
|
|
}));
|
|
|
|
// Set sock new
|
|
g.sock_new = 0;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
// Decrypt the data
|
|
data = string_decrypt(g.encryption, data);
|
|
|
|
// Convert it from JSON
|
|
data = JSON.parse(data);
|
|
|
|
// Is this an error
|
|
if(data.mode == "error")
|
|
{
|
|
// Is this an authentication error
|
|
if(data.error == "auth")
|
|
{
|
|
// Destroy the connection
|
|
client.destroy();
|
|
|
|
// Delete the connections
|
|
delete connections[connection_id];
|
|
delete profiles[connection_id];
|
|
|
|
// Update the profiles
|
|
profiles_export();
|
|
profiles_reload();
|
|
}
|
|
}
|
|
|
|
// Logged in
|
|
if(data.mode == "login")
|
|
{
|
|
console.log("Recieved details:", data.chats, data.users);
|
|
|
|
// Save the varibles sent
|
|
g.users = data.users;
|
|
g.chats = data.chats;
|
|
|
|
// Switch to this chat
|
|
profile_switch_to(connection_id);
|
|
}
|
|
|
|
// New user
|
|
if(data.mode == "new_user")
|
|
{
|
|
console.log("User signed up")
|
|
|
|
// Set the data
|
|
g.users[data.user] = data.data;
|
|
|
|
// Is this screen active
|
|
if(active_profile == connection_id)
|
|
{
|
|
// Refresh this profile
|
|
profile_switch_to(connection_id);
|
|
}
|
|
}
|
|
|
|
// New chat
|
|
if(data.mode == "new_chat")
|
|
{
|
|
console.log("New chat created");
|
|
|
|
// Set the data
|
|
g.chats.push({
|
|
name: data.name,
|
|
messages: []
|
|
});
|
|
|
|
// Update the screen if its active
|
|
if(active_profile == connection_id)
|
|
{
|
|
// Refresh this profile
|
|
profile_switch_to(connection_id);
|
|
}
|
|
}
|
|
|
|
// New message
|
|
if(data.mode == "new_message")
|
|
{
|
|
console.log("Recieved new message from "+data.from
|
|
+" in channel "+data.channel.toString());
|
|
|
|
// Push the new messages to the chat
|
|
g.chats[data.channel].messages.push({
|
|
message: data.message,
|
|
from: data.from
|
|
});
|
|
|
|
// Is the chat and server active
|
|
if(data.channel == active_chat && connection_id == active_profile)
|
|
{
|
|
// Refresh the chat
|
|
chat_switch_to(active_chat);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
client.on('close', function(data)
|
|
{
|
|
console.log("Connection closed:", connection_id)
|
|
|
|
// Close the connection
|
|
delete connections[connection_id];
|
|
|
|
// Attempt to restart the connection in 10 seconds
|
|
console.log("Attempting reconnection...")
|
|
setTimeout(function()
|
|
{
|
|
// Attempt reconnection
|
|
console.log("Attempted reconection.");
|
|
try
|
|
{
|
|
profile_connect(connection_id);
|
|
}
|
|
|
|
catch(e)
|
|
{
|
|
console.log("Caught error", e);
|
|
}
|
|
},
|
|
1000);
|
|
});
|
|
|
|
g.rsa_task.on('message', function(message)
|
|
{
|
|
//console.log(message)
|
|
|
|
// Parse the message from JSON
|
|
var data = JSON.parse(message);
|
|
|
|
// Is this a rsa load response
|
|
if(data['mode'] == 'load')
|
|
{
|
|
// Get the rsa key
|
|
g.rsa_task.send(JSON.stringify({
|
|
mode: "get"
|
|
}));
|
|
}
|
|
|
|
// Is this an rsa get response
|
|
if(data['mode'] == 'get')
|
|
{
|
|
// Send the public key to the server
|
|
client.write(send_ordered(JSON.stringify({
|
|
key: data['public'],
|
|
mode: "pubkey"
|
|
})));
|
|
}
|
|
|
|
// Is this an rsa data decrypt response
|
|
if(data['mode'] == 'decrypt')
|
|
{
|
|
// Set the key
|
|
g.encryption = make_encryption_key(atob(data['out']));
|
|
|
|
//console.log("Sending 1:", data['out']);
|
|
|
|
// Send login
|
|
console.log("Sending login")
|
|
socket_write(client, g.encryption, {
|
|
mode: "login",
|
|
username: profile.username,
|
|
password: profile.password
|
|
});
|
|
|
|
// Save the details
|
|
g.username = profile.username;
|
|
}
|
|
});
|
|
|
|
// Return the global varibles
|
|
return g;
|
|
}
|