Merge branch 'clearnet' into multidevice-publicchat

This commit is contained in:
Ryan Tharp 2019-12-06 13:56:25 -08:00 committed by GitHub
commit 549e3b4516
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 192 additions and 6 deletions

View file

@ -1429,6 +1429,12 @@
"description":
"Warning notification that this version of the app has expired"
},
"clockOutOfSync": {
"message":
"Your clock is out of sync. Please update your clock and try again.",
"description":
"Notifcation that user's clock is out of sync with Loki's servers."
},
"upgrade": {
"message": "Upgrade",
"description":
@ -2247,5 +2253,18 @@
},
"noFriendsToAdd": {
"message": "no friends to add"
},
"couldNotDecryptMessage": {
"message": "Couldn't decrypt a message"
},
"confirmSessionRestore": {
"message":
"Would you like to start a new session with $pubkey$? Only do so if you know this pubkey.",
"placeholders": {
"pubkey": {
"content": "$1",
"example": ""
}
}
}
}

View file

@ -819,6 +819,7 @@
<script type='text/javascript' src='js/views/device_pairing_dialog_view.js'></script>
<script type='text/javascript' src='js/views/device_pairing_words_dialog_view.js'></script>
<script type='text/javascript' src='js/views/create_group_dialog_view.js'></script>
<script type='text/javascript' src='js/views/confirm_session_reset_view.js'></script>
<script type='text/javascript' src='js/views/edit_profile_dialog_view.js'></script>
<script type='text/javascript' src='js/views/invite_friends_dialog_view.js'></script>
<script type='text/javascript' src='js/views/user_details_dialog_view.js'></script>

View file

@ -11,7 +11,7 @@
libloki,
libsignal,
StringView,
BlockedNumberController
BlockedNumberController,
*/
// eslint-disable-next-line func-names
@ -125,6 +125,9 @@
'loki/loki_icon_128.png',
]);
// Set server-client time difference
window.LokiPublicChatAPI.setClockParams();
// We add this to window here because the default Node context is erased at the end
// of preload.js processing
window.setImmediate = window.nodeSetImmediate;
@ -1053,6 +1056,12 @@
}
});
Whisper.events.on('showSessionRestoreConfirmation', options => {
if (appView) {
appView.showSessionRestoreConfirmation(options);
}
});
Whisper.events.on('showNicknameDialog', options => {
if (appView) {
appView.showNicknameDialog(options);
@ -1944,6 +1953,48 @@
}
async function onError(ev) {
const noSession =
ev.error &&
ev.error.message &&
ev.error.message.indexOf('No record for device') === 0;
const pubkey = ev.proto.source;
if (noSession) {
const convo = await ConversationController.getOrCreateAndWait(
pubkey,
'private'
);
if (!convo.get('sessionRestoreSeen')) {
convo.set({ sessionRestoreSeen: true });
await window.Signal.Data.updateConversation(
convo.id,
convo.attributes,
{ Conversation: Whisper.Conversation }
);
window.Whisper.events.trigger('showSessionRestoreConfirmation', {
pubkey,
onOk: () => {
convo.sendMessage('', null, null, null, null, {
sessionRestoration: true,
});
},
});
} else {
window.log.verbose(
`Already seen session restore for pubkey: ${pubkey}`
);
if (ev.confirm) {
ev.confirm();
}
}
// We don't want to display any failed messages in the conversation:
return;
}
const { error } = ev;
window.log.error('background onError:', Errors.toLogFormat(error));

View file

@ -1438,7 +1438,8 @@
attachments,
quote,
preview,
groupInvitation = null
groupInvitation = null,
otherOptions = {}
) {
this.clearTypingTimers();
@ -1534,9 +1535,13 @@
messageWithSchema.source = textsecure.storage.user.getNumber();
messageWithSchema.sourceDevice = 1;
}
const { sessionRestoration = false } = otherOptions;
const attributes = {
...messageWithSchema,
groupInvitation,
sessionRestoration,
id: window.getGuid(),
};
@ -1617,6 +1622,7 @@
}
options.groupInvitation = groupInvitation;
options.sessionRestoration = sessionRestoration;
const groupNumbers = this.getRecipients();

View file

@ -102,6 +102,8 @@
this.propsForResetSessionNotification = this.getPropsForResetSessionNotification();
} else if (this.isGroupUpdate()) {
this.propsForGroupNotification = this.getPropsForGroupNotification();
} else if (this.isSessionRestoration()) {
// do nothing
} else if (this.isFriendRequest()) {
this.propsForFriendRequest = this.getPropsForFriendRequest();
} else if (this.isGroupInvitation()) {
@ -270,6 +272,13 @@
isGroupInvitation() {
return !!this.get('groupInvitation');
},
isSessionRestoration() {
const flag = textsecure.protobuf.DataMessage.Flags.SESSION_RESTORE;
// eslint-disable-next-line no-bitwise
const sessionRestoreFlag = !!(this.get('flags') & flag);
return !!this.get('sessionRestoration') || sessionRestoreFlag;
},
getNotificationText() {
const description = this.getDescription();
if (description) {
@ -390,6 +399,16 @@
}
const conversation = await this.getSourceDeviceConversation();
// If we somehow received an old friend request (e.g. after having restored
// from seed, we won't be able to accept it, we should initiate our own
// friend request to reset the session:
if (conversation.get('sessionRestoreSeen')) {
conversation.sendMessage('', null, null, null, null, {
sessionRestoration: true,
});
return;
}
this.set({ friendStatus: 'accepted' });
await window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message,
@ -1979,6 +1998,15 @@
initialMessage.flags ===
textsecure.protobuf.DataMessage.Flags.BACKGROUND_FRIEND_REQUEST;
if (
// eslint-disable-next-line no-bitwise
initialMessage.flags &
textsecure.protobuf.DataMessage.Flags.SESSION_RESTORE
) {
// Show that the session reset is "in progress" even though we had a valid session
this.set({ endSessionType: 'ongoing' });
}
if (message.isFriendRequest() && backgroundFrReq) {
// Check if the contact is a member in one of our private groups:
const groupMember = window

View file

@ -254,6 +254,10 @@
const dialog = new Whisper.UpdateGroupDialogView(groupConvo);
this.el.append(dialog.el);
},
showSessionRestoreConfirmation(options) {
const dialog = new Whisper.ConfirmSessionResetView(options);
this.el.append(dialog.el);
},
showLeaveGroupDialog(groupConvo) {
const dialog = new Whisper.LeaveGroupDialogView(groupConvo);
this.el.append(dialog.el);

View file

@ -0,0 +1,50 @@
/* global Whisper, i18n */
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
Whisper.ConfirmSessionResetView = Whisper.View.extend({
className: 'loki-dialog modal',
initialize({ pubkey, onOk }) {
this.title = i18n('couldNotDecryptMessage');
this.onOk = onOk;
this.messageText = i18n('confirmSessionRestore', pubkey);
this.okText = i18n('yes');
this.cancelText = i18n('cancel');
this.close = this.close.bind(this);
this.confirm = this.confirm.bind(this);
this.$el.focus();
this.render();
},
render() {
this.dialogView = new Whisper.ReactWrapperView({
className: 'leave-group-dialog',
Component: window.Signal.Components.ConfirmDialog,
props: {
titleText: this.title,
messageText: this.messageText,
okText: this.okText,
cancelText: this.cancelText,
onConfirm: this.confirm,
onClose: this.close,
},
});
this.$el.append(this.dialogView.el);
return this;
},
async confirm() {
this.onOk();
this.close();
},
close() {
this.remove();
},
});
})();

View file

@ -28,6 +28,11 @@
return { toastMessage: i18n('expiredWarning') };
},
});
Whisper.ClockOutOfSyncToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: i18n('clockOutOfSync') };
},
});
Whisper.BlockedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: i18n('unblockToSend') };
@ -1959,6 +1964,11 @@
if (extension.expired()) {
toast = new Whisper.ExpiredToast();
}
if (!window.clientClockSynced) {
// Check to see if user has updated their clock to current time
const clockSynced = await window.LokiPublicChatAPI.setClockParams();
toast = clockSynced ? toast : new Whisper.ClockOutOfSyncToast();
}
if (this.model.isPrivate() && storage.isBlocked(this.model.id)) {
toast = new Whisper.BlockedToast();
}

View file

@ -69,6 +69,11 @@
Component: Components.GroupNotification,
props: this.model.propsForGroupNotification,
};
} else if (this.model.isSessionRestoration()) {
return {
Component: Components.ResetSessionNotification,
props: this.model.getPropsForResetSessionNotification(),
};
} else if (this.model.propsForFriendRequest) {
return {
Component: Components.FriendRequest,

View file

@ -825,7 +825,6 @@ MessageReceiver.prototype.extend({
} else {
handleSessionReset = async result => result;
}
switch (envelope.type) {
case textsecure.protobuf.Envelope.Type.CIPHERTEXT:
window.log.info('message from', this.getEnvelopeId(envelope));
@ -975,6 +974,9 @@ MessageReceiver.prototype.extend({
.catch(error => {
let errorToThrow = error;
const noSession =
error && error.message.indexOf('No record for device') === 0;
if (error && error.message === 'Unknown identity key') {
// create an error that the UI will pick up and ask the
// user if they want to re-negotiate
@ -984,8 +986,8 @@ MessageReceiver.prototype.extend({
buffer.toArrayBuffer(),
error.identityKey
);
} else {
// re-throw
} else if (!noSession) {
// We want to handle "no-session" error, not re-throw it
throw error;
}
const ev = new Event('error');
@ -1895,6 +1897,8 @@ MessageReceiver.prototype.extend({
decrypted.attachments = [];
} else if (decrypted.flags & FLAGS.BACKGROUND_FRIEND_REQUEST) {
// do nothing
} else if (decrypted.flags & FLAGS.SESSION_RESTORE) {
// do nothing
} else if (decrypted.flags & FLAGS.UNPAIRING_REQUEST) {
// do nothing
} else if (decrypted.flags !== 0) {

View file

@ -28,6 +28,7 @@ function Message(options) {
this.profileKey = options.profileKey;
this.profile = options.profile;
this.groupInvitation = options.groupInvitation;
this.sessionRestoration = options.sessionRestoration || false;
if (!(this.recipients instanceof Array)) {
throw new Error('Invalid recipient list');
@ -171,6 +172,10 @@ Message.prototype = {
);
}
if (this.sessionRestoration) {
proto.flags = textsecure.protobuf.DataMessage.Flags.SESSION_RESTORE;
}
this.dataMessage = proto;
return proto;
},
@ -952,7 +957,7 @@ MessageSender.prototype = {
? textsecure.protobuf.DataMessage.Flags.BACKGROUND_FRIEND_REQUEST
: undefined;
const { groupInvitation } = options;
const { groupInvitation, sessionRestoration } = options;
return this.sendMessage(
{
@ -968,6 +973,7 @@ MessageSender.prototype = {
profile,
flags,
groupInvitation,
sessionRestoration,
},
options
);

View file

@ -105,6 +105,7 @@ message DataMessage {
END_SESSION = 1;
EXPIRATION_TIMER_UPDATE = 2;
PROFILE_KEY_UPDATE = 4;
SESSION_RESTORE = 64;
UNPAIRING_REQUEST = 128;
BACKGROUND_FRIEND_REQUEST = 256;
}

View file

@ -576,6 +576,7 @@
<script type='text/javascript' src='../js/views/banner_view.js' data-cover></script>
<script type='text/javascript' src='../js/views/clear_data_view.js'></script>
<script type='text/javascript' src='../js/views/create_group_dialog_view.js'></script>
<script type='text/javascript' src='../js/views/confirm_session_reset_view.js'></script>
<script type='text/javascript' src='../js/views/invite_friends_dialog_view.js'></script>
<script type='text/javascript' src='../js/views/beta_release_disclaimer_view.js'></script>