* key API changes moxie made because he disliked the other API
 * remove atmosphere
 * Fix some bugs in the send path, update for new send API
 * Send HTML
This commit is contained in:
Matt Corallo 2014-03-25 15:27:19 -04:00
parent 000a5e1440
commit 136a8941c1
8 changed files with 182 additions and 3070 deletions

View file

@ -2,7 +2,6 @@
<head>
<script type="text/javascript" src="js-deps/nacl-common.js"></script>
<script type="text/javascript" src="js-deps/jquery.js"></script>
<script type="text/javascript" src="js-deps/jquery.atmosphere.js"></script>
<script type="text/javascript" src="js-deps/core.js"></script>
<script type="text/javascript" src="js-deps/enc-base64.js"></script>
<script type="text/javascript" src="js-deps/cipher-core.js"></script>
@ -17,7 +16,6 @@
<script type="text/javascript" src="js-deps/ProtoBuf.min.js"></script>
<script type="text/javascript" src="js/helpers.js"></script>
<script type="text/javascript" src="js/api.js"></script>
<script type="text/javascript" src="js/fake_api.js"></script>
<script type="text/javascript" src="js/background.js"></script>
</head>
<body data-name="curve25519" data-tools="pnacl" data-configs="Debug Release" data-path="pnacl/{config}">

File diff suppressed because it is too large Load diff

View file

