Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
converse.js
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
converse.js
Commits
bcd68457
Commit
bcd68457
authored
Jul 22, 2018
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Show fingerprints in the user details modal
updates #497
parent
ce447e40
Changes
12
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
698 additions
and
383 deletions
+698
-383
css/converse.css
css/converse.css
+45
-38
dist/converse.js
dist/converse.js
+390
-291
locale/af/LC_MESSAGES/converse.po
locale/af/LC_MESSAGES/converse.po
+1
-1
sass/_forms.scss
sass/_forms.scss
+5
-0
sass/_profile.scss
sass/_profile.scss
+5
-0
sass/_variables.scss
sass/_variables.scss
+2
-0
spec/omemo.js
spec/omemo.js
+123
-6
spec/roster.js
spec/roster.js
+0
-11
src/converse-chatview.js
src/converse-chatview.js
+9
-12
src/converse-omemo.js
src/converse-omemo.js
+58
-11
src/templates/user_details_modal.html
src/templates/user_details_modal.html
+44
-13
src/utils/core.js
src/utils/core.js
+16
-0
No files found.
css/converse.css
View file @
bcd68457
...
...
@@ -2346,7 +2346,7 @@
--primary
:
#387592
;
--secondary
:
#6c757d
;
--success
:
#3AA569
;
--info
:
#
17a2b8
;
--info
:
#
3AA569
;
--warning
:
#ffc107
;
--danger
:
#E77051
;
--light
:
#f8f9fa
;
...
...
@@ -3594,24 +3594,24 @@
box-shadow
:
0
0
0
0.2rem
rgba
(
58
,
165
,
105
,
0.5
);
}
#conversejs
.btn-info
{
color
:
#fff
;
background-color
:
#
17a2b8
;
border-color
:
#
17a2b8
;
}
background-color
:
#
3AA569
;
border-color
:
#
3AA569
;
}
#conversejs
.btn-info
:hover
{
color
:
#fff
;
background-color
:
#
138496
;
border-color
:
#
117a8b
;
}
background-color
:
#
308957
;
border-color
:
#
2d7f51
;
}
#conversejs
.btn-info
:focus
,
#conversejs
.btn-info.focus
{
box-shadow
:
0
0
0
0.2rem
rgba
(
23
,
162
,
184
,
0.5
);
}
box-shadow
:
0
0
0
0.2rem
rgba
(
58
,
165
,
105
,
0.5
);
}
#conversejs
.btn-info.disabled
,
#conversejs
.btn-info
:disabled
{
color
:
#fff
;
background-color
:
#
17a2b8
;
border-color
:
#
17a2b8
;
}
background-color
:
#
3AA569
;
border-color
:
#
3AA569
;
}
#conversejs
.btn-info
:not
(
:disabled
)
:not
(
.disabled
)
:active
,
#conversejs
.btn-info
:not
(
:disabled
)
:not
(
.disabled
)
.active
,
.show
>
#conversejs
.btn-info.dropdown-toggle
{
color
:
#fff
;
background-color
:
#
117a8b
;
border-color
:
#
10707f
;
}
background-color
:
#
2d7f51
;
border-color
:
#
29764b
;
}
#conversejs
.btn-info
:not
(
:disabled
)
:not
(
.disabled
)
:active:focus
,
#conversejs
.btn-info
:not
(
:disabled
)
:not
(
.disabled
)
.active
:focus
,
.show
>
#conversejs
.btn-info.dropdown-toggle
:focus
{
box-shadow
:
0
0
0
0.2rem
rgba
(
23
,
162
,
184
,
0.5
);
}
box-shadow
:
0
0
0
0.2rem
rgba
(
58
,
165
,
105
,
0.5
);
}
#conversejs
.btn-warning
{
color
:
#212529
;
background-color
:
#ffc107
;
...
...
@@ -3753,25 +3753,25 @@
#conversejs
.btn-outline-success
:not
(
:disabled
)
:not
(
.disabled
)
:active:focus
,
#conversejs
.btn-outline-success
:not
(
:disabled
)
:not
(
.disabled
)
.active
:focus
,
.show
>
#conversejs
.btn-outline-success.dropdown-toggle
:focus
{
box-shadow
:
0
0
0
0.2rem
rgba
(
58
,
165
,
105
,
0.5
);
}
#conversejs
.btn-outline-info
{
color
:
#
17a2b8
;
color
:
#
3AA569
;
background-color
:
transparent
;
background-image
:
none
;
border-color
:
#
17a2b8
;
}
border-color
:
#
3AA569
;
}
#conversejs
.btn-outline-info
:hover
{
color
:
#fff
;
background-color
:
#
17a2b8
;
border-color
:
#
17a2b8
;
}
background-color
:
#
3AA569
;
border-color
:
#
3AA569
;
}
#conversejs
.btn-outline-info
:focus
,
#conversejs
.btn-outline-info.focus
{
box-shadow
:
0
0
0
0.2rem
rgba
(
23
,
162
,
184
,
0.5
);
}
box-shadow
:
0
0
0
0.2rem
rgba
(
58
,
165
,
105
,
0.5
);
}
#conversejs
.btn-outline-info.disabled
,
#conversejs
.btn-outline-info
:disabled
{
color
:
#
17a2b8
;
color
:
#
3AA569
;
background-color
:
transparent
;
}
#conversejs
.btn-outline-info
:not
(
:disabled
)
:not
(
.disabled
)
:active
,
#conversejs
.btn-outline-info
:not
(
:disabled
)
:not
(
.disabled
)
.active
,
.show
>
#conversejs
.btn-outline-info.dropdown-toggle
{
color
:
#fff
;
background-color
:
#
17a2b8
;
border-color
:
#
17a2b8
;
}
background-color
:
#
3AA569
;
border-color
:
#
3AA569
;
}
#conversejs
.btn-outline-info
:not
(
:disabled
)
:not
(
.disabled
)
:active:focus
,
#conversejs
.btn-outline-info
:not
(
:disabled
)
:not
(
.disabled
)
.active
:focus
,
.show
>
#conversejs
.btn-outline-info.dropdown-toggle
:focus
{
box-shadow
:
0
0
0
0.2rem
rgba
(
23
,
162
,
184
,
0.5
);
}
box-shadow
:
0
0
0
0.2rem
rgba
(
58
,
165
,
105
,
0.5
);
}
#conversejs
.btn-outline-warning
{
color
:
#ffc107
;
background-color
:
transparent
;
...
...
@@ -4582,11 +4582,11 @@
background-color
:
#2d7f51
;
}
#conversejs
.badge-info
{
color
:
#fff
;
background-color
:
#
17a2b8
;
}
background-color
:
#
3AA569
;
}
#conversejs
.badge-info
[
href
]
:hover
,
#conversejs
.badge-info
[
href
]
:focus
{
color
:
#fff
;
text-decoration
:
none
;
background-color
:
#
117a8b
;
}
background-color
:
#
2d7f51
;
}
#conversejs
.badge-warning
{
color
:
#212529
;
background-color
:
#ffc107
;
}
...
...
@@ -4658,13 +4658,13 @@
#conversejs
.alert-success
.alert-link
{
color
:
#11301f
;
}
#conversejs
.alert-info
{
color
:
#
0c5460
;
background-color
:
#d
1ecf
1
;
border-color
:
#
bee5eb
;
}
color
:
#
1e5637
;
background-color
:
#d
8ede
1
;
border-color
:
#
c8e6d5
;
}
#conversejs
.alert-info
hr
{
border-top-color
:
#
abdde5
;
}
border-top-color
:
#
b6dec8
;
}
#conversejs
.alert-info
.alert-link
{
color
:
#
062c33
;
}
color
:
#
11301f
;
}
#conversejs
.alert-warning
{
color
:
#856404
;
background-color
:
#fff3cd
;
...
...
@@ -4782,15 +4782,15 @@
background-color
:
#1e5637
;
border-color
:
#1e5637
;
}
#conversejs
.list-group-item-info
{
color
:
#
0c5460
;
background-color
:
#
bee5eb
;
}
color
:
#
1e5637
;
background-color
:
#
c8e6d5
;
}
#conversejs
.list-group-item-info.list-group-item-action
:hover
,
#conversejs
.list-group-item-info.list-group-item-action
:focus
{
color
:
#
0c5460
;
background-color
:
#
abdde5
;
}
color
:
#
1e5637
;
background-color
:
#
b6dec8
;
}
#conversejs
.list-group-item-info.list-group-item-action.active
{
color
:
#fff
;
background-color
:
#
0c5460
;
border-color
:
#
0c5460
;
}
background-color
:
#
1e5637
;
border-color
:
#
1e5637
;
}
#conversejs
.list-group-item-warning
{
color
:
#856404
;
background-color
:
#ffeeba
;
}
...
...
@@ -5179,11 +5179,11 @@
#conversejs
button
.bg-success
:focus
{
background-color
:
#2d7f51
!important
;
}
#conversejs
.bg-info
{
background-color
:
#
17a2b8
!important
;
}
background-color
:
#
3AA569
!important
;
}
#conversejs
a
.bg-info
:hover
,
#conversejs
a
.bg-info
:focus
,
#conversejs
button
.bg-info
:hover
,
#conversejs
button
.bg-info
:focus
{
background-color
:
#
117a8b
!important
;
}
background-color
:
#
2d7f51
!important
;
}
#conversejs
.bg-warning
{
background-color
:
#ffc107
!important
;
}
#conversejs
a
.bg-warning
:hover
,
#conversejs
a
.bg-warning
:focus
,
...
...
@@ -5239,7 +5239,7 @@
#conversejs
.border-success
{
border-color
:
#3AA569
!important
;
}
#conversejs
.border-info
{
border-color
:
#
17a2b8
!important
;
}
border-color
:
#
3AA569
!important
;
}
#conversejs
.border-warning
{
border-color
:
#ffc107
!important
;
}
#conversejs
.border-danger
{
...
...
@@ -6792,9 +6792,9 @@
#conversejs
a
.text-success
:hover
,
#conversejs
a
.text-success
:focus
{
color
:
#2d7f51
!important
;
}
#conversejs
.text-info
{
color
:
#
17a2b8
!important
;
}
color
:
#
3AA569
!important
;
}
#conversejs
a
.text-info
:hover
,
#conversejs
a
.text-info
:focus
{
color
:
#
117a8b
!important
;
}
color
:
#
2d7f51
!important
;
}
#conversejs
.text-warning
{
color
:
#ffc107
!important
;
}
#conversejs
a
.text-warning
:hover
,
#conversejs
a
.text-warning
:focus
{
...
...
@@ -7213,6 +7213,9 @@ body.reset {
@media
screen
and
(
max-height
:
450px
)
{
#conversejs
{
left
:
0
;
}
}
#conversejs
.btn--small
{
font-size
:
80%
;
font-weight
:
normal
;
}
#conversejs
form
.form-group
{
margin-bottom
:
2em
;
}
#conversejs
form
.form-check-label
{
...
...
@@ -7291,6 +7294,10 @@ body.reset {
#conversejs
#user-profile-modal
label
{
font-weight
:
bold
;
}
#conversejs
.fingerprint-trust
{
display
:
flex
;
justify-content
:
space-between
;
font-size
:
95%
;
}
#conversejs
.chatbox-navback
{
display
:
none
;
}
...
...
dist/converse.js
View file @
bcd68457
This diff is collapsed.
Click to expand it.
locale/af/LC_MESSAGES/converse.po
View file @
bcd68457
...
...
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: Converse.js 0.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-07-22 11:17+0200\n"
"PO-Revision-Date: 2018-07-22 1
2:12
+0200\n"
"PO-Revision-Date: 2018-07-22 1
5:37
+0200\n"
"Last-Translator: JC Brand <jc@opkode.com>\n"
"Language-Team: Afrikaans <https://hosted.weblate.org/projects/conversejs/"
"translations/af/>\n"
...
...
sass/_forms.scss
View file @
bcd68457
#conversejs
{
.btn--small
{
font-size
:
80%
;
font-weight
:
normal
;
}
form
{
.form-group
{
margin-bottom
:
2em
;
...
...
sass/_profile.scss
View file @
bcd68457
...
...
@@ -4,4 +4,9 @@
font-weight
:
bold
;
}
}
.fingerprint-trust
{
display
:
flex
;
justify-content
:
space-between
;
font-size
:
95%
;
}
}
sass/_variables.scss
View file @
bcd68457
...
...
@@ -34,6 +34,8 @@ $green: #3AA569;
$dark-green
:
#1E9652
;
$darkest-green
:
#0E763B
;
$info
:
$green
!
default
;
$lightest-green
:
#E7FBF0
;
$light-green
:
#5CBC86
;
$green
:
#3AA569
;
...
...
spec/omemo.js
View file @
bcd68457
...
...
@@ -245,7 +245,7 @@
devicelist
=
_converse
.
devicelists
.
get
(
contact_jid
);
expect
(
devicelist
.
devices
.
length
).
toBe
(
1
);
let
device
=
devicelist
.
devices
.
at
(
0
);
expect
(
device
.
get
(
'
bundle
'
).
identity_key
).
toBe
(
3333
);
expect
(
device
.
get
(
'
bundle
'
).
identity_key
).
toBe
(
'
3333
'
);
expect
(
device
.
get
(
'
bundle
'
).
signed_prekey
.
public_key
).
toBe
(
'
1111
'
);
expect
(
device
.
get
(
'
bundle
'
).
signed_prekey
.
id
).
toBe
(
4223
);
expect
(
device
.
get
(
'
bundle
'
).
signed_prekey
.
signature
).
toBe
(
'
2222
'
);
...
...
@@ -276,7 +276,7 @@
devicelist
=
_converse
.
devicelists
.
get
(
contact_jid
);
expect
(
devicelist
.
devices
.
length
).
toBe
(
1
);
device
=
devicelist
.
devices
.
at
(
0
);
expect
(
device
.
get
(
'
bundle
'
).
identity_key
).
toBe
(
7777
);
expect
(
device
.
get
(
'
bundle
'
).
identity_key
).
toBe
(
'
7777
'
);
expect
(
device
.
get
(
'
bundle
'
).
signed_prekey
.
public_key
).
toBe
(
'
5555
'
);
expect
(
device
.
get
(
'
bundle
'
).
signed_prekey
.
id
).
toBe
(
4223
);
expect
(
device
.
get
(
'
bundle
'
).
signed_prekey
.
signature
).
toBe
(
'
6666
'
);
...
...
@@ -309,7 +309,7 @@
expect
(
devicelist
.
devices
.
at
(
0
).
get
(
'
id
'
)).
toBe
(
'
555
'
);
expect
(
devicelist
.
devices
.
at
(
1
).
get
(
'
id
'
)).
toBe
(
'
123456789
'
);
device
=
devicelist
.
devices
.
at
(
1
);
expect
(
device
.
get
(
'
bundle
'
).
identity_key
).
toBe
(
1111
);
expect
(
device
.
get
(
'
bundle
'
).
identity_key
).
toBe
(
'
1111
'
);
expect
(
device
.
get
(
'
bundle
'
).
signed_prekey
.
public_key
).
toBe
(
'
8888
'
);
expect
(
device
.
get
(
'
bundle
'
).
signed_prekey
.
id
).
toBe
(
9999
);
expect
(
device
.
get
(
'
bundle
'
).
signed_prekey
.
signature
).
toBe
(
'
3333
'
);
...
...
@@ -396,8 +396,15 @@
`</publish>`
+
`</pubsub>`
+
`</iq>`
)
done
();
});
const
stanza
=
$iq
({
'
from
'
:
_converse
.
bare_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
_converse
.
api
.
waitUntil
(
'
OMEMOInitialized
'
);
}).
then
(
done
).
catch
(
_
.
partial
(
console
.
error
,
_
));
}));
it
(
"
adds a toolbar button for starting an encrypted chat session
"
,
...
...
@@ -494,7 +501,6 @@
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
...
...
@@ -565,6 +571,117 @@
done
();
}).
catch
(
_
.
partial
(
console
.
error
,
_
));
}));
it
(
"
shows OMEMO device fingerprints in the user details modal
"
,
mock
.
initConverseWithPromises
(
null
,
[
'
rosterGroupsFetched
'
],
{},
function
(
done
,
_converse
)
{
let
iq_stanza
;
test_utils
.
createContacts
(
_converse
,
'
current
'
,
1
);
const
contact_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
test_utils
.
openChatBoxFor
(
_converse
,
contact_jid
);
// We simply emit, to avoid doing all the setup work
_converse
.
emit
(
'
OMEMOInitialized
'
);
const
view
=
_converse
.
chatboxviews
.
get
(
contact_jid
);
const
show_modal_button
=
view
.
el
.
querySelector
(
'
.show-user-details-modal
'
);
show_modal_button
.
click
();
const
modal
=
view
.
user_details_modal
;
test_utils
.
waitUntil
(()
=>
u
.
isVisible
(
modal
.
el
),
1000
).
then
(()
=>
{
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
iq[to="
'
+
contact_jid
+
'
"] query[node="eu.siacs.conversations.axolotl.devicelist"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;
}
return
node
;
}).
length
;});
}).
then
(()
=>
{
iq_stanza
;
expect
(
iq_stanza
.
outerHTML
).
toBe
(
`<iq type="get" from="dummy@localhost" to="max.frankfurter@localhost" xmlns="jabber:client" id="
${
iq_stanza
.
getAttribute
(
'
id
'
)}
">`
+
`<query xmlns="http://jabber.org/protocol/disco#items" node="eu.siacs.conversations.axolotl.devicelist"/>`
+
`</iq>`
);
const
stanza
=
$iq
({
'
from
'
:
contact_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
,
}).
c
(
'
query
'
,
{
'
xmlns
'
:
'
http://jabber.org/protocol/disco#items
'
,
'
node
'
:
'
eu.siacs.conversations.axolotl.devicelist
'
}).
c
(
'
device
'
,
{
'
id
'
:
'
555
'
}).
up
()
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
test_utils
.
waitUntil
(()
=>
u
.
isVisible
(
modal
.
el
),
1000
).
then
(
function
()
{
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
iq[to="
'
+
contact_jid
+
'
"] items[node="eu.siacs.conversations.axolotl.bundles:555"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;
}
return
node
;
}).
length
;});
});
}).
then
(()
=>
{
expect
(
iq_stanza
.
outerHTML
).
toBe
(
`<iq type="get" from="dummy@localhost" to="max.frankfurter@localhost" xmlns="jabber:client" id="
${
iq_stanza
.
getAttribute
(
'
id
'
)}
">`
+
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`
+
`<items node="eu.siacs.conversations.axolotl.bundles:555"/>`
+
`</pubsub>`
+
`</iq>`
);
const
stanza
=
$iq
({
'
from
'
:
contact_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
,
}).
c
(
'
pubsub
'
,
{
'
xmlns
'
:
'
http://jabber.org/protocol/pubsub
'
}).
c
(
'
items
'
,
{
'
node
'
:
"
eu.siacs.conversations.axolotl.bundles:555
"
})
.
c
(
'
item
'
)
.
c
(
'
bundle
'
,
{
'
xmlns
'
:
'
eu.siacs.conversations.axolotl
'
})
.
c
(
'
signedPreKeyPublic
'
,
{
'
signedPreKeyId
'
:
'
4223
'
}).
t
(
btoa
(
'
1111
'
)).
up
()
.
c
(
'
signedPreKeySignature
'
).
t
(
btoa
(
'
2222
'
)).
up
()
.
c
(
'
identityKey
'
).
t
(
btoa
(
'
3333
'
)).
up
()
.
c
(
'
prekeys
'
)
.
c
(
'
preKeyPublic
'
,
{
'
preKeyId
'
:
'
1
'
}).
t
(
btoa
(
'
1001
'
)).
up
()
.
c
(
'
preKeyPublic
'
,
{
'
preKeyId
'
:
'
2
'
}).
t
(
btoa
(
'
1002
'
)).
up
()
.
c
(
'
preKeyPublic
'
,
{
'
preKeyId
'
:
'
3
'
}).
t
(
btoa
(
'
1003
'
));
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
const
view
=
_converse
.
chatboxviews
.
get
(
contact_jid
);
const
modal
=
view
.
user_details_modal
;
return
test_utils
.
waitUntil
(()
=>
modal
.
el
.
querySelectorAll
(
'
.fingerprints .fingerprint
'
).
length
);
}).
then
(()
=>
{
const
view
=
_converse
.
chatboxviews
.
get
(
contact_jid
);
const
modal
=
view
.
user_details_modal
;
expect
(
modal
.
el
.
querySelectorAll
(
'
.fingerprints .fingerprint
'
).
length
).
toBe
(
1
);
const
el
=
modal
.
el
.
querySelector
(
'
.fingerprints .fingerprint
'
);
expect
(
el
.
textContent
).
toBe
(
'
f56d6351aa71cff0debea014d13525e42036187a
'
);
expect
(
modal
.
el
.
querySelectorAll
(
'
input[type="radio"]
'
).
length
).
toBe
(
2
);
let
trusted_radio
=
modal
.
el
.
querySelector
(
'
input[type="radio"][name="555"][value="1"]
'
);
expect
(
trusted_radio
.
checked
).
toBe
(
true
);
let
untrusted_radio
=
modal
.
el
.
querySelector
(
'
input[type="radio"][name="555"][value="-1"]
'
);
expect
(
untrusted_radio
.
checked
).
toBe
(
false
);
// Test that the device can be set to untrusted
untrusted_radio
.
click
();
trusted_radio
=
document
.
querySelector
(
'
input[type="radio"][name="555"][value="1"]
'
);
expect
(
trusted_radio
.
hasAttribute
(
'
checked
'
)).
toBe
(
false
);
untrusted_radio
=
document
.
querySelector
(
'
input[type="radio"][name="555"][value="-1"]
'
);
expect
(
untrusted_radio
.
hasAttribute
(
'
checked
'
)).
toBe
(
true
);
done
();
});
}));
});
describe
(
"
A chatbox with an active OMEMO session
"
,
function
()
{
...
...
spec/roster.js
View file @
bcd68457
...
...
@@ -389,7 +389,6 @@
function
(
done
,
_converse
)
{
_converse
.
roster_groups
=
true
;
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
_converse
.
rosterview
.
render
();
test_utils
.
openControlBox
();
...
...
@@ -430,7 +429,6 @@
function
(
done
,
_converse
)
{
_converse
.
roster_groups
=
true
;
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
_converse
.
rosterview
.
render
();
...
...
@@ -477,7 +475,6 @@
_converse
.
roster_groups
=
true
;
var
groups
=
[
'
colleagues
'
,
'
friends
'
];
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
test_utils
.
openControlBox
();
_converse
.
rosterview
.
render
();
...
...
@@ -576,7 +573,6 @@
null
,
[
'
rosterGroupsFetched
'
],
{},
function
(
done
,
_converse
)
{
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
test_utils
.
openControlBox
();
_converse
.
roster
.
create
({
...
...
@@ -726,7 +722,6 @@
var
i
,
t
;
test_utils
.
openControlBox
();
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
for
(
i
=
0
;
i
<
mock
.
pend_names
.
length
;
i
++
)
{
_converse
.
roster
.
create
({
...
...
@@ -908,7 +903,6 @@
test_utils
.
waitUntil
(()
=>
$
(
_converse
.
rosterview
.
el
).
find
(
'
.roster-group li
'
).
length
,
700
)
.
then
(
function
()
{
var
jid
,
t
;
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
var
$roster
=
$
(
_converse
.
rosterview
.
el
);
for
(
var
i
=
0
;
i
<
mock
.
cur_names
.
length
;
i
++
)
{
...
...
@@ -935,7 +929,6 @@
return
$
(
_converse
.
rosterview
.
el
).
find
(
'
.roster-group li
'
).
length
;
},
700
).
then
(
function
()
{
var
jid
,
t
;
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
var
$roster
=
$
(
_converse
.
rosterview
.
el
);
for
(
var
i
=
0
;
i
<
mock
.
cur_names
.
length
;
i
++
)
{
...
...
@@ -963,7 +956,6 @@
return
$
(
_converse
.
rosterview
.
el
).
find
(
'
.roster-group li
'
).
length
;
},
700
).
then
(
function
()
{
var
jid
,
t
;
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
var
$roster
=
$
(
_converse
.
rosterview
.
el
);
for
(
var
i
=
0
;
i
<
mock
.
cur_names
.
length
;
i
++
)
{
...
...
@@ -991,7 +983,6 @@
return
$
(
_converse
.
rosterview
.
el
).
find
(
'
.roster-group li
'
).
length
;
},
700
).
then
(
function
()
{
var
jid
,
t
;
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
var
$roster
=
$
(
_converse
.
rosterview
.
el
);
for
(
var
i
=
0
;
i
<
mock
.
cur_names
.
length
;
i
++
)
{
...
...
@@ -1020,7 +1011,6 @@
},
500
)
.
then
(
function
()
{
var
jid
,
t
;
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
var
$roster
=
$
(
_converse
.
rosterview
.
el
);
for
(
var
i
=
0
;
i
<
mock
.
cur_names
.
length
;
i
++
)
{
...
...
@@ -1151,7 +1141,6 @@
names
.
push
(
$
(
item
).
text
().
replace
(
/^
\s
+|
\s
+$/g
,
''
));
}
};
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
.
rosterview
,
'
update
'
).
and
.
callThrough
();
spyOn
(
_converse
.
controlboxtoggle
,
'
showControlBox
'
).
and
.
callThrough
();
for
(
i
=
0
;
i
<
mock
.
req_names
.
length
;
i
++
)
{
...
...
src/converse-chatview.js
View file @
bcd68457
...
...
@@ -239,32 +239,29 @@
events
:
{
'
click button.remove-contact
'
:
'
removeContact
'
,
'
click button.refresh-contact
'
:
'
refreshContact
'
'
click button.refresh-contact
'
:
'
refreshContact
'
,
'
click .fingerprint-trust .btn input
'
:
'
toggleDeviceTrust
'
},
initialize
()
{
_converse
.
BootstrapModal
.
prototype
.
initialize
.
apply
(
this
,
arguments
);
this
.
model
.
on
(
'
contactAdded
'
,
this
.
registerContactEventHandlers
,
this
);
this
.
model
.
on
(
'
change
'
,
this
.
render
,
this
);
this
.
registerContactEventHandlers
();
_converse
.
emit
(
'
userDetailsModalInitialized
'
,
this
.
model
);
},
toHTML
()
{
return
tpl_user_details_modal
(
_
.
extend
(
this
.
model
.
toJSON
(),
this
.
model
.
vcard
.
toJSON
(),
{
'
_
'
:
_
,
'
__
'
:
__
,
'
view
'
:
this
,
'
_converse
'
:
_converse
,
'
allow_contact_removal
'
:
_converse
.
allow_contact_removal
,
'
alt_profile_image
'
:
__
(
"
The User's Profile Image
"
),
'
display_name
'
:
this
.
model
.
getDisplayName
(),
'
is_roster_contact
'
:
!
_
.
isUndefined
(
this
.
model
.
contact
),
'
label_close
'
:
__
(
'
Close
'
),
'
label_email
'
:
__
(
'
Email
'
),
'
label_fullname
'
:
__
(
'
Full Name
'
),
'
label_jid
'
:
__
(
'
Jabber ID
'
),
'
label_nickname
'
:
__
(
'
Nickname
'
),
'
label_remove
'
:
__
(
'
Remove as contact
'
),
'
label_refresh
'
:
__
(
'
Refresh
'
),
'
label_role
'
:
__
(
'
Role
'
),
'
label_url
'
:
__
(
'
URL
'
)
'
is_roster_contact
'
:
!
_
.
isUndefined
(
this
.
model
.
contact
)
}));
},
...
...
src/converse-omemo.js
View file @
bcd68457
...
...
@@ -44,7 +44,7 @@
}
});
return
{
'
identity_key
'
:
parseInt
(
bundle_el
.
querySelector
(
'
identityKey
'
).
textContent
,
10
)
,
'
identity_key
'
:
bundle_el
.
querySelector
(
'
identityKey
'
).
textContent
,
'
signed_prekey
'
:
{
'
id
'
:
parseInt
(
signed_prekey_public_el
.
getAttribute
(
'
signedPreKeyId
'
),
10
),
'
public_key
'
:
signed_prekey_public_el
.
textContent
,
...
...
@@ -65,6 +65,27 @@
overrides
:
{
UserDetailsModal
:
{
events
:
{
'
click .fingerprint-trust .btn input
'
:
'
toggleDeviceTrust
'
},
initialize
()
{
const
{
_converse
}
=
this
.
__super__
;
const
jid
=
this
.
model
.
get
(
'
jid
'
);
this
.
devicelist
=
_converse
.
devicelists
.
get
(
jid
)
||
_converse
.
devicelists
.
create
({
'
jid
'
:
jid
});
this
.
devicelist
.
devices
.
on
(
'
change:bundle
'
,
this
.
render
,
this
);
this
.
devicelist
.
devices
.
on
(
'
change:trusted
'
,
this
.
render
,
this
);
return
this
.
__super__
.
initialize
.
apply
(
this
,
arguments
);
},
toggleDeviceTrust
(
ev
)
{
const
radio
=
ev
.
target
;
const
device
=
this
.
devicelist
.
devices
.
get
(
radio
.
getAttribute
(
'
name
'
));
device
.
save
(
'
trusted
'
,
parseInt
(
radio
.
value
,
10
));
}
},
ChatBox
:
{
getBundlesAndBuildSessions
()
{
...
...
@@ -77,7 +98,6 @@
this
.
buildSessions
(
devices
)
.
then
(()
=>
resolve
(
devices
))
.
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
ERROR
));
}).
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
ERROR
));
}).
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
ERROR
));
}).
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
ERROR
));
...
...
@@ -194,6 +214,27 @@
_converse
.
NUM_PREKEYS
=
100
;
// Set here so that tests can override
function
generateFingerprint
(
device
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
device
.
getBundle
().
then
((
bundle
)
=>
{
// TODO: only generate fingerprints when necessary
crypto
.
subtle
.
digest
(
'
SHA-1
'
,
u
.
base64ToArrayBuffer
(
bundle
[
'
identity_key
'
]))
.
then
((
fp
)
=>
{
bundle
[
'
fingerprint
'
]
=
u
.
arrayBufferToHex
(
fp
);
device
.
save
(
'
bundle
'
,
bundle
);
device
.
trigger
(
'
change:bundle
'
);
// Doesn't get triggered automatically due to pass-by-reference
resolve
();
}).
catch
(
reject
);
});
});
}
_converse
.
getFingerprintsForContact
=
function
(
jid
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
_converse
.
getDevicesForContact
(
jid
)
.
then
((
devices
)
=>
Promise
.
all
(
devices
.
map
(
d
=>
generateFingerprint
(
d
))).
then
(
resolve
).
catch
(
reject
));
});
}
_converse
.
getDevicesForContact
=
function
(
jid
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
...
...
@@ -405,14 +446,15 @@
'
from
'
:
_converse
.
bare_jid
,
'
to
'
:
this
.
get
(
'
jid
'
)
}).
c
(
'
pubsub
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
PUBSUB
})
.
c
(
'
items
'
,
{
'
xmlns
'
:
`
${
Strophe
.
NS
.
OMEMO_BUNDLES
}
:
${
this
.
get
(
'
id
'
)}
`
});
.
c
(
'
items
'
,
{
'
node
'
:
`
${
Strophe
.
NS
.
OMEMO_BUNDLES
}
:
${
this
.
get
(
'
id
'
)}
`
});
_converse
.
connection
.
sendIQ
(
stanza
,
(
iq
)
=>
{
const
publish_el
=
sizzle
(
`items[node="
${
Strophe
.
NS
.
OMEMO_BUNDLES
}
:
${
this
.
get
(
'
id
'
)}
"]`
,
stanza
).
pop
();
const
bundle_el
=
sizzle
(
`bundle[xmlns="
${
Strophe
.
NS
.
OMEMO
}
"]`
,
publish_el
).
pop
();
this
.
save
(
parseBundle
(
bundle_el
));
resolve
();
const
publish_el
=
sizzle
(
`items[node="
${
Strophe
.
NS
.
OMEMO_BUNDLES
}
:
${
this
.
get
(
'
id
'
)}
"]`
,
iq
).
pop
(),
bundle_el
=
sizzle
(
`bundle[xmlns="
${
Strophe
.
NS
.
OMEMO
}
"]`
,
publish_el
).
pop
(),
bundle
=
parseBundle
(
bundle_el
);
this
.
save
(
'
bundle
'
,
bundle
);
resolve
(
bundle
);
},
reject
,
_converse
.
IQ_TIMEOUT
...
...
@@ -479,7 +521,7 @@
(
iq
)
=>
{
_
.
forEach
(
iq
.
querySelectorAll
(
'
device
'
),
(
dev
)
=>
this
.
devices
.
create
({
'
id
'
:
dev
.
getAttribute
(
'
id
'
)})
(
dev
)
=>
this
.
devices
.
create
({
'
id
'
:
dev
.
getAttribute
(
'
id
'
)
,
'
jid
'
:
this
.
get
(
'
jid
'
)
})
);
resolve
();
},
...
...
@@ -493,7 +535,7 @@
* server.
* https://xmpp.org/extensions/xep-0384.html#usecases-announcing
*/
this
.
devices
.
create
({
'
id
'
:
device_id
});
this
.
devices
.
create
({
'
id
'
:
device_id
,
'
jid
'
:
this
.
get
(
'
jid
'
)
});
return
new
Promise
((
resolve
,
reject
)
=>
{
const
stanza
=
$iq
({
'
from
'
:
_converse
.
bare_jid
,
...
...
@@ -589,7 +631,7 @@
jid
=
stanza
.
getAttribute
(
'
from
'
),
bundle_el
=
sizzle
(
`item > bundle`
,
items_el
).
pop
(),
devicelist
=
_converse
.
devicelists
.
get
(
jid
)
||
_converse
.
devicelists
.
create
({
'
jid
'
:
jid
}),
device
=
devicelist
.
devices
.
get
(
device_id
)
||
devicelist
.
devices
.
create
({
'
id
'
:
device_id
});
device
=
devicelist
.
devices
.
get
(
device_id
)
||
devicelist
.
devices
.
create
({
'
id
'
:
device_id
,
'
jid
'
:
jid
});
device
.
save
({
'
bundle
'
:
parseBundle
(
bundle_el
)});
}
...
...
@@ -613,7 +655,7 @@
if
(
dev
)
{
dev
.
save
({
'
active
'
:
true
});
}
else
{
devices
.
create
({
'
id
'
:
device_id
})
devices
.
create
({
'
id
'
:
device_id
,
'
jid
'
:
jid
})
}
});
// Make sure our own device is on the list (i.e. if it was
...
...
@@ -661,6 +703,11 @@
_converse
.
api
.
listen
.
on
(
'
statusInitialized
'
,
initOMEMO
);
_converse
.
api
.
listen
.
on
(
'
addClientFeatures
'
,
()
=>
_converse
.
api
.
disco
.
own
.
features
.
add
(
Strophe
.
NS
.
OMEMO_DEVICELIST
+
"
notify
"
));
_converse
.
api
.
listen
.
on
(
'
userDetailsModalInitialized
'
,
(
contact
)
=>
{
const
jid
=
contact
.
get
(
'
jid
'
);
_converse
.
getFingerprintsForContact
(
jid
).
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
ERROR
));
});
}
});
}));
src/templates/user_details_modal.html
View file @
bcd68457
<div
class=
"modal fade"
id=
"user-
profile-modal"
tabindex=
"-1"
role=
"dialog"
aria-labelledby=
"user-profile
-modal-label"
aria-hidden=
"true"
>
<div
class=
"modal fade"
id=
"user-
details-modal"
tabindex=
"-1"
role=
"dialog"
aria-labelledby=
"user-details
-modal-label"
aria-hidden=
"true"
>
<div
class=
"modal-dialog"
role=
"document"
>
<div
class=
"modal-content"
>
<div
class=
"modal-header"
>
<h5
class=
"modal-title"
id=
"user-
profile
-modal-label"
>
{{{o.display_name}}}
</h5>
<button
type=
"button"
class=
"close"
data-dismiss=
"modal"
aria-label=
"{{{o.
label_close
}}}"
><span
aria-hidden=
"true"
>
×
</span></button>
<h5
class=
"modal-title"
id=
"user-
details
-modal-label"
>
{{{o.display_name}}}
</h5>
<button
type=
"button"
class=
"close"
data-dismiss=
"modal"
aria-label=
"{{{o.
__('Close')
}}}"
><span
aria-hidden=
"true"
>
×
</span></button>
</div>
<div
class=
"modal-body"
>
{[ if (o.image) { ]}
<img
alt=
"{{{o.
alt_profile_image
}}}"
<img
alt=
"{{{o.
__('The User\'s Profile Image')
}}}"
class=
"img-thumbnail avatar align-self-center mb-3"
height=
"100"
width=
"100"
src=
"data:{{{o.image_type}}};base64,{{{o.image}}}"
/>
{[ } ]}
{[ if (o.fullname) { ]}
<p><label>
{{{o.
label_fullname
}}}:
</label>
{{{o.fullname}}}
</p>
<p><label>
{{{o.
__('Full Name')
}}}:
</label>
{{{o.fullname}}}
</p>
{[ } ]}
<p><label>
{{{o.
label_jid
}}}:
</label>
{{{o.jid}}}
</p>
<p><label>
{{{o.
__('XMPP Address')
}}}:
</label>
{{{o.jid}}}
</p>
{[ if (o.nickname) { ]}
<p><label>
{{{o.
label_nickname
}}}:
</label>
{{{o.nickname}}}
</p>
<p><label>
{{{o.
__('Nickname')
}}}:
</label>
{{{o.nickname}}}
</p>
{[ } ]}
{[ if (o.url) { ]}
<p><label>
{{{o.
label_url
}}}:
</label>
<a
target=
"_blank"
rel=
"noopener"
href=
"{{{o.url}}}"
>
{{{o.url}}}
</a></p>
<p><label>
{{{o.
__('URL')
}}}:
</label>
<a
target=
"_blank"
rel=
"noopener"
href=
"{{{o.url}}}"
>
{{{o.url}}}
</a></p>
{[ } ]}
{[ if (o.email) { ]}
<p><label>
{{{o.
label_email
}}}:
</label>
<a
href=
"mailto:{{{o.email}}}"
>
{{{o.email}}}
</a></p>
<p><label>
{{{o.
__('Email')
}}}:
</label>
<a
href=
"mailto:{{{o.email}}}"
>
{{{o.email}}}
</a></p>
{[ } ]}
{[ if (o.role) { ]}
<p><label>
{{{o.label_role}}}:
</label>
{{{o.role}}}
</p>
<p><label>
{{{o.__('Role')}}}:
</label>
{{{o.role}}}
</p>
{[ } ]}
{[ if (o._converse.pluggable.plugins['converse-omemo'].enabled()) { ]}
<hr>
<ul
class=
"list-group fingerprints"
>
<li
class=
"list-group-item active"
>
{{{o.__('OMEMO Fingerprints')}}}
</li>
{[ if (!o.view.devicelist.devices) { ]}
<li
class=
"list-group-item"
><span
class=
"spinner fa fa-spinner centered"
/></li>
{[ } ]}
{[ if (o.view.devicelist.devices) { ]}
{[ o.view.devicelist.devices.each(function (device) { ]}
{[ if (device.get('bundle')
&&
device.get('bundle').fingerprint) { ]}
<li
class=
"list-group-item"
>
<form
class=
"fingerprint-trust"
>
<span
class=
"fingerprint"
>
{{{device.get('bundle').fingerprint}}}
</span>
<div
class=
"btn-group btn-group-toggle"
>
<label
class=
"btn btn--small {[ if (device.get('trusted') !== -1) { ]} btn-primary active {[ } else { ]} btn-secondary {[ } ]}"
>
<input
type=
"radio"
name=
"{{{device.get('id')}}}"
value=
"1"
{[
if
(
device
.
get
('
trusted
')
!==
-1
)
{
]}
checked=
"checked"
{[
}
]}
>
{{{o.__('Trusted')}}}
</label>
<label
class=
"btn btn--small {[ if (device.get('trusted') === -1) { ]} btn-primary active {[ } else { ]} btn-secondary {[ } ]}"
>
<input
type=
"radio"
name=
"{{{device.get('id')}}}"
value=
"-1"
{[
if
(
device
.
get
('
trusted
'
) =
==
-1
)
{
]}
checked=
"checked"
{[
}
]}
>
{{{o.__('Untrusted')}}}
</label>
</div>
</form>
</li>
{[ } ]}
{[ }); ]}
{[ } ]}
</ul>
{[ } ]}
</div>
<div
class=
"modal-footer"
>
{[ if (o.allow_contact_removal
&&
o.is_roster_contact) { ]}
<button
type=
"button"
class=
"btn btn-danger remove-contact"
><i
class=
"fa fa-trash"
>
</i>
{{{o.
label_remove
}}}
</button>
<button
type=
"button"
class=
"btn btn-danger remove-contact"
><i
class=
"fa fa-trash"
>
</i>
{{{o.
__('Remove as contact')
}}}
</button>
{[ } ]}
<button
type=
"button"
class=
"btn btn-info refresh-contact"
><i
class=
"fa fa-refresh"
>
</i>
{{{o.
label_refresh
}}}
</button>
<button
type=
"button"
class=
"btn btn-secondary"
data-dismiss=
"modal"
>
{{{o.
label_close
}}}
</button>
<button
type=
"button"
class=
"btn btn-info refresh-contact"
><i
class=
"fa fa-refresh"
>
</i>
{{{o.
__('Refresh')
}}}
</button>
<button
type=
"button"
class=
"btn btn-secondary"
data-dismiss=
"modal"
>
{{{o.
__('Close')
}}}
</button>
</div>
</div>
</div>
...
...
src/utils/core.js
View file @
bcd68457
...
...
@@ -846,6 +846,22 @@
return
result
;
};
u
.
arrayBufferToHex
=
function
(
ab
)
{
const
hexCodes
=
[];
const
padding
=
'
00000000
'
;
const
view
=
new
window
.
DataView
(
ab
);
for
(
var
i
=
0
;
i
<
view
.
byteLength
;
i
+=
4
)
{
// Using getUint32 reduces the number of iterations needed (we process 4 bytes each time)
const
value
=
view
.
getUint32
(
i
)
// toString(16) will give the hex representation of the number without padding
const
stringValue
=
value
.
toString
(
16
)
// We use concatenation and slice for padding
const
paddedValue
=
(
padding
+
stringValue
).
slice
(
-
padding
.
length
)
hexCodes
.
push
(
paddedValue
);
}
return
hexCodes
.
join
(
""
);
};
u
.
arrayBufferToString
=
function
(
ab
)
{
var
enc
=
new
TextDecoder
(
"
utf-8
"
);
return
enc
.
decode
(
new
Uint8Array
(
ab
));
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment