Merge branch 'clearnet' into pairing_QR

pull/643/head
sachaaaaa 6 years ago committed by GitHub
commit 51a9d032f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1025,6 +1025,15 @@
pubKey
);
});
Whisper.events.on('deviceUnpairingRequested', async pubKey => {
await libloki.storage.removePairingAuthorisationForSecondaryPubKey(
pubKey
);
await window.lokiFileServerAPI.updateOurDeviceMapping();
// TODO: we should ensure the message was sent and retry automatically if not
await libloki.api.sendUnpairingMessageToSecondary(pubKey);
});
}
window.getSyncRequest = () =>

@ -127,6 +127,7 @@
}
this.selected = false;
window.contextMenuShown = false;
generateProps();
},
@ -657,6 +658,7 @@
onCopyText: () => this.copyText(),
onSelectMessage: () => this.selectMessage(),
onSelectMessageUnchecked: () => this.selectMessageUnchecked(),
onCopyPubKey: () => this.copyPubKey(),
onReply: () => this.trigger('reply', this),
onRetrySend: () => this.retrySend(),
@ -966,7 +968,8 @@
});
},
selectMessage() {
// Select message even if the context menu is shown
selectMessageUnchecked() {
this.selected = !this.selected;
const convo = this.getConversation();
@ -980,6 +983,14 @@
this.trigger('change');
},
selectMessage() {
if (window.contextMenuShown || this.get('isRss')) {
return;
}
this.selectMessageUnchecked();
},
copyText() {
clipboard.writeText(this.get('body'));
window.Whisper.events.trigger('showToast', {

@ -224,6 +224,10 @@ class LokiFileServerAPI {
uploadPrivateAttachment(data) {
return this._server.uploadData(data);
}
clearOurDeviceMappingAnnotations() {
return this._server.setSelfAnnotation(DEVICE_MAPPING_ANNOTATION_KEY, null);
}
}
module.exports = LokiFileServerAPI;

@ -223,6 +223,9 @@
dialog.on('devicePairingRequestRejected', pubKey =>
Whisper.events.trigger('devicePairingRequestRejected', pubKey)
);
dialog.on('deviceUnpairingRequested', pubKey =>
Whisper.events.trigger('deviceUnpairingRequested', pubKey)
);
dialog.once('close', () => {
Whisper.events.off('devicePairingRequestReceived');
});

@ -5,7 +5,6 @@
textsecure,
ConversationController,
$,
lokiFileServerAPI,
QRCode,
*/
@ -122,10 +121,7 @@
this.pubKey = this.pubKeyRequests.pop();
},
async confirmUnpairDevice() {
await libloki.storage.removePairingAuthorisationForSecondaryPubKey(
this.pubKeyToUnpair
);
await lokiFileServerAPI.updateOurDeviceMapping();
this.trigger('deviceUnpairingRequested', this.pubKeyToUnpair);
this.reset();
this.showView();
},

@ -107,6 +107,27 @@
secondaryDevicePubKey,
});
}
function sendUnpairingMessageToSecondary(pubKey) {
const flags = textsecure.protobuf.DataMessage.Flags.UNPAIRING_REQUEST;
const dataMessage = new textsecure.protobuf.DataMessage({
flags,
});
const content = new textsecure.protobuf.Content({
dataMessage,
});
const options = { messageType: 'device-unpairing' };
const outgoingMessage = new textsecure.OutgoingMessage(
null, // server
Date.now(), // timestamp,
[pubKey], // numbers
content, // message
true, // silent
() => null, // callback
options
);
return outgoingMessage.sendToNumber(pubKey);
}
// Serialise as <Element0.length><Element0><Element1.length><Element1>...
// This is an implementation of the reciprocal of contacts_parser.js
function serialiseByteBuffers(buffers) {
@ -226,6 +247,7 @@
broadcastOnlineStatus,
sendPairingAuthorisation,
createPairingAuthorisationProtoMessage,
sendUnpairingMessageToSecondary,
createContactSyncProtoMessage,
};
})();

@ -1286,7 +1286,8 @@ MessageReceiver.prototype.extend({
this.processDecrypted(envelope, msg).then(async message => {
const groupId = message.group && message.group.id;
const isBlocked = this.isGroupBlocked(groupId);
const isMe = envelope.source === textsecure.storage.user.getNumber();
const ourPubKey = textsecure.storage.user.getNumber();
const isMe = envelope.source === ourPubKey;
const conversation = window.ConversationController.get(envelope.source);
const isLeavingGroup = Boolean(
message.group &&
@ -1294,6 +1295,68 @@ MessageReceiver.prototype.extend({
);
const friendRequest =
envelope.type === textsecure.protobuf.Envelope.Type.FRIEND_REQUEST;
const { UNPAIRING_REQUEST } = textsecure.protobuf.DataMessage.Flags;
// eslint-disable-next-line no-bitwise
const isUnpairingRequest = Boolean(message.flags & UNPAIRING_REQUEST);
if (!friendRequest && isUnpairingRequest) {
// TODO: move high-level pairing logic to libloki.multidevice.xx
const unpairingRequestIsLegit = async () => {
const isSecondary = textsecure.storage.get('isSecondaryDevice');
if (!isSecondary) {
return false;
}
const primaryPubKey = window.storage.get('primaryDevicePubKey');
// TODO: allow unpairing from any paired device?
if (envelope.source !== primaryPubKey) {
return false;
}
const primaryMapping = await lokiFileServerAPI.getUserDeviceMapping(
primaryPubKey
);
if (!primaryMapping) {
return false;
}
// We expect the primary device to have updated its mapping
// before sending the unpairing request
const found = primaryMapping.authorisations.find(
authorisation => authorisation.secondaryDevicePubKey === ourPubKey
);
// our pubkey should NOT be in the primary device mapping
return !found;
};
const legit = await unpairingRequestIsLegit();
this.removeFromCache(envelope);
if (legit) {
// remove our device mapping annotations from file server
await lokiFileServerAPI.clearOurDeviceMappingAnnotations();
// Delete the account and restart
try {
await window.Signal.Logs.deleteAll();
await window.Signal.Data.removeAll();
await window.Signal.Data.close();
await window.Signal.Data.removeDB();
await window.Signal.Data.removeOtherData();
// TODO generate an empty db with a flag
// to display a message about the unpairing
// after the app restarts
} catch (error) {
window.log.error(
'Something went wrong deleting all data:',
error && error.stack ? error.stack : error
);
}
window.restart();
}
}
// Check if we need to update any profile names
if (!isMe && conversation) {
@ -1787,6 +1850,8 @@ MessageReceiver.prototype.extend({
decrypted.attachments = [];
} else if (decrypted.flags & FLAGS.BACKGROUND_FRIEND_REQUEST) {
// do nothing
} else if (decrypted.flags & FLAGS.UNPAIRING_REQUEST) {
// do nothing
} else if (decrypted.flags !== 0) {
throw new Error('Unknown flags in message');
}

@ -443,6 +443,8 @@ OutgoingMessage.prototype = {
switch (type) {
case 'friend-request':
return 4 * 24 * 60 * 60 * 1000; // 4 days for friend request message
case 'device-unpairing':
return 4 * 24 * 60 * 60 * 1000; // 4 days for device unpairing
case 'onlineBroadcast':
return 60 * 1000; // 1 minute for online broadcast message
case 'typing':

@ -469,6 +469,6 @@ window.pubkeyPattern = /@[a-fA-F0-9]{64,66}\b/g;
window.SMALL_GROUP_SIZE_LIMIT = 10;
window.lokiFeatureFlags = {
multiDeviceUnpairing: false,
multiDeviceUnpairing: true,
privateGroupChats: false,
};

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

@ -35,6 +35,7 @@ import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
declare global {
interface Window {
shortenPubkey: any;
contextMenuShown: boolean;
}
}
@ -105,6 +106,7 @@ export interface Props {
onClickLinkPreview?: (url: string) => void;
onCopyText?: () => void;
onSelectMessage: () => void;
onSelectMessageUnchecked: () => void;
onReply?: () => void;
onRetrySend?: () => void;
onDownload?: (isDangerous: boolean) => void;
@ -847,7 +849,7 @@ export class Message extends React.PureComponent<Props, State> {
const {
attachments,
onCopyText,
onSelectMessage,
onSelectMessageUnchecked,
direction,
status,
isDeletable,
@ -876,8 +878,27 @@ export class Message extends React.PureComponent<Props, State> {
}
};
const onContextMenuShown = () => {
window.contextMenuShown = true;
};
const onContextMenuHidden = () => {
// This function will called before the click event
// on the message would trigger (and I was unable to
// prevent propagation in this case), so use a short timeout
setTimeout(() => {
window.contextMenuShown = false;
}, 100);
};
// CONTEXT MENU "Select Message" does not work
return (
<ContextMenu id={triggerId}>
<ContextMenu
id={triggerId}
onShow={onContextMenuShown}
onHide={onContextMenuHidden}
>
{!multipleAttachments && attachments && attachments[0] ? (
<MenuItem
attributes={{
@ -895,7 +916,7 @@ export class Message extends React.PureComponent<Props, State> {
) : null}
<MenuItem onClick={wrap(onCopyText)}>{i18n('copyMessage')}</MenuItem>
<MenuItem onClick={wrap(onSelectMessage)}>
<MenuItem onClick={wrap(onSelectMessageUnchecked)}>
{i18n('selectMessage')}
</MenuItem>
<MenuItem
@ -1058,6 +1079,8 @@ export class Message extends React.PureComponent<Props, State> {
divClasses.push('public-chat-message-wrapper');
}
const enableContextMenu = !isRss && !multiSelectMode;
return (
<div
className={classNames(divClasses)}
@ -1107,10 +1130,10 @@ export class Message extends React.PureComponent<Props, State> {
{isRss || multiSelectMode
? null
: this.renderMenu(isIncoming, triggerId)}
{multiSelectMode ? null : this.renderContextMenu(triggerId)}
{multiSelectMode
? null
: this.renderContextMenu(rightClickTriggerId)}
{enableContextMenu ? this.renderContextMenu(triggerId) : null}
{enableContextMenu
? this.renderContextMenu(rightClickTriggerId)
: null}
</div>
</ContextMenuTrigger>
</div>

Loading…
Cancel
Save