<?php
//
// Switch/Router Config Wizard
// Copyright (c) 2008-2024 - Nagios Enterprises, LLC. All rights reserved.
//

include_once(dirname(__FILE__) . '/../configwizardhelper.inc.php');

m_switch_configwizard_init();

function m_switch_configwizard_init()
{
    $name = "m_switch";
    $args = array(
        CONFIGWIZARD_NAME =>               $name,
        CONFIGWIZARD_VERSION =>            "1.0.1",
        CONFIGWIZARD_TYPE =>               CONFIGWIZARD_TYPE_MONITORING,
        CONFIGWIZARD_DESCRIPTION =>        _("Monitor Meraki Switches"),
        CONFIGWIZARD_DISPLAYTITLE =>       _("Meraki Switch"),
        CONFIGWIZARD_FUNCTION =>           "m_switch_configwizard_func",
        CONFIGWIZARD_PREVIEWIMAGE =>       "m_switch.png",
        CONFIGWIZARD_FILTER_GROUPS =>      array('network'),
        CONFIGWIZARD_REQUIRES_VERSION =>   60100
    );
    register_configwizard($name, $args);
}

/**
 *
 * Generates the instructions to be passed to the command line to generate an MRTG configuration.
 *
 * @param $snmpopts     Array - Given by the getstage1html
 * @param $address      String - Address of the network device
 * @param $port         Integer - Port of the network device
 * @param $snmpversion  String - Must be either 1, 2, 3
 * @param $defaultspeed String - If nothing is returned by ifSpeed, use this value (in Mbps)
 *
 * @return String - String to be executed
 */
function m_switch_configwizard_get_cfgmaker_cmd($snmpopts, $address, $port, $snmpversion = "1", $defaultspeed = 100, $admindown = "on", $interface_reference_style = "nr")
{
    $snmpversion = (in_array($snmpversion, array("1", "2c", "3")) ? $snmpversion : "1");
    $cmd = "/usr/bin/cfgmaker ";

    // If user wants to scan administratively down interfaces
    if ($admindown == "on") {
        $args[] = "--no-down";
    } else {
        $args[] = "--show-op-down";
    }

    $interface_reference_styles = array("nr", "name", "descr", "eth", "type");
    if (!in_array($interface_reference_style, $interface_reference_styles)) {
        $interface_reference_style = "nr";
    }

    $args[] = "--ifref=$interface_reference_style";
    $args[] = "--noreversedns";
    $args[] = "--if-template=/usr/local/nagiosxi/html/includes/configwizards/m_switch/template";
    $args[] = "--zero-speed";
    $args[] = escapeshellarg($defaultspeed * 1000000);
    $opts = !empty($port) ? ":".intval($port)."::::" : ":::::";

    if ($snmpversion != "3") {
        // Run SNMPv1, SNMPv2, SNMPv2c code here
        $username = (array_key_exists('snmpcommunity', $snmpopts) ? $snmpopts['snmpcommunity'] : 'public');

        $args[] = escapeshellarg("{$username}@{$address}{$opts}" . (int) $snmpversion);

        file_put_contents("/tmp/bs", escapeshellarg("{$username}@{$address}{$opts}" . (int) $snmpversion));

    } else {
        // Run SNMPv3 code here
        $opts .= 3;
        $args[] = "--enablesnmpv3";
        $args[] = "--snmp-options=$opts";

        if (!empty($snmpopts['v3_username'])) {
            $args[] = "--username=" . escapeshellarg($snmpopts['v3_username']);
        }

        if (!empty($snmpopts['v3_auth_password'])) {
            $args[] = "--authprotocol=" . escapeshellarg(strtolower($snmpopts['v3_auth_proto']));
            $args[] = "--authpassword=" . escapeshellarg($snmpopts['v3_auth_password']);
        }

        if (!empty($snmpopts['v3_priv_password'])) {
            $args[] = "--privprotocol=" . escapeshellarg(strtolower($snmpopts['v3_priv_proto']));
            $args[] = "--privpassword=" . escapeshellarg($snmpopts['v3_priv_password']);
        }

        $args[] = "--contextengineid=0";
        $args[] = escapeshellarg($address);
    }

    $cmd .= implode(' ', $args);

    // Run the cfgmaker through the user macros
    $cmd = nagiosccm_replace_user_macros($cmd);

    return $cmd;
}

/**
 *
 * Main configwizard driver function
 *
 * @param string $mode
 * @param null   $inargs
 * @param        $outargs
 * @param        $result
 *
 * @return string
 */
function m_switch_configwizard_func($mode = "", $inargs = null, &$outargs = null, &$result = null)
{
    global $cfg;

    $wizard_name = "m_switch";

    // Initialize return code and output
    $result = 0;
    $output = "";

    // Initialize output args - pass back the same data we got
    $outargs[CONFIGWIZARD_PASSBACK_DATA] = $inargs;

    switch ($mode) {

        case CONFIGWIZARD_MODE_GETSTAGE1HTML:
            $address = grab_array_var($inargs, "ip_address", "");
            $address_port = grab_array_var($inargs, "port", 161);
            $snmpversion = grab_array_var($inargs, "snmpversion", "2c");
            $snmpopts_serial = grab_array_var($inargs, "snmpopts_serial", "");
            $selectedhostconfig = grab_array_var($inargs, "selectedhostconfig", "");
            $operation = grab_array_var($inargs, "operation", "new");

            if (!empty($snmpopts_serial)) {
                $snmpopts = json_decode(base64_decode($snmpopts_serial), true);

            } else {
                // Set the defaults if nothing is set yet
                $snmpopts_default = array(
                    "snmpcommunity" => "public",
                    "v3_security_level" => "",
                    "v3_username" => "",
                    "v3_auth_password" => "",
                    "v3_priv_password" => "",
                    "v3_auth_proto" => "",
                    "v3_priv_proto" => "",
                );

                $snmpopts = grab_array_var($inargs, "snmpopts", $snmpopts_default);
            }

            $portnames = grab_array_var($inargs, "portnames", "number");
            $interface_reference_style = grab_array_var($inargs, "interface_reference_style", "nr");
            $scaninterfaces = grab_array_var($inargs, "scaninterfaces", "on");
            $default_port_speed = grab_array_var($inargs, "default_port_speed", 1000);
            $bulk_options = grab_array_var($inargs, 'bulk_options', '');
            $bulk_fields_serial = grab_array_var($inargs, 'bulk_fields_serial', base64_encode(json_encode(array())));
            $bulk_fields_serial = json_decode(base64_decode($bulk_fields_serial), true);
            $bulk_fields = grab_array_var($inargs, 'bulk_fields', $bulk_fields_serial);
            $warn_speed_in_percent = grab_array_var($inargs, "warn_speed_in_percent", 60);
            $warn_speed_out_percent = grab_array_var($inargs, "warn_speed_out_percent", 60);
            $crit_speed_in_percent = grab_array_var($inargs, "crit_speed_in_percent", 80);
            $crit_speed_out_percent = grab_array_var($inargs, "crit_speed_out_percent", 80);
            $admindown = grab_array_var($inargs, "admindown", "on");
            
            # Handle Meraki API switch data
            $config_mode = grab_array_var($inargs, "config_mode", "manual");
            $meraki_api_key = grab_array_var($inargs, "meraki_api_key", "");
            $selected_switches = grab_array_var($inargs, "selected_switches", "");
            $meraki_switches_data = grab_array_var($inargs, "meraki_switches_data", "");
            
            # Process selected switches data if available
            $meraki_switches = array();
            if (!empty($meraki_switches_data)) {
                $decoded_switches = json_decode($meraki_switches_data, true);
                if (is_array($decoded_switches)) {
                    $meraki_switches = $decoded_switches;
                    
                    # If using Meraki API mode and have switches, use first switch data
                    if ($config_mode === 'meraki' && count($meraki_switches) > 0) {
                        $first_switch = $meraki_switches[0];
                        
                        # Override form values with first switch data
                        if (!empty($first_switch['lanIp'])) {
                            $address = $first_switch['lanIp'];
                        }
                        
                        if (!empty($first_switch['snmpversion'])) {
                            $snmpversion = $first_switch['snmpversion'];
                        }
                        
                        if (!empty($first_switch['snmpopts']) && is_array($first_switch['snmpopts'])) {
                            $snmpopts = array_merge($snmpopts, $first_switch['snmpopts']);
                        }
                    }
                }
            }

            # Get the existing host/node configurations.
            # TODO: Include passwords/secrets?
            $nodes = get_configwizard_hosts($wizard_name);

            ########################################################################################
            # Load the html
            # - The html needs to end up in the $output string, so use ob_start() and ob_get_clean()
            #   to load the PHP from the Step1 file into the $output string.
            ########################################################################################
            ob_start();
            include __DIR__.'/steps/step1.php';
            $output = ob_get_clean();

            break;

        case CONFIGWIZARD_MODE_VALIDATESTAGE1DATA:

            // Get variables that were passed to us
            $address = grab_array_var($inargs, "ip_address", "");
            $address_port = grab_array_var($inargs, "port", 161);
            $address = trim(nagiosccm_replace_user_macros($address));
            $address_port = nagiosccm_replace_user_macros($address_port);

            $snmpopts = grab_array_var($inargs, "snmpopts", array());
            $scaninterfaces = grab_array_var($inargs, "scaninterfaces");
            $admindown = grab_array_var($inargs, "admindown");
            $bulk_options = grab_array_var($inargs, "bulk_options", '');
            $bulk_fields_serial = grab_array_var($inargs, 'bulk_fields_serial', '');
            $interface_reference_style = grab_array_var($inargs, 'interface_reference_style', 'nr');
            $csv_arr = [];
            $csv = explode("\n", $bulk_options);
            $csv_arr = array_map('str_getcsv', $csv);

            if (!empty($bulk_fields_serial)) {
                $bulk_fields = json_decode(base64_decode($bulk_fields_serial), true);

            } else {
                $bulk_fields_default = array(
                    'ip_address',
                );

                $bulk_fields = grab_array_var($inargs, 'bulk_fields', $bulk_fields_default);
                $bulk_fields_serial = base64_encode(json_encode($bulk_fields));
            }

            $snmpversion = grab_array_var($inargs, "snmpversion", "2c");
            $default_port_speed = grab_array_var($inargs, "default_port_speed", 100);
            
            # Handle Meraki API switch data in validation
            $config_mode = grab_array_var($inargs, "config_mode", "manual");
            $meraki_switches_data = grab_array_var($inargs, "meraki_switches_data", "");
            
            # Process selected switches data if available
            if (!empty($meraki_switches_data)) {
                $decoded_switches = json_decode($meraki_switches_data, true);
                if (is_array($decoded_switches) && $config_mode === 'meraki' && count($decoded_switches) > 0) {
                    $first_switch = $decoded_switches[0];
                    
                    # Override validation values with first switch data
                    if (!empty($first_switch['lanIp'])) {
                        $address = $first_switch['lanIp'];
                    }
                    
                    if (!empty($first_switch['snmpversion'])) {
                        $snmpversion = $first_switch['snmpversion'];
                    }
                    
                    if (!empty($first_switch['snmpopts']) && is_array($first_switch['snmpopts'])) {
                        $snmpopts = array_merge($snmpopts, $first_switch['snmpopts']);
                    }
                }
            }
            
            $errors = 0;
            $errmsg = array();

            // Do error checking
            if (have_value($address) == false) {
                $errmsg[$errors++] = _("No address specified.");
            } else if (!valid_ip($address)) {
                $errmsg[$errors++] = _("Invalid IP address.");
            } else if (empty($snmpopts['snmpcommunity']) && empty($snmpopts['v3_username'])) {
                $errmsg[$errors++] = _("Either community or username is required.");
            } else if (count($csv_arr[0]) !== count(array_filter($bulk_fields))) {
                $errmsg[$errors++] = _("The number of fields selected must match the number of inputs in the CSV under Bulk Configuration.");
            }

            // check passwords for bad characters (! and ;)
            // if (!empty($snmpopts["v3_auth_password"])) {
            //     if (strpos($snmpopts["v3_auth_password"], '!') || strpos($snmpopts["v3_auth_password"], ';')) {
            //         $errmsg[$errors++] = _("You cannot use '!' or ';' characters in authentication password field.");
            //     }
            // }

            // if (!empty($snmpopts["v3_priv_password"])) {
            //     if (strpos($snmpopts["v3_priv_password"], '!') || strpos($snmpopts["v3_priv_password"], ';')) {
            //         $errmsg[$errors++] = _("You cannot use '!' or ';' characters in privileged password field.");
            //     }
            // }

            // Error results
            if ($errors > 0) {
                $outargs[CONFIGWIZARD_ERROR_MESSAGES] = $errmsg;
                $result = 1;
            }

            // If user wants to scan interfaces, immediately launch the command and start working on it....
            if ($scaninterfaces == "on") {
                # Handle Meraki mode - run cfgmaker for each selected switch
                if ($config_mode === 'meraki' && !empty($meraki_switches_data)) {
                    $decoded_switches = json_decode($meraki_switches_data, true);
                    if (is_array($decoded_switches) && count($decoded_switches) > 0) {
                        # Run cfgmaker for each selected switch
                        foreach ($decoded_switches as $i => $switch) {
                            if (!empty($switch['lanIp'])) {
                                $switch_address = $switch['lanIp'];
                                $switch_snmpopts = isset($switch['snmpopts']) ? array_merge($snmpopts, $switch['snmpopts']) : $snmpopts;
                                $switch_snmpversion = isset($switch['snmpversion']) ? $switch['snmpversion'] : $snmpversion;
                                
                                
                                $outfile = m_switch_configwizard_get_walk_file($switch_address);
                                $donefile = $outfile . ".done";

                                // Get rid of old "done" file if it exists
                                if (file_exists($donefile)) {
                                    unlink($donefile);
                                }

                                // launch cfgmaker command to scan for ports
                                $cfgmaker_cmd = m_switch_configwizard_get_cfgmaker_cmd($switch_snmpopts, $switch_address, $address_port, $switch_snmpversion, $default_port_speed, $admindown, $interface_reference_style);
                                $cmd = $cfgmaker_cmd . " > " . escapeshellarg($outfile) . " 2> /usr/local/nagiosxi/var/components/cfgmakerdebug.log ; touch " . escapeshellarg($donefile) . " &";
                                exec($cmd);
                            }
                        }
                    }
                } else {
                    # Manual mode - single switch processing
                    $outfile = m_switch_configwizard_get_walk_file($address);
                    $donefile = $outfile . ".done";

                    // Get rid of old "done" file if it exists
                    if (file_exists($donefile)) {
                        unlink($donefile);
                    }

                    // launch cfgmaker command to scan for ports
                    $cfgmaker_cmd = m_switch_configwizard_get_cfgmaker_cmd($snmpopts, $address, $address_port, $snmpversion, $default_port_speed, $admindown, $interface_reference_style);
                    $cmd = $cfgmaker_cmd . " > " . escapeshellarg($outfile) . " 2> /usr/local/nagiosxi/var/components/cfgmakerdebug.log ; touch " . escapeshellarg($donefile) . " &";
                    exec($cmd);
                }
            }

            break;

        case CONFIGWIZARD_MODE_GETSTAGE2HTML:

            // Get variables that were passed to us
            $address = grab_array_var($inargs, "ip_address");
            $interface_reference_style = grab_array_var($inargs, "interface_reference_style", 'nr');
            $address_port = grab_array_var($inargs, "port", 161);
            $snmpopts_serial = grab_array_var($inargs, "snmpopts_serial", "");
            $selectedhostconfig = grab_array_var($inargs, "selectedhostconfig", "");
            $operation = grab_array_var($inargs, "operation", "new");
            
            # Handle Meraki API switch data for step2
            $config_mode = grab_array_var($inargs, "config_mode", "manual");
            $meraki_switches_data = grab_array_var($inargs, "meraki_switches_data", "");

            if (!empty($snmpopts_serial)) {
                $snmpopts = json_decode(base64_decode($snmpopts_serial), true);

            } else {
                $snmpopts_default = array(
                    "snmpcommunity" => "public",
                    "v3_security_level" => "",
                    "v3_username" => "",
                    "v3_auth_password" => "",
                    "v3_priv_password" => "",
                    "v3_auth_proto" => "md5",
                    "v3_priv_proto" => "des"
                );

                $snmpopts = grab_array_var($inargs, "snmpopts", $snmpopts_default);
                $snmpopts_serial = base64_encode(json_encode($snmpopts));
            }

            $vendor = grab_array_var($inargs, "vendor", "");
            $portnames = grab_array_var($inargs, "portnames");
            $scaninterfaces = grab_array_var($inargs, "scaninterfaces");
            $admindown = grab_array_var($inargs, "admindown");

            $bulk_options = grab_array_var($inargs, 'bulk_options', '');
            $bulk_fields_serial = grab_array_var($inargs, 'bulk_fields_serial', '');

            if (!empty($bulk_fields_serial)) {
                $bulk_fields = json_decode(base64_decode($bulk_fields_serial), true);

            } else {
                $bulk_fields_default = array(
                    'ip_address'
                );

                $bulk_fields = grab_array_var($inargs, 'bulk_fields', $bulk_fields_default);
                $bulk_fields_serial = base64_encode(json_encode($bulk_fields));
            }

            $snmpversion = grab_array_var($inargs, "snmpversion", "2c");
            $default_port_speed = grab_array_var($inargs, "default_port_speed", 100);
            $warn_speed_in_percent = grab_array_var($inargs, "warn_speed_in_percent", 50);
            $warn_speed_out_percent = grab_array_var($inargs, "warn_speed_out_percent", 50);
            $crit_speed_in_percent = grab_array_var($inargs, "crit_speed_in_percent", 80);
            $crit_speed_out_percent = grab_array_var($inargs, "crit_speed_out_percent", 80);
            
            # For Meraki mode with multiple switches, get all switches data
            $all_switches = array();
            if ($config_mode === 'meraki' && !empty($meraki_switches_data)) {
                $decoded_switches = json_decode($meraki_switches_data, true);
                if (is_array($decoded_switches) && count($decoded_switches) > 0) {
                    $all_switches = $decoded_switches;
                }
            }
            
            # Set hostname based on mode
            if (!empty($all_switches)) {
                # For multiple switches, use a generic name or first switch name
                $hostname = !empty($all_switches[0]['name']) ? $all_switches[0]['name'] : @gethostbyaddr($address);
            } else {
                $hostname = @gethostbyaddr($address);
            }

            // Array for removal of illegal chars in port meta data -SW
            $badchars = explode(" ", "; ` ~ ! $ % ^ & * | ' \" < > ? , ( ) = \\ { } [ ]");

            ########################################################################################
            # Load the html
            # - The html needs to end up in the $output string, so use ob_start() and ob_get_clean()
            #   to load the PHP from the Step2 file into the $output string.
            ########################################################################################
            ob_start();
            include __DIR__.'/steps/step2.php';
            $output = ob_get_clean();

            break;

        case CONFIGWIZARD_MODE_VALIDATESTAGE2DATA:

            // get variables that were passed to us
            $hostname = grab_array_var($inargs, "hostname");
            $hostname = nagiosccm_replace_user_macros($hostname);
            $address = grab_array_var($inargs, "ip_address");
            $address_port = grab_array_var($inargs, "port", 161);
            $portnames = grab_array_var($inargs, "portnames");
            $snmpopts_serial = grab_array_var($inargs, "snmpopts_serial", "");

            if (!empty($snmpopts_serial)) {
                $snmpopts = json_decode(base64_decode($snmpopts_serial), true);
            } else {
                $snmpopts_default = array(
                    "snmpcommunity" => "public",
                    "v3_security_level" => "",
                    "v3_username" => "",
                    "v3_auth_password" => "",
                    "v3_priv_password" => "",
                    "v3_auth_proto" => "md5",
                    "v3_priv_proto" => "des"
                );

                $snmpopts = grab_array_var($inargs, "snmpopts", $snmpopts_default);
                $snmpopts_serial = base64_encode(json_encode($snmpopts));
            }

            $snmpversion = grab_array_var($inargs, "snmpversion", "1");
            $vendor = grab_array_var($inargs, "vendor");
            $scaninterfaces = grab_array_var($inargs, "scaninterfaces");
            $admindown = grab_array_var($inargs, "admindown");
            $bulk_options = grab_array_var($inargs, "bulk_options", '');
            $bulk_fields_serial = grab_array_var($inargs, 'bulk_fields_serial', '');

            if (!empty($bulk_fields_serial)) {
                $bulk_fields = json_decode(base64_decode($bulk_fields_serial), true);
            } else {
                $bulk_fields_default = array(
                    'ip_address'
                );

                $bulk_fields = grab_array_var($inargs, 'bulk_fields', $bulk_fields_default);
                $bulk_fields_serial = base64_encode(json_encode($bulk_fields));
            }

            $warn_speed_in_percent = grab_array_var($inargs, "warn_speed_in_percent", 50);
            $warn_speed_out_percent = grab_array_var($inargs, "warn_speed_out_percent", 50);
            $crit_speed_in_percent = grab_array_var($inargs, "crit_speed_in_percent", 80);
            $crit_speed_out_percent = grab_array_var($inargs, "crit_speed_out_percent", 80);

            // Check for errors
            $errors = 0;
            $errmsg = array();

            if (is_valid_host_name($hostname) == false) {
                $errmsg[$errors++] = _("Invalid host name.");
            }

            // TODO - check rate in/out warning and critical thresholds

            if ($errors > 0) {
                $outargs[CONFIGWIZARD_ERROR_MESSAGES] = $errmsg;
                $result = 1;
            }

            break;

        case CONFIGWIZARD_MODE_GETSTAGE3HTML:

            // get variables that were passed to us
            $address = grab_array_var($inargs, "ip_address");
            $address_port = grab_array_var($inargs, "port", 161);
            $hostname = grab_array_var($inargs, "hostname");
            $interface_reference_style = grab_array_var($inargs, "interface_reference_style", 'nr');
            $vendor = grab_array_var($inargs, "vendor");
            $portnames = grab_array_var($inargs, "portnames");
            $snmpopts_serial = grab_array_var($inargs, "snmpopts_serial", "");
            if ($snmpopts_serial != "") {
                $snmpopts = json_decode(base64_decode($snmpopts_serial), true);
            } else {

                $snmpopts_default = array(
                    "snmpcommunity" => "public",
                    "v3_security_level" => "",
                    "v3_username" => "",
                    "v3_auth_password" => "",
                    "v3_priv_password" => "",
                    "v3_auth_proto" => "md5",
                    "v3_priv_proto" => "des",
                );
                $snmpopts = grab_array_var($inargs, "snmpopts", $snmpopts_default);
                $snmpopts_serial = base64_encode(json_encode($snmpopts));
            }
            $snmpversion = grab_array_var($inargs, "snmpversion", "1");
            $scaninterfaces = grab_array_var($inargs, "scaninterfaces");
            $admindown = grab_array_var($inargs, "admindown");
            $bulk_options = grab_array_var($inargs, "bulk_options", '');
            $bulk_fields_serial = grab_array_var($inargs, 'bulk_fields_serial', '');
            if (!empty($bulk_fields_serial)) {
                $bulk_fields = json_decode(base64_decode($bulk_fields_serial), true);
            }
            else {
                $bulk_fields_default = array(
                    'ip_address', 'hostname'
                );
                $bulk_fields = grab_array_var($inargs, 'bulk_fields', $bulk_fields_default);
                $bulk_fields_serial = base64_encode(json_encode($bulk_fields));
            }
            $warn_speed_in_percent = grab_array_var($inargs, "warn_speed_in_percent", 50);
            $warn_speed_out_percent = grab_array_var($inargs, "warn_speed_out_percent", 50);
            $crit_speed_in_percent = grab_array_var($inargs, "crit_speed_in_percent", 80);
            $crit_speed_out_percent = grab_array_var($inargs, "crit_speed_out_percent", 80);

            $services = grab_array_var($inargs, "services");
            $serviceargs = grab_array_var($inargs, "serviceargs");

            $services_serial = grab_array_var($inargs, "services_serial", base64_encode(json_encode($services)));
            $serviceargs_serial = grab_array_var($inargs, "serviceargs_serial", base64_encode(json_encode($serviceargs)));

            # Handle Meraki API switch data passthrough
            $config_mode = grab_array_var($inargs, "config_mode", "manual");
            $meraki_switches_data = grab_array_var($inargs, "meraki_switches_data", "");
            $debug_enabled = grab_array_var($inargs, "debug_enabled", "");


            $output .= '

        <input type="hidden" name="ip_address" value="' . encode_form_val($address) . '" />
        <input type="hidden" name="port" value="' . encode_form_val($address_port) . '" />
        <input type="hidden" name="hostname" value="' . encode_form_val($hostname) . '" />
        <input type="hidden" name="interface_reference_style" value="' . encode_form_val($interface_reference_style) . '" />
        <input type="hidden" name="snmpopts_serial" value="' . $snmpopts_serial . '" />
        <input type="hidden" name="snmpversion" value="' . encode_form_val($snmpversion) . '" />
        <input type="hidden" name="vendor" value="' . encode_form_val($vendor) . '" />
        <input type="hidden" name="portnames" value="' . encode_form_val($portnames) . '" />
        <input type="hidden" name="scaninterfaces" value="' . encode_form_val($scaninterfaces) . '" />
        <input type="hidden" name="admindown" value="' . encode_form_val($admindown) . '" />
        <input type="hidden" name="bulk_options" value="' . encode_form_val($bulk_options) . '" />
        <input type="hidden" name="bulk_fields_serial" value="' . $bulk_fields_serial . '" />
        <input type="hidden" name="warn_speed_in_percent" value="' . encode_form_val($warn_speed_in_percent) . '" />
        <input type="hidden" name="crit_speed_in_percent" value="' . encode_form_val($crit_speed_in_percent) . '" />
        <input type="hidden" name="warn_speed_out_percent" value="' . encode_form_val($warn_speed_out_percent) . '" />
        <input type="hidden" name="crit_speed_out_percent" value="' . encode_form_val($crit_speed_out_percent) . '" />
        <input type="hidden" name="services_serial" value="' . $services_serial . '" />
        <input type="hidden" name="serviceargs_serial" value="' . $serviceargs_serial . '" />
        <input type="hidden" name="config_mode" value="' . encode_form_val($config_mode) . '" />
        <input type="hidden" name="meraki_switches_data" value="' . encode_form_val($meraki_switches_data) . '" />
';
            break;

        case CONFIGWIZARD_MODE_VALIDATESTAGE3DATA:

            break;

        case CONFIGWIZARD_MODE_GETFINALSTAGEHTML:

            $output = '';
            break;

        case CONFIGWIZARD_MODE_GETOBJECTS:

            // get variables that were passed to us
            $hostname = grab_array_var($inargs, "hostname", "");
            $ip_address = grab_array_var($inargs, "ip_address", "");
            $address_port = grab_array_var($inargs, "port", 161);
            $interface_reference_style = grab_array_var($inargs, "interface_reference_style", 'nr');

            $snmpopts_serial = grab_array_var($inargs, "snmpopts_serial", "");
            if ($snmpopts_serial != "") {
                $snmpopts = json_decode(base64_decode($snmpopts_serial), true);
            } else {

                $snmpopts_default = array(
                    "snmpcommunity" => "public",
                    "v3_security_level" => "",
                    "v3_username" => "",
                    "v3_auth_password" => "",
                    "v3_priv_password" => "",
                    "v3_auth_proto" => "md5",
                    "v3_priv_proto" => "des",
                );
                $snmpopts = grab_array_var($inargs, "snmpopts", $snmpopts_default);
                $snmpopts_serial = base64_encode(json_encode($snmpopts));
            }

            $snmpversion = grab_array_var($inargs, "snmpversion", "1");
            $portnames = grab_array_var($inargs, "portnames");
            $scaninterfaces = grab_array_var($inargs, "scaninterfaces");
            $admindown = grab_array_var($inargs, "admindown");
            $bulk_options = trim(grab_array_var($inargs, "bulk_options", ''));
            $bulk_fields_serial = grab_array_var($inargs, 'bulk_fields_serial', '');
            if (!empty($bulk_fields_serial)) {
                $bulk_fields = json_decode(base64_decode($bulk_fields_serial), true);
            }
            else {
                $bulk_fields_default = array(
                    'ip_address', 'hostname'
                );
                $bulk_fields = grab_array_var($inargs, 'bulk_fields', $bulk_fields_default);
                $bulk_fields_serial = base64_encode(json_encode($bulk_fields));
            }
            $vendor = grab_array_var($inargs, "vendor");

            $services_serial = grab_array_var($inargs, "services_serial", "");
            $serviceargs_serial = grab_array_var($inargs, "serviceargs_serial", "");

            $services = json_decode(base64_decode($services_serial), true);
            $serviceargs = json_decode(base64_decode($serviceargs_serial), true);
            if (empty($serviceargs)) {
                $serviceargs = array();
            }


            if ($snmpversion === "3") {
                $bulk_fields_count = 8;
            }
            else {
                $bulk_fields_count = 3;
            }
            $bulk_fields = array_slice($bulk_fields, 0, $bulk_fields_count);
            $bulk_fields = array_filter($bulk_fields);
            $bulk_fields = implode(",", $bulk_fields);
            $bulk_options = $bulk_fields . "\n" . $bulk_options;

            // save data for later use in re-entrance
            $meta_arr = array();
            $meta_arr["hostname"] = $hostname;
            $meta_arr["ip_address"] = $ip_address;
            $meta_arr["snmpopts_serial"] = $snmpopts_serial;
            $meta_arr["snmpversion"] = $snmpversion;
            $meta_arr["portnames"] = $portnames;
            $meta_arr["scaninterfaces"] = $scaninterfaces;
            $meta_arr["admindown"] = $admindown;
            $meta_arr["vendor"] = $vendor;
            $meta_arr["services"] = $services;
            $meta_arr["serviceargs"] = $serviceargs;

            //save_configwizard_object_meta($wizard_name, $hostname, "", $meta_arr);

            $original_values = array();
            foreach ($snmpopts as $k => $v) {
                $original_values[$k] = $v;
            }

            $objs = array();

            # Handle Meraki switch processing
            $config_mode = grab_array_var($inargs, "config_mode", "manual");
            $meraki_switches_data = grab_array_var($inargs, "meraki_switches_data", "");
            
            if ($config_mode === 'meraki' && !empty($meraki_switches_data)) {
                # Process Meraki switches directly
                $decoded_switches = json_decode($meraki_switches_data, true);
                if (is_array($decoded_switches) && count($decoded_switches) > 0) {
                    $bulk_array = array();
                    foreach ($decoded_switches as $i => $switch) {
                        if (!empty($switch['lanIp'])) {
                            # Create switch entry for bulk processing
                            $switch_entry = array(
                                'ip_address' => $switch['lanIp'],
                                'hostname' => !empty($switch['name']) ? preg_replace('/[^a-zA-Z0-9\-_\.]/', '-', $switch['name']) : $switch['lanIp'],
                                'snmpopts_serial' => $snmpopts_serial,
                                'snmpversion' => isset($switch['snmpversion']) ? $switch['snmpversion'] : $snmpversion,
                                'portnames' => $portnames,
                                'scaninterfaces' => $scaninterfaces,
                                'admindown' => $admindown,
                                'vendor' => $vendor,
                                'services' => $services,
                                'serviceargs' => $serviceargs
                            );
                            
                            # Add switch-specific SNMP options
                            if (isset($switch['snmpopts']) && is_array($switch['snmpopts'])) {
                                foreach ($switch['snmpopts'] as $key => $value) {
                                    $switch_entry[$key] = $value;
                                }
                                # Update serialized SNMP options for this switch
                                $switch_snmpopts = array_merge($snmpopts, $switch['snmpopts']);
                                $switch_entry['snmpopts_serial'] = base64_encode(json_encode($switch_snmpopts));
                            }
                            
                            $bulk_array[] = $switch_entry;
                        }
                    }
                    $count = count($bulk_array);
                } else {
                    # Fallback to single switch if no Meraki data
                    $bulk_array = array($meta_arr);
                    $count = 1;
                }
            } else {
                # Manual mode - use existing bulk CSV processing
                $bulk_array = json_decode(mer_real_str_getcsv($bulk_options), true);
                $count = count($bulk_array);
                $bulk_array[$count] = $meta_arr;
                $count++;
            }


            # For Meraki mode, each switch has its own MRTG file, for manual mode use the original IP
            if ($config_mode === 'meraki') {
                # Process each switch with its own MRTG configuration
                $original_values['ip_address'] = '';
                $original_values['hostname'] = '';
            } else {
                # Manual mode - single switch
                m_switch_configwizard_add_cfg_to_mrtg($ip_address);
                $original_cfg_text = file_get_contents("/etc/mrtg/conf.d/{$ip_address}.cfg");
                $original_values['ip_address'] = $ip_address;
                $original_values['hostname'] = $hostname;
            }

            for ($i = 0; $i < $count; $i++) {

                // Every new m_switch must have a unique hostname and address.
                if (empty($bulk_array[$i]['ip_address'])) {
                        continue;
                }
                

                //////////////////////
                // Replace serviceargs according to bulk arguments
                //////////////////////
                if (empty($bulk_array[$i]['hostname'])) {
                    $bulk_array[$i]['hostname'] = gethostbyaddr($bulk_array[$i]['ip_address']);
                }
                if (empty($bulk_array[$i]['hostname'])) {
                    // If gethostbyaddr failed, it's supposed to give us the IP address unmodified.
                    // We're just keeping it honest :)
                    $bulk_array[$i]['hostname'] = $bulk_array[$i]['ip_address'];
                }

                $bulk_array[$i]['ip_address'] = trim($bulk_array[$i]['ip_address']);
                $bulk_array[$i]['hostname'] = trim($bulk_array[$i]['hostname']);
                
                # For Meraki mode, set up MRTG for each switch individually
                if ($config_mode === 'meraki') {
                    m_switch_configwizard_add_cfg_to_mrtg($bulk_array[$i]['ip_address']);
                    $original_cfg_text = file_get_contents("/etc/mrtg/conf.d/{$bulk_array[$i]['ip_address']}.cfg");
                    
                    # Update original values for this specific switch
                    if ($i == 0) {
                        # For first switch, set up base original values
                        $original_values['ip_address'] = $bulk_array[$i]['ip_address'];
                        $original_values['hostname'] = $bulk_array[$i]['hostname'];
                        
                        # Set SNMP options from switch data
                        if (isset($bulk_array[$i]['snmpopts_serial'])) {
                            $switch_snmpopts = json_decode(base64_decode($bulk_array[$i]['snmpopts_serial']), true);
                            if (is_array($switch_snmpopts)) {
                                foreach ($switch_snmpopts as $k => $v) {
                                    $original_values[$k] = $v;
                                }
                            }
                        }
                    }
                }

                // $original_values is limited to snmp options + ip_address, hostname
                foreach ($original_values as $k => $v) {
                    $serviceargs[$k] = (empty($bulk_array[$i][$k]) ? $v : $bulk_array[$i][$k]);
                }
                
                # For Meraki mode, ensure proper SNMP options are set for this specific switch
                if ($config_mode === 'meraki' && isset($bulk_array[$i]['snmpopts_serial'])) {
                    $switch_snmpopts = json_decode(base64_decode($bulk_array[$i]['snmpopts_serial']), true);
                    if (is_array($switch_snmpopts)) {
                        foreach ($switch_snmpopts as $k => $v) {
                            $serviceargs[$k] = $v;
                        }
                    }
                    # Set snmpversion for this switch
                    if (isset($bulk_array[$i]['snmpversion'])) {
                        $serviceargs['snmpversion'] = $bulk_array[$i]['snmpversion'];
                    }
                }

                //////////////////////
                // Create Host config
                //////////////////////
                if (!host_exists($serviceargs['hostname'])) {
                    $objs[] = array(
                        "type" => OBJECTTYPE_HOST,
                        "use" => "xiwizard_switch_host",
                        "host_name" => $serviceargs['hostname'],
                        "address" => $serviceargs['ip_address'],
                        "icon_image" => "m_switch.png",
                        "statusmap_image" => "m_switch.png",
                        "_xiwizard" => $wizard_name,
                    );
                } else {
                }

                //////////////////////
                // Create MRTG configuration file
                //////////////////////

                // Generate the cfg file for this address & write it out.
                $search_array = array(
                    $original_values['ip_address'],
                );
                $replace_array = array(
                    $serviceargs['ip_address']
                );

                if ($snmpversion != 3) {
                    $search_array[] = $original_values['snmpcommunity'];
                    $replace_array[] = $serviceargs['snmpcommunity'];
                }
                else {
                    $search_array[] = "username=>'".$original_values['v3_username']."'";
                    $replace_array[] = "username=>'".$serviceargs['v3_username']."'";

                    if (strpos(strtolower($snmpopts['v3_security_level']), 'noauth') === false) {
                        $search_array[] = strtolower("authprotocol=>'".$original_values['v3_auth_proto']."'");
                        $replace_array[] = strtolower("authprotocol=>'".$serviceargs['v3_auth_proto']."'");
                        $search_array[] = "authpassword=>'".$original_values['v3_auth_password']."'";
                        $replace_array[] = "authpassword=>'".$serviceargs['v3_auth_password']."'";
                    }
                    if (strpos(strtolower($snmpopts['v3_security_level']), 'nopriv') === false) {
                        $search_array[] = strtolower("privprotocol=>'".$original_values['v3_priv_proto']."'");
                        $replace_array[] = strtolower("privprotocol=>'".$serviceargs['v3_priv_proto']."'");
                        $search_array[] = "privpassword=>'".$original_values['v3_priv_password']."'";
                        $replace_array[] = "privpassword=>'".$serviceargs['v3_priv_password']."'";
                    }
                }
                $new_cfg_text = str_replace($search_array, $replace_array, $original_cfg_text);
                file_put_contents("/etc/mrtg/conf.d/{$serviceargs['ip_address']}.cfg", $new_cfg_text);

                // Create a reference for the MRTG file we just created.
                $objs[] = array(
                    "type" => OBJECTTYPE_MRTG_FILE,
                    "file_name" => "{$serviceargs['ip_address']}.cfg",
                    "host_name" => $serviceargs['hostname'],
                );


                //////////////////////
                // Create Services
                //////////////////////

                $have_bandwidth = false;

                // see which services we should monitor
                foreach ($services as $svc => $svcstate) {


                    switch ($svc) {

                        case "ping":

                            $objs[] = array(
                                "type" => OBJECTTYPE_SERVICE,
                                "host_name" => $serviceargs['hostname'],
                                "service_description" => "Ping",
                                "use" => "xiwizard_switch_ping_service",
                                "_xiwizard" => $wizard_name,
                            );
                            break;

                        case "port":


                            // set various snmpopts to escape bad characters ; and !
                            $escape_these = array(
                                'v3_auth_proto',
                                'v3_auth_password',
                                'v3_priv_proto',
                                'v3_priv_password',
                                'v3_security_level',
                                'v3_username'
                            );

                            foreach ($escape_these as $key) {
                                if (!empty($serviceargs[$key])) {
                                    $serviceargs[$key] = str_replace('!', '\!', $serviceargs[$key]);
                                    $serviceargs[$key] = escapeshellarg($serviceargs[$key]);
                                }
                            }

                            foreach ($svcstate as $port_id => $portstate) {
                                
                                // For multi-switch mode, extract switch IP and original port ID
                                $port_switch_ip = '';
                                $original_port_id = $port_id;
                                if (isset($serviceargs['switch_ip']) && isset($serviceargs['switch_ip'][$port_id])) {
                                    $port_switch_ip = $serviceargs['switch_ip'][$port_id];
                                    if (isset($serviceargs['original_port_id']) && isset($serviceargs['original_port_id'][$port_id])) {
                                        $original_port_id = $serviceargs['original_port_id'][$port_id];
                                    }
                                }

                                $port_name = $original_port_id;
                                if (is_numeric($original_port_id)) {
                                    $port_name = _("Port ") . $port_name;
                                }
                                
                                $badchars = explode(" ", "` ~ ! $ % ^ & * | ' \" < > ? , ( ) = \\ { } [ ]");
                                if (array_key_exists("portname", $serviceargs)) {
                                    if (array_key_exists($port_id, $serviceargs["portname"])) {

                                        $port_name = $serviceargs["portname"][$port_id];
                                        $port_name = str_replace($badchars, " ", $port_name);
                                    }
                                }

                                // monitor bandwidth
                                if (array_key_exists("bandwidth", $serviceargs) && array_key_exists($port_id, $serviceargs["bandwidth"])) {

                                    $have_bandwidth = true;

                                    $warn_pair = $serviceargs["bandwidth_warning_input_value"][$port_id] . "," . $serviceargs["bandwidth_warning_output_value"][$port_id];
                                    $crit_pair = $serviceargs["bandwidth_critical_input_value"][$port_id] . "," . $serviceargs["bandwidth_critical_output_value"][$port_id];

                                    switch ($serviceargs["bandwidth_speed_label"][$port_id]) {

                                        case "Gbps":
                                            $label = "G";
                                            break;

                                        case "Mbps":
                                            $label = "M";
                                            break;

                                        case "Kbps":
                                            $label = "K";
                                            break;

                                        default:
                                            $label = "B";
                                            break;
                                    }

                                    // Use switch IP for RRD file path in multi-switch mode
                                    $rrd_switch_ip = !empty($port_switch_ip) ? $port_switch_ip : $serviceargs['ip_address'];
                                    $rrd_port_id = !empty($original_port_id) ? $original_port_id : $port_id;
                                    
                                    $objs[] = array(
                                        "type" => OBJECTTYPE_SERVICE,
                                        "host_name" => $serviceargs['hostname'],
                                        "service_description" => $port_name . " Bandwidth",
                                        "use" => "xiwizard_switch_port_bandwidth_service",
                                        "check_command" => "check_xi_service_mrtgtraf!" . strtolower($rrd_switch_ip) . "_" . strtolower($rrd_port_id) . ".rrd!" . $warn_pair . "!" . $crit_pair . "!" . $label,
                                        "_xiwizard" => $wizard_name,
                                    );
                                }

                                // monitor port status
                                if (array_key_exists("portstatus", $serviceargs) && array_key_exists($port_id, $serviceargs["portstatus"])) {


                                    $raw_port_id = $serviceargs['raw_port_id'][$port_id];
                                    
                                    // Use correct switch IP for port status monitoring
                                    $status_switch_ip = !empty($port_switch_ip) ? $port_switch_ip : $serviceargs['ip_address'];

                                    if ($snmpversion != 3) {

                                        if ($interface_reference_style == 'eth') {
                                            $raw_port_id = mr_hyphen_ethernet_to_hex($raw_port_id);
                                        }
                                        $objs[] = array(
                                            "type" => OBJECTTYPE_SERVICE,
                                            "host_name" => $serviceargs['hostname'],
                                            "service_description" => $port_name . " Status",
                                            "use" => "xiwizard_switch_port_status_service",
                                            "check_command" => "check_xi_service_ifoperstatus_$interface_reference_style!" . $serviceargs['snmpcommunity'] . "!" . $raw_port_id . "!-v " . intval($snmpversion) . " -p " . $address_port . " -H " . $status_switch_ip,
                                            "_xiwizard" => $wizard_name,
                                        );

                                    } else {

                                        // If privilege password
                                        $priv_password_and_proto = "";
                                        if (!empty($serviceargs['v3_priv_password'])) {
                                            $priv_password_and_proto = "-x {$serviceargs['v3_priv_proto']}";
                                            $priv_password_and_proto .= " -X {$serviceargs['v3_priv_password']}";
                                        }

                                        $snmppass = '';
                                        if (!empty($serviceargs['v3_auth_password'])) {
                                            $snmppass = "-A {$serviceargs['v3_auth_password']}";
                                        }

                                        $snmpauthproto = '';
                                        if (!empty($serviceargs['v3_auth_proto'])) {
                                            $snmpauthproto = "-a {$serviceargs['v3_auth_proto']}";
                                        }

                                        $snmpuser = "-u {$serviceargs['v3_username']}";

                                        $objs[] = array(
                                            "type" => OBJECTTYPE_SERVICE,
                                            "host_name" => $serviceargs['hostname'],
                                            "service_description" => $port_name . " Status",
                                            "use" => "xiwizard_switch_port_status_service",
                                            "check_command" => "check_xi_service_ifoperstatusnag_$interface_reference_style!{$raw_port_id}!-v{$snmpversion} {$snmpuser} {$snmppass} {$priv_password_and_proto} {$snmpauthproto} -l {$serviceargs['v3_security_level']} -H {$status_switch_ip}!{$address_port}",
                                            "_xiwizard" => $wizard_name,
                                        );
                                    }
                                }
                            }
                            break;

                        default:
                            break;
                    }
                }

            }

            //echo "OBJECTS:<BR>";
            //print_r($objs);
            //exit();


            // Set permissions on rrd files so nagios can read them!
            m_switch_configwizard_update_mrtg();

            // return the object definitions to the wizard
            $outargs[CONFIGWIZARD_NAGIOS_OBJECTS] = $objs;
            break;

        default:
            break;
    }

    return $output;
}


/**
 *
 * Return a standardardized location for walk/cfgmaker files
 *
 * @param $address string the address to use to build the cfgmaker file location
 *
 * @return string - absolute path to file for cfgmaker walk
 *
 */
function m_switch_configwizard_get_walk_file($address) {
    $address = str_replace(array('/', '..'), '', $address);
    return get_tmp_dir() . "/mrtg-{$address}";
}


 /**
*
* Read a cfgmaker file to determine port information
*
* @param $address string - the address used to locate the cfgmaker file
*
* @return array - port information scanned from file
*/
function m_switch_configwizard_read_walk_file($address, $interface_reference_style='nr')
{
    $cfgmaker_file = m_switch_configwizard_get_walk_file($address);


    $output = array();

    // open the walk file for reading
    $cfgmaker_file_handle = fopen($cfgmaker_file, 'r');
    if ($cfgmaker_file_handle) {

        $port_id = false;
        $description = false;
        $ifname = false;
        $iftype = false;
        $alias = false;

        while (!feof($cfgmaker_file_handle)) {

            $buf = fgets($cfgmaker_file_handle);

            // skip comments
            $pos = strpos($buf, '#');
            if ($pos === 0)
                continue;

            // we only care about lines that contain a few keywords, so lets check for those first
            // and if we're sure our lines don't contain them, skip so as not to perform a lot of unecessary regex -bh

            if (strpos($buf, 'Target[') !== false) {

                // found the target line (contains port number)
                if (preg_match('/Target\[' . $address . '_([^\]]+)\]/', $buf, $matches)) {

                    $port_id = $matches[1];
                    $human_identifier = $port_id;
                    if (is_numeric($human_identifier)) {
                        $human_identifier = _('Port ') . $human_identifier;
                    }

                    if (!array_key_exists($port_id, $output)) {
                        $output[$port_id] = array(
                            'max_bytes'     => 0,
                            'name'          => $human_identifier,
                            'description'   => $human_identifier,
                            'alias'         => $human_identifier,
                            'type'          => $human_identifier,
                        );
                    }

                    continue;
                }
            }

            if (strpos($buf, 'MaxBytes[') !== false) {

                // we have the port speed
                if (preg_match('/MaxBytes\[' . $address . '_([^\]]+)\]: ([0-9\.]+)/', $buf, $matches)) {

                    $port_id = $matches[1];
                    $max_bytes = $matches[2];
                    $human_identifier = $port_id;
                    if (is_numeric($human_identifier)) {
                        
                        $human_identifier = _('Port ') . $human_identifier;
                    }

                    if (!array_key_exists($port_id, $output)) {

                        $output[$port_id] = array(
                            'max_bytes'     => $max_bytes,
                            'name'          => $human_identifier,
                            'description'   => $human_identifier,
                            'alias'         => $human_identifier,
                            'type'          => $human_identifier,
                        );

                    } else {
                        $output[$port_id]['max_bytes'] = $max_bytes;
                    }

                    continue;
                }
            }

            // We need the lines directly after <td>Description:</td> and <td>ifName:</td> and <td>Alias:</td>
            if (strpos($buf, '<td>Description:</td>') !== false) {
                $description = true;
                continue;
            }

            if ($description) {
                $description = str_replace(array('<td>', '</td>'), '', $buf);
                $description = trim($description);
                $output[$port_id]['description'] = html_entity_decode($description, ENT_QUOTES);

                $description = false;
                continue;
            }

            if (strpos($buf, '<td>ifName:</td>') !== false) {
                $ifname = true;
                continue;
            }

            if ($ifname) {
                $ifname = str_replace(array('<td>', '</td>'), '', $buf);
                $ifname = trim($ifname);
                $output[$port_id]['name'] = html_entity_decode($ifname, ENT_QUOTES);

                $ifname = false;
                continue;
            }

            if (strpos($buf, '<td>ifType:</td>') !== false) {
                $iftype = true;
                continue;
            }

            if ($iftype) {
                $iftype = str_replace(array('<td>', '</td>'), '', $buf);
                $iftype = trim($iftype);
                $output[$port_id]['type'] = html_entity_decode($iftype, ENT_QUOTES);

                $iftype = false;
                continue;
            }

            if (strpos($buf, '<td>Alias:</td>') !== false) {
                $alias = true;
                continue;
            }

            if ($alias) {
                $alias = str_replace(array('<td>', '</td>'), '', $buf);
                $alias = trim($alias);
                if ( $alias !== '' ) {
                    // If the Alias data is not empty, use it.
                    $output[$port_id]['alias'] = html_entity_decode($alias, ENT_QUOTES);
                }
                else {
                    // If the Alias data is  empty, replace it with the Description.
                    // This is to ensure if an empty ifAlias field is in the device, that we will use valid data in the wizard.
                    $output[$port_id]['alias'] = $output[$port_id]['description'];
                }

                $alias = false;
                continue;
            }
        }
        //end IF FILE is open

        fclose($cfgmaker_file_handle);
    }

    foreach ($output as $port_id => $port_info) {
        $raw_port_id = $port_id;
        switch ($interface_reference_style) {
            case 'type':
                $raw_port_id = $output[$port_id]['type'];
                break;
            case 'name':
                $raw_port_id = $output[$port_id]['name'];
                break;
            case 'descr':
                $raw_port_id = $output[$port_id]['description'];
                break;
            default: /* do nothing */ break;
        }

        $output[$port_id]['raw_port_id'] = $raw_port_id;
    }

    return $output;
}


/**
*
* Add a specific cfgmaker file to mrtg config
* NOTE: we use copy+exec(sed) because it's a bit faster than all of the calls to fopen/fwrite/etc.
*       this is a pretty big deal on larger systems!
*
* @param $address string - address to search in
*
* @return bool
*/
function m_switch_configwizard_add_cfg_to_mrtg($address)
{
    // get the data that we need
    $mrtg_confd_dir = "/etc/mrtg/conf.d";
    $mrtg_cfg_file = "{$address}.cfg";
    $absolute_mrtg_cfg_file = "{$mrtg_confd_dir}/{$mrtg_cfg_file}";
    $cfgmaker_file = m_switch_configwizard_get_walk_file($address);

    // check if the file already exists for useful debugging
    $mrtg_confd_contents = scandir($mrtg_confd_dir);
    if (in_array($mrtg_cfg_file, $mrtg_confd_contents)) {
    } else {
    }

    // copy the cfgmaker file to the mrtg cfg destination
    if (!copy($cfgmaker_file, $absolute_mrtg_cfg_file)) {
        return false;
    }

    $infoline = "#### ADDED BY NAGIOSXI (User: ". get_user_attr(0, 'username') .", DATE: ". get_datetime_string(time()) .") ####";

    // Insert a new header line.
    exec("sed -i '1 i {$infoline}' ".escapeshellarg($absolute_mrtg_cfg_file));

    return true;
}


/**
*
* Updates the mrtg config to stop nagios from alerting that rrd files don't exist
*
*/
function m_switch_configwizard_update_mrtg() {

    if (@is_executable("/usr/bin/mrtg")) {
        // For whatever reason this won't return immediately without the redirectors
        $cmd = "((env LANG=C /usr/bin/mrtg /etc/mrtg/mrtg.cfg --group=nagios --lock-file /var/lib/mrtg/wizard.lock  || true) ";
        $cmd .= "&& chmod 664 /var/lib/mrtg/*.rrd ";
        $cmd .= "&& chgrp nagios /var/lib/mrtg/*.rrd) >/dev/null 2>&1 &";
        exec($cmd, $out);
    }
}


/**
 *
 * Append some human readable information to maxbytes/portspeed
 *
 * @param $max_bytes
 * @param $speed
 * @param $label
 *
 * @return string
 */
function m_switch_configwizard_get_readable_port_line_speed($max_bytes, &$speed, &$label)
{
    $bps = $max_bytes * 8;

    $kbps = $bps / 1e3;
    $mbps = $bps / 1e6;
    $gbps = $bps / 1e9;

    if ($gbps >= 1) {

        $speed = $gbps;
        $label = "Gbps";

    } else if ($mbps >= 1) {

        $speed = $mbps;
        $label = "Mbps";

    } else if ($kbps >= 1) {

        $speed = $kbps;
        $label = "Kbps";

    } else {

        $speed = $bps . " bps";
        $label = "bps";
    }

    $output = number_format($speed, 2) . " " . $label;
    return $output;
}


/**
 *
 * Recalculate warn/crit in/out speeds based on label/readability
 *
 * @param $warn_in_speed
 * @param $warn_out_speed
 * @param $crit_in_speed
 * @param $crit_out_speed
 * @param $label
 */
function m_switch_configwizard_recalculate_speeds(&$warn_in_speed, &$warn_out_speed, &$crit_in_speed, &$crit_out_speed, &$label)
{

    while (1) {

        if ($label == "bps")
            break;

        $maxval = max($warn_in_speed, $warn_out_speed, $crit_in_speed, $crit_out_speed);

        if ($maxval < 1) {

            switch ($label) {

                case "Gbps":
                    $label = "Mbps";
                    break;

                case "Mbps":
                    $label = "Kbps";
                    break;

                case "Kbps":
                    $label = "bps";
                    break;

                default:
                    break;
            }

            // bump down a level
            $warn_in_speed  *= 1000;
            $warn_out_speed *= 1000;
            $crit_in_speed  *= 1000;
            $crit_out_speed *= 1000;

        } else {
            break;
        }
    }
}


/**
 *
 * Get a sane size value to display in a textbox
 *
 * @param $rate - the number to format (in a sane way)
 * @param $start_decimals, optional, default 2 - how many decimal places to try for (via number_format)
 * @param $max_chars, optional, default 5 - the maximum amount of characters to allow after formatting
 *
 */
function m_switch_configwizard_get_sane_display_rate($rate, $start_decimals = 2, $max_chars = 5)
{
    $display = (string) number_format($rate, $start_decimals);

    while (strlen($display) > $max_chars) {

        $display = substr($display, 0, -1);
    }

    // get rid of a trailing period no matter what
    if (substr($display, -1) === '.')
        $display = substr($display, 0, -1);

    $display = str_replace(',', '', $display);

    return $display;
}

// Retrieved from https://www.php.net/manual/en/function.str-getcsv.php
// and modified to work on strings instead of files.
// Returns an array-of-arrays or the serialized equivalent.
function mer_real_str_getcsv($strCsv, $serialize = true) {
    $csv = explode("\n", $strCsv);
    $acc = array();

    $csv = array_map('str_getcsv', $csv);

    array_walk($csv, function(&$a) use ($csv) {
        $a = array_combine($csv[0], $a);
    });

    array_shift($csv); # remove column header

    return ($serialize ? json_encode($csv) : $csv);
}

function mr_hyphen_ethernet_to_hex($ethernet_address) {
    $ethernet_address = str_replace('-', '', $ethernet_address);
    return "0x$ethernet_address";
}
/**
 *
 * Debug logging - dummy function override for xiver < 5.3.0
 *
 */
if (!function_exists('debug')) {
    function debug() { }
}

