Commit 85f6a75f authored by JC Brand's avatar JC Brand

Escape user-generated input to avoid injection attacks

parent bfd3e7f0
...@@ -205,8 +205,9 @@ require.config({ ...@@ -205,8 +205,9 @@ require.config({
// Configuration for requirejs-tpl // Configuration for requirejs-tpl
// Use Mustache style syntax for variable interpolation // Use Mustache style syntax for variable interpolation
templateSettings: { templateSettings: {
evaluate : /\{\[([\s\S]+?)\]\}/g, 'escape': /\{\{\{([\s\S]+?)\}\}\}/g,
interpolate : /\{\{([\s\S]+?)\}\}/g 'evaluate': /\{\[([\s\S]+?)\]\}/g,
'interpolate': /\{\{([\s\S]+?)\}\}/g
} }
}, },
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
object as first parameter. [jcbrand] object as first parameter. [jcbrand]
- Use lodash instead of underscore.js [jcbrand] - Use lodash instead of underscore.js [jcbrand]
- #486 Honor existing mam user configuration [throwaway42] - #486 Honor existing mam user configuration [throwaway42]
- Escape user-generated input to prevent JS-injection attacks. (Thanks to SamWhited) [jcbrand]
## 2.0.5 (Unreleased) ## 2.0.5 (Unreleased)
- #743, #751, #753 Update to Strophe 1.2.12. SASL-EXTERNAL now has reduced priority, so it won't - #743, #751, #753 Update to Strophe 1.2.12. SASL-EXTERNAL now has reduced priority, so it won't
......
...@@ -512,10 +512,36 @@ ...@@ -512,10 +512,36 @@
} }
})); }));
it("indicates moderators by means of a special css class and tooltip", mock.initConverse(function (_converse) { it("escapes occupant nicknames when rendering them, to avoid JS-injection attacks", mock.initConverse(function (_converse) {
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy'); test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
/* <presence xmlns="jabber:client" to="jc@chat.example.org/converse.js-17184538"
* from="oo@conference.chat.example.org/&lt;img src=&quot;x&quot; onerror=&quot;alert(123)&quot;/&gt;">
* <x xmlns="http://jabber.org/protocol/muc#user">
* <item jid="jc@chat.example.org/converse.js-17184538" affiliation="owner" role="moderator"/>
* <status code="110"/>
* </x>
* </presence>"
*/
var presence = $pres({
to:'dummy@localhost/pda',
from:"lounge@localhost/&lt;img src=&quot;x&quot; onerror=&quot;alert(123)&quot;/&gt;"
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
.c('item').attrs({
jid: 'someone@localhost',
role: 'moderator',
}).up()
.c('status').attrs({code:'110'}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(presence));
var view = _converse.chatboxviews.get('lounge@localhost'); var view = _converse.chatboxviews.get('lounge@localhost');
var occupant = view.$el.find('.occupant-list').find('li');
expect(occupant.length).toBe(2);
expect($(occupant).last().text()).toBe("&lt;img src=&quot;x&quot; onerror=&quot;alert(123)&quot;/&gt;");
}));
it("indicates moderators by means of a special css class and tooltip", mock.initConverse(function (_converse) {
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
var view = _converse.chatboxviews.get('lounge@localhost');
var presence = $pres({ var presence = $pres({
to:'dummy@localhost/pda', to:'dummy@localhost/pda',
from:'lounge@localhost/moderatorman' from:'lounge@localhost/moderatorman'
...@@ -787,6 +813,17 @@ ...@@ -787,6 +813,17 @@
expect($chat_content.find('.chat-info').text()).toBe('Topic set by ralphm to: '+text); expect($chat_content.find('.chat-info').text()).toBe('Topic set by ralphm to: '+text);
})); }));
it("escapes the subject before rendering it, to avoid JS-injection attacks", mock.initConverse(function (_converse) {
test_utils.openAndEnterChatRoom(_converse, 'jdev', 'conference.jabber.org', 'jc');
spyOn(window, 'alert');
var subject = '<img src="x" onerror="alert(\'XSS\');"/>';
var view = _converse.chatboxviews.get('jdev@conference.jabber.org');
view.setChatRoomSubject('ralphm', subject);
var $chat_content = view.$el.find('.chat-content');
expect($chat_content.find('.chat-info').length).toBe(1);
expect($chat_content.find('.chat-info').text()).toBe('Topic set by ralphm to: '+subject);
}));
it("informs users if their nicknames has been changed.", mock.initConverse(function (_converse) { it("informs users if their nicknames has been changed.", mock.initConverse(function (_converse) {
/* The service then sends two presence stanzas to the full JID /* The service then sends two presence stanzas to the full JID
* of each occupant (including the occupant who is changing his * of each occupant (including the occupant who is changing his
......
...@@ -268,16 +268,16 @@ ...@@ -268,16 +268,16 @@
* can then at least tell gettext to scan for it so that these * can then at least tell gettext to scan for it so that these
* strings are picked up by the translation machinery. * strings are picked up by the translation machinery.
*/ */
301: ___("<strong>%1$s</strong> has been banned"), 301: ___("%1$s has been banned"),
303: ___("<strong>%1$s</strong>'s nickname has changed"), 303: ___("%1$s's nickname has changed"),
307: ___("<strong>%1$s</strong> has been kicked out"), 307: ___("%1$s has been kicked out"),
321: ___("<strong>%1$s</strong> has been removed because of an affiliation change"), 321: ___("%1$s has been removed because of an affiliation change"),
322: ___("<strong>%1$s</strong> has been removed for not being a member") 322: ___("%1$s has been removed for not being a member")
}, },
new_nickname_messages: { new_nickname_messages: {
210: ___('Your nickname has been automatically set to: <strong>%1$s</strong>'), 210: ___('Your nickname has been automatically set to: %1$s'),
303: ___('Your nickname has been changed to: <strong>%1$s</strong>') 303: ___('Your nickname has been changed to: %1$s')
} }
}; };
......
<div class="chat-message {{extra_classes}}" data-isodate="{{isodate}}"> <div class="chat-message {{{extra_classes}}}" data-isodate="{{{isodate}}}">
<span class="chat-msg-author chat-msg-{{sender}}">{{time}} **{{username}} </span> <span class="chat-msg-author chat-msg-{{{sender}}}">{{{time}}} **{{{username}}}&nbsp;</span>
<span class="chat-msg-content"><!-- message gets added here via renderMessage --></span> <span class="chat-msg-content"><!-- message gets added here via renderMessage --></span>
</div> </div>
<dl class="add-converse-contact dropdown"> <dl class="add-converse-contact dropdown">
<dt id="xmpp-contact-search" class="fancy-dropdown"> <dt id="xmpp-contact-search" class="fancy-dropdown">
<a class="toggle-xmpp-contact-form icon-plus" href="#" title="{{label_click_to_chat}}"> {{label_add_contact}}</a> <a class="toggle-xmpp-contact-form icon-plus" href="#" title="{{{label_click_to_chat}}}"> {{{label_add_contact}}}</a>
</dt> </dt>
<dd class="search-xmpp"><ul></ul></dd> <dd class="search-xmpp"><ul></ul></dd>
</dl> </dl>
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<input type="text" <input type="text"
name="identifier" name="identifier"
class="username" class="username"
placeholder="{{label_contact_username}}"/> placeholder="{{{label_contact_username}}}"/>
<button class="pure-button button-primary" type="submit">{{label_add}}</button> <button class="pure-button button-primary" type="submit">{{{label_add}}}</button>
</form> </form>
</li> </li>
<dd class="available-chatroom"> <dd class="available-chatroom">
<a class="open-room" data-room-jid="{{jid}}" title="{{open_title}}" href="#">{{name}}</a> <a class="open-room" data-room-jid="{{{jid}}}" title="{{{open_title}}}" href="#">{{{name}}}</a>
<a class="remove-bookmark icon-close" data-room-jid="{{jid}}" data-bookmark-name="{{name}}" <a class="remove-bookmark icon-close" data-room-jid="{{{jid}}}" data-bookmark-name="{{{name}}}"
title="{{info_remove}}" href="#">&nbsp;</a> title="{{{info_remove}}}" href="#">&nbsp;</a>
<a class="room-info icon-room-info" data-room-jid="{{jid}}" <a class="room-info icon-room-info" data-room-jid="{{{jid}}}"
title="{{info_title}}" href="#">&nbsp;</a> title="{{{info_title}}}" href="#">&nbsp;</a>
</dd> </dd>
<a href="#" class="bookmarks-toggle icon-{{toggle_state}}" title="{{desc_bookmarks}}">{{label_bookmarks}}</a> <a href="#" class="bookmarks-toggle icon-{{{toggle_state}}}" title="{{{desc_bookmarks}}}">{{{label_bookmarks}}}</a>
<dl class="bookmarks rooms-list"></dl> <dl class="bookmarks rooms-list"></dl>
<form id="set-custom-xmpp-status" class="pure-form"> <form id="set-custom-xmpp-status" class="pure-form">
<fieldset> <fieldset>
<span class="input-button-group"> <span class="input-button-group">
<input type="text" class="custom-xmpp-status" value="{{status_message}}" placeholder="{{label_custom_status}}"/> <input type="text" class="custom-xmpp-status" value="{{{status_message}}}" placeholder="{{{label_custom_status}}}"/>
<input type="submit" class="pure-button button-primary" value="{{label_save}}"/> <input type="submit" class="pure-button button-primary" value="{{{label_save}}}"/>
</span> </span>
</fieldset> </fieldset>
</form> </form>
<div class="xmpp-status"> <div class="xmpp-status">
<a class="choose-xmpp-status {{chat_status}} icon-{{chat_status}}" data-value="{{status_message}}" href="#" title="{{desc_change_status}}"> <a class="choose-xmpp-status {{{chat_status}}} icon-{{{chat_status}}}" data-value="{{{status_message}}}" href="#" title="{{{desc_change_status}}}">
{{status_message}} {{{status_message}}}
</a> </a>
<a class="change-xmpp-status-message icon-pencil" href="#" title="{{desc_custom_status}}"></a> <a class="change-xmpp-status-message icon-pencil" href="#" title="{{{desc_custom_status}}}"></a>
</div> </div>
<div class="chat-area"> <div class="chat-area">
<div class="chat-content"></div> <div class="chat-content"></div>
<div class="new-msgs-indicator hidden">▼ {{ unread_msgs }} ▼</div> <div class="new-msgs-indicator hidden">▼ {{{ unread_msgs }}} ▼</div>
<form class="sendXMPPMessage" action="" method="post"> <form class="sendXMPPMessage" action="" method="post">
{[ if (show_toolbar) { ]} {[ if (show_toolbar) { ]}
<ul class="chat-toolbar no-text-select"></ul> <ul class="chat-toolbar no-text-select"></ul>
{[ } ]} {[ } ]}
<textarea type="text" class="chat-textarea" <textarea type="text" class="chat-textarea"
placeholder="{{label_message}}"/> placeholder="{{{label_message}}}"/>
</form> </form>
</div> </div>
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
<div class="dragresize dragresize-topleft"></div> <div class="dragresize dragresize-topleft"></div>
<div class="dragresize dragresize-left"></div> <div class="dragresize dragresize-left"></div>
<div class="chat-head chat-head-chatbox"> <div class="chat-head chat-head-chatbox">
<a class="chatbox-btn close-chatbox-button icon-close" title="{{info_close}}"></a> <a class="chatbox-btn close-chatbox-button icon-close" title="{{{info_close}}}"></a>
<div class="chat-title"> <div class="chat-title">
{[ if (url) { ]} {[ if (url) { ]}
<a href="{{url}}" target="_blank" rel="noopener" class="user"> <a href="{{{url}}}" target="_blank" rel="noopener" class="user">
{[ } ]} {[ } ]}
{{ title }} {{{ title }}}
{[ if (url) { ]} {[ if (url) { ]}
</a> </a>
{[ } ]} {[ } ]}
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
</div> </div>
<div class="chat-body"> <div class="chat-body">
<div class="chat-content"></div> <div class="chat-content"></div>
<div class="new-msgs-indicator hidden">▼ {{ unread_msgs }} ▼</div> <div class="new-msgs-indicator hidden">▼ {{{ unread_msgs }}} ▼</div>
{[ if (show_textarea) { ]} {[ if (show_textarea) { ]}
<form class="sendXMPPMessage" action="" method="post"> <form class="sendXMPPMessage" action="" method="post">
{[ if (show_toolbar) { ]} {[ if (show_toolbar) { ]}
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
<textarea <textarea
type="text" type="text"
class="chat-textarea" class="chat-textarea"
placeholder="{{label_personal_message}}"/> placeholder="{{{label_personal_message}}}"/>
</form> </form>
{[ } ]} {[ } ]}
</div> </div>
......
<a class="chatbox-btn toggle-chatbox-button icon-minus" title="{{info_minimize}}"></a> <a class="chatbox-btn toggle-chatbox-button icon-minus" title="{{{info_minimize}}}"></a>
<div class="chatroom-form-container"> <div class="chatroom-form-container">
<form class="pure-form converse-form chatroom-form"> <form class="pure-form converse-form chatroom-form">
<fieldset> <fieldset>
<legend>{{heading}}</legend> <legend>{{{heading}}}</legend>
<label>{{label_name}}</label> <label>{{{label_name}}}</label>
<input type="text" name="name" required="required"/> <input type="text" name="name" required="required"/>
<label>{{label_autojoin}}</label> <label>{{{label_autojoin}}}</label>
<input type="checkbox" name="autojoin"/> <input type="checkbox" name="autojoin"/>
<label>{{label_nick}}</label> <label>{{{label_nick}}}</label>
<input type="text" name="nick" value="{{default_nick}}"/> <input type="text" name="nick" value="{{{default_nick}}}"/>
</fieldset> </fieldset>
<fieldset> <fieldset>
<input class="pure-button button-primary" type="submit" value="{{label_submit}}"/> <input class="pure-button button-primary" type="submit" value="{{{label_submit}}}"/>
<input class="pure-button button-cancel" type="button" value="{{label_cancel}}"/> <input class="pure-button button-cancel" type="button" value="{{{label_cancel}}}"/>
</fieldset> </fieldset>
</form> </form>
</div> </div>
<a class="chatbox-btn toggle-bookmark icon-pushpin <a class="chatbox-btn toggle-bookmark icon-pushpin
{[ if (bookmarked) {]} {[ if (bookmarked) {]}
button-on button-on
{[ } ]}" title="{{info_toggle_bookmark}}"></a> {[ } ]}" title="{{{info_toggle_bookmark}}}"></a>
<a class="chatbox-btn close-chatbox-button icon-close" title="{{info_close}}"></a> <a class="chatbox-btn close-chatbox-button icon-close" title="{{{info_close}}}"></a>
{[ if (affiliation == 'owner') { ]} {[ if (affiliation == 'owner') { ]}
<a class="chatbox-btn configure-chatroom-button icon-wrench" title="{{info_configure}} "></a> <a class="chatbox-btn configure-chatroom-button icon-wrench" title="{{{info_configure}}} "></a>
{[ } ]} {[ } ]}
<div class="chat-title"> <div class="chat-title">
{{ _.escape(name) }} {{{ name }}}
<p class="chatroom-topic"><p/> <p class="chatroom-topic"><p/>
</div> </div>
<div class="chatroom-form-container"> <div class="chatroom-form-container">
<form class="pure-form converse-form chatroom-form converse-centered-form"> <form class="pure-form converse-form chatroom-form converse-centered-form">
<fieldset> <fieldset>
<label>{{heading}}</label> <label>{{{heading}}}</label>
<p class="validation-message">{{validation_message}}</p> <p class="validation-message">{{{validation_message}}}</p>
<input type="text" required="required" name="nick" class="new-chatroom-nick" placeholder="{{label_nickname}}"/> <input type="text" required="required" name="nick" class="new-chatroom-nick" placeholder="{{{label_nickname}}}"/>
</fieldset> </fieldset>
<fieldset> <fieldset>
<input type="submit" class="pure-button button-primary" name="join" value="{{label_join}}"/> <input type="submit" class="pure-button button-primary" name="join" value="{{{label_join}}}"/>
</fieldset> </fieldset>
</form> </form>
</div> </div>
<div class="chatroom-form-container"> <div class="chatroom-form-container">
<form class="pure-form converse-form chatroom-form"> <form class="pure-form converse-form chatroom-form">
<fieldset> <fieldset>
<legend>{{heading}}</legend> <legend>{{{heading}}}</legend>
<label>{{label_password}}</label> <label>{{{label_password}}}</label>
<input type="password" name="password"/> <input type="password" name="password"/>
</fieldset> </fieldset>
<fieldset> <fieldset>
<input class="pure-button button-primary" type="submit" value="{{label_submit}}"/> <input class="pure-button button-primary" type="submit" value="{{{label_submit}}}"/>
</fieldset> </fieldset>
</form> </form>
</div> </div>
<!-- <div class="occupants"> --> <!-- <div class="occupants"> -->
{[ if (allow_muc_invitations) { ]} {[ if (allow_muc_invitations) { ]}
<form class="pure-form room-invite"> <form class="pure-form room-invite">
<input class="invited-contact" placeholder="{{label_invitation}}" type="text"/> <input class="invited-contact" placeholder="{{{label_invitation}}}" type="text"/>
</form> </form>
{[ } ]} {[ } ]}
<p class="occupants-heading">{{label_occupants}}:</p> <p class="occupants-heading">{{{label_occupants}}}:</p>
<ul class="occupant-list"></ul> <ul class="occupant-list"></ul>
<!-- </div> --> <!-- </div> -->
{[ if (show_emoticons) { ]} {[ if (show_emoticons) { ]}
<li class="toggle-smiley icon-happy" title="{{label_insert_smiley}}"> <li class="toggle-smiley icon-happy" title="{{{label_insert_smiley}}}">
<ul> <ul>
<li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li> <li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li>
<li><a class="icon-wink" href="#" data-emoticon=";)"></a></li> <li><a class="icon-wink" href="#" data-emoticon=";)"></a></li>
...@@ -18,12 +18,12 @@ ...@@ -18,12 +18,12 @@
</li> </li>
{[ } ]} {[ } ]}
{[ if (show_call_button) { ]} {[ if (show_call_button) { ]}
<li class="toggle-call"><a class="icon-phone" title="{{label_start_call}}"></a></li> <li class="toggle-call"><a class="icon-phone" title="{{{label_start_call}}}"></a></li>
{[ } ]} {[ } ]}
{[ if (show_occupants_toggle) { ]} {[ if (show_occupants_toggle) { ]}
<li class="toggle-occupants"><a class="icon-hide-users" title="{{label_hide_occupants}}"></a></li> <li class="toggle-occupants"><a class="icon-hide-users" title="{{{label_hide_occupants}}}"></a></li>
{[ } ]} {[ } ]}
{[ if (show_clear_button) { ]} {[ if (show_clear_button) { ]}
<li class="toggle-clear"><a class="icon-remove" title="{{label_clear}}"></a></li> <li class="toggle-clear"><a class="icon-remove" title="{{{label_clear}}}"></a></li>
{[ } ]} {[ } ]}
<form class="pure-form set-xmpp-status" action="" method="post"> <form class="pure-form set-xmpp-status" action="" method="post">
<span id="xmpp-status-holder"> <span id="xmpp-status-holder">
<select id="select-xmpp-status" style="display:none"> <select id="select-xmpp-status" style="display:none">
<option value="online">{{label_online}}</option> <option value="online">{{{label_online}}}</option>
<option value="dnd">{{label_busy}}</option> <option value="dnd">{{{label_busy}}}</option>
<option value="away">{{label_away}}</option> <option value="away">{{{label_away}}}</option>
{[ if (include_offline_state) { ]} {[ if (include_offline_state) { ]}
<option value="offline">{{label_offline}}</option> <option value="offline">{{{label_offline}}}</option>
{[ } ]} {[ } ]}
{[ if (allow_logout) { ]} {[ if (allow_logout) { ]}
<option value="logout">{{label_logout}}</option> <option value="logout">{{{label_logout}}}</option>
{[ } ]} {[ } ]}
</select> </select>
</span> </span>
......
<li><a class="s {[ if (is_current) { ]} current {[ } ]}" <li><a class="s {[ if (is_current) { ]} current {[ } ]}"
data-id="users" href="#users"> data-id="users" href="#users">
{{label_contacts}} {{{label_contacts}}}
</a></li> </a></li>
<span class="conn-feedback">{{label_toggle}}</span> <span class="conn-feedback">{{{label_toggle}}}</span>
<field var="{{name}}">{[ if (_.isArray(value)) { ]} <field var="{{{name}}}">{[ if (_.isArray(value)) { ]}
{[ _.each(value,function(arrayValue) { ]}<value>{{arrayValue}}</value>{[ }); ]} {[ _.each(value,function(arrayValue) { ]}<value>{{{arrayValue}}}</value>{[ }); ]}
{[ } else { ]} {[ } else { ]}
<value>{{value}}</value> <value>{{{value}}}</value>
{[ } ]}</field> {[ } ]}</field>
{[ if (label) { ]} {[ if (label) { ]}
<label> <label>
{{label}} {{{label}}}
</label> </label>
{[ } ]} {[ } ]}
<img src="data:{{type}};base64,{{data}}"> <img src="data:{{{type}}};base64,{{{data}}}">
<input name="{{name}}" type="text" {[ if (required) { ]} class="required" {[ } ]} > <input name="{{{name}}}" type="text" {[ if (required) { ]} class="required" {[ } ]} >
<label>{{label}}</label> <label>{{{label}}}</label>
<input name="{{name}}" type="{{type}}" {{checked}}> <input name="{{{name}}}" type="{{{type}}}" {{{checked}}}>
{[ if (label) { ]} {[ if (label) { ]}
<label> <label>
{{label}} {{{label}}}
</label> </label>
{[ } ]} {[ } ]}
<input name="{{name}}" type="{{type}}" <input name="{{{name}}}" type="{{{type}}}"
{[ if (value) { ]} value="{{value}}" {[ } ]} {[ if (value) { ]} value="{{{value}}}" {[ } ]}
{[ if (required) { ]} class="required" {[ } ]} > {[ if (required) { ]} class="required" {[ } ]} >
<label>{{label}}</label> <label>{{{label}}}</label>
<select name="{{name}}" {[ if (multiple) { ]} multiple="multiple" {[ } ]}>{{options}}</select> <select name="{{{name}}}" {[ if (multiple) { ]} multiple="multiple" {[ } ]}>{{options}}</select>
<label class="label-ta">{{label}}</label> <label class="label-ta">{{{label}}}</label>
<textarea name="{{name}}">{{value}}</textarea> <textarea name="{{{name}}}">{{{value}}}</textarea>
{[ if (label) { ]} {[ if (label) { ]}
<label> <label>
{{label}} {{{label}}}
</label> </label>
{[ } ]} {[ } ]}
<div class="input-group"> <div class="input-group">
<input name="{{name}}" type="{{type}}" <input name="{{{name}}}" type="{{{type}}}"
{[ if (value) { ]} value="{{value}}" {[ } ]} {[ if (value) { ]} value="{{{value}}}" {[ } ]}
{[ if (required) { ]} class="required" {[ } ]} /> {[ if (required) { ]} class="required" {[ } ]} />
<span title="{{domain}}">{{domain}}</span> <span title="{{{domain}}}">{{{domain}}}</span>
</div> </div>
<a href="#" class="group-toggle icon-{{toggle_state}}" title="{{desc_group_toggle}}">{{label_group}}</a> <a href="#" class="group-toggle icon-{{{toggle_state}}}" title="{{{desc_group_toggle}}}">{{{label_group}}}</a>
<div class="chat-info">{{message}}</div> <div class="chat-info">{{{message}}}</div>
...@@ -4,17 +4,17 @@ ...@@ -4,17 +4,17 @@
{[ } ]} {[ } ]}
{[ if (!auto_login) { ]} {[ if (!auto_login) { ]}
{[ if (authentication == LOGIN || authentication == EXTERNAL) { ]} {[ if (authentication == LOGIN || authentication == EXTERNAL) { ]}
<label>{{label_username}}</label> <label>{{{label_username}}}</label>
<input type="text" name="jid" placeholder="{{placeholder_username}}"> <input type="text" name="jid" placeholder="{{{placeholder_username}}}">
{[ if (authentication !== EXTERNAL) { ]} {[ if (authentication !== EXTERNAL) { ]}
<label>{{label_password}}</label> <label>{{{label_password}}}</label>
<input type="password" name="password" placeholder="{{placeholder_password}}"> <input type="password" name="password" placeholder="{{{placeholder_password}}}">
{[ } ]} {[ } ]}
<input class="pure-button button-primary" type="submit" value="{{label_login}}"> <input class="pure-button button-primary" type="submit" value="{{{label_login}}}">
<span class="conn-feedback"></span> <span class="conn-feedback"></span>
{[ } ]} {[ } ]}
{[ if (authentication == ANONYMOUS) { ]} {[ if (authentication == ANONYMOUS) { ]}
<input type="pure-button button-primary" class="submit login-anon" value="{{label_anon_login}}"/> <input type="pure-button button-primary" class="submit login-anon" value="{{{label_anon_login}}}"/>
{[ } ]} {[ } ]}
{[ if (authentication == PREBIND) { ]} {[ if (authentication == PREBIND) { ]}
<p>Disconnected.</p> <p>Disconnected.</p>
......
<li><a class="current" href="#login-dialog">{{label_sign_in}}</a></li> <li><a class="current" href="#login-dialog">{{{label_sign_in}}}</a></li>
<div class="chat-message {{extra_classes}}" data-isodate="{{isodate}}" data-msgid="{{msgid}}"> <div class="chat-message {{{extra_classes}}}" data-isodate="{{{isodate}}}" data-msgid="{{{msgid}}}">
<span class="chat-msg-author chat-msg-{{sender}}">{{time}} {{username}}:&nbsp;</span> <span class="chat-msg-author chat-msg-{{{sender}}}">{{{time}}} {{{username}}}:&nbsp;</span>
<span class="chat-msg-content"><!-- message gets added here via renderMessage --></span> <span class="chat-msg-content"><!-- message gets added here via renderMessage --></span>
</div> </div>
<time class="chat-info chat-date" data-isodate="{{isodate}}">{{datestring}}</time> <time class="chat-info chat-date" data-isodate="{{{isodate}}}">{{{datestring}}}</time>
<li class="{{role}} occupant" id="{{id}}" <li class="{{{role}}} occupant" id="{{{id}}}"
{[ if (role === "moderator") { ]} {[ if (role === "moderator") { ]}
title="{{desc_moderator}} {{hint_occupant}}" title="{{{desc_moderator}}} {{{hint_occupant}}}"
{[ } ]} {[ } ]}
{[ if (role === "occupant") { ]} {[ if (role === "occupant") { ]}
title="{{desc_occupant}} {{hint_occupant}}" title="{{{desc_occupant}}} {{{hint_occupant}}}"
{[ } ]} {[ } ]}
{[ if (role === "visitor") { ]} {[ if (role === "visitor") { ]}
title="{{desc_visitor}} {{hint_occupant}}" title="{{{desc_visitor}}} {{{hint_occupant}}}"
{[ } ]}>{{nick}}</li> {[ } ]}>{{{nick}}}</li>
{[ if (allow_chat_pending_contacts) { ]} {[ if (allow_chat_pending_contacts) { ]}
<a class="open-chat"href="#"> <a class="open-chat"href="#">
{[ } ]} {[ } ]}
<span class="pending-contact-name" title="Name: {{fullname}} <span class="pending-contact-name" title="Name: {{{fullname}}}
JID: {{jid}}">{{fullname}}</span> JID: {{{jid}}}">{{{fullname}}}</span>
{[ if (allow_chat_pending_contacts) { ]} {[ if (allow_chat_pending_contacts) { ]}
</a> </a>
{[ } ]} {[ } ]}
<a class="remove-xmpp-contact icon-remove" title="{{desc_remove}}" href="#"></a> <a class="remove-xmpp-contact icon-remove" title="{{{desc_remove}}}" href="#"></a>
<form id="converse-register" class="pure-form converse-form"> <form id="converse-register" class="pure-form converse-form">
<span class="reg-feedback"></span> <span class="reg-feedback"></span>
<label>{{label_domain}}</label> <label>{{{label_domain}}}</label>
<input type="text" name="domain" placeholder="{{domain_placeholder}}"> <input type="text" name="domain" placeholder="{{{domain_placeholder}}}">
<p class="form-help">{{help_providers}} <a href="{{href_providers}}" class="url" target="_blank" rel="noopener">{{help_providers_link}}</a>.</p> <p class="form-help">{{{help_providers}}} <a href="{{{href_providers}}}" class="url" target="_blank" rel="noopener">{{{help_providers_link}}}</a>.</p>
<input class="pure-button button-primary" type="submit" value="{{label_register}}"> <input class="pure-button button-primary" type="submit" value="{{{label_register}}}">
</form> </form>
<li><a class="s" href="#register">{{label_register}}</a></li> <li><a class="s" href="#register">{{{label_register}}}</a></li>
<p class="provider-title">{{domain}}</p> <p class="provider-title">{{{domain}}}</p>
<a href='https://xmpp.net/result.php?domain={{domain}}&amp;type=client'> <a href='https://xmpp.net/result.php?domain={{{domain}}}&amp;type=client'>
<img class="provider-score" src='https://xmpp.net/badge.php?domain={{domain}}' alt='xmpp.net score' /> <img class="provider-score" src='https://xmpp.net/badge.php?domain={{{domain}}}' alt='xmpp.net score' />
</a> </a>
<p class="title">{{title}}</p> <p class="title">{{{title}}}</p>
<p class="instructions">{{instructions}}</p> <p class="instructions">{{{instructions}}}</p>
<span class="spinner login-submit"/> <span class="spinner login-submit"/>
<p class="info">{{info_message}}</p> <p class="info">{{{info_message}}}</p>
<button class="pure-button button-cancel hor_centered">{{cancel}}</button> <button class="pure-button button-cancel hor_centered">{{{cancel}}}</button>
{[ if (allow_chat_pending_contacts) { ]} {[ if (allow_chat_pending_contacts) { ]}
<a class="open-chat"href="#"> <a class="open-chat"href="#">
{[ } ]} {[ } ]}
<span class="req-contact-name" title="Name: {{fullname}} <span class="req-contact-name" title="Name: {{{fullname}}}
JID: {{jid}}">{{fullname}}</span> JID: {{{jid}}}">{{{fullname}}}</span>
{[ if (allow_chat_pending_contacts) { ]} {[ if (allow_chat_pending_contacts) { ]}
</a> </a>
{[ } ]} {[ } ]}
<span class="request-actions"> <span class="request-actions">
<a class="accept-xmpp-request icon-checkmark" title="{{desc_accept}}" href="#"></a> <a class="accept-xmpp-request icon-checkmark" title="{{{desc_accept}}}" href="#"></a>
<a class="decline-xmpp-request icon-close" title="{{desc_decline}}" href="#"></a> <a class="decline-xmpp-request icon-close" title="{{{desc_decline}}}" href="#"></a>
</span> </span>
<!-- FIXME: check markup in mockup --> <!-- FIXME: check markup in mockup -->
<div class="room-info"> <div class="room-info">
<p class="room-info"><strong>{{label_desc}}</strong> {{desc}}</p> <p class="room-info"><strong>{{{label_desc}}}</strong> {{{desc}}}</p>
<p class="room-info"><strong>{{label_occ}}</strong> {{occ}}</p> <p class="room-info"><strong>{{{label_occ}}}</strong> {{{occ}}}</p>
<p class="room-info"><strong>{{label_features}}</strong> <p class="room-info"><strong>{{{label_features}}}</strong>
<ul> <ul>
{[ if (passwordprotected) { ]} {[ if (passwordprotected) { ]}
<li class="room-info locked">{{label_requires_auth}}</li> <li class="room-info locked">{{{label_requires_auth}}}</li>
{[ } ]} {[ } ]}
{[ if (hidden) { ]} {[ if (hidden) { ]}
<li class="room-info">{{label_hidden}}</li> <li class="room-info">{{{label_hidden}}}</li>
{[ } ]} {[ } ]}
{[ if (membersonly) { ]} {[ if (membersonly) { ]}
<li class="room-info">{{label_requires_invite}}</li> <li class="room-info">{{{label_requires_invite}}}</li>
{[ } ]} {[ } ]}
{[ if (moderated) { ]} {[ if (moderated) { ]}
<li class="room-info">{{label_moderated}}</li> <li class="room-info">{{{label_moderated}}}</li>
{[ } ]} {[ } ]}
{[ if (nonanonymous) { ]} {[ if (nonanonymous) { ]}
<li class="room-info">{{label_non_anon}}</li> <li class="room-info">{{{label_non_anon}}}</li>
{[ } ]} {[ } ]}
{[ if (open) { ]} {[ if (open) { ]}
<li class="room-info">{{label_open_room}}</li> <li class="room-info">{{{label_open_room}}}</li>
{[ } ]} {[ } ]}
{[ if (persistent) { ]} {[ if (persistent) { ]}
<li class="room-info">{{label_permanent_room}}</li> <li class="room-info">{{{label_permanent_room}}}</li>
{[ } ]} {[ } ]}
{[ if (publicroom) { ]} {[ if (publicroom) { ]}
<li class="room-info">{{label_public}}</li> <li class="room-info">{{{label_public}}}</li>
{[ } ]} {[ } ]}
{[ if (semianonymous) { ]} {[ if (semianonymous) { ]}
<li class="room-info">{{label_semi_anon}}</li> <li class="room-info">{{{label_semi_anon}}}</li>
{[ } ]} {[ } ]}
{[ if (temporary) { ]} {[ if (temporary) { ]}
<li class="room-info">{{label_temp_room}}</li> <li class="room-info">{{{label_temp_room}}}</li>
{[ } ]} {[ } ]}
{[ if (unmoderated) { ]} {[ if (unmoderated) { ]}
<li class="room-info">{{label_unmoderated}}</li> <li class="room-info">{{{label_unmoderated}}}</li>
{[ } ]} {[ } ]}
</ul> </ul>
</p> </p>
......
<dd class="available-chatroom"> <dd class="available-chatroom">
<a class="open-room" data-room-jid="{{jid}}" <a class="open-room" data-room-jid="{{{jid}}}"
title="{{open_title}}" href="#">{{_.escape(name)}}</a> title="{{{open_title}}}" href="#">{{{_.escape(name)}}}</a>
<a class="room-info icon-room-info" data-room-jid="{{jid}}" <a class="room-info icon-room-info" data-room-jid="{{{jid}}}"
title="{{info_title}}" href="#">&nbsp;</a> title="{{{info_title}}}" href="#">&nbsp;</a>
</dd> </dd>
<a class="open-chat" title="{{title_fullname}}: {{fullname}} <a class="open-chat" title="{{{title_fullname}}}: {{{fullname}}}
JID: {{jid}} JID: {{{jid}}}
{{desc_chat}}" href="#"><span class="icon-{{chat_status}}" title="{{desc_status}}"></span>{{fullname}}</a> {{{desc_chat}}}" href="#"><span class="icon-{{{chat_status}}}" title="{{{desc_status}}}"></span>{{{fullname}}}</a>
{[ if (allow_contact_removal) { ]} {[ if (allow_contact_removal) { ]}
<a class="remove-xmpp-contact icon-remove" title="{{desc_remove}}" href="#"></a> <a class="remove-xmpp-contact icon-remove" title="{{{desc_remove}}}" href="#"></a>
{[ } ]} {[ } ]}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<input type="text" <input type="text"
name="identifier" name="identifier"
class="username" class="username"
placeholder="{{label_contact_name}}"/> placeholder="{{{label_contact_name}}}"/>
<button type="submit">{{label_search}}</button> <button type="submit">{{{label_search}}}</button>
</form> </form>
</li> </li>
<option value="{{value}}" {[ if (selected) { ]} selected="selected" {[ } ]} >{{label}}</option> <option value="{{{value}}}" {[ if (selected) { ]} selected="selected" {[ } ]} >{{{label}}}</option>
<li> <li>
<a href="#" class="{{ value }}" data-value="{{ value }}"> <a href="#" class="{{{ value }}}" data-value="{{{ value }}}">
<span class="icon-{{ value }}"></span> <span class="icon-{{{ value }}}"></span>
{{ text }} {{{ text }}}
</a> </a>
</li> </li>
{{Minimized}} <span id="minimized-count">({{num_minimized}})</span> {{{Minimized}}} <span id="minimized-count">({{{num_minimized}}})</span>
<span class="unread-message-count" <span class="unread-message-count"
{[ if (!num_unread) { ]} style="display: none" {[ } ]} {[ if (!num_unread) { ]} style="display: none" {[ } ]}
href="#">{{num_unread}}</span> href="#">{{{num_unread}}}</span>
{[ if (show_emoticons) { ]} {[ if (show_emoticons) { ]}
<li class="toggle-smiley icon-happy" title="{{label_insert_smiley}}"> <li class="toggle-smiley icon-happy" title="{{{label_insert_smiley}}}">
<ul> <ul>
<li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li> <li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li>
<li><a class="icon-wink" href="#" data-emoticon=";)"></a></li> <li><a class="icon-wink" href="#" data-emoticon=";)"></a></li>
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
</li> </li>
{[ } ]} {[ } ]}
{[ if (show_call_button) { ]} {[ if (show_call_button) { ]}
<li class="toggle-call"><a class="icon-phone" title="{{label_start_call}}"></a></li> <li class="toggle-call"><a class="icon-phone" title="{{{label_start_call}}}"></a></li>
{[ } ]} {[ } ]}
{[ if (show_clear_button) { ]} {[ if (show_clear_button) { ]}
<li class="toggle-clear"><a class="icon-remove" title="{{label_clear}}"></a></li> <li class="toggle-clear"><a class="icon-remove" title="{{{label_clear}}}"></a></li>
{[ } ]} {[ } ]}
{[ if (allow_otr) { ]} {[ if (allow_otr) { ]}
<li class="toggle-otr {{otr_status_class}}" title="{{otr_tooltip}}"> <li class="toggle-otr {{{otr_status_class}}}" title="{{{otr_tooltip}}}">
<span class="chat-toolbar-text">{{otr_translated_status}}</span> <span class="chat-toolbar-text">{{{otr_translated_status}}}</span>
{[ if (otr_status == UNENCRYPTED) { ]} {[ if (otr_status == UNENCRYPTED) { ]}
<span class="icon-unlocked"></span> <span class="icon-unlocked"></span>
{[ } ]} {[ } ]}
...@@ -15,17 +15,17 @@ ...@@ -15,17 +15,17 @@
{[ } ]} {[ } ]}
<ul> <ul>
{[ if (otr_status == UNENCRYPTED) { ]} {[ if (otr_status == UNENCRYPTED) { ]}
<li><a class="start-otr" href="#">{{label_start_encrypted_conversation}}</a></li> <li><a class="start-otr" href="#">{{{label_start_encrypted_conversation}}}</a></li>
{[ } ]} {[ } ]}
{[ if (otr_status != UNENCRYPTED) { ]} {[ if (otr_status != UNENCRYPTED) { ]}
<li><a class="start-otr" href="#">{{label_refresh_encrypted_conversation}}</a></li> <li><a class="start-otr" href="#">{{{label_refresh_encrypted_conversation}}}</a></li>
<li><a class="end-otr" href="#">{{label_end_encrypted_conversation}}</a></li> <li><a class="end-otr" href="#">{{{label_end_encrypted_conversation}}}</a></li>
<li><a class="auth-otr" data-scheme="smp" href="#">{{label_verify_with_smp}}</a></li> <li><a class="auth-otr" data-scheme="smp" href="#">{{{label_verify_with_smp}}}</a></li>
{[ } ]} {[ } ]}
{[ if (otr_status == UNVERIFIED) { ]} {[ if (otr_status == UNVERIFIED) { ]}
<li><a class="auth-otr" data-scheme="fingerprint" href="#">{{label_verify_with_fingerprints}}</a></li> <li><a class="auth-otr" data-scheme="fingerprint" href="#">{{{label_verify_with_fingerprints}}}</a></li>
{[ } ]} {[ } ]}
<li><a href="http://www.cypherpunks.ca/otr/help/3.2.0/levels.php" target="_blank" rel="noopener">{{label_whats_this}}</a></li> <li><a href="http://www.cypherpunks.ca/otr/help/3.2.0/levels.php" target="_blank" rel="noopener">{{{label_whats_this}}}</a></li>
</ul> </ul>
</li> </li>
{[ } ]} {[ } ]}
<a class="chatbox-btn close-chatbox-button icon-close"></a> <a class="chatbox-btn close-chatbox-button icon-close"></a>
<a class="chat-head-message-count" <a class="chat-head-message-count"
{[ if (!num_unread) { ]} style="display: none" {[ } ]} {[ if (!num_unread) { ]} style="display: none" {[ } ]}
href="#">{{num_unread}}</a> href="#">{{{num_unread}}}</a>
<a href="#" class="restore-chat" title="{{tooltip}}"> <a href="#" class="restore-chat" title="{{{tooltip}}}">
{{ title }} {{{ title }}}
</a> </a>
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