Commit a06d2c49 authored by JC Brand's avatar JC Brand

Implement own device removal via stanza

parent 26e93658
...@@ -8575,8 +8575,10 @@ body.reset { ...@@ -8575,8 +8575,10 @@ body.reset {
margin-top: 1em; } margin-top: 1em; }
#conversejs #converse-modals .btn { #conversejs #converse-modals .btn {
font-weight: normal; } font-weight: normal; }
#conversejs #converse-modals #user-profile-modal label { #conversejs #converse-modals #user-profile-modal .profile-form label {
font-weight: bold; } font-weight: bold; }
#conversejs #converse-modals #user-profile-modal .fingerprint-removal label {
padding: 0.75rem 1.25rem; }
#conversejs #converse-modals #user-profile-modal .list-group-item { #conversejs #converse-modals #user-profile-modal .list-group-item {
display: flex; display: flex;
justify-content: left; justify-content: left;
......
...@@ -16,9 +16,17 @@ ...@@ -16,9 +16,17 @@
} }
#user-profile-modal { #user-profile-modal {
.profile-form {
label { label {
font-weight: bold; font-weight: bold;
} }
}
.fingerprint-removal {
label {
padding: 0.75rem 1.25rem;
}
}
.list-group-item { .list-group-item {
display: flex; display: flex;
justify-content: left; justify-content: left;
......
...@@ -259,7 +259,7 @@ ...@@ -259,7 +259,7 @@
}); });
this.messages = new _converse.Messages(); this.messages = new _converse.Messages();
this.messages.browserStorage = new Backbone.BrowserStorage[_converse.storage]( this.messages.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.messages${this.get('jid')}${_converse.bare_jid}`)); `converse.messages${this.get('jid')}${_converse.bare_jid}`);
this.messages.chatbox = this; this.messages.chatbox = this;
this.messages.on('change:upload', (message) => { this.messages.on('change:upload', (message) => {
...@@ -626,7 +626,7 @@ ...@@ -626,7 +626,7 @@
onConnected () { onConnected () {
this.browserStorage = new Backbone.BrowserStorage.session( this.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.chatboxes-${_converse.bare_jid}`)); `converse.chatboxes-${_converse.bare_jid}`);
this.registerMessageHandler(); this.registerMessageHandler();
this.fetch({ this.fetch({
'add': true, 'add': true,
......
...@@ -77,21 +77,25 @@ ...@@ -77,21 +77,25 @@
}, },
initialize () { initialize () {
const { _converse } = this.__super__, const { _converse } = this.__super__;
device_id = _converse.omemo_store.get('device_id'); this.debouncedRender = _.debounce(this.render, 50);
this.devicelist = _converse.devicelists.get(_converse.bare_jid); this.devicelist = _converse.devicelists.get(_converse.bare_jid);
this.devicelist.devices.on('change:bundle', this.debouncedRender, this);
this.devicelist.devices.on('reset', this.debouncedRender, this);
return this.__super__.initialize.apply(this, arguments);
},
beforeRender () {
const { _converse } = this.__super__,
device_id = _converse.omemo_store.get('device_id').toString();
this.current_device = this.devicelist.devices.get(device_id); this.current_device = this.devicelist.devices.get(device_id);
this.other_devices = this.devicelist.devices.filter(d => (d.get('id') !== device_id)); this.other_devices = this.devicelist.devices.filter(d => (d.get('id') !== device_id));
this.devicelist.devices.on('change:bundle', this.render, this);
return this.__super__.initialize.apply(this, arguments);
}, },
selectAll (ev) { selectAll (ev) {
let sibling = ev.target.parentElement.nextElementSibling; let sibling = u.ancestor(ev.target, 'li');
while (sibling) { while (sibling) {
sibling.firstElementChild.checked = ev.target.checked; sibling.querySelector('input[type="checkbox"]').checked = ev.target.checked;
sibling = sibling.nextElementSibling; sibling = sibling.nextElementSibling;
} }
}, },
...@@ -99,9 +103,20 @@ ...@@ -99,9 +103,20 @@
removeSelectedFingerprints (ev) { removeSelectedFingerprints (ev) {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
ev.target.querySelector('.select-all').checked = false
const checkboxes = ev.target.querySelectorAll('.fingerprint-removal-item input[type="checkbox"]:checked'), const checkboxes = ev.target.querySelectorAll('.fingerprint-removal-item input[type="checkbox"]:checked'),
device_ids = _.map(checkboxes, 'value'); device_ids = _.map(checkboxes, 'value');
this.devicelist.removeOwnDevices(device_ids); this.devicelist.removeOwnDevices(device_ids)
.then(this.modal.hide)
.catch(err => {
const { _converse } = this.__super__,
{ __ } = _converse;
_converse.log(err, Strophe.LogLevel.ERROR);
_converse.api.alert.show(
Strophe.LogLevel.ERROR,
__('Error'), [__('Sorry, an error occurred while trying to remove the devices.')]
)
});
}, },
}, },
...@@ -628,21 +643,27 @@ ...@@ -628,21 +643,27 @@
return Promise.resolve(); return Promise.resolve();
}, },
createNewDeviceBundle () {
return generateBundle().then((data) => {
// TODO: should storeSession be used here?
_converse.omemo_store.save(data);
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
},
fetchSession () { fetchSession () {
if (_.isUndefined(this._setup_promise)) { if (_.isUndefined(this._setup_promise)) {
this._setup_promise = new Promise((resolve, reject) => { this._setup_promise = new Promise((resolve, reject) => {
this.fetch({ this.fetch({
'success': () => { 'success': () => {
if (!_converse.omemo_store.get('device_id')) { if (!_converse.omemo_store.get('device_id')) {
generateBundle() this.createNewDeviceBundle().then(resolve).catch(resolve);
.then((data) => {
// TODO: should storeSession be used here?
_converse.omemo_store.save(data);
resolve();
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
} else { } else {
resolve(); resolve();
} }
},
'error': () => {
this.createNewDeviceBundle().then(resolve).catch(resolve);
} }
}); });
}); });
...@@ -704,9 +725,9 @@ ...@@ -704,9 +725,9 @@
initialize () { initialize () {
this.devices = new _converse.Devices(); this.devices = new _converse.Devices();
this.devices.browserStorage = new Backbone.BrowserStorage.session( const id = `converse.devicelist-${_converse.bare_jid}-${this.get('jid')}`;
b64_sha1(`converse.devicelist-${_converse.bare_jid}-${this.get('jid')}`) this.devices.id = id;
); this.devices.browserStorage = new Backbone.BrowserStorage.session(id);
this.fetchDevices(); this.fetchDevices();
}, },
...@@ -720,6 +741,9 @@ ...@@ -720,6 +741,9 @@
} else { } else {
resolve(); resolve();
} }
},
'error': () => {
this.fetchDevicesFromServer().then(resolve).catch(reject);
} }
}); });
}); });
...@@ -749,13 +773,7 @@ ...@@ -749,13 +773,7 @@
}); });
}, },
addDeviceToList (device_id) { publishDevices () {
/* Add this device to our list of devices stored on the
* server.
* https://xmpp.org/extensions/xep-0384.html#usecases-announcing
*/
this.devices.create({'id': device_id, 'jid': this.get('jid')});
return new Promise((resolve, reject) => {
const stanza = $iq({ const stanza = $iq({
'from': _converse.bare_jid, 'from': _converse.bare_jid,
'type': 'set' 'type': 'set'
...@@ -767,12 +785,27 @@ ...@@ -767,12 +785,27 @@
_.each(this.devices.where({'active': true}), (device) => { _.each(this.devices.where({'active': true}), (device) => {
stanza.c('device', {'id': device.get('id')}).up(); stanza.c('device', {'id': device.get('id')}).up();
}); });
_converse.connection.sendIQ(stanza, resolve, reject, _converse.IQ_TIMEOUT); return _converse.api.sendIQ(stanza);
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR)); },
addOwnDevice (device_id) {
/* Add this device to our list of devices stored on the
* server.
* https://xmpp.org/extensions/xep-0384.html#usecases-announcing
*/
if (this.get('jid') !== _converse.bare_jid) {
throw new Error("Cannot add device to someone else's device list");
}
this.devices.create({'id': device_id.toString(), 'jid': this.get('jid')});
return this.publishDevices();
}, },
removeOwnDevices (device_ids) { removeOwnDevices (device_ids) {
// TODO if (this.get('jid') !== _converse.bare_jid) {
throw new Error("Cannot remove devices from someone else's device list");
}
this.devices.reset(this.devices.filter(d => (!_.includes(device_ids, d.get('id').toString()))));
return this.publishDevices();
} }
}); });
...@@ -814,18 +847,19 @@ ...@@ -814,18 +847,19 @@
} }
function fetchDeviceLists () { function fetchDeviceLists () {
return new Promise((resolve, reject) => _converse.devicelists.fetch({'success': resolve})); return new Promise((resolve, reject) => _converse.devicelists.fetch({
'success': resolve,
'error': resolve
}));
} }
function fetchOwnDevices () { function fetchOwnDevices () {
return new Promise((resolve, reject) => { return fetchDeviceLists().then(() => {
fetchDeviceLists().then(() => {
let own_devicelist = _converse.devicelists.get(_converse.bare_jid); let own_devicelist = _converse.devicelists.get(_converse.bare_jid);
if (_.isNil(own_devicelist)) { if (_.isNil(own_devicelist)) {
own_devicelist = _converse.devicelists.create({'jid': _converse.bare_jid}); own_devicelist = _converse.devicelists.create({'jid': _converse.bare_jid});
} }
own_devicelist.fetchDevices().then(resolve).catch(reject); return own_devicelist.fetchDevices();
});
}); });
} }
...@@ -834,14 +868,14 @@ ...@@ -834,14 +868,14 @@
* Also, deduplicate devices if necessary. * Also, deduplicate devices if necessary.
*/ */
const devicelist = _converse.devicelists.get(_converse.bare_jid), const devicelist = _converse.devicelists.get(_converse.bare_jid),
device_id = _converse.omemo_store.get('device_id'), device_id = _converse.omemo_store.get('device_id').toString(),
own_device = devicelist.devices.findWhere({'id': device_id}); own_device = devicelist.devices.findWhere({'id': device_id});
if (!own_device) { if (!own_device) {
return devicelist.addDeviceToList(device_id); return devicelist.addOwnDevice(device_id);
} else if (!own_device.get('active')) { } else if (!own_device.get('active')) {
own_device.set('active', true, {'silent': true}); own_device.set('active', true, {'silent': true});
return devicelist.addDeviceToList(device_id); return devicelist.addOwnDevice(device_id);
} else { } else {
return Promise.resolve(); return Promise.resolve();
} }
...@@ -903,18 +937,19 @@ ...@@ -903,18 +937,19 @@
function restoreOMEMOSession () { function restoreOMEMOSession () {
if (_.isUndefined(_converse.omemo_store)) { if (_.isUndefined(_converse.omemo_store)) {
_converse.omemo_store = new _converse.OMEMOStore(); _converse.omemo_store = new _converse.OMEMOStore();
_converse.omemo_store.browserStorage = new Backbone.BrowserStorage[_converse.storage]( const id = b64_sha1(`converse.omemosession-${_converse.bare_jid}`);
b64_sha1(`converse.omemosession-${_converse.bare_jid}`) _converse.omemo_store.id = id;
); _converse.omemo_store.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
} }
return _converse.omemo_store.fetchSession(); return _converse.omemo_store.fetchSession();
} }
function initOMEMO() { function initOMEMO() {
_converse.devicelists = new _converse.DeviceLists(); _converse.devicelists = new _converse.DeviceLists();
_converse.devicelists.browserStorage = new Backbone.BrowserStorage[_converse.storage]( const id = `converse.devicelists-${_converse.bare_jid}`;
b64_sha1(`converse.devicelists-${_converse.bare_jid}`) _converse.devicelists.id = id;
); _converse.devicelists.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
fetchOwnDevices() fetchOwnDevices()
.then(() => restoreOMEMOSession()) .then(() => restoreOMEMOSession())
.then(() => updateOwnDeviceList()) .then(() => updateOwnDeviceList())
......
...@@ -68,37 +68,47 @@ ...@@ -68,37 +68,47 @@
<form class="converse-form fingerprint-removal"> <form class="converse-form fingerprint-removal">
<ul class="list-group fingerprints"> <ul class="list-group fingerprints">
<li class="list-group-item active">{{{o.__("This device's OMEMO fingerprint")}}}</li> <li class="list-group-item active">{{{o.__("This device's OMEMO fingerprint")}}}</li>
<li class="fingerprint-removal-item list-group-item"> <li class="list-group-item">
{[ if (o.view.current_device.get('bundle') && o.view.current_device.get('bundle').fingerprint) { ]} {[ if (o.view.current_device.get('bundle') && o.view.current_device.get('bundle').fingerprint) { ]}
<input type="checkbox" value="{{{o.view.current_device.get('id')}}}"
aria-label="{{{o.__('Checkbox for removing the following fingerprint')}}}">
<span class="fingerprint">{{{o.view.current_device.get('bundle').fingerprint}}}</span> <span class="fingerprint">{{{o.view.current_device.get('bundle').fingerprint}}}</span>
{[ } else {]} {[ } else {]}
<span class="spinner fa fa-spinner centered"/> <span class="spinner fa fa-spinner centered"/>
{[ } ]} {[ } ]}
</li> </li>
</ul> </ul>
{[ if (o.view.other_devices) { ]} {[ if (o.view.other_devices.length) { ]}
<ul class="list-group fingerprints"> <ul class="list-group fingerprints">
<li class="list-group-item active"> <li class="list-group-item nopadding active">
<label>
<input type="checkbox" class="select-all" title="{{{o.__('Select all')}}}" <input type="checkbox" class="select-all" title="{{{o.__('Select all')}}}"
aria-label="{{{o.__('Checkbox to select fingerprints of all other OMEMO devices')}}}"> aria-label="{{{o.__('Checkbox to select fingerprints of all other OMEMO devices')}}}">
{{{o.__('Other OMEMO-enabled devices')}}} {{{o.__('Other OMEMO-enabled devices')}}}
</label>
</li> </li>
{[ o._.forEach(o.view.other_devices, function (device) { ]} {[ o._.forEach(o.view.other_devices, function (device) { ]}
{[ if (device.get('bundle') && device.get('bundle').fingerprint) { ]} {[ if (device.get('bundle') && device.get('bundle').fingerprint) { ]}
<li class="fingerprint-removal-item list-group-item"> <li class="fingerprint-removal-item list-group-item nopadding">
<label>
<input type="checkbox" value="{{{device.get('id')}}}" <input type="checkbox" value="{{{device.get('id')}}}"
aria-label="{{{o.__('Checkbox for selecting the following fingerprint')}}}"> aria-label="{{{o.__('Checkbox for selecting the following fingerprint')}}}">
<span class="fingerprint">{{{device.get('bundle').fingerprint}}}</span> <span class="fingerprint">{{{device.get('bundle').fingerprint}}}</span>
</label>
</li>
{[ } else {]}
<li class="fingerprint-removal-item list-group-item nopadding">
<label>
<input type="checkbox" value="{{{device.get('id')}}}"
aria-label="{{{o.__('Checkbox for selecting the following fingerprint')}}}">
<span>{{{o.__('Device without a fingerprint')}}}</span>
</label>
</li> </li>
{[ } ]} {[ } ]}
{[ }); ]} {[ }); ]}
</ul> </ul>
{[ } ]}
<div class="form-group"> <div class="form-group">
<button type="submit" class="save-form btn btn-primary">{{{o.__('Remove checked devices and close')}}}</button> <button type="submit" class="save-form btn btn-primary">{{{o.__('Remove checked devices and close')}}}</button>
</div> </div>
{[ } ]}
</form> </form>
</div> </div>
{[ } ]} {[ } ]}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment