Escape user-generated input to avoid injection attacks

This commit is contained in:
JC Brand 2017-02-13 08:23:42 +00:00
parent bfd3e7f0b6
commit 85f6a75fed
54 changed files with 203 additions and 164 deletions

View File

@ -205,8 +205,9 @@ require.config({
// Configuration for requirejs-tpl
// Use Mustache style syntax for variable interpolation
templateSettings: {
evaluate : /\{\[([\s\S]+?)\]\}/g,
interpolate : /\{\{([\s\S]+?)\}\}/g
'escape': /\{\{\{([\s\S]+?)\}\}\}/g,
'evaluate': /\{\[([\s\S]+?)\]\}/g,
'interpolate': /\{\{([\s\S]+?)\}\}/g
}
},

View File

@ -8,6 +8,7 @@
object as first parameter. [jcbrand]
- Use lodash instead of underscore.js [jcbrand]
- #486 Honor existing mam user configuration [throwaway42]
- Escape user-generated input to prevent JS-injection attacks. (Thanks to SamWhited) [jcbrand]
## 2.0.5 (Unreleased)
- #743, #751, #753 Update to Strophe 1.2.12. SASL-EXTERNAL now has reduced priority, so it won't

View File

@ -512,10 +512,36 @@
}
}));
it("escapes occupant nicknames when rendering them, to avoid JS-injection attacks", mock.initConverse(function (_converse) {
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 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({
to:'dummy@localhost/pda',
from:'lounge@localhost/moderatorman'
@ -787,6 +813,17 @@
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) {
/* The service then sends two presence stanzas to the full JID
* of each occupant (including the occupant who is changing his

View File

@ -268,16 +268,16 @@
* can then at least tell gettext to scan for it so that these
* strings are picked up by the translation machinery.
*/
301: ___("<strong>%1$s</strong> has been banned"),
303: ___("<strong>%1$s</strong>'s nickname has changed"),
307: ___("<strong>%1$s</strong> has been kicked out"),
321: ___("<strong>%1$s</strong> has been removed because of an affiliation change"),
322: ___("<strong>%1$s</strong> has been removed for not being a member")
301: ___("%1$s has been banned"),
303: ___("%1$s's nickname has changed"),
307: ___("%1$s has been kicked out"),
321: ___("%1$s has been removed because of an affiliation change"),
322: ___("%1$s has been removed for not being a member")
},
new_nickname_messages: {
210: ___('Your nickname has been automatically set to: <strong>%1$s</strong>'),
303: ___('Your nickname has been changed to: <strong>%1$s</strong>')
210: ___('Your nickname has been automatically set to: %1$s'),
303: ___('Your nickname has been changed to: %1$s')
}
};

View File

@ -1,4 +1,4 @@
<div class="chat-message {{extra_classes}}" data-isodate="{{isodate}}">
<span class="chat-msg-author chat-msg-{{sender}}">{{time}} **{{username}} </span>
<div class="chat-message {{{extra_classes}}}" data-isodate="{{{isodate}}}">
<span class="chat-msg-author chat-msg-{{{sender}}}">{{{time}}} **{{{username}}}&nbsp;</span>
<span class="chat-msg-content"><!-- message gets added here via renderMessage --></span>
</div>

View File

@ -1,6 +1,6 @@
<dl class="add-converse-contact 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>
<dd class="search-xmpp"><ul></ul></dd>
</dl>

View File

@ -3,7 +3,7 @@
<input type="text"
name="identifier"
class="username"
placeholder="{{label_contact_username}}"/>
<button class="pure-button button-primary" type="submit">{{label_add}}</button>
placeholder="{{{label_contact_username}}}"/>
<button class="pure-button button-primary" type="submit">{{{label_add}}}</button>
</form>
</li>

View File

@ -1,7 +1,7 @@
<dd class="available-chatroom">
<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}}"
title="{{info_remove}}" href="#">&nbsp;</a>
<a class="room-info icon-room-info" data-room-jid="{{jid}}"
title="{{info_title}}" href="#">&nbsp;</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}}}"
title="{{{info_remove}}}" href="#">&nbsp;</a>
<a class="room-info icon-room-info" data-room-jid="{{{jid}}}"
title="{{{info_title}}}" href="#">&nbsp;</a>
</dd>

View File

@ -1,2 +1,2 @@
<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>

View File

@ -1,8 +1,8 @@
<form id="set-custom-xmpp-status" class="pure-form">
<fieldset>
<span class="input-button-group">
<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="text" class="custom-xmpp-status" value="{{{status_message}}}" placeholder="{{{label_custom_status}}}"/>
<input type="submit" class="pure-button button-primary" value="{{{label_save}}}"/>
</span>
</fieldset>
</form>

View File

@ -1,6 +1,6 @@
<div class="xmpp-status">
<a class="choose-xmpp-status {{chat_status}} icon-{{chat_status}}" data-value="{{status_message}}" href="#" title="{{desc_change_status}}">
{{status_message}}
<a class="choose-xmpp-status {{{chat_status}}} icon-{{{chat_status}}}" data-value="{{{status_message}}}" href="#" title="{{{desc_change_status}}}">
{{{status_message}}}
</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>

View File

@ -1,11 +1,11 @@
<div class="chat-area">
<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">
{[ if (show_toolbar) { ]}
<ul class="chat-toolbar no-text-select"></ul>
{[ } ]}
<textarea type="text" class="chat-textarea"
placeholder="{{label_message}}"/>
placeholder="{{{label_message}}}"/>
</form>
</div>

View File

@ -3,12 +3,12 @@
<div class="dragresize dragresize-topleft"></div>
<div class="dragresize dragresize-left"></div>
<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">
{[ if (url) { ]}
<a href="{{url}}" target="_blank" rel="noopener" class="user">
<a href="{{{url}}}" target="_blank" rel="noopener" class="user">
{[ } ]}
{{ title }}
{{{ title }}}
{[ if (url) { ]}
</a>
{[ } ]}
@ -17,7 +17,7 @@
</div>
<div class="chat-body">
<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) { ]}
<form class="sendXMPPMessage" action="" method="post">
{[ if (show_toolbar) { ]}
@ -26,7 +26,7 @@
<textarea
type="text"
class="chat-textarea"
placeholder="{{label_personal_message}}"/>
placeholder="{{{label_personal_message}}}"/>
</form>
{[ } ]}
</div>

View File

@ -1 +1 @@
<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>

View File

@ -1,17 +1,17 @@
<div class="chatroom-form-container">
<form class="pure-form converse-form chatroom-form">
<fieldset>
<legend>{{heading}}</legend>
<label>{{label_name}}</label>
<legend>{{{heading}}}</legend>
<label>{{{label_name}}}</label>
<input type="text" name="name" required="required"/>
<label>{{label_autojoin}}</label>
<label>{{{label_autojoin}}}</label>
<input type="checkbox" name="autojoin"/>
<label>{{label_nick}}</label>
<input type="text" name="nick" value="{{default_nick}}"/>
<label>{{{label_nick}}}</label>
<input type="text" name="nick" value="{{{default_nick}}}"/>
</fieldset>
<fieldset>
<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-primary" type="submit" value="{{{label_submit}}}"/>
<input class="pure-button button-cancel" type="button" value="{{{label_cancel}}}"/>
</fieldset>
</form>
</div>

View File

@ -1,4 +1,4 @@
<a class="chatbox-btn toggle-bookmark icon-pushpin
{[ if (bookmarked) {]}
button-on
{[ } ]}" title="{{info_toggle_bookmark}}"></a>
{[ } ]}" title="{{{info_toggle_bookmark}}}"></a>

View File

@ -1,8 +1,8 @@
<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') { ]}
<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">
{{ _.escape(name) }}
{{{ name }}}
<p class="chatroom-topic"><p/>
</div>

View File

@ -1,12 +1,12 @@
<div class="chatroom-form-container">
<form class="pure-form converse-form chatroom-form converse-centered-form">
<fieldset>
<label>{{heading}}</label>
<p class="validation-message">{{validation_message}}</p>
<input type="text" required="required" name="nick" class="new-chatroom-nick" placeholder="{{label_nickname}}"/>
<label>{{{heading}}}</label>
<p class="validation-message">{{{validation_message}}}</p>
<input type="text" required="required" name="nick" class="new-chatroom-nick" placeholder="{{{label_nickname}}}"/>
</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>
</form>
</div>

View File

@ -1,12 +1,12 @@
<div class="chatroom-form-container">
<form class="pure-form converse-form chatroom-form">
<fieldset>
<legend>{{heading}}</legend>
<label>{{label_password}}</label>
<legend>{{{heading}}}</legend>
<label>{{{label_password}}}</label>
<input type="password" name="password"/>
</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>
</form>
</div>

View File

@ -1,9 +1,9 @@
<!-- <div class="occupants"> -->
{[ if (allow_muc_invitations) { ]}
<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>
{[ } ]}
<p class="occupants-heading">{{label_occupants}}:</p>
<p class="occupants-heading">{{{label_occupants}}}:</p>
<ul class="occupant-list"></ul>
<!-- </div> -->

View File

@ -1,5 +1,5 @@
{[ if (show_emoticons) { ]}
<li class="toggle-smiley icon-happy" title="{{label_insert_smiley}}">
<li class="toggle-smiley icon-happy" title="{{{label_insert_smiley}}}">
<ul>
<li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li>
<li><a class="icon-wink" href="#" data-emoticon=";)"></a></li>
@ -18,12 +18,12 @@
</li>
{[ } ]}
{[ 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) { ]}
<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) { ]}
<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>
{[ } ]}

View File

@ -1,14 +1,14 @@
<form class="pure-form set-xmpp-status" action="" method="post">
<span id="xmpp-status-holder">
<select id="select-xmpp-status" style="display:none">
<option value="online">{{label_online}}</option>
<option value="dnd">{{label_busy}}</option>
<option value="away">{{label_away}}</option>
<option value="online">{{{label_online}}}</option>
<option value="dnd">{{{label_busy}}}</option>
<option value="away">{{{label_away}}}</option>
{[ if (include_offline_state) { ]}
<option value="offline">{{label_offline}}</option>
<option value="offline">{{{label_offline}}}</option>
{[ } ]}
{[ if (allow_logout) { ]}
<option value="logout">{{label_logout}}</option>
<option value="logout">{{{label_logout}}}</option>
{[ } ]}
</select>
</span>

View File

@ -1,4 +1,4 @@
<li><a class="s {[ if (is_current) { ]} current {[ } ]}"
data-id="users" href="#users">
{{label_contacts}}
{{{label_contacts}}}
</a></li>

View File

@ -1 +1 @@
<span class="conn-feedback">{{label_toggle}}</span>
<span class="conn-feedback">{{{label_toggle}}}</span>

View File

@ -1,5 +1,5 @@
<field var="{{name}}">{[ if (_.isArray(value)) { ]}
{[ _.each(value,function(arrayValue) { ]}<value>{{arrayValue}}</value>{[ }); ]}
<field var="{{{name}}}">{[ if (_.isArray(value)) { ]}
{[ _.each(value,function(arrayValue) { ]}<value>{{{arrayValue}}}</value>{[ }); ]}
{[ } else { ]}
<value>{{value}}</value>
<value>{{{value}}}</value>
{[ } ]}</field>

View File

@ -1,9 +1,9 @@
{[ if (label) { ]}
<label>
{{label}}
{{{label}}}
</label>
{[ } ]}
<img src="data:{{type}};base64,{{data}}">
<input name="{{name}}" type="text" {[ if (required) { ]} class="required" {[ } ]} >
<img src="data:{{{type}}};base64,{{{data}}}">
<input name="{{{name}}}" type="text" {[ if (required) { ]} class="required" {[ } ]} >

View File

@ -1,2 +1,2 @@
<label>{{label}}</label>
<input name="{{name}}" type="{{type}}" {{checked}}>
<label>{{{label}}}</label>
<input name="{{{name}}}" type="{{{type}}}" {{{checked}}}>

View File

@ -1,8 +1,8 @@
{[ if (label) { ]}
<label>
{{label}}
{{{label}}}
</label>
{[ } ]}
<input name="{{name}}" type="{{type}}"
{[ if (value) { ]} value="{{value}}" {[ } ]}
<input name="{{{name}}}" type="{{{type}}}"
{[ if (value) { ]} value="{{{value}}}" {[ } ]}
{[ if (required) { ]} class="required" {[ } ]} >

View File

@ -1,2 +1,2 @@
<label>{{label}}</label>
<select name="{{name}}" {[ if (multiple) { ]} multiple="multiple" {[ } ]}>{{options}}</select>
<label>{{{label}}}</label>
<select name="{{{name}}}" {[ if (multiple) { ]} multiple="multiple" {[ } ]}>{{options}}</select>

View File

@ -1,2 +1,2 @@
<label class="label-ta">{{label}}</label>
<textarea name="{{name}}">{{value}}</textarea>
<label class="label-ta">{{{label}}}</label>
<textarea name="{{{name}}}">{{{value}}}</textarea>

View File

@ -1,11 +1,11 @@
{[ if (label) { ]}
<label>
{{label}}
{{{label}}}
</label>
{[ } ]}
<div class="input-group">
<input name="{{name}}" type="{{type}}"
{[ if (value) { ]} value="{{value}}" {[ } ]}
<input name="{{{name}}}" type="{{{type}}}"
{[ if (value) { ]} value="{{{value}}}" {[ } ]}
{[ if (required) { ]} class="required" {[ } ]} />
<span title="{{domain}}">{{domain}}</span>
<span title="{{{domain}}}">{{{domain}}}</span>
</div>

View File

@ -1 +1 @@
<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>

View File

@ -1 +1 @@
<div class="chat-info">{{message}}</div>
<div class="chat-info">{{{message}}}</div>

View File

@ -4,17 +4,17 @@
{[ } ]}
{[ if (!auto_login) { ]}
{[ if (authentication == LOGIN || authentication == EXTERNAL) { ]}
<label>{{label_username}}</label>
<input type="text" name="jid" placeholder="{{placeholder_username}}">
<label>{{{label_username}}}</label>
<input type="text" name="jid" placeholder="{{{placeholder_username}}}">
{[ if (authentication !== EXTERNAL) { ]}
<label>{{label_password}}</label>
<input type="password" name="password" placeholder="{{placeholder_password}}">
<label>{{{label_password}}}</label>
<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>
{[ } ]}
{[ 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) { ]}
<p>Disconnected.</p>

View File

@ -1 +1 @@
<li><a class="current" href="#login-dialog">{{label_sign_in}}</a></li>
<li><a class="current" href="#login-dialog">{{{label_sign_in}}}</a></li>

View File

@ -1,4 +1,4 @@
<div class="chat-message {{extra_classes}}" data-isodate="{{isodate}}" data-msgid="{{msgid}}">
<span class="chat-msg-author chat-msg-{{sender}}">{{time}} {{username}}:&nbsp;</span>
<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-content"><!-- message gets added here via renderMessage --></span>
</div>

View File

@ -1 +1 @@
<time class="chat-info chat-date" data-isodate="{{isodate}}">{{datestring}}</time>
<time class="chat-info chat-date" data-isodate="{{{isodate}}}">{{{datestring}}}</time>

View File

@ -1,10 +1,10 @@
<li class="{{role}} occupant" id="{{id}}"
<li class="{{{role}}} occupant" id="{{{id}}}"
{[ if (role === "moderator") { ]}
title="{{desc_moderator}} {{hint_occupant}}"
title="{{{desc_moderator}}} {{{hint_occupant}}}"
{[ } ]}
{[ if (role === "occupant") { ]}
title="{{desc_occupant}} {{hint_occupant}}"
title="{{{desc_occupant}}} {{{hint_occupant}}}"
{[ } ]}
{[ if (role === "visitor") { ]}
title="{{desc_visitor}} {{hint_occupant}}"
{[ } ]}>{{nick}}</li>
title="{{{desc_visitor}}} {{{hint_occupant}}}"
{[ } ]}>{{{nick}}}</li>

View File

@ -1,9 +1,9 @@
{[ if (allow_chat_pending_contacts) { ]}
<a class="open-chat"href="#">
{[ } ]}
<span class="pending-contact-name" title="Name: {{fullname}}
JID: {{jid}}">{{fullname}}</span>
<span class="pending-contact-name" title="Name: {{{fullname}}}
JID: {{{jid}}}">{{{fullname}}}</span>
{[ if (allow_chat_pending_contacts) { ]}
</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>

View File

@ -1,7 +1,7 @@
<form id="converse-register" class="pure-form converse-form">
<span class="reg-feedback"></span>
<label>{{label_domain}}</label>
<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>
<input class="pure-button button-primary" type="submit" value="{{label_register}}">
<label>{{{label_domain}}}</label>
<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>
<input class="pure-button button-primary" type="submit" value="{{{label_register}}}">
</form>

View File

@ -1 +1 @@
<li><a class="s" href="#register">{{label_register}}</a></li>
<li><a class="s" href="#register">{{{label_register}}}</a></li>

View File

@ -1,6 +1,6 @@
<p class="provider-title">{{domain}}</p>
<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' />
<p class="provider-title">{{{domain}}}</p>
<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' />
</a>
<p class="title">{{title}}</p>
<p class="instructions">{{instructions}}</p>
<p class="title">{{{title}}}</p>
<p class="instructions">{{{instructions}}}</p>

View File

@ -1,3 +1,3 @@
<span class="spinner login-submit"/>
<p class="info">{{info_message}}</p>
<button class="pure-button button-cancel hor_centered">{{cancel}}</button>
<p class="info">{{{info_message}}}</p>
<button class="pure-button button-cancel hor_centered">{{{cancel}}}</button>

View File

@ -1,12 +1,12 @@
{[ if (allow_chat_pending_contacts) { ]}
<a class="open-chat"href="#">
{[ } ]}
<span class="req-contact-name" title="Name: {{fullname}}
JID: {{jid}}">{{fullname}}</span>
<span class="req-contact-name" title="Name: {{{fullname}}}
JID: {{{jid}}}">{{{fullname}}}</span>
{[ if (allow_chat_pending_contacts) { ]}
</a>
{[ } ]}
<span class="request-actions">
<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="accept-xmpp-request icon-checkmark" title="{{{desc_accept}}}" href="#"></a>
<a class="decline-xmpp-request icon-close" title="{{{desc_decline}}}" href="#"></a>
</span>

View File

@ -1,41 +1,41 @@
<!-- FIXME: check markup in mockup -->
<div class="room-info">
<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_features}}</strong>
<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_features}}}</strong>
<ul>
{[ if (passwordprotected) { ]}
<li class="room-info locked">{{label_requires_auth}}</li>
<li class="room-info locked">{{{label_requires_auth}}}</li>
{[ } ]}
{[ if (hidden) { ]}
<li class="room-info">{{label_hidden}}</li>
<li class="room-info">{{{label_hidden}}}</li>
{[ } ]}
{[ if (membersonly) { ]}
<li class="room-info">{{label_requires_invite}}</li>
<li class="room-info">{{{label_requires_invite}}}</li>
{[ } ]}
{[ if (moderated) { ]}
<li class="room-info">{{label_moderated}}</li>
<li class="room-info">{{{label_moderated}}}</li>
{[ } ]}
{[ if (nonanonymous) { ]}
<li class="room-info">{{label_non_anon}}</li>
<li class="room-info">{{{label_non_anon}}}</li>
{[ } ]}
{[ if (open) { ]}
<li class="room-info">{{label_open_room}}</li>
<li class="room-info">{{{label_open_room}}}</li>
{[ } ]}
{[ if (persistent) { ]}
<li class="room-info">{{label_permanent_room}}</li>
<li class="room-info">{{{label_permanent_room}}}</li>
{[ } ]}
{[ if (publicroom) { ]}
<li class="room-info">{{label_public}}</li>
<li class="room-info">{{{label_public}}}</li>
{[ } ]}
{[ if (semianonymous) { ]}
<li class="room-info">{{label_semi_anon}}</li>
<li class="room-info">{{{label_semi_anon}}}</li>
{[ } ]}
{[ if (temporary) { ]}
<li class="room-info">{{label_temp_room}}</li>
<li class="room-info">{{{label_temp_room}}}</li>
{[ } ]}
{[ if (unmoderated) { ]}
<li class="room-info">{{label_unmoderated}}</li>
<li class="room-info">{{{label_unmoderated}}}</li>
{[ } ]}
</ul>
</p>

