2
0

omemo.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. (function (root, factory) {
  2. define(["jasmine", "mock", "test-utils"], factory);
  3. } (this, function (jasmine, mock, test_utils) {
  4. var Strophe = converse.env.Strophe;
  5. var b64_sha1 = converse.env.b64_sha1;
  6. var $iq = converse.env.$iq;
  7. var $msg = converse.env.$msg;
  8. var _ = converse.env._;
  9. var u = converse.env.utils;
  10. describe("The OMEMO module", function() {
  11. it("will add processing hints to sent out encrypted <message> stanzas",
  12. mock.initConverseWithPromises(
  13. null, ['rosterGroupsFetched'], {},
  14. function (done, _converse) {
  15. // TODO
  16. done();
  17. }));
  18. it("updates device lists based on PEP messages",
  19. mock.initConverseWithPromises(
  20. null, ['rosterGroupsFetched'], {},
  21. function (done, _converse) {
  22. let iq_stanza;
  23. test_utils.createContacts(_converse, 'current');
  24. const contact_jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@localhost';
  25. test_utils.waitUntil(function () {
  26. return _.filter(
  27. _converse.connection.IQ_stanzas,
  28. (iq) => {
  29. const node = iq.nodeTree.querySelector('iq[to="'+_converse.bare_jid+'"] query[node="eu.siacs.conversations.axolotl.devicelist"]');
  30. if (node) { iq_stanza = iq.nodeTree;}
  31. return node;
  32. }).length;
  33. }).then(function () {
  34. expect(iq_stanza.outerHTML).toBe(
  35. '<iq type="get" from="dummy@localhost" to="dummy@localhost" xmlns="jabber:client" id="'+iq_stanza.getAttribute("id")+'">'+
  36. '<query xmlns="http://jabber.org/protocol/disco#items" '+
  37. 'node="eu.siacs.conversations.axolotl.devicelist"/>'+
  38. '</iq>');
  39. const stanza = $iq({
  40. 'from': contact_jid,
  41. 'id': iq_stanza.getAttribute('id'),
  42. 'to': _converse.bare_jid,
  43. 'type': 'result',
  44. }).c('query', {
  45. 'xmlns': 'http://jabber.org/protocol/disco#items',
  46. 'node': 'eu.siacs.conversations.axolotl.devicelist'
  47. }).c('device', {'id': '555'}).up()
  48. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  49. expect(_converse.devicelists.length).toBe(1);
  50. const devicelist = _converse.devicelists.get(_converse.bare_jid);
  51. expect(devicelist.devices.length).toBe(1);
  52. expect(devicelist.devices.at(0).get('id')).toBe('555');
  53. return test_utils.waitUntil(() => _converse.devicelists);
  54. }).then(function () {
  55. // We simply emit, to avoid doing all the setup work
  56. _converse.emit('OMEMOInitialized');
  57. let stanza = $msg({
  58. 'from': contact_jid,
  59. 'to': _converse.bare_jid,
  60. 'type': 'headline',
  61. 'id': 'update_01',
  62. }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
  63. .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
  64. .c('item')
  65. .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
  66. .c('device', {'id': '1234'})
  67. .c('device', {'id': '4223'})
  68. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  69. expect(_converse.devicelists.length).toBe(2);
  70. let devices = _converse.devicelists.get(contact_jid).devices;
  71. expect(devices.length).toBe(2);
  72. expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('1234,4223');
  73. expect(devices.get('1234').get('active')).toBe(true);
  74. expect(devices.get('4223').get('active')).toBe(true);
  75. stanza = $msg({
  76. 'from': contact_jid,
  77. 'to': _converse.bare_jid,
  78. 'type': 'headline',
  79. 'id': 'update_02',
  80. }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
  81. .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
  82. .c('item')
  83. .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
  84. .c('device', {'id': '4223'})
  85. .c('device', {'id': '4224'})
  86. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  87. expect(_converse.devicelists.length).toBe(2);
  88. expect(devices.length).toBe(3);
  89. expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('1234,4223,4224');
  90. expect(devices.get('1234').get('active')).toBe(false);
  91. expect(devices.get('4223').get('active')).toBe(true);
  92. expect(devices.get('4224').get('active')).toBe(true);
  93. // Check that own devicelist gets updated
  94. stanza = $msg({
  95. 'from': _converse.bare_jid,
  96. 'to': _converse.bare_jid,
  97. 'type': 'headline',
  98. 'id': 'update_03',
  99. }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
  100. .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
  101. .c('item')
  102. .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
  103. .c('device', {'id': '555'})
  104. .c('device', {'id': '777'})
  105. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  106. expect(_converse.devicelists.length).toBe(2);
  107. devices = _converse.devicelists.get(_converse.bare_jid).devices;
  108. expect(devices.length).toBe(3);
  109. expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,555,777');
  110. expect(devices.get('123456789').get('active')).toBe(true);
  111. expect(devices.get('555').get('active')).toBe(true);
  112. expect(devices.get('777').get('active')).toBe(true);
  113. _converse.connection.IQ_stanzas = [];
  114. // Check that own device gets re-added
  115. stanza = $msg({
  116. 'from': _converse.bare_jid,
  117. 'to': _converse.bare_jid,
  118. 'type': 'headline',
  119. 'id': 'update_04',
  120. }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
  121. .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
  122. .c('item')
  123. .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
  124. .c('device', {'id': '444'})
  125. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  126. return test_utils.waitUntil(function () {
  127. return _.filter(
  128. _converse.connection.IQ_stanzas,
  129. (iq) => {
  130. const node = iq.nodeTree.querySelector('iq[from="'+_converse.bare_jid+'"] publish[node="eu.siacs.conversations.axolotl.devicelist"]');
  131. if (node) { iq_stanza = iq.nodeTree;}
  132. return node;
  133. }).length;
  134. });
  135. }).then(function () {
  136. // Check that our own device is added again, but that removed
  137. // devices are not added.
  138. expect(iq_stanza.outerHTML).toBe(
  139. '<iq from="dummy@localhost" type="set" xmlns="jabber:client" id="'+iq_stanza.getAttribute('id')+'">'+
  140. '<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
  141. '<publish node="eu.siacs.conversations.axolotl.devicelist">'+
  142. '<item>'+
  143. '<list xmlns="eu.siacs.conversations.axolotl"/>'+
  144. '<device id="123456789"/>'+
  145. '<device id="444"/>'+
  146. '</item>'+
  147. '</publish>'+
  148. '</pubsub>'+
  149. '</iq>');
  150. expect(_converse.devicelists.length).toBe(2);
  151. const devices = _converse.devicelists.get(_converse.bare_jid).devices;
  152. // The device id for this device (123456789) was also generated and added to the list,
  153. // which is why we have 4 devices now.
  154. expect(devices.length).toBe(4);
  155. expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,444,555,777');
  156. expect(devices.get('123456789').get('active')).toBe(true);
  157. expect(devices.get('444').get('active')).toBe(true);
  158. expect(devices.get('555').get('active')).toBe(false);
  159. expect(devices.get('777').get('active')).toBe(false);
  160. done();
  161. });
  162. }));
  163. it("updates device bundles based on PEP messages",
  164. mock.initConverseWithPromises(
  165. null, ['rosterGroupsFetched'], {},
  166. function (done, _converse) {
  167. let iq_stanza;
  168. test_utils.createContacts(_converse, 'current');
  169. const contact_jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@localhost';
  170. test_utils.waitUntil(function () {
  171. return _.filter(
  172. _converse.connection.IQ_stanzas,
  173. (iq) => {
  174. const node = iq.nodeTree.querySelector('iq[to="'+_converse.bare_jid+'"] query[node="eu.siacs.conversations.axolotl.devicelist"]');
  175. if (node) { iq_stanza = iq.nodeTree;}
  176. return node;
  177. }).length;
  178. }).then(function () {
  179. expect(iq_stanza.outerHTML).toBe(
  180. '<iq type="get" from="dummy@localhost" to="dummy@localhost" xmlns="jabber:client" id="'+iq_stanza.getAttribute("id")+'">'+
  181. '<query xmlns="http://jabber.org/protocol/disco#items" '+
  182. 'node="eu.siacs.conversations.axolotl.devicelist"/>'+
  183. '</iq>');
  184. const stanza = $iq({
  185. 'from': contact_jid,
  186. 'id': iq_stanza.getAttribute('id'),
  187. 'to': _converse.bare_jid,
  188. 'type': 'result',
  189. }).c('query', {
  190. 'xmlns': 'http://jabber.org/protocol/disco#items',
  191. 'node': 'eu.siacs.conversations.axolotl.devicelist'
  192. }).c('device', {'id': '555'}).up()
  193. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  194. expect(_converse.devicelists.length).toBe(1);
  195. return test_utils.waitUntil(() => _converse.devicelists);
  196. }).then(function () {
  197. // We simply emit, to avoid doing all the setup work
  198. expect(_converse.devicelists.length).toBe(1);
  199. let devicelist = _converse.devicelists.get(_converse.bare_jid);
  200. expect(devicelist.devices.length).toBe(2);
  201. expect(devicelist.devices.at(0).get('id')).toBe('555');
  202. expect(devicelist.devices.at(1).get('id')).toBe('123456789');
  203. _converse.emit('OMEMOInitialized');
  204. let stanza = $msg({
  205. 'from': contact_jid,
  206. 'to': _converse.bare_jid,
  207. 'type': 'headline',
  208. 'id': 'update_01',
  209. }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
  210. .c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:555'})
  211. .c('item')
  212. .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
  213. .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t('1111').up()
  214. .c('signedPreKeySignature').t('2222').up()
  215. .c('identityKey').t('3333').up()
  216. .c('prekeys')
  217. .c('preKeyPublic', {'preKeyId': '1001'}).up()
  218. .c('preKeyPublic', {'preKeyId': '1002'}).up()
  219. .c('preKeyPublic', {'preKeyId': '1003'});
  220. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  221. expect(_converse.devicelists.length).toBe(2);
  222. devicelist = _converse.devicelists.get(contact_jid);
  223. expect(devicelist.devices.length).toBe(1);
  224. let device = devicelist.devices.at(0);
  225. expect(device.get('bundle').identity_key).toBe(3333);
  226. expect(device.get('bundle').signed_prekey.public_key).toBe('1111');
  227. expect(device.get('bundle').signed_prekey.id).toBe(4223);
  228. expect(device.get('bundle').signed_prekey.signature).toBe('2222');
  229. expect(device.get('bundle').prekeys.length).toBe(3);
  230. expect(device.get('bundle').prekeys[0].id).toBe(1001);
  231. expect(device.get('bundle').prekeys[1].id).toBe(1002);
  232. expect(device.get('bundle').prekeys[2].id).toBe(1003);
  233. stanza = $msg({
  234. 'from': contact_jid,
  235. 'to': _converse.bare_jid,
  236. 'type': 'headline',
  237. 'id': 'update_02',
  238. }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
  239. .c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:555'})
  240. .c('item')
  241. .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
  242. .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t('5555').up()
  243. .c('signedPreKeySignature').t('6666').up()
  244. .c('identityKey').t('7777').up()
  245. .c('prekeys')
  246. .c('preKeyPublic', {'preKeyId': '2001'}).up()
  247. .c('preKeyPublic', {'preKeyId': '2002'}).up()
  248. .c('preKeyPublic', {'preKeyId': '2003'});
  249. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  250. expect(_converse.devicelists.length).toBe(2);
  251. devicelist = _converse.devicelists.get(contact_jid);
  252. expect(devicelist.devices.length).toBe(1);
  253. device = devicelist.devices.at(0);
  254. expect(device.get('bundle').identity_key).toBe(7777);
  255. expect(device.get('bundle').signed_prekey.public_key).toBe('5555');
  256. expect(device.get('bundle').signed_prekey.id).toBe(4223);
  257. expect(device.get('bundle').signed_prekey.signature).toBe('6666');
  258. expect(device.get('bundle').prekeys.length).toBe(3);
  259. expect(device.get('bundle').prekeys[0].id).toBe(2001);
  260. expect(device.get('bundle').prekeys[1].id).toBe(2002);
  261. expect(device.get('bundle').prekeys[2].id).toBe(2003);
  262. stanza = $msg({
  263. 'from': _converse.bare_jid,
  264. 'to': _converse.bare_jid,
  265. 'type': 'headline',
  266. 'id': 'update_03',
  267. }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
  268. .c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:123456789'})
  269. .c('item')
  270. .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
  271. .c('signedPreKeyPublic', {'signedPreKeyId': '9999'}).t('8888').up()
  272. .c('signedPreKeySignature').t('3333').up()
  273. .c('identityKey').t('1111').up()
  274. .c('prekeys')
  275. .c('preKeyPublic', {'preKeyId': '3001'}).up()
  276. .c('preKeyPublic', {'preKeyId': '3002'}).up()
  277. .c('preKeyPublic', {'preKeyId': '3003'});
  278. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  279. expect(_converse.devicelists.length).toBe(2);
  280. devicelist = _converse.devicelists.get(_converse.bare_jid);
  281. expect(devicelist.devices.length).toBe(2);
  282. expect(devicelist.devices.at(0).get('id')).toBe('555');
  283. expect(devicelist.devices.at(1).get('id')).toBe('123456789');
  284. device = devicelist.devices.at(1);
  285. expect(device.get('bundle').identity_key).toBe(1111);
  286. expect(device.get('bundle').signed_prekey.public_key).toBe('8888');
  287. expect(device.get('bundle').signed_prekey.id).toBe(9999);
  288. expect(device.get('bundle').signed_prekey.signature).toBe('3333');
  289. expect(device.get('bundle').prekeys.length).toBe(3);
  290. expect(device.get('bundle').prekeys[0].id).toBe(3001);
  291. expect(device.get('bundle').prekeys[1].id).toBe(3002);
  292. expect(device.get('bundle').prekeys[2].id).toBe(3003);
  293. done();
  294. });
  295. }));
  296. it("publishes a bundle with which an encrypted session can be created",
  297. mock.initConverseWithPromises(
  298. null, ['rosterGroupsFetched'], {},
  299. function (done, _converse) {
  300. _converse.NUM_PREKEYS = 2; // Restrict to 2, otherwise the resulting stanza is too large to easily test
  301. let iq_stanza;
  302. test_utils.createContacts(_converse, 'current', 1);
  303. const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  304. test_utils.waitUntil(function () {
  305. return _.filter(
  306. _converse.connection.IQ_stanzas,
  307. (iq) => {
  308. const node = iq.nodeTree.querySelector('iq[to="'+_converse.bare_jid+'"] query[node="eu.siacs.conversations.axolotl.devicelist"]');
  309. if (node) { iq_stanza = iq.nodeTree;}
  310. return node;
  311. }).length;
  312. }).then(function () {
  313. const stanza = $iq({
  314. 'from': contact_jid,
  315. 'id': iq_stanza.getAttribute('id'),
  316. 'to': _converse.bare_jid,
  317. 'type': 'result',
  318. }).c('query', {
  319. 'xmlns': 'http://jabber.org/protocol/disco#items',
  320. 'node': 'eu.siacs.conversations.axolotl.devicelist'
  321. }).c('device', {'id': '482886413b977930064a5888b92134fe'}).up()
  322. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  323. expect(_converse.devicelists.length).toBe(1);
  324. test_utils.openChatBoxFor(_converse, contact_jid);
  325. return test_utils.waitUntil(() => {
  326. return _.filter(_converse.connection.IQ_stanzas, function (iq) {
  327. const node = iq.nodeTree.querySelector('publish[node="eu.siacs.conversations.axolotl.devicelist"]');
  328. if (node) { iq_stanza = iq.nodeTree; }
  329. return node;
  330. }).length;
  331. });
  332. }).then(function () {
  333. const stanza = $iq({
  334. 'from': _converse.bare_jid,
  335. 'id': iq_stanza.getAttribute('id'),
  336. 'to': _converse.bare_jid,
  337. 'type': 'result'});
  338. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  339. return test_utils.waitUntil(() => {
  340. return _.filter(_converse.connection.IQ_stanzas, function (iq) {
  341. const node = iq.nodeTree.querySelector('publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]');
  342. if (node) { iq_stanza = iq.nodeTree; }
  343. return node;
  344. }).length;
  345. });
  346. }).then(function () {
  347. expect(iq_stanza.outerHTML).toBe(
  348. `<iq from="dummy@localhost" type="set" xmlns="jabber:client" id="${iq_stanza.getAttribute('id')}">`+
  349. `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
  350. `<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`+
  351. `<item>`+
  352. `<bundle xmlns="eu.siacs.conversations.axolotl">`+
  353. `<signedPreKeyPublic signedPreKeyId="0">${btoa('1234')}</signedPreKeyPublic>`+
  354. `<signedPreKeySignature>${btoa('11112222333344445555')}</signedPreKeySignature>`+
  355. `<identityKey>${btoa('1234')}</identityKey>`+
  356. `<prekeys>`+
  357. `<preKeyPublic preKeyId="0">${btoa('1234')}</preKeyPublic>`+
  358. `<preKeyPublic preKeyId="1">${btoa('1234')}</preKeyPublic>`+
  359. `</prekeys>`+
  360. `</bundle>`+
  361. `</item>`+
  362. `</publish>`+
  363. `</pubsub>`+
  364. `</iq>`)
  365. done();
  366. });
  367. }));
  368. it("adds a toolbar button for starting an encrypted chat session",
  369. mock.initConverseWithPromises(
  370. null, ['rosterGroupsFetched'], {},
  371. function (done, _converse) {
  372. let iq_stanza;
  373. test_utils.createContacts(_converse, 'current', 1);
  374. const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  375. test_utils.waitUntil(function () {
  376. return _.filter(
  377. _converse.connection.IQ_stanzas,
  378. (iq) => {
  379. const node = iq.nodeTree.querySelector('iq[to="'+_converse.bare_jid+'"] query[node="eu.siacs.conversations.axolotl.devicelist"]');
  380. if (node) { iq_stanza = iq.nodeTree;}
  381. return node;
  382. }).length;
  383. }).then(function () {
  384. expect(iq_stanza.outerHTML).toBe(
  385. '<iq type="get" from="dummy@localhost" to="dummy@localhost" xmlns="jabber:client" id="'+iq_stanza.getAttribute("id")+'">'+
  386. '<query xmlns="http://jabber.org/protocol/disco#items" '+
  387. 'node="eu.siacs.conversations.axolotl.devicelist"/>'+
  388. '</iq>');
  389. const stanza = $iq({
  390. 'from': contact_jid,
  391. 'id': iq_stanza.getAttribute('id'),
  392. 'to': _converse.bare_jid,
  393. 'type': 'result',
  394. }).c('query', {
  395. 'xmlns': 'http://jabber.org/protocol/disco#items',
  396. 'node': 'eu.siacs.conversations.axolotl.devicelist'
  397. }).c('device', {'id': '482886413b977930064a5888b92134fe'}).up()
  398. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  399. expect(_converse.devicelists.length).toBe(1);
  400. const devicelist = _converse.devicelists.get(_converse.bare_jid);
  401. expect(devicelist.devices.length).toBe(1);
  402. expect(devicelist.devices.at(0).get('id')).toBe('482886413b977930064a5888b92134fe');
  403. test_utils.openChatBoxFor(_converse, contact_jid);
  404. return test_utils.waitUntil(() => {
  405. return _.filter(_converse.connection.IQ_stanzas, function (iq) {
  406. const node = iq.nodeTree.querySelector('publish[node="eu.siacs.conversations.axolotl.devicelist"]');
  407. if (node) { iq_stanza = iq.nodeTree; }
  408. return node;
  409. }).length;
  410. });
  411. }).then(function () {
  412. expect(iq_stanza.outerHTML).toBe(
  413. '<iq from="dummy@localhost" type="set" xmlns="jabber:client" id="'+iq_stanza.getAttribute('id')+'">'+
  414. '<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
  415. '<publish node="eu.siacs.conversations.axolotl.devicelist">'+
  416. '<item>'+
  417. '<list xmlns="eu.siacs.conversations.axolotl"/>'+
  418. '<device id="482886413b977930064a5888b92134fe"/>'+
  419. '<device id="123456789"/>'+
  420. '</item>'+
  421. '</publish>'+
  422. '</pubsub>'+
  423. '</iq>');
  424. const stanza = $iq({
  425. 'from': _converse.bare_jid,
  426. 'id': iq_stanza.getAttribute('id'),
  427. 'to': _converse.bare_jid,
  428. 'type': 'result'});
  429. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  430. return test_utils.waitUntil(() => {
  431. return _.filter(_converse.connection.IQ_stanzas, function (iq) {
  432. const node = iq.nodeTree.querySelector('publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]');
  433. if (node) { iq_stanza = iq.nodeTree; }
  434. return node;
  435. }).length;
  436. });
  437. }).then(function () {
  438. expect(iq_stanza.getAttributeNames().sort().join()).toBe(["from", "type", "xmlns", "id"].sort().join());
  439. expect(iq_stanza.querySelector('prekeys').childNodes.length).toBe(100);
  440. const signed_prekeys = iq_stanza.querySelectorAll('signedPreKeyPublic');
  441. expect(signed_prekeys.length).toBe(1);
  442. const signed_prekey = signed_prekeys[0];
  443. expect(signed_prekey.getAttribute('signedPreKeyId')).toBe('0')
  444. expect(iq_stanza.querySelectorAll('signedPreKeySignature').length).toBe(1);
  445. expect(iq_stanza.querySelectorAll('identityKey').length).toBe(1);
  446. const stanza = $iq({
  447. 'from': _converse.bare_jid,
  448. 'id': iq_stanza.getAttribute('id'),
  449. 'to': _converse.bare_jid,
  450. 'type': 'result'});
  451. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  452. return test_utils.waitUntil(() => {
  453. return _.filter(
  454. _converse.connection.IQ_stanzas,
  455. (iq) => {
  456. const node = iq.nodeTree.querySelector('iq[to="'+contact_jid+'"] query[node="eu.siacs.conversations.axolotl.devicelist"]');
  457. if (node) { iq_stanza = iq.nodeTree; }
  458. return node;
  459. }).length;});
  460. }).then(function () {
  461. expect(iq_stanza.outerHTML).toBe(
  462. '<iq type="get" from="dummy@localhost" to="'+contact_jid+'" xmlns="jabber:client" id="'+iq_stanza.getAttribute("id")+'">'+
  463. '<query xmlns="http://jabber.org/protocol/disco#items" '+
  464. 'node="eu.siacs.conversations.axolotl.devicelist"/>'+
  465. '</iq>');
  466. const stanza = $iq({
  467. 'from': contact_jid,
  468. 'id': iq_stanza.getAttribute('id'),
  469. 'to': _converse.bare_jid,
  470. 'type': 'result',
  471. }).c('query', {
  472. 'xmlns': 'http://jabber.org/protocol/disco#items',
  473. 'node': 'eu.siacs.conversations.axolotl.devicelist'
  474. }).c('device', {'id': '368866411b877c30064a5f62b917cffe'}).up()
  475. .c('device', {'id': '3300659945416e274474e469a1f0154c'}).up()
  476. .c('device', {'id': '4e30f35051b7b8b42abe083742187228'}).up()
  477. .c('device', {'id': 'ae890ac52d0df67ed7cfdf51b644e901'});
  478. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  479. expect(_converse.devicelists.length).toBe(2);
  480. const devicelist = _converse.devicelists.get(contact_jid);
  481. expect(devicelist.devices.length).toBe(4);
  482. expect(devicelist.devices.at(0).get('id')).toBe('368866411b877c30064a5f62b917cffe');
  483. expect(devicelist.devices.at(1).get('id')).toBe('3300659945416e274474e469a1f0154c');
  484. expect(devicelist.devices.at(2).get('id')).toBe('4e30f35051b7b8b42abe083742187228');
  485. expect(devicelist.devices.at(3).get('id')).toBe('ae890ac52d0df67ed7cfdf51b644e901');
  486. return test_utils.waitUntil(() => _converse.chatboxviews.get(contact_jid).el.querySelector('.chat-toolbar'));
  487. }).then(function () {
  488. const view = _converse.chatboxviews.get(contact_jid);
  489. const toolbar = view.el.querySelector('.chat-toolbar');
  490. expect(view.model.get('omemo_active')).toBe(undefined);
  491. const toggle = toolbar.querySelector('.toggle-omemo');
  492. expect(_.isNull(toggle)).toBe(false);
  493. expect(u.hasClass('fa-unlock', toggle)).toBe(true);
  494. expect(u.hasClass('fa-lock', toggle)).toBe(false);
  495. spyOn(view, 'toggleOMEMO').and.callThrough();
  496. view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
  497. toolbar.querySelector('.toggle-omemo').click();
  498. expect(view.toggleOMEMO).toHaveBeenCalled();
  499. expect(view.model.get('omemo_active')).toBe(true);
  500. return test_utils.waitUntil(() => u.hasClass('fa-lock', toolbar.querySelector('.toggle-omemo')));
  501. }).then(function () {
  502. const view = _converse.chatboxviews.get(contact_jid);
  503. const toolbar = view.el.querySelector('.chat-toolbar');
  504. const toggle = toolbar.querySelector('.toggle-omemo');
  505. expect(u.hasClass('fa-unlock', toggle)).toBe(false);
  506. expect(u.hasClass('fa-lock', toggle)).toBe(true);
  507. const textarea = view.el.querySelector('.chat-textarea');
  508. textarea.value = 'This message will be sent encrypted';
  509. view.keyPressed({
  510. target: textarea,
  511. preventDefault: _.noop,
  512. keyCode: 13
  513. });
  514. done();
  515. }).catch(_.partial(console.error, _));
  516. }));
  517. });
  518. describe("A chatbox with an active OMEMO session", function() {
  519. it("will not show the spoiler toolbar button",
  520. mock.initConverseWithPromises(
  521. null, ['rosterGroupsFetched'], {},
  522. function (done, _converse) {
  523. // TODO
  524. done()
  525. }));
  526. });
  527. }));