Viewing File: /usr/local/cpanel/whostmgr/docroot/templates/hulkd/views/hulkdBlacklistController.js

/*
# cpanel - whostmgr/docroot/templates/hulkd/views/hulkdBlacklistController.js
#                                                  Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/* eslint camelcase: 0, no-prototype-builtins: 0 */

define(
    [
        "angular",
        "jquery",
        "lodash",
        "cjt/util/locale",
        "app/utils/download",
        "uiBootstrap",
        "cjt/directives/toggleSortDirective",
        "cjt/decorators/paginationDecorator",
        "cjt/decorators/growlDecorator",
        "cjt/filters/startFromFilter",
        "app/services/HulkdDataSource",
    ],
    function(angular, $, _, LOCALE, Download) {

        "use strict";

        // Retrieve the current application
        var app = angular.module("App");
        app.config([ "$compileProvider",
            function($compileProvider) {
                $compileProvider.aHrefSanitizationWhitelist(/^blob:https:/);
            },
        ]);

        var controller = app.controller(
            "hulkdBlacklistController",
            ["$scope", "$filter", "$routeParams", "$uibModal", "HulkdDataSource", "growl", "PAGE", "$timeout",
                function($scope, $filter, $routeParams, $uibModal, HulkdDataSource, growl, PAGE, $timeout) {

                    $scope.blacklist_reverse = false;

                    $scope.blacklist = [];
                    $scope.blacklist_comments = {};

                    $scope.adding_batch_to_blacklist = false;

                    $scope.new_blacklist_records = "";

                    $scope.ip_being_edited = false;
                    $scope.current_ip = null;
                    $scope.current_comment = "";
                    $scope.updating_comment = false;

                    $scope.modal_instance = null;

                    $scope.loading = false;

                    $scope.downloadAllLink = "";
                    $scope.downloadSelectionLink = "";

                    $scope.meta = {
                        sortDirection: "asc",
                        sortBy: "black_ip",
                        sortType: "",
                        sortReverse: false,
                        filter: "",
                        maxPages: 0,
                        totalItems: $scope.blacklist.length || 0,
                        currentPage: 1,
                        pageNumberStart: 0,
                        pageNumberEnd: 0,
                        pageSize: 20,
                        pageSizes: [20, 50, 100],
                    };

                    $scope.LOCALE = LOCALE;

                    var filters = {
                        filter: $filter("filter"),
                        orderBy: $filter("orderBy"),
                        startFrom: $filter("startFrom"),
                        limitTo: $filter("limitTo"),
                    };

                    $scope.delete_in_progress = false;
                    $scope.ips_to_delete = [];

                    $scope.selecting_page_size = false;

                    $scope.edit_blacklist_ip = function(blacklist_ip) {
                        $scope.current_ip = blacklist_ip;
                        $scope.current_comment = $scope.blacklist_comments.hasOwnProperty(blacklist_ip) ? $scope.blacklist_comments[blacklist_ip] : "";
                        $scope.ip_being_edited = true;
                        var blacklist_comment_field = $("#blacklist_current_comment");
                        var wait_id = setInterval( function() {
                            if (blacklist_comment_field.is(":visible")) {
                                blacklist_comment_field.focus();
                                blacklist_comment_field.select();
                                clearInterval(wait_id);
                            }
                        }, 250);
                    };

                    $scope.cancel_blacklist_editing = function() {
                        $scope.current_ip = null;
                        $scope.current_comment = "";
                        $scope.ip_being_edited = false;
                        $scope.focus_on_blacklist();
                    };

                    $scope.delete_tooltip = function(ip_address) {
                        return LOCALE.maketext("Click to delete “[_1]” from the blacklist.", ip_address);
                    };

                    $scope.edit_tooltip = function(ip_address) {
                        return LOCALE.maketext("Click to edit the comment for “[_1]”.", ip_address);
                    };

                    $scope.update_blacklist_comment = function() {
                        if ($scope.updating_comment) {
                            return;
                        }

                        $scope.updating_comment = true;
                        HulkdDataSource.add_to_blacklist([ { ip: $scope.current_ip, comment: $scope.current_comment } ])
                            .then( function(results) {
                                $scope.blacklist_comments = HulkdDataSource.blacklist_comments;

                                // Growl out each success from the batch.
                                results.updated.forEach(function(ip) {
                                    growl.success(LOCALE.maketext("You have successfully updated the comment for “[_1]”.", _.escape(ip)));
                                });

                                // Report the failures from the batch.
                                var rejectedMessages = [];
                                Object.keys(results.rejected).forEach(function(ip) {
                                    rejectedMessages.push(_.escape(ip) + ": " + _.escape(results.rejected[ip]));
                                });

                                if (rejectedMessages.length > 0) {
                                    var accumulatedMessages = LOCALE.maketext("Some records failed to update.") + "<br>";
                                    accumulatedMessages += rejectedMessages.join("<br>");
                                    growl.error(accumulatedMessages);
                                }

                            }, function(error) {
                                growl.error(error);
                            })
                            .finally(function() {
                                $scope.updating_comment = false;
                                $scope.cancel_blacklist_editing();
                                $scope.focus_on_blacklist();
                            });
                    };

                    var ipV6 = /^(([\da-fA-F]{1,4}:){4})(([\da-fA-F]{1,4}:){3})([\da-fA-F]{1,4})$/;
                    var ipV4Range = /^((\d{1,3}.){3}\d{1,3})-((\d{1,3}.){3}\d{1,3})$/;
                    var ipRangeTest = /-/;
                    var ipV6Test = /:/;

                    /**
                     * Separates long ipv4 and ipv6 addresses with br tags.
                     * Also, supports separating ipv4 and ipv6 address ranges.
                     *
                     * @param {string} ip - an ip address
                     * @todo Implement this as an Angular Filter in a separate file
                     */
                    $scope.splitLongIp = function(ip) {

                        // ipv6?
                        if (ipV6Test.test(ip)) {

                            // is this a range?
                            if (ipRangeTest.test(ip)) {

                                // format the ipv6 addresses in range format
                                var ipv6Addresses = ip.split(ipRangeTest);
                                var ipv6AddressRange = "";

                                // get the first part of the range
                                var match = ipV6.exec(ipv6Addresses[0]);
                                if (match) {
                                    ipv6AddressRange += match[1] + "<br>" + match[3] + match[5];
                                }

                                // add the range separator
                                ipv6AddressRange += "-<br>";

                                // get the second part of the range
                                match = ipV6.exec(ipv6Addresses[1]);
                                if (match) {
                                    ipv6AddressRange += match[1] + "<br>" + match[3] + match[5];
                                }

                                // if all we have is -<br>, then forget it
                                if (ipv6AddressRange.length > 5) {
                                    return ipv6AddressRange;
                                }
                            } else {

                                // format the ipv6 address
                                var v6match = ipV6.exec(ip);
                                if (v6match) {
                                    return v6match[1] + "<br>" + v6match[3] + v6match[5];
                                }
                            }
                        } else {

                            // format the ipv4 range
                            var v4rangeMatch = ipV4Range.exec(ip);
                            if (v4rangeMatch) {
                                return v4rangeMatch[1] + "-<br>" + v4rangeMatch[3];
                            }
                        }

                        // could not format it, just return it
                        return ip;
                    };

                    $scope.$watch(function() {
                        return HulkdDataSource.enabled;
                    }, function() {
                        $scope.load_list();
                    });

                    $scope.$watchGroup([ "blacklist.length", "meta.filteredList.length" ], function() {
                        if ($scope.blacklist.length === 0 || $scope.meta.filteredList.length === 0) {
                            $("#blacklist_select_all_checkbox").prop("checked", false);
                        }
                    });

                    $scope.selectPage = function(page) {
                        $("#blacklist_select_all_checkbox").prop("checked", false);

                        // set the page if requested
                        if (page && angular.isNumber(page)) {
                            $scope.meta.currentPage = page;
                        }

                        $scope.load_list();
                    };

                    $scope.selectPageSize = function() {
                        return $scope.load_list({ reset_focus: false });
                    };

                    /**
                     * Filter the list by the `meta.filter`.
                     */
                    $scope.filterList = function() {
                        $scope.meta.currentPage = 1;
                        $scope.load_list({ reset_focus: false });
                    };

                    /**
                     * Clear the filter if it is set.
                     */
                    $scope.toggleFilter = function() {
                        $scope.meta.filter = "";
                        $scope.load_list({ reset_focus: false });
                    };

                    $scope.sortList = function(meta) {
                        $scope.meta.sortReverse = (meta.sortDirection === "asc") ? false : true;
                        $scope.applyFilters();
                    };

                    $scope.orderByComments = function(comment_object, ip_list) {
                        var comments_as_pairs = _.toPairs(comment_object);
                        var ips_as_pairs = [];

                        // get the IPs that have no comments
                        for (var i = 0; i < ip_list.length; i++) {
                            if (!_.has(comment_object, ip_list[i] )) {
                                var one_entry = [ip_list[i], "" ];
                                ips_as_pairs.push(one_entry);
                            }
                        }

                        // sort the IPs that have no comments
                        var sorted_pairs = _.sortBy(ips_as_pairs, function(pair) {
                            return $scope.ip_padder(pair[0]);
                        });

                        // sort the comments first by comment, then by IP address
                        comments_as_pairs.sort(compareComments);

                        // create an array of the IPs from the sorted comments
                        var just_ips_comments = _.map(comments_as_pairs, function(pair) {
                            return pair[0];
                        });

                        // create an array of the sorted IPs with no comments
                        var just_ips = _.map(sorted_pairs, function(pair) {
                            return pair[0];
                        });

                        // put the IPs with comments and the IPs without comments together
                        var stuck_together = just_ips_comments.concat(just_ips);

                        if ($scope.meta.sortDirection === "desc") {
                            return stuck_together.reverse();
                        }

                        return stuck_together;
                    };

                    /**
                     * Apply the sort, filter and pagination to the blacklist data.
                     *
                     * @returns {string[]} List of ips that pass the filters.
                     */
                    $scope.applyFilters = function() {
                        var filteredList = [];
                        var start, limit;

                        filteredList = $scope.blacklist;

                        // Sort
                        if ($scope.meta.sortDirection !== "" && $scope.meta.sortBy !== "") {
                            if ($scope.meta.sortBy === "black_ip") {
                                filteredList = filters.orderBy(filteredList, $scope.ip_padder, $scope.meta.sortReverse);
                            } else {
                                filteredList = $scope.orderByComments($scope.blacklist_comments, $scope.blacklist);
                            }
                        }

                        // Totals
                        $scope.meta.totalItems = $scope.blacklist.length;

                        // Filter content
                        var expected = $scope.meta.filter.toLowerCase();
                        if (expected) {
                            filteredList = filters.filter(filteredList, function(actual) {
                                return actual.indexOf(expected) !== -1 ||
                                       ($scope.blacklist_comments[actual] && $scope.blacklist_comments[actual].toLowerCase().indexOf(expected) !== -1);
                            });
                        }

                        // Track the filtered size separatly
                        $scope.meta.filteredItems = filteredList.length;

                        // Pagination
                        start = ($scope.meta.currentPage - 1) * $scope.meta.pageSize;
                        limit = $scope.meta.pageSize;
                        filteredList = filters.limitTo(filters.startFrom(filteredList, start), limit);

                        $scope.meta.pageNumberStart = start + 1;
                        $scope.meta.pageNumberEnd = ($scope.meta.currentPage * $scope.meta.pageSize);


                        if ($scope.meta.totalItems === 0) {
                            $scope.meta.pageNumberStart = 0;
                        }

                        if ($scope.meta.pageNumberEnd > $scope.meta.totalItems) {
                            $scope.meta.pageNumberEnd = $scope.meta.totalItems;
                        }

                        $scope.meta.filteredList = filteredList;

                        return filteredList;
                    };

                    $scope.load_list = function(options) {
                        if (HulkdDataSource.enabled && !$scope.loading) {

                            $scope.loading = true;
                            var reset_focus = typeof options !== "undefined" && options.hasOwnProperty("reset_focus") ? options.reset_focus : true;

                            if (HulkdDataSource.blacklist_is_cached) {
                                $scope.blacklist = HulkdDataSource.blacklist;
                                $scope.blacklist_comments = HulkdDataSource.blacklist_comments;
                                $scope.downloadAllLink = $scope.generateDownloadAllLink();
                                $scope.applyFilters();
                                if (reset_focus) {
                                    $scope.focus_on_blacklist();
                                }
                                $scope.loading = false;
                            } else {
                                $scope.meta.filteredList = [];
                                return HulkdDataSource.load_list("black")
                                    .then(function() {
                                        $scope.blacklist = HulkdDataSource.blacklist;
                                        $scope.blacklist_comments = HulkdDataSource.blacklist_comments;
                                        $scope.downloadAllLink = $scope.generateDownloadAllLink();
                                        $scope.applyFilters();
                                    }, function(error) {
                                        growl.error(error);
                                    })
                                    .finally(function() {
                                        if (reset_focus) {
                                            $scope.focus_on_blacklist();
                                        }
                                        $scope.selecting_page_size = false;
                                        $scope.loading = false;
                                    });
                            }
                        }
                        return null;
                    };

                    $scope.force_load_blacklist = function() {
                        HulkdDataSource.blacklist_is_cached = false;
                        $scope.blacklist = [];
                        $scope.blacklist_comments = {};
                        $scope.meta.filteredList = [];
                        return $scope.load_list();
                    };

                    $scope.delete_confirmation_message = function() {
                        if ($scope.ips_to_delete.length === 1) {
                            return LOCALE.maketext("Do you want to permanently delete “[_1]” from the blacklist?", $scope.ips_to_delete[0]);
                        } else {
                            return LOCALE.maketext("Do you want to permanently delete [quant,_1,record,records] from the backlist?", $scope.ips_to_delete.length);
                        }
                    };

                    $scope.itemsAreChecked = function() {
                        return $(".blacklist_select_item").filter(":checked").length > 0;
                    };

                    $scope.check_blacklist_selection = function() {
                        if ($(".blacklist_select_item").filter(":not(:checked)").length === 0) {
                            $("#blacklist_select_all_checkbox").prop("checked", true);
                        } else {
                            $("#blacklist_select_all_checkbox").prop("checked", false);
                        }
                        $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink();
                    };

                    /**
                     * Get the list of ips selected in the UI.
                     *
                     * @returns <string[]> List if ips selected.
                     */
                    $scope.getSelection = function()  {
                        var selected_items = [],
                            $selected_dom_nodes = $(".blacklist_select_item:checked");

                        if ($selected_dom_nodes.length === 0) {
                            return [];
                        }

                        $selected_dom_nodes.each( function() {
                            selected_items.push($(this).data("ip"));
                        });

                        return selected_items;
                    };

                    $scope.confirm_blacklist_deletion = function(ip_to_delete) {
                        if ($scope.blacklist.length === 0) {
                            return false;
                        }

                        $scope.delete_in_progress = true;
                        if (ip_to_delete !== undefined) {
                            $scope.ips_to_delete = [ip_to_delete];
                            $scope.is_single_deletion = true;
                        } else {
                            var selected_items = $scope.getSelection();
                            if (selected_items.length === 0) {
                                return false;
                            }
                            $scope.ips_to_delete = selected_items;
                            $scope.is_single_deletion = false;
                        }

                        $scope.modal_instance = $uibModal.open({
                            templateUrl: "confirm_blacklist_deletion.html",
                            scope: $scope,
                        });

                        return true;
                    };

                    $scope.clear_modal_instance = function() {
                        if ($scope.modal_instance) {
                            $scope.modal_instance.close();
                            $scope.modal_instance = null;
                        }
                    };

                    $scope.cancel_deletion = function() {
                        $scope.delete_in_progress = false;
                        $scope.ips_to_delete = [];
                        $scope.clear_modal_instance();
                        $scope.focus_on_blacklist();
                    };

                    $scope.delete_blacklist_ips = function(is_single_deletion) {
                        $scope.clear_modal_instance();
                        HulkdDataSource.remove_from_blacklist($scope.ips_to_delete)
                            .then( function(results) {
                                $scope.blacklist = HulkdDataSource.blacklist;
                                $scope.blacklist_comments = HulkdDataSource.blacklist_comments;
                                $scope.applyFilters();
                                $scope.focus_on_blacklist();

                                if (results.removed.length === 1) {
                                    growl.success(LOCALE.maketext("You have successfully deleted “[_1]” from the blacklist.", _.escape(results.removed[0])));
                                } else {
                                    growl.success(LOCALE.maketext("You have successfully deleted [quant,_1,record,records] from the blacklist.", results.removed.length));
                                }

                                if (results.not_removed.keys && results.not_removed.keys.length > 0) {
                                    growl.warning(LOCALE.maketext("The system was unable to delete [quant,_1,record,records] from the blacklist.", results.not_removed.keys.length));
                                }
                            }, function(error) {
                                growl.error(error);
                            })
                            .finally( function() {
                                $scope.delete_in_progress = false;
                                $scope.ips_to_delete = [];
                                if (!is_single_deletion) {
                                    $scope.deselect_all_blacklist();
                                }

                                // Since this is using JQuery/DOM, we have to wait another tick for the UI to update
                                // before we try to get the selection.
                                $timeout(function() {
                                    $scope.downloadAllLink = $scope.generateDownloadAllLink();
                                    $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink();
                                });
                            });
                    };

                    $scope.confirm_delete_all = function() {
                        if ($scope.blacklist.length === 0) {
                            return false;
                        }
                        $scope.delete_in_progress = true;

                        $scope.modal_instance = $uibModal.open({
                            templateUrl: "confirm_blacklist_delete_all.html",
                            scope: $scope,
                        });

                        return true;
                    };

                    $scope.cancel_delete_all = function() {
                        $scope.delete_in_progress = false;
                        $scope.clear_modal_instance();
                        $scope.focus_on_blacklist();
                    };

                    $scope.delete_all = function() {
                        $scope.clear_modal_instance();
                        HulkdDataSource.remove_all_from_blacklist()
                            .then( function(results) {
                                $scope.blacklist = HulkdDataSource.blacklist;
                                $scope.blacklist_comments = HulkdDataSource.blacklist_comments;
                                $scope.applyFilters();
                                $scope.focus_on_blacklist();
                                if (results.not_removed.keys && results.not_removed.keys.length > 0) {
                                    growl.success(LOCALE.maketext("You have successfully deleted [quant,_1,record,records] from the blacklist.", results.removed.keys.length));
                                    growl.warning(LOCALE.maketext("The system was unable to delete [quant,_1,record,records] from the blacklist.", results.not_removed.keys.length));
                                } else {
                                    growl.success(LOCALE.maketext("You have deleted all records from the blacklist."));
                                }
                            }, function(error) {
                                growl.error(error);
                            })
                            .finally( function() {
                                $scope.delete_in_progress = false;
                                $scope.deselect_all_blacklist();

                                // Since this is using JQuery/DOM, we have to wait another tick for the UI to update
                                // before we try to get the selection.
                                $timeout(function() {
                                    $scope.downloadAllLink = $scope.generateDownloadAllLink();
                                    $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink();
                                });
                            });
                    };

                    $scope.select_all_blacklist = function() {
                        if ($scope.blacklist.length === 0) {
                            return false;
                        }
                        $(".blacklist_select_item").prop("checked", true);
                        $("#blacklist_select_all_checkbox").prop("checked", true);

                        $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink();

                        return true;
                    };

                    $scope.deselect_all_blacklist = function() {
                        if ($scope.blacklist.length === 0) {
                            return false;
                        }
                        $(".blacklist_select_item").prop("checked", false);
                        $("#blacklist_select_all_checkbox").prop("checked", false);

                        $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink();

                        return true;
                    };

                    $scope.toggle_blacklist_selection = function() {
                        if ($("#blacklist_select_all_checkbox").prop("checked") === true) {
                            $scope.select_all_blacklist();
                        } else {
                            $scope.deselect_all_blacklist();
                        }
                    };

                    $scope.focus_on_blacklist = function() {
                        var blacklist_batch_field = $("#blacklist_batch_add");
                        var wait_id = setInterval( function() {
                            if (blacklist_batch_field.is(":visible")) {
                                blacklist_batch_field.focus();
                                blacklist_batch_field.select();
                                clearInterval(wait_id);
                            }
                        }, 250);
                    };

                    /**
                     *
                     * @typedef Record
                     * @property {string} ip
                     * @property {string?} comment
                     */

                    /**
                     * Parse the batch of records.
                     *
                     * @param {string} text
                     * @returns {Record[]}
                     */
                    function parseBatch(text) {
                        var lines = text.split("\n");
                        var records = [];

                        for (var i = 0; i < lines.length; i++) {
                            var line = lines[i];
                            if (line && line.length > 0) {
                                var parts = line.split("#");
                                var ip = parts.shift().trim();
                                var comment = parts.join("#").trim();
                                records.push({
                                    ip: ip,
                                    comment: comment,
                                });
                            }
                        }
                        return records;
                    }

                    /**
                     * Add a batch of records to the blacklist.
                     *
                     * @async
                     * @returns
                     */
                    $scope.add_to_blacklist = function() {
                        if (!$scope.new_blacklist_records || $scope.adding_batch_to_blacklist) {
                            return;
                        }

                        var records = parseBatch($scope.new_blacklist_records);
                        return $scope._add_to_blacklist(records);
                    };

                    /**
                     * Add a batch of blacklist records.
                     *
                     * @private
                     * @param {Record[]} batch
                     * @returns
                     */
                    $scope._add_to_blacklist = function(batch) {
                        $scope.adding_batch_to_blacklist = true;
                        HulkdDataSource.add_to_blacklist(batch)
                            .then( function(results) {
                                $scope.blacklist = HulkdDataSource.blacklist;
                                $scope.blacklist_comments = HulkdDataSource.blacklist_comments;
                                $scope.downloadAllLink = $scope.generateDownloadAllLink();
                                $scope.applyFilters();

                                if (results.added.length === 1) {
                                    growl.success(LOCALE.maketext("You have successfully added “[_1]” to the blacklist.", _.escape(results.added[0])));
                                } else if (results.added.length > 1) {
                                    growl.success(LOCALE.maketext("You have successfully added [quant,_1,IP address,IP addresses] to the blacklist.", results.added.length));
                                }

                                if (results.updated.length === 1) {
                                    growl.success(LOCALE.maketext("You have successfully updated the comment for “[_1]”.", _.escape(results.updated[0])));
                                } else if (results.updated.length > 1) {
                                    growl.success(LOCALE.maketext("You have successfully updated the [numerate,_1,comment,comments] for [quant,_1,IP address,IP addresses].", results.updated.length));
                                }

                                var rejectedIps = Object.keys(results.rejected);
                                if (rejectedIps.length > 0) {
                                    var accumulatedMessages = LOCALE.maketext("Some IP addresses were not added to the blacklist.");
                                    accumulatedMessages += "<br>\n";

                                    // Put the rejected ips/comments back in the list.
                                    $scope.new_blacklist_records = rejectedIps.map(function(ip) {
                                        var record = batch.find(function(record) {
                                            return record.ip === ip;
                                        });
                                        if (record && record.comment) {
                                            return ip + " # " + record.comment + "\n";
                                        }
                                        return ip + "\n";
                                    }).join("");

                                    // Report the problems in the growl
                                    accumulatedMessages += "<ul>\n";
                                    rejectedIps.forEach(function(ip) {
                                        if (results.rejected[ip]) {
                                            accumulatedMessages += "<li>" + _.escape(results.rejected[ip]) + "</li>\n";
                                        }
                                    });
                                    accumulatedMessages += "</ul>\n";
                                    growl.error(accumulatedMessages);
                                } else {
                                    $scope.new_blacklist_records = "";
                                }
                            }, function(error_details) {
                                var error = error_details.main_message;

                                // Format the individual partial errors.
                                var secondary_count = error_details.secondary_messages.length;
                                if (secondary_count > 0) {
                                    error += "<ul>\n";
                                }

                                error_details.secondary_messages.forEach(function(message) {
                                    error += "<li>" + _.escape(message) + "</li>\n";
                                });

                                if (secondary_count > 0) {
                                    error += "</ul>\n";
                                }

                                growl.error(error);
                            })
                            .finally(function() {
                                $scope.adding_batch_to_blacklist = false;
                                $scope.focus_on_blacklist();
                            });
                    };

                    // Handle auto-adding an ip from a query param or POST
                    if (($routeParams["ip"] && $routeParams["ip"].length > 0) ||
                        PAGE.ipToAdd !== null) {
                        var ip;
                        var comment = "";

                        if ($routeParams["ip"] && $routeParams["ip"].length > 0) {

                            // added via a query param
                            ip = $routeParams["ip"];
                        } else if (PAGE.ipToAdd !== null) {

                            // added via a POST and stuffed into PAGE
                            ip = PAGE.ipToAdd;
                        }

                        // clear the ip so we don't add it again
                        PAGE.ipToAdd = null;

                        if (ip !== void 0) {
                            $scope._add_to_blacklist([ { ip: ip, comment: comment } ]);
                        }
                    }

                    // TODO: Make this a utility system: ip-comparison
                    $scope.ip_padder = function(unpadded) {
                        var padded_ip = "";
                        if (unpadded) {
                            var split_ip = unpadded.split(".");
                            for (var i = 0; i < split_ip.length; i++) {
                                var this_section = split_ip[i];
                                while ( this_section.length < 3) {
                                    this_section = "0" + this_section;
                                }
                                padded_ip += this_section;
                            }
                        }
                        return padded_ip;
                    };

                    function compareComments(a, b) {

                        // sort by comment
                        if (a[1].toLowerCase() < b[1].toLowerCase()) {
                            return -1;
                        }
                        if (a[1].toLowerCase() > b[1].toLowerCase()) {
                            return 1;
                        }

                        // we have a duplicate comment, so sort by IP address
                        if ($scope.ip_padder(a[0]) < $scope.ip_padder(b[0])) {
                            return -1;
                        }
                        if ($scope.ip_padder(a[0]) > $scope.ip_padder(b[0])) {
                            return 1;
                        }

                        // we have a duplicate comment and IP
                        return 0;
                    }

                    /**
                     * Generate the download name.
                     *
                     * @returns {string} - the name of the download.
                     */
                    $scope.downloadName = function() {
                        return Download.getDownloadName("blacklist");
                    };

                    /**
                     * @typedef IpRecord
                     * @property {string} ip - ip address or range.
                     * @property {string?} comment - comment associated with the ip or range.
                     */

                    /**
                     * Package the ips and comments into a records structure
                     *
                     * @param {string[]} ips
                     * @param {Dictionary<string,string>} comments
                     * @returns {IpRecord[]}
                     */
                    function getRecords(ips, comments) {
                        var list = [];
                        ips.forEach(function(ip) {
                            var comment = comments[ip];
                            list.push({ ip: ip, comment: comment });
                        });
                        return list;
                    }

                    /**
                     * Generate a data blob url that contains all the blacklist ips.
                     *
                     * @returns {string} - data url.
                     */
                    $scope.generateDownloadAllLink = function() {
                        if ($scope.downloadAllLink) {

                            // Clean up the previous url
                            Download.cleanupDownloadUrl($scope.downloadAllLink);
                            $scope.downloadAllLink = null;
                        }

                        var ips = $scope.blacklist;
                        if (!ips || ips.length === 0) {
                            return "";
                        }

                        var list = getRecords(ips, $scope.blacklist_comments);
                        return Download.getTextDownloadUrl(Download.formatList(list));
                    };

                    /**
                     * Generate a data blob url that contains the selected blacklist ips.
                     *
                     * @returns {string} - data url.
                     */
                    $scope.generateDownloadSelectionLink = function() {
                        if ($scope.downloadSelectionLink) {

                            // Clean up the previous url
                            Download.cleanupDownloadUrl($scope.downloadSelectionLink);
                            $scope.downloadSelectionLink = null;
                        }

                        if (!$scope.blacklist || $scope.blacklist.length === 0) {
                            return "";
                        }

                        var selection = $scope.getSelection();
                        if (!selection || selection.length === 0) {
                            return "";
                        }

                        var list = getRecords(selection, $scope.blacklist_comments);

                        return Download.getTextDownloadUrl(Download.formatList(list));
                    };


                    $scope.focus_on_blacklist();
                },
            ]);

        return controller;
    }
);
Back to Directory File Manager