View File

@ -1,6 +1,6 @@
<dd class="available-chatroom">
<a class="open-room" data-room-jid="{{jid}}"
title="{{open_title}}" href="#">{{_.escape(name)}}</a>
<a class="room-info icon-room-info" data-room-jid="{{jid}}"
title="{{info_title}}" href="#">&nbsp;</a>
<a class="open-room" data-room-jid="{{{jid}}}"
title="{{{open_title}}}" href="#">{{{_.escape(name)}}}</a>
<a class="room-info icon-room-info" data-room-jid="{{{jid}}}"
title="{{{info_title}}}" href="#">&nbsp;</a>
</dd>

View File

@ -1,6 +1,6 @@
<a class="open-chat" title="{{title_fullname}}: {{fullname}}
JID: {{jid}}
{{desc_chat}}" href="#"><span class="icon-{{chat_status}}" title="{{desc_status}}"></span>{{fullname}}</a>
<a class="open-chat" title="{{{title_fullname}}}: {{{fullname}}}
JID: {{{jid}}}
{{{desc_chat}}}" href="#"><span class="icon-{{{chat_status}}}" title="{{{desc_status}}}"></span>{{{fullname}}}</a>
{[ 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>
{[ } ]}

View File

@ -3,7 +3,7 @@
<input type="text"
name="identifier"
class="username"
placeholder="{{label_contact_name}}"/>
<button type="submit">{{label_search}}</button>
placeholder="{{{label_contact_name}}}"/>
<button type="submit">{{{label_search}}}</button>
</form>
</li>

View File

@ -1 +1 @@
<option value="{{value}}" {[ if (selected) { ]} selected="selected" {[ } ]} >{{label}}</option>
<option value="{{{value}}}" {[ if (selected) { ]} selected="selected" {[ } ]} >{{{label}}}</option>

View File

@ -1,6 +1,6 @@
<li>
<a href="#" class="{{ value }}" data-value="{{ value }}">
<span class="icon-{{ value }}"></span>
{{ text }}
<a href="#" class="{{{ value }}}" data-value="{{{ value }}}">
<span class="icon-{{{ value }}}"></span>
{{{ text }}}
</a>
</li>

View File

@ -1,4 +1,4 @@
{{Minimized}} <span id="minimized-count">({{num_minimized}})</span>
{{{Minimized}}} <span id="minimized-count">({{{num_minimized}}})</span>
<span class="unread-message-count"
{[ if (!num_unread) { ]} style="display: none" {[ } ]}
href="#">{{num_unread}}</span>
href="#">{{{num_unread}}}</span>

View File

@ -1,5 +1,5 @@
{[ if (show_emoticons) { ]}
<li class="toggle-smiley icon-happy" title="{{label_insert_smiley}}">
<li class="toggle-smiley icon-happy" title="{{{label_insert_smiley}}}">
<ul>
<li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li>
<li><a class="icon-wink" href="#" data-emoticon=";)"></a></li>
@ -18,8 +18,8 @@
</li>
{[ } ]}
{[ 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) { ]}
<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>
{[ } ]}

