Commit 0464060f authored by JC Brand's avatar JC Brand

Initial attempt at adding support for multi-session nicks

updates #1197
parent 682bace8
...@@ -68708,7 +68708,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ ...@@ -68708,7 +68708,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
verifyRoles(roles) { verifyRoles(roles) {
const me = this.model.occupants.findWhere({ const me = this.model.occupants.findWhere({
'jid': _converse.bare_jid 'bare_jid': _converse.bare_jid
}); });
if (!_.includes(roles, me.get('role'))) { if (!_.includes(roles, me.get('role'))) {
...@@ -68721,7 +68721,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ ...@@ -68721,7 +68721,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
verifyAffiliations(affiliations) { verifyAffiliations(affiliations) {
const me = this.model.occupants.findWhere({ const me = this.model.occupants.findWhere({
'jid': _converse.bare_jid 'bare_jid': _converse.bare_jid
}); });
if (!_.includes(affiliations, me.get('affiliation'))) { if (!_.includes(affiliations, me.get('affiliation'))) {
...@@ -68744,7 +68744,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ ...@@ -68744,7 +68744,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
if (!this.model.occupants.findWhere({ if (!this.model.occupants.findWhere({
'nick': args[0] 'nick': args[0]
}) && !this.model.occupants.findWhere({ }) && !this.model.occupants.findWhere({
'jid': args[0] 'bare_jid': args[0]
})) { })) {
this.showErrorMessage(__('Error: couldn\'t find a groupchat participant "%1$s"', args[0])); this.showErrorMessage(__('Error: couldn\'t find a groupchat participant "%1$s"', args[0]));
return false; return false;
...@@ -70358,10 +70358,8 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } ...@@ -70358,10 +70358,8 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
return null; return null;
} }
const occupant = this.occupants.findOccupant({ const occupant = this.occupants.findWhere({
'nick': longest_match 'nick': longest_match
}) || this.occupants.findOccupant({
'jid': longest_match
}); });
if (!occupant) { if (!occupant) {
...@@ -70375,8 +70373,8 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } ...@@ -70375,8 +70373,8 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
'type': 'mention' 'type': 'mention'
}; };
if (occupant.get('jid')) { if (occupant.get('bare_jid')) {
obj.uri = `xmpp:${occupant.get('jid')}`; obj.uri = `xmpp:${occupant.get('bare_jid')}`;
} }
return obj; return obj;
...@@ -70980,30 +70978,41 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } ...@@ -70980,30 +70978,41 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
return true; return true;
} }
const occupant = this.occupants.findOccupant(data); const occupant = this.occupants.findWhere({
'nick': data.nick
});
if (data.type === 'unavailable' && occupant) { if (data.type === 'unavailable' && occupant) {
if (!_.includes(data.states, converse.MUC_NICK_CHANGED_CODE) && !occupant.isMember()) {
// We only destroy the occupant if this is not a nickname change operation. // We only destroy the occupant if this is not a nickname change operation.
// and if they're not on the member lists. // and if they're not on the member lists.
if (!_.includes(data.states, converse.MUC_NICK_CHANGED_CODE) && !occupant.isMember()) {
// Before destroying we set the new data, so // Before destroying we set the new data, so
// that we can show the disconnection message. // that we can show the disconnection message.
occupant.set(data); // TODO: see whether we need to check for multiple jids
const attributes = _.extend(data, {
'bare_jid': data.jid ? Strophe.getBareJidFromJid(data.jid) : undefined,
'jids': data.jid ? [data.jid] : []
});
occupant.set(attributes);
occupant.destroy(); occupant.destroy();
return; return;
} }
} }
const jid = Strophe.getBareJidFromJid(data.jid); if (occupant) {
const attributes = _.extend(data, { const attributes = _.extend(data, {
'jid': jid ? jid : undefined, 'bare_jid': data.jid ? Strophe.getBareJidFromJid(data.jid) : undefined,
'resource': data.jid ? Strophe.getResourceFromJid(data.jid) : undefined 'jids': data.jid ? _.concat(occupant.get('jids'), data.jid) : []
}); });
if (occupant) {
occupant.save(attributes); occupant.save(attributes);
} else { } else {
const attributes = _.extend(data, {
'bare_jid': data.jid ? Strophe.getBareJidFromJid(data.jid) : undefined,
'jids': data.jid ? [data.jid] : []
});
this.occupants.create(attributes); this.occupants.create(attributes);
} }
}, },
...@@ -71263,7 +71272,8 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } ...@@ -71263,7 +71272,8 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
}); });
_converse.ChatRoomOccupant = Backbone.Model.extend({ _converse.ChatRoomOccupant = Backbone.Model.extend({
defaults: { defaults: {
'show': 'offline' 'show': 'offline',
'jids': []
}, },
initialize(attributes) { initialize(attributes) {
...@@ -71328,7 +71338,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } ...@@ -71328,7 +71338,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
}); });
_.each(removed_members, occupant => { _.each(removed_members, occupant => {
if (occupant.get('jid') === _converse.bare_jid) { if (occupant.get('bare_jid') === _converse.bare_jid) {
return; return;
} }
...@@ -71338,17 +71348,9 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } ...@@ -71338,17 +71348,9 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
}); });
_.each(new_members, attrs => { _.each(new_members, attrs => {
let occupant; const occupant = this.findWhere({
if (attrs.jid) {
occupant = this.findOccupant({
'jid': attrs.jid
});
} else {
occupant = this.findOccupant({
'nick': attrs.nick 'nick': attrs.nick
}); });
}
if (occupant) { if (occupant) {
occupant.save(attrs); occupant.save(attrs);
...@@ -71357,27 +71359,6 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } ...@@ -71357,27 +71359,6 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
} }
}); });
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
},
findOccupant(data) {
/* Try to find an existing occupant based on the passed in
* data object.
*
* If we have a JID, we use that as lookup variable,
* otherwise we use the nick. We don't always have both,
* but should have at least one or the other.
*/
const jid = Strophe.getBareJidFromJid(data.jid);
if (jid !== null) {
return this.where({
'jid': jid
}).pop();
} else {
return this.where({
'nick': data.nick
}).pop();
}
} }
}); });
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
Backbone = converse.env.Backbone, Backbone = converse.env.Backbone,
u = converse.env.utils; u = converse.env.utils;
return describe("Chatrooms", function () { describe("Chatrooms", function () {
describe("The \"rooms\" API", function () { describe("The \"rooms\" API", function () {
it("has a method 'close' which closes rooms by JID or all rooms when called with no arguments", it("has a method 'close' which closes rooms by JID or all rooms when called with no arguments",
...@@ -1150,6 +1151,41 @@ ...@@ -1150,6 +1151,41 @@
}).catch(_.partial(console.error, _)); }).catch(_.partial(console.error, _));
})); }));
it("can handle multi-session nicknames",
mock.initConverseWithPromises(null, ['rosterGroupsFetched'], {}, function (done, _converse) {
test_utils.openAndEnterChatRoom(_converse, 'coven', 'chat.shakespeare.lit', 'oldhag').then(() => {
let presence = $pres({
'to':'dummy@localhost/pda',
'from':"coven@chat.shakespeare.lit/my1stNick"
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
.c('item').attrs({
jid: 'someone@localhost/foo',
role: 'moderator',
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
const view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
let occupants = view.el.querySelector('.occupant-list').querySelectorAll('li .occupant-nick');
expect(occupants.length).toBe(2);
expect(occupants.pop().textContent.trim()).toBe("my1stNick");
presence = $pres({
'to':'dummy@localhost/pda',
'from':"coven@chat.shakespeare.lit/my2ndNick"
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
.c('item').attrs({
jid: 'someone@localhost/bar',
role: 'moderator',
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
occupants = view.el.querySelector('.occupant-list').querySelectorAll('li .occupant-nick');
expect(occupants.length).toBe(3);
expect(occupants.pop().textContent.trim()).toBe("my2ndNick");
done();
});
}));
it("escapes occupant nicknames when rendering them, to avoid JS-injection attacks", it("escapes occupant nicknames when rendering them, to avoid JS-injection attacks",
mock.initConverseWithPromises(null, ['rosterGroupsFetched'], {}, function (done, _converse) { mock.initConverseWithPromises(null, ['rosterGroupsFetched'], {}, function (done, _converse) {
...@@ -1229,8 +1265,7 @@ ...@@ -1229,8 +1265,7 @@
.c('item').attrs({ .c('item').attrs({
jid: contact_jid, jid: contact_jid,
role: 'visitor', role: 'visitor',
}).up() });
.c('status').attrs({code:'110'}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(presence)); _converse.connection._dataRecv(test_utils.createRequest(presence));
occupants = view.el.querySelector('.occupant-list').querySelectorAll('li'); occupants = view.el.querySelector('.occupant-list').querySelectorAll('li');
......
...@@ -833,7 +833,7 @@ ...@@ -833,7 +833,7 @@
}, },
verifyRoles (roles) { verifyRoles (roles) {
const me = this.model.occupants.findWhere({'jid': _converse.bare_jid}); const me = this.model.occupants.findWhere({'bare_jid': _converse.bare_jid});
if (!_.includes(roles, me.get('role'))) { if (!_.includes(roles, me.get('role'))) {
this.showErrorMessage(__(`Forbidden: you do not have the necessary role in order to do that.`)) this.showErrorMessage(__(`Forbidden: you do not have the necessary role in order to do that.`))
return false; return false;
...@@ -842,7 +842,7 @@ ...@@ -842,7 +842,7 @@
}, },
verifyAffiliations (affiliations) { verifyAffiliations (affiliations) {
const me = this.model.occupants.findWhere({'jid': _converse.bare_jid}); const me = this.model.occupants.findWhere({'bare_jid': _converse.bare_jid});
if (!_.includes(affiliations, me.get('affiliation'))) { if (!_.includes(affiliations, me.get('affiliation'))) {
this.showErrorMessage(__(`Forbidden: you do not have the necessary affiliation in order to do that.`)) this.showErrorMessage(__(`Forbidden: you do not have the necessary affiliation in order to do that.`))
return false; return false;
...@@ -860,7 +860,7 @@ ...@@ -860,7 +860,7 @@
); );
return false; return false;
} }
if (!this.model.occupants.findWhere({'nick': args[0]}) && !this.model.occupants.findWhere({'jid': args[0]})) { if (!this.model.occupants.findWhere({'nick': args[0]}) && !this.model.occupants.findWhere({'bare_jid': args[0]})) {
this.showErrorMessage(__('Error: couldn\'t find a groupchat participant "%1$s"', args[0])); this.showErrorMessage(__('Error: couldn\'t find a groupchat participant "%1$s"', args[0]));
return false; return false;
} }
......
...@@ -341,8 +341,7 @@ ...@@ -341,8 +341,7 @@
// match. // match.
return null; return null;
} }
const occupant = this.occupants.findOccupant({'nick': longest_match}) || const occupant = this.occupants.findWhere({'nick': longest_match});
this.occupants.findOccupant({'jid': longest_match});
if (!occupant) { if (!occupant) {
return null; return null;
} }
...@@ -352,8 +351,8 @@ ...@@ -352,8 +351,8 @@
'value': longest_match, 'value': longest_match,
'type': 'mention' 'type': 'mention'
}; };
if (occupant.get('jid')) { if (occupant.get('bare_jid')) {
obj.uri = `xmpp:${occupant.get('jid')}` obj.uri = `xmpp:${occupant.get('bare_jid')}`
} }
return obj; return obj;
}, },
...@@ -873,26 +872,34 @@ ...@@ -873,26 +872,34 @@
if (data.type === 'error' || (!data.jid && !data.nick)) { if (data.type === 'error' || (!data.jid && !data.nick)) {
return true; return true;
} }
const occupant = this.occupants.findOccupant(data); const occupant = this.occupants.findWhere({'nick': data.nick});
if (data.type === 'unavailable' && occupant) { if (data.type === 'unavailable' && occupant) {
if (!_.includes(data.states, converse.MUC_NICK_CHANGED_CODE) && !occupant.isMember()) {
// We only destroy the occupant if this is not a nickname change operation. // We only destroy the occupant if this is not a nickname change operation.
// and if they're not on the member lists. // and if they're not on the member lists.
if (!_.includes(data.states, converse.MUC_NICK_CHANGED_CODE) && !occupant.isMember()) {
// Before destroying we set the new data, so // Before destroying we set the new data, so
// that we can show the disconnection message. // that we can show the disconnection message.
occupant.set(data); // TODO: see whether we need to check for multiple jids
const attributes = _.extend(data, {
'bare_jid': data.jid ? Strophe.getBareJidFromJid(data.jid) : undefined,
'jids': data.jid ? [data.jid] : []
});
occupant.set(attributes);
occupant.destroy(); occupant.destroy();
return; return;
} }
} }
const jid = Strophe.getBareJidFromJid(data.jid); if (occupant) {
const attributes = _.extend(data, { const attributes = _.extend(data, {
'jid': jid ? jid : undefined, 'bare_jid': data.jid ? Strophe.getBareJidFromJid(data.jid) : undefined,
'resource': data.jid ? Strophe.getResourceFromJid(data.jid) : undefined 'jids': data.jid ? _.concat(occupant.get('jids'), data.jid) : []
}); });
if (occupant) {
occupant.save(attributes); occupant.save(attributes);
} else { } else {
const attributes = _.extend(data, {
'bare_jid': data.jid ? Strophe.getBareJidFromJid(data.jid) : undefined,
'jids': data.jid ? [data.jid] : []
});
this.occupants.create(attributes); this.occupants.create(attributes);
} }
}, },
...@@ -1113,7 +1120,8 @@ ...@@ -1113,7 +1120,8 @@
_converse.ChatRoomOccupant = Backbone.Model.extend({ _converse.ChatRoomOccupant = Backbone.Model.extend({
defaults: { defaults: {
'show': 'offline' 'show': 'offline',
'jids': []
}, },
initialize (attributes) { initialize (attributes) {
...@@ -1175,19 +1183,14 @@ ...@@ -1175,19 +1183,14 @@
!f.includes(m.get('jid'), new_jids); !f.includes(m.get('jid'), new_jids);
}); });
_.each(removed_members, (occupant) => { _.each(removed_members, occupant => {
if (occupant.get('jid') === _converse.bare_jid) { return; } if (occupant.get('bare_jid') === _converse.bare_jid) { return; }
if (occupant.get('show') === 'offline') { if (occupant.get('show') === 'offline') {
occupant.destroy(); occupant.destroy();
} }
}); });
_.each(new_members, (attrs) => { _.each(new_members, attrs => {
let occupant; const occupant = this.findWhere({'nick': attrs.nick});
if (attrs.jid) {
occupant = this.findOccupant({'jid': attrs.jid});
} else {
occupant = this.findOccupant({'nick': attrs.nick});
}
if (occupant) { if (occupant) {
occupant.save(attrs); occupant.save(attrs);
} else { } else {
...@@ -1195,22 +1198,6 @@ ...@@ -1195,22 +1198,6 @@
} }
}); });
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
},
findOccupant (data) {
/* Try to find an existing occupant based on the passed in
* data object.
*
* If we have a JID, we use that as lookup variable,
* otherwise we use the nick. We don't always have both,
* but should have at least one or the other.
*/
const jid = Strophe.getBareJidFromJid(data.jid);
if (jid !== null) {
return this.where({'jid': jid}).pop();
} else {
return this.where({'nick': data.nick}).pop();
}
} }
}); });
...@@ -1218,7 +1205,7 @@ ...@@ -1218,7 +1205,7 @@
_converse.RoomsPanelModel = Backbone.Model.extend({ _converse.RoomsPanelModel = Backbone.Model.extend({
defaults: { defaults: {
'muc_domain': '', 'muc_domain': '',
}, }
}); });
......
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