@ -7,8 +7,8 @@ var URL_CALLS = {};
URL_CALLS['accounts'] = "/v1/accounts";
URL_CALLS['devices'] = "/v1/devices";
URL_CALLS['keys'] = "/v1/keys";
URL_CALLS['push'] = "/v1/messagesocket";
URL_CALLS['messages'] = "/v1/messages/";
URL_CALLS['push'] = "/v1/websocket";
URL_CALLS['messages'] = "/v1/messages";
var API = new function() {
@ -119,16 +119,34 @@ var API = new function() {
call : 'keys',
httpType : 'GET',
do_auth : true,
urlParameters : "/" + getNumberFromString(number) + "?multikeys",
success_callback : success_callback,
urlParameters : "/" + getNumberFromString(number) + "/*",
success_callback : function(response) {
//TODO: Do this conversion somewhere else?
var res = response.keys;
for (var i = 0; i < res.length; i++) {
res[i].identityKey = base64DecToArr(res[i].identityKey);
res[i].publicKey = base64DecToArr(res[i].publicKey);
if (res[i].keyId === undefined)
res[i].keyId = 0;
}
success_callback(res);
},
error_callback : error_callback
});
};
this.sendMessages = function(jsonData, success_callback, error_callback) {
this.sendMessages = function(destination, messageArray, success_callback, error_callback) {
//TODO: Do this conversion somewhere else?
for (var i = 0; i < messageArray.length; i++)
messageArray[i].body = btoa(messageArray[i].body);
var jsonData = { messages: messageArray };
if (messageArray[0].relay !== undefined)
jsonData.relay = messageArray[0].relay;
this.doAjax({
call : 'messages',
httpType : 'POST',
httpType : 'PUT',
urlParameters : '/' + destination,
do_auth : true,
jsonData : jsonData,
success_callback : success_callback,

View file

@ -206,6 +206,11 @@ function getEncodedNumber(number) {
return number;
}
function verifyNumber(string) {
//TODO: fancy country-code guessing and number verification
return getEncodedNumber(string.trim());
}
function getDeviceId(encodedNumber) {
var split = encodedNumber.split(".");
if (split.length > 1)
@ -865,51 +870,51 @@ var crypto_tests = {};
function subscribeToPush(message_callback) {
var user = storage.getUnencrypted("number_id");
var password = storage.getEncrypted("password");
var request = { url: URL_BASE + URL_CALLS['push'] + "/?user=%2B" + getString(user).substring(1) + "&password=" + getString(password),
method: 'GET',
fallbackMethod: 'GET',
transport: 'websocket',
fallbackTransport: 'websocket',
logLevel: 'debug', //TODO
trackMessageLength: false,
//data: "user=" + getString(user) + "&password=" + getString(password),
onOpen: function(response) {
console.log('Connected to server using ' + response.transport);
},
onMessage: function(response) {
try {
// Some bug in Atmosphere.js is forcing trackMessageLength to true
var message = JSON.parse(response.responseBody.split("|")[1]);
} catch (e) {
console.log('Error parsing server JSON message: ' + response.responseBody.split("|")[1]);
return;
}
var URL = URL_BASE.replace(/^http:/g, "ws:").replace(/^https:/g, "wss:") + URL_CALLS['push'] + "/?user=%2B" + getString(user).substring(1) + "&password=" + getString(password);
var socket = new WebSocket(URL);
var proto;
try {
var plaintext = crypto.decryptWebsocketMessage(message.message);
var proto = decodeIncomingPushMessageProtobuf(plaintext);
// After this point, a) decoding errors are not the server's fault, and
// b) we should handle them gracefully and tell the user they received an invalid message
API.pushMessage(message.id);
} catch (e) {
console.log("Error decoding message: " + e);
return;
}
//TODO: GUI
socket.onerror = function(socketEvent) {
console.log('Server is down :(');
setTimeout(function() { subscribeToPush(message_callback); }, 1000);
};
socket.onclose = function(socketEvent) {
console.log('Server closed :(');
setTimeout(function() { subscribeToPush(message_callback); }, 1000);
};
socket.onopen = function(socketEvent) {
console.log('Connected to server!');
};
try {
crypto.handleIncomingPushMessageProto(proto, function(decrypted) {
message_callback(decrypted);
}); // Decrypts/decodes/fills in fields/etc
} catch (e) {
//TODO: Tell the user decryption failed
}
},
onError: function(response) {
console.log('Server is down :(');
//TODO: GUI
}};
$.atmosphere.subscribe(request);
socket.onmessage = function(response) {
try {
// Some bug in Atmosphere.js is forcing trackMessageLength to true
var message = JSON.parse(response.responseBody.split("|")[1]);
} catch (e) {
console.log('Error parsing server JSON message: ' + response.responseBody.split("|")[1]);
return;
}
var proto;
try {
var plaintext = crypto.decryptWebsocketMessage(message.message);
var proto = decodeIncomingPushMessageProtobuf(plaintext);
// After this point, a) decoding errors are not the server's fault, and
// b) we should handle them gracefully and tell the user they received an invalid message
API.pushMessage(message.id);
} catch (e) {
console.log("Error decoding message: " + e);
return;
}
try {
crypto.handleIncomingPushMessageProto(proto, function(decrypted) {
message_callback(decrypted);
}); // Decrypts/decodes/fills in fields/etc
} catch (e) {
//TODO: Tell the user decryption failed
}
};
}
// success_callback(identity_key), error_callback(error_msg)
@ -937,83 +942,98 @@ function getKeysForNumber(number, success_callback, error_callback) {
// success_callback(server success/failure map), error_callback(error_msg)
// message == PushMessageContentProto (NOT STRING)
function sendMessageToDevices(deviceObjectList, message, success_callback, error_callback) {
function sendMessageToDevices(number, deviceObjectList, message, success_callback, error_callback) {
var jsonData = [];
for (var i = 0; i < deviceObjectList.legnth; i++) {
var encryptedMsg = encryptMessageFor(deviceObjectList[i], message);
jsonData[jsonData.length] = {
type: encryptedMsg.type,
destination: deviceObjectList[i].encodedNumber,
body: encryptedMsg.body,
relay: deviceObjectList[i].relay,
timestamp: new Date().getTime()
};
//TODO: need to encrypt with session key?
var relay = undefined;
var doSend = function() {
API.sendMessages(number, jsonData,
function(result) {
success_callback(result);
}, function(code) {
error_callback(code);
}
);
}
API.sendMessages(jsonData,
function(result) {
if (result.missingDeviceIds.length > 0) {
var responsesLeft = result.missingDeviceIds.length;
var errorThrown = 0;
for (var i = 0; i < result.missingDeviceIds.length; i++) {
getKeysForNumber(result.missingDeviceIds[i], function(identity_key) {
responsesLeft--;
if (responsesLeft == 0 && errorThrown == 0)
sendMessageToDevices(deviceObjectList, message, success_callback, error_callback);
}, function(error_msg) {
errorThrown++;
if (errorThrown == 1)
error_callback("Failed to retreive new device keys for number " + result.missingDeviceIds[i]);
});
var addEncryptionFor;
addEncryptionFor = function(i) {
crypto.encryptMessageFor(deviceObjectList[i], message, function(encryptedMsg) {
jsonData[i] = {
type: encryptedMsg.type,
destination: deviceObjectList[i].encodedNumber,
body: encryptedMsg.body,
timestamp: new Date().getTime()
};
if (deviceObjectList[i].relay !== undefined) {
jsonData[i].relay = deviceObjectList[i].relay;
if (relay === undefined)
relay = jsonData[i].relay;
else if (relay != jsonData[i].relay) {
error_callback("Mismatched relays for number " + number);
return;
}
} else {
success_callback(result);
if (relay === undefined)
relay = "";
else if (relay != "") {
error_callback("Mismatched relays for number " + number);
return;
}
}
}, function(code) {
error_callback("Failed to conect to data channel: " + code);
}
);
if (i+1 < deviceObjectList.length)
addEncryptionFor(i+1);
else
doSend();
});
//TODO: need to encrypt with session key?
}
addEncryptionFor(0);
}
// success_callback(success/failure map, see second-to-last line), error_callback(error_msg)
function sendMessageToNumbers(numbers, message, success_callback, error_callback) {
var deviceObjectList = [];
// callback(success/failure map, see code)
// message == PushMessageContentProto (NOT STRING)
function sendMessageToNumbers(numbers, message, callback) {
var numbersCompleted = 0;
var errors = [];
var successfulNumbers = [];
var numberCompleted = function() {
numbersCompleted++;
if (numbersCompleted >= numbers.length)
callback({success: successfulNumbers, failure: errors});
}
var registerError = function(number, message) {
errors[errors.length] = { number: number, reason: message };
numberCompleted();
}
var doSendMessage = function(number, devicesForNumber, message) {
sendMessageToDevices(number, devicesForNumber, message, function(result) {
successfulNumbers[successfulNumbers.length] = number;
numberCompleted();
}, function(error_code) {
//TODO: Re-request keys for number here
if (error_code == 410 || error_code == 409) {}
registerError(number, message);
});
}
var deviceDatasMissing = 0;
var loopDone = 0;
var errorThrown = 0;
for (var i = 0; i < numbers.length; i++) {
var devicesForNumber = getDeviceObjectListFromNumber(numbers[i]);
for (var j = 0; j < devicesForNumber.length; j++)
deviceObjectList[deviceObjectList.length] = devicesForNumber[j];
if (devicesForNumber.length == 0) {
deviceDatasMissing++;
getKeysForNumber(numbers[i], function(identity_key) {
deviceDatasMissing--;
if (deviceDatasMissing == 0 && loopDone && errorThrown == 0)
sendMessageToNumbers(numbers, message, success_callback, error_callback);
doSendMessage(numbers[i], devicesForNumber, message);
}, function(error_msg) {
errorThrown++;
if (errorThrown == 1)
error_callback("Failed to retreive new device keys for number " + numbers[i]);
registerError(numbers[i], "Failed to retreive new device keys for number " + numbers[i]);
});
}
} else
doSendMessage(numbers[i], devicesForNumber, message);
}
if (deviceDatasMissing > 0 || errorThrown > 0) {
loopDone = 1;
return;
}
return sendMessageToDevices(deviceObjectList, message, function(result) {
var successNumbers = {};
var failureNumbers = {};
for (var i = 0; i < result.success; i++)
successNumbers[getNumberFromString(result.success[i])] = 1;
for (var i = 0; i < result.failure; i++)
failureNumbers[getNumberFromString(result.success[i])] = 1;
success_callback({success: successNumbers, failure: failureNumbers});
}, error_callback);
}
function requestIdentityPrivKeyFromMasterDevice(number, identityKey) {

View file

@ -41,10 +41,12 @@ registerOnLoadFunction(function() {
var sendDestinations = [conversation[0].sender];
for (var j = 0; j < conversation[0].destinations.length; j++)
sendDestinations[sendDestinations.length] = conversation[0].destinations[j];
sendMessageToNumbers(sendDestinations, { message: $('#text' + i).val() }, function(result) {
console.log("Sent message: " + JSON.stringify(result));
}, function(error_msg) {
alert(error_msg); //TODO
var messageProto = new PushMessageContentProtobuf();
messageProto.body = $('#text' + i).val();
sendMessageToNumbers(sendDestinations, messageProto, function(result) {
console.log("Sent message: " + result);
});
});
ul.append('</li>');
@ -59,5 +61,23 @@ registerOnLoadFunction(function() {
fillMessages();
storage.putUnencrypted("unreadCount", 0);
chrome.browserAction.setBadgeText({text: ""});
$("#popup_send_button").click(function() {
var numbers = [];
var splitString = $("#popup_send_numbers").val().split(",");
for (var i = 0; i < splitString.length; i++) {
try {
numbers.push(verifyNumber(splitString[i]));
} catch (numberError) {
//TODO
alert(numberError);
}
}
var messageProto = new PushMessageContentProtobuf();
messageProto.body = $("#popup_send_message").val();
sendMessageToNumbers(numbers, messageProto,
//TODO: Handle result
function(thing) {console.log(thing);});
});
}
});

View file

@ -27,7 +27,6 @@
<script type="text/javascript" src="js-deps/nacl-common.js"></script>
<script type="text/javascript" src="js-deps/jquery.js"></script>
<script type="text/javascript" src="js-deps/jquery.atmosphere.js"></script>
<script type="text/javascript" src="js-deps/core.js"></script>
<script type="text/javascript" src="js-deps/enc-base64.js"></script>
<script type="text/javascript" src="js-deps/cipher-core.js"></script>

View file

@ -11,11 +11,16 @@
<ul id="messages">
</ul>
</div>
<div id="send" style="display:none;"></div>
<div id="send" style="display:none;">
<form>
<input id="popup_send_numbers" />
<input id="popup_send_text" />
<a id="popup_send_button">Send</a>
</form>
</div>
<script type="text/javascript" src="js-deps/nacl-common.js"></script>
<script type="text/javascript" src="js-deps/jquery.js"></script>
<script type="text/javascript" src="js-deps/jquery.atmosphere.js"></script>
<script type="text/javascript" src="js-deps/core.js"></script>
<script type="text/javascript" src="js-deps/enc-base64.js"></script>
<script type="text/javascript" src="js-deps/cipher-core.js"></script>
@ -30,7 +35,6 @@
<script type="text/javascript" src="js-deps/ProtoBuf.min.js"></script>
<script type="text/javascript" src="js/helpers.js"></script>
<script type="text/javascript" src="js/api.js"></script>
<script type="text/javascript" src="js/fake_api.js"></script>
<script type="text/javascript" src="js/popup.js"></script>
</body>
</html>

View file

@ -12,7 +12,6 @@
<script type="text/javascript" src="js-deps/nacl-common.js"></script>
<script type="text/javascript" src="js-deps/jquery.js"></script>
<script type="text/javascript" src="js-deps/jquery.atmosphere.js"></script>
<script type="text/javascript" src="js-deps/core.js"></script>
<script type="text/javascript" src="js-deps/enc-base64.js"></script>
<script type="text/javascript" src="js-deps/cipher-core.js"></script>
@ -26,6 +25,9 @@
<script type="text/javascript" src="js-deps/ByteBuffer.min.js"></script>
<script type="text/javascript" src="js-deps/ProtoBuf.min.js"></script>
<script type="text/javascript" src="js/helpers.js"></script>
<!-- TODO: Tests for api stuff -->
<script type="text/javascript" src="js/api.js"></script>
<script type="text/javascript" src="js/fake_api.js"></script>
<script type="text/javascript" src="js/test.js"></script>
</body>
</html>