View File

@ -1,6 +1,6 @@
{[ if (allow_otr) { ]}
<li class="toggle-otr {{otr_status_class}}" title="{{otr_tooltip}}">
<span class="chat-toolbar-text">{{otr_translated_status}}</span>
<li class="toggle-otr {{{otr_status_class}}}" title="{{{otr_tooltip}}}">
<span class="chat-toolbar-text">{{{otr_translated_status}}}</span>
{[ if (otr_status == UNENCRYPTED) { ]}
<span class="icon-unlocked"></span>
{[ } ]}
@ -15,17 +15,17 @@
{[ } ]}
<ul>
{[ 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) { ]}
<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="auth-otr" data-scheme="smp" href="#">{{label_verify_with_smp}}</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="auth-otr" data-scheme="smp" href="#">{{{label_verify_with_smp}}}</a></li>
{[ } ]}
{[ 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>
</li>
{[ } ]}

View File

@ -1,7 +1,7 @@
<a class="chatbox-btn close-chatbox-button icon-close"></a>
<a class="chat-head-message-count"
{[ if (!num_unread) { ]} style="display: none" {[ } ]}
href="#">{{num_unread}}</a>
<a href="#" class="restore-chat" title="{{tooltip}}">
{{ title }}
href="#">{{{num_unread}}}</a>
<a href="#" class="restore-chat" title="{{{tooltip}}}">
{{{ title }}}
</a>