<commit_before>(function () {
    "use strict";

    angular.module("blocktrail.setup")
        .controller("SetupChangePasswordCtrl", SetupChangePasswordCtrl);

    function SetupChangePasswordCtrl($scope, $stateParams, $http, $q, $sce, PasswordStrength, dialogService, $log, $filter,
                                     sdkService, $translate, CONFIG, passwordRecoveryService) {

        $scope.bip39EN = blocktrailSDK.bip39wordlist;
        $scope.stepCount = 0;
        $scope.working = false;
        $scope.error = null;
        $scope.form = {
            email : "",
            ERS: "",
            newPassword: "",
            newPasswordRepeat: "",
            passwordCheck: null
        };

        $scope.walletVersions = ["v1", "v2", "v3"];

        // Password reset data
        var secret = null;
        var twoFactorToken = null;

        // Get state parameters
        var token = $stateParams.token;
        var walletVersion = $stateParams.version;
        var requires2FA = $stateParams.requires_2fa;

        var recoverySecret = passwordRecoveryService.requestRecoverySecret(token);

        var watchListenerERS = $scope.$watch('form.ERS', function (newVal, oldVal) {
            function strcmp (a, b) {
                return (a < b ? -1 : ( a > b ? 1 : 0 ));
            }
            // Remove line breaks
            if ($scope.form.ERS && $scope.form.ERS.length > 0) {
                $scope.form.ERS = $scope.form.ERS.replace(/[\r\n]+/g, " "); // https://stackoverflow.com/a/34936253
            }

            if (newVal && oldVal) {
                var oldWords = oldVal.split(' ');
                // If we add a word
                if (newVal.split(' ').length == 1 && oldWords.length > 1) {
                    delete oldWords[oldWords.length - 1];
                    // Special case when removing last word
                    if (strcmp(oldVal, newVal + ' ' + newVal[0])) {
                        $scope.form.ERS = oldWords.join(' ') + newVal + ' ';
                    } else {
                        $scope.form.ERS = oldWords.join(' ').slice(0, -1);
                    }
                }
            }
        });

        $scope.checkPassword = function () {
            if($scope.form.newPassword !== $scope.form.newPasswordRepeat) {
                $scope.form.passwordCheck = {score: -1};
                return $q.when(false);
            }

            if (!$scope.form.newPassword || !$scope.form.newPasswordRepeat) {
                $scope.form.passwordCheck = null;
                return $q.when(false);
            }

            return PasswordStrength.check($scope.form.newPassword, [$scope.form.email, "<EMAIL>", "wallet"])
                .then(function(result) {
                    result.duration = $filter("duration")(result.crack_times_seconds.online_no_throttling_10_per_second * 1000);
                    $scope.form.passwordCheck = result;
                    return result;
                });
        };

        function generateBackupPageTwo (identifier, newEncryptedWalletSecretMnemonic) {
            return dialogService.alert({
                title: $translate.instant('CHANGE_PASSWORD'),
                bodyHtml: $sce.trustAsHtml($translate.instant('CHANGE_PASSWORD_BACKUP')),
                ok: $translate.instant('BACKUP_DOWNLOAD_ADDITIONAL_PAGE_PDF')
            }).result.then(function () {
                var backup = new sdkService.BackupGenerator(
                    identifier,
                    {
                        encryptedSecret: newEncryptedWalletSecretMnemonic
                    },
                    {},
                    {
                        page1: false,
                        page2: true,
                        page3: false
                    }
                );

                try {
                    backup.generatePDF (function(err, pdf) {
                        if (err) {
                            $log.error(err);
                            $scope.alert({
                                title: $translate.instant('ERROR'),
                                body: "" + err
                            });
                        } else {
                            pdf.save("BlockTrail Additional Recovery Data Sheet - " + identifier + ".pdf");
                        }
                    });
                } catch (error) {
                    $log.error("Backup generation error", error);
                }
            });
        }

        function askFor2FA () {
            return dialogService.prompt(
                $translate.instant("SETUP_LOGIN"),
                $translate.instant("MSG_MISSING_TWO_FACTOR_TOKEN")
            ).result.then(function (result) {
                twoFactorToken = result;
            });
        }

        $scope.encryptNewERS = function() {
            // If 2FA is required, ask for and and then continue
            if (requires2FA == true && twoFactorToken == null) {
                return askFor2FA().then(function () {
                    $scope.encryptNewERS();
                });
            } else {
                // Encrypt secret with new Password, generate mnemonic and password hash
                var encryptedData = passwordRecoveryService.encryptSecretWithPassword(secret, $scope.form.newPassword, walletVersion);

                // Create data object
                var data = {
                    token: token,
                    new_password_hash: <PASSWORD>Data.password_hash,
                    new_encrypted_secret: encryptedData.encrypted_secret
                };

                if(requires2FA && twoFactorToken) {
                    data['two_factor_token'] = twoFactorToken;
                }

                $http.post(CONFIG.API_URL + "/v1/" + (CONFIG.TESTNET ? "t" : "") + CONFIG.NETWORK + "/recovery/change-password", data).then(function (res) {
                    // Generate backup PDF
                    generateBackupPageTwo("", encryptedData.secret_mnemonic);
                    $scope.stepCount = 2;
                }, function (err) {
                    // Error handling
                    twoFactorToken = null; // Clear the used 2FA token
                    if (err.data && err.data.msg && err.data.msg === "invalid recovery token") {
                        return dialogService.alert(
                            $translate.instant("INVALID_RECOVERY_TOKEN"),
                            $translate.instant("MSG_INVALID_RECOVER_TOKEN")
                        ).result;
                    } else if (err.data && err.data.msg && err.data.msg === "invalid 2FA token") {
                        twoFactorToken = null;
                        return dialogService.alert(
                            $translate.instant("BAD_TOKEN"),
                            $translate.instant("MSG_BAD_TOKEN")
                        ).result;
                    } else {
                        return dialogService.alert(
                            $translate.instant("RECOVERY_ERROR"),
                            $translate.instant("MSG_PASSWORD_NOT_CHANGED")
                        ).result;
                    }
                });
            }
        };

        $scope.decryptERS = function() {
            if ($scope.working) {
                return false;
            }
            $scope.error = null;
            $scope.working = true;

            if ($scope.form.ERS.length > 0) {<commit_msg>enforce password strength check with error dialog<commit_after>(function () {
    "use strict";

    angular.module("blocktrail.setup")
        .controller("SetupChangePasswordCtrl", SetupChangePasswordCtrl);

    function SetupChangePasswordCtrl($scope, $stateParams, $http, $q, $sce, PasswordStrength, dialogService, $log, $filter,
                                     sdkService, $translate, CONFIG, passwordRecoveryService) {

        $scope.bip39EN = blocktrailSDK.bip39wordlist;
        $scope.stepCount = 0;
        $scope.working = false;
        $scope.error = null;
        $scope.form = {
            email : "",
            ERS: "",
            newPassword: "",
            newPasswordRepeat: "",
            passwordCheck: null
        };

        $scope.walletVersions = ["v1", "v2", "v3"];

        // Password reset data
        var secret = null;
        var twoFactorToken = null;

        // Get state parameters
        var token = $stateParams.token;
        var walletVersion = $stateParams.version;
        var requires2FA = $stateParams.requires_2fa;

        var recoverySecret = passwordRecoveryService.requestRecoverySecret(token);

        var watchListenerERS = $scope.$watch('form.ERS', function (newVal, oldVal) {
            function strcmp (a, b) {
                return (a < b ? -1 : ( a > b ? 1 : 0 ));
            }
            // Remove line breaks
            if ($scope.form.ERS && $scope.form.ERS.length > 0) {
                $scope.form.ERS = $scope.form.ERS.replace(/[\r\n]+/g, " "); // https://stackoverflow.com/a/34936253
            }

            if (newVal && oldVal) {
                var oldWords = oldVal.split(' ');
                // If we add a word
                if (newVal.split(' ').length == 1 && oldWords.length > 1) {
                    delete oldWords[oldWords.length - 1];
                    // Special case when removing last word
                    if (strcmp(oldVal, newVal + ' ' + newVal[0])) {
                        $scope.form.ERS = oldWords.join(' ') + newVal + ' ';
                    } else {
                        $scope.form.ERS = oldWords.join(' ').slice(0, -1);
                    }
                }
            }
        });

        $scope.checkPassword = function () {
            if($scope.form.newPassword !== $scope.form.newPasswordRepeat) {
                $scope.form.passwordCheck = {score: -1};
                return $q.when(false);
            }

            if (!$scope.form.newPassword || !$scope.form.newPasswordRepeat) {
                $scope.form.passwordCheck = null;
                return $q.when(false);
            }

            return PasswordStrength.check($scope.form.newPassword, [$scope.form.email, "<EMAIL>", "wallet"])
                .then(function(result) {
                    result.duration = $filter("duration")(result.crack_times_seconds.online_no_throttling_10_per_second * 1000);
                    $scope.form.passwordCheck = result;
                    return result;
                });
        };

        function generateBackupPageTwo (identifier, newEncryptedWalletSecretMnemonic) {
            return dialogService.alert({
                title: $translate.instant('CHANGE_PASSWORD'),
                bodyHtml: $sce.trustAsHtml($translate.instant('CHANGE_PASSWORD_BACKUP')),
                ok: $translate.instant('BACKUP_DOWNLOAD_ADDITIONAL_PAGE_PDF')
            }).result.then(function () {
                var backup = new sdkService.BackupGenerator(
                    identifier,
                    {
                        encryptedSecret: newEncryptedWalletSecretMnemonic
                    },
                    {},
                    {
                        page1: false,
                        page2: true,
                        page3: false
                    }
                );

                try {
                    backup.generatePDF (function(err, pdf) {
                        if (err) {
                            $log.error(err);
                            $scope.alert({
                                title: $translate.instant('ERROR'),
                                body: "" + err
                            });
                        } else {
                            pdf.save("BlockTrail Additional Recovery Data Sheet - " + identifier + ".pdf");
                        }
                    });
                } catch (error) {
                    $log.error("Backup generation error", error);
                }
            });
        }

        function askFor2FA () {
            return dialogService.prompt(
                $translate.instant("SETUP_LOGIN"),
                $translate.instant("MSG_MISSING_TWO_FACTOR_TOKEN")
            ).result.then(function (result) {
                twoFactorToken = result;
            });
        }

        $scope.encryptNewERS = function() {
            $scope.checkPassword().then(function (result) {
                if (result === false) {
                    return dialogService.alert(
                        $translate.instant("RECOVERY_ERROR"),
                        $translate.instant("MSG_BAD_PASSWORD_REPEAT")
                    ).result;
                } else if (result.score < CONFIG.REQUIRED_PASSWORD_STRENGTH) {
                    return dialogService.alert(
                        $translate.instant("RECOVERY_ERROR"),
                        $translate.instant("MSG_WEAK_PASSWORD")
                    ).result;
                }
                // If 2FA is required, ask for and and then continue
                else if (requires2FA == true && twoFactorToken == null) {
                    return askFor2FA().then(function () {
                        $scope.encryptNewERS();
                    });
                } else {
                    // Encrypt secret with new Password, generate mnemonic and password hash
                    var encryptedData = passwordRecoveryService.encryptSecretWithPassword(secret, $scope.form.newPassword, walletVersion);

                    // Create data object
                    var data = {
                        token: token,
                        new_password_hash: <PASSWORD>,
                        new_encrypted_secret: encryptedData.encrypted_secret,
                        password_score: result.score
                    };

                    if(requires2FA && twoFactorToken) {
                        data['two_factor_token'] = twoFactorToken;
                    }

                    $http.post(CONFIG.API_URL + "/v1/" + (CONFIG.TESTNET ? "t" : "") + CONFIG.NETWORK + "/recovery/change-password", data).then(function (res) {
                        // Generate backup PDF
                        generateBackupPageTwo("", encryptedData.secret_mnemonic);
                        $scope.stepCount = 2;
                    }, function (err) {
                        // Error handling
                        twoFactorToken = null; // Clear the used 2FA token
                        if (err.data && err.data.msg && err.data.msg === "invalid recovery token") {
                            return dialogService.alert(
                                $translate.instant("INVALID_RECOVERY_TOKEN"),
                                $translate.instant("MSG_INVALID_RECOVER_TOKEN")
                            ).result;
                        } else if (err.data && err.data.msg && err.data.msg === "invalid 2FA token") {
                            twoFactorToken = null;
                            return dialogService.alert(
                                $translate.instant("BAD_TOKEN"),
                                $translate.instant("MSG_BAD_TOKEN")
                            ).result;
                        } else {
                            return dialogService.alert(
                                $translate.instant("RECOVERY_ERROR"),
                                $translate.instant("MSG_PASSWORD_NOT_CHANGED")
                            ).result;
                        }
                    });
                }
            });
        };

        $scope.decryptERS = function() {
            if ($scope.working) {
                return false;
            }
            $scope.error = null;
            $scope.working = true;

            if ($scope.form.ERS.length > 0) {
{'fraction_non_alphanumeric': 0.10740552878518894, 'fraction_numerical': 0.006086735987826528, 'mean_word_length': 1.227510238666855, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 15, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7393509', 'n_tokens_mistral': 4060, 'n_tokens_neox': 3726, 'n_words': 714}