/* global Whisper, i18n, _, Signal, passwordUtil */ // eslint-disable-next-line func-names (function() { 'use strict'; window.Whisper = window.Whisper || {}; const PasswordDialogView = Whisper.View.extend({ className: 'loki-dialog password-dialog modal', templateName: 'password-dialog', initialize(options) { this.type = options.type; this.resolve = options.resolve; this.okText = options.okText || i18n('ok'); this.reject = options.reject; this.cancelText = options.cancelText || i18n('cancel'); this.title = options.title; this.render(); this.updateUI(); }, events: { keyup: 'onKeyup', 'click .ok': 'ok', 'click .cancel': 'cancel', }, render_attributes() { return { showCancel: !this.hideCancel, cancel: this.cancelText, ok: this.okText, title: this.title, }; }, async updateUI() { if (this.disableOkButton()) { this.$('.ok').prop('disabled', true); } else { this.$('.ok').prop('disabled', false); } }, disableOkButton() { const password = this.$('#password').val(); return _.isEmpty(password); }, async validate() { const password = this.$('#password').val(); const passwordConfirmation = this.$('#password-confirmation').val(); const pairValidation = this.validatePasswordPair( password, passwordConfirmation ); const hashValidation = await this.validatePasswordHash(password); return pairValidation || hashValidation; }, async validatePasswordHash(password) { // Check if the password matches the hash we have stored const hash = await Signal.Data.getPasswordHash(); if (hash && !passwordUtil.matchesHash(password, hash)) { return i18n('invalidPassword'); } return null; }, validatePasswordPair(password, passwordConfirmation) { if (!_.isEmpty(password)) { // Check if the password is first valid const passwordValidation = passwordUtil.validatePassword( password, i18n ); if (passwordValidation) { return passwordValidation; } // Check if the confirmation password is the same if ( !passwordConfirmation || password.trim() !== passwordConfirmation.trim() ) { return i18n('passwordsDoNotMatch'); } } return null; }, okPressed() { const password = this.$('#password').val(); if (this.type === 'set') { window.setPassword(password.trim()); } else if (this.type === 'remove') { window.setPassword(null, password.trim()); } }, okErrored() { if (this.type === 'set') { this.showError(i18n('setPasswordFail')); } else if (this.type === 'remove') { this.showError(i18n('removePasswordFail')); } }, async ok() { const error = await this.validate(); if (error) { this.showError(error); return; } // Clear any errors this.showError(null); try { this.okPressed(); this.remove(); if (this.resolve) { this.resolve(); } } catch (e) { this.okErrored(); } }, cancel() { this.remove(); if (this.reject) { this.reject(); } }, onKeyup(event) { this.updateUI(); switch (event.key) { case 'Enter': this.ok(); break; case 'Escape': case 'Esc': this.cancel(); break; default: return; } event.preventDefault(); }, focusCancel() { this.$('.cancel').focus(); }, showError(message) { if (_.isEmpty(message)) { this.$('.error').text(''); this.$('.error').hide(); } else { this.$('.error').text(`Error: ${message}`); this.$('.error').show(); } }, }); const ChangePasswordDialogView = PasswordDialogView.extend({ templateName: 'password-change-dialog', disableOkButton() { const oldPassword = this.$('#old-password').val(); const newPassword = this.$('#new-password').val(); return _.isEmpty(oldPassword) || _.isEmpty(newPassword); }, async validate() { const oldPassword = this.$('#old-password').val(); // Validate the old password if (!_.isEmpty(oldPassword)) { const oldPasswordValidation = passwordUtil.validatePassword( oldPassword, i18n ); if (oldPasswordValidation) { return oldPasswordValidation; } } else { return i18n('typeInOldPassword'); } const password = this.$('#new-password').val(); const passwordConfirmation = this.$('#new-password-confirmation').val(); const pairValidation = this.validatePasswordPair( password, passwordConfirmation ); const hashValidation = await this.validatePasswordHash(oldPassword); return pairValidation || hashValidation; }, okPressed() { const oldPassword = this.$('#old-password').val(); const newPassword = this.$('#new-password').val(); window.setPassword(newPassword.trim(), oldPassword.trim()); }, okErrored() { this.showError(i18n('changePasswordFail')); }, }); Whisper.getPasswordDialogView = (type, resolve, reject) => { // This is a differently styled dialog if (type === 'change') { return new ChangePasswordDialogView({ title: i18n('changePassword'), okTitle: i18n('change'), resolve, reject, }); } // Set and Remove is basically the same UI const title = type === 'remove' ? i18n('removePassword') : i18n('setPassword'); const okTitle = type === 'remove' ? i18n('remove') : i18n('set'); return new PasswordDialogView({ title, okTitle, type, resolve, reject, }); }; })();