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
39f1fc4e
Commit
39f1fc4e
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
556 additions
and
244 deletions
+556
-244
css/converse.css
css/converse.css
+45
-38
dist/converse.js
dist/converse.js
+274
-160
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
+104
-1
spec/roster.js
spec/roster.js
+0
-11
src/converse-chatview.js
src/converse-chatview.js
+25
-10
src/converse-omemo.js
src/converse-omemo.js
+36
-11
src/templates/user_details_modal.html
src/templates/user_details_modal.html
+43
-12
src/utils/core.js
src/utils/core.js
+16
-0
No files found.
css/converse.css
View file @
39f1fc4e
...
...
@@ -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 @
39f1fc4e
This diff is collapsed.
Click to expand it.
locale/af/LC_MESSAGES/converse.po
View file @
39f1fc4e
...
...
@@ -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 @
39f1fc4e
#conversejs
{
.btn--small
{
font-size
:
80%
;
font-weight
:
normal
;
}
form
{
.form-group
{
margin-bottom
:
2em
;
...
...
sass/_profile.scss
View file @
39f1fc4e
...
...
@@ -4,4 +4,9 @@
font-weight
:
bold
;
}
}
.fingerprint-trust
{
display
:
flex
;
justify-content
:
space-between
;
font-size
:
95%
;
}
}
sass/_variables.scss
View file @
39f1fc4e
...
...
@@ -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 @
39f1fc4e
...
...
@@ -396,6 +396,15 @@
`</publish>`
+
`</pubsub>`
+
`</iq>`
)
spyOn
(
_converse
,
'
emit
'
);
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
));
expect
(
_converse
.
emit
).
toHaveBeenCalledWith
(
'
OMEMOInitialized
'
);
done
();
});
}));
...
...
@@ -494,7 +503,6 @@
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
...
...
@@ -565,6 +573,101 @@
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
'
);
done
();
});
}));
});
describe
(
"
A chatbox with an active OMEMO session
"
,
function
()
{
...
...
spec/roster.js
View file @
39f1fc4e
...
...
@@ -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 @
39f1fc4e
...
...
@@ -245,26 +245,41 @@
initialize
()
{
_converse
.
BootstrapModal
.
prototype
.
initialize
.
apply
(
this
,
arguments
);
this
.
model
.
on
(
'
contactAdded
'
,
this
.
registerContactEventHandlers
,
this
);
this
.
model
.
on
(
'
change
'
,
this
.
render
,
this
);
// XXX: Leaky abstraction from converse-omemo
// In part, we're hampered by the fact that we can't
// have sub-views inside a VDOMView.
// If we did, we could put the OMEMO part of this modal
// inside another view and have it render as a sub-view.
// However, for this we'd need some kind of registry and
// way to look up sub-views by tag from the template (which
// I assume is what for example vue.js does).
this
.
has_omemo
=
_converse
.
pluggable
.
plugins
[
'
converse-omemo
'
].
enabled
();
if
(
this
.
has_omemo
)
{
const
jid
=
this
.
model
.
get
(
'
jid
'
);
this
.
devicelist
=
_converse
.
devicelists
.
get
(
jid
)
||
_converse
.
devicelists
.
create
({
'
jid
'
:
jid
});
this
.
devicelist
.
devices
.
on
(
'
change:fingerprint
'
,
this
.
render
,
this
);
}
else
{
this
.
devicelist
=
{};
}
this
.
registerContactEventHandlers
();
_converse
.
emit
(
'
userDetailsModalInitialized
'
,
this
.
model
);
},
toHTML
()
{
return
tpl_user_details_modal
(
_
.
extend
(
this
.
model
.
toJSON
(),
this
.
model
.
vcard
.
toJSON
(),
{
'
_
'
:
_
,
'
__
'
:
__
,
'
has_omemo
'
:
this
.
has_omemo
,
'
devicelist
'
:
this
.
devicelist
,
'
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 @
39f1fc4e
...
...
@@ -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
,
...
...
@@ -77,7 +77,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 +193,26 @@
_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
);
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 +424,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 +499,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 +513,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 +609,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 +633,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 +681,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 @
39f1fc4e
<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) { ]}
...
...
@@ -12,28 +12,59 @@
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.has_omemo) { ]}
<hr>
<ul
class=
"list-group fingerprints"
>
<li
class=
"list-group-item active"
>
{{{o.__('OMEMO Fingerprints')}}}
</li>
{[ if (!o.devicelist.devices) { ]}
<li
class=
"list-group-item"
><span
class=
"spinner fa fa-spinner centered"
/></li>
{[ } ]}
{[ if (o.devicelist.devices) { ]}
{[ o.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-primary btn--small"
{[
if
(
device
.
get
('
trusted
')
!=
-1
)
{
]}
active
{[
}
]}
>
<input
type=
"radio"
name=
"trust-{{{device.get('id')}}}"
value=
"trusted"
{[
if
(
device
.
get
('
trusted
')
!=
-1
)
{
]}
checked
{[
}
]}
>
{{{o.__('Trusted')}}}
</label>
<label
class=
"btn btn-secondary btn--small"
{[
if
(
device
.
get
('
trusted
')
!=
-1
)
{
]}
active
{[
}
]}
>
<input
type=
"radio"
name=
"trust-{{{device.get('id')}}}"
value=
"untrusted"
{[
if
(
device
.
get
('
trusted
'
) =
=
-1
)
{
]}
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 @
39f1fc4e
...
...
@@ -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