<?php

/*
 * LMS version 1.11-git
 *
 *  (C) Copyright 2001-2022 LMS Developers
 *
 *  Please, see the doc/AUTHORS for more information about authors!
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License Version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
 *  USA.
 *
 *  $Id$
 */

$id = intval($_GET['id']);

if ($api) {
    if (!$LMS->NetDevExists($id)) {
        $SESSION->close();
        die;
    }

    if (!isset($_POST['in'])) {
        die;
    }
    $netdev = json_decode(base64_decode($_POST['in']), true);
} else {
    if (!$LMS->NetDevExists($id)) {
        $SESSION->redirect('?m=netdevlist');
    }

    if (isset($_POST['netdev'])) {
        $netdev = $_POST['netdev'];
    }
}

$action = !empty($_GET['action']) ? $_GET['action'] : '';
$edit = '';
$subtitle = '';

$netdevipActions = array_flip([
    'addip',
    'formaddip',
    'editip',
    'formeditip',
]);
if (!empty($action) && isset($netdevipActions[$action])) {
    $netdev_empty_mac = ConfigHelper::getConfig('netdevices.empty_mac', '', true);

    if (strlen($netdev_empty_mac)) {
        if (check_mac($netdev_empty_mac)) {
            $netdev_empty_mac = Utils::normalizeMac($netdev_empty_mac);
        } else {
            $netdev_empty_mac = '';
        }
    }

    $SMARTY->assign('netdev_empty_mac', $netdev_empty_mac);
}

switch ($action) {
    case 'updatenodefield':
        $LMS->updateNodeField($_POST['nodeid'], $_POST['field'], $_POST['val']);
        die();
    break;

    case 'replace':
        $dev1 = $LMS->GetNetDev($_GET['id']);
        $dev2 = $LMS->GetNetDev($_GET['netdev']);

        if ($dev1['ports'] < $dev2['takenports']) {
            $error['replace'] = trans('It scans for ports in source device!');
        } elseif ($dev2['ports'] < $dev1['takenports']) {
            $error['replace'] = trans('It scans for ports in destination device!');
        }

        if (!$error) {
            $links1 = $DB->GetAll('(SELECT type, 
				(CASE src WHEN ? THEN dst ELSE src END) AS id,
				speed, technology,
				(CASE src WHEN ? THEN srcport ELSE dstport END) AS srcport,
				(CASE src WHEN ? THEN dstport ELSE srcport END) AS dstport,
				(CASE src WHEN ? THEN srcradiosector ELSE dstradiosector END) AS srcradiosector,
				(CASE src WHEN ? THEN dstradiosector ELSE srcradiosector END) AS dstradiosector
				FROM netlinks WHERE src = ? OR dst = ?)
			UNION
				(SELECT linktype AS type,
					id,
					linkspeed AS speed, linktechnology AS technology,
					port AS srcport,
					NULL AS dstport,
					linkradiosector AS srcradiosector,
					NULL AS dstradiosector
				FROM nodes WHERE netdev = ? AND ownerid IS NOT NULL)
			ORDER BY srcport', array($dev1['id'], $dev1['id'], $dev1['id'], $dev1['id'], $dev1['id'],
                    $dev1['id'], $dev1['id'], $dev1['id']));

            $links2 = $DB->GetAll('(SELECT type,
				(CASE src WHEN ? THEN dst ELSE src END) AS id,
				speed, technology,
				(CASE src WHEN ? THEN srcport ELSE dstport END) AS srcport,
				(CASE src WHEN ? THEN dstport ELSE srcport END) AS dstport,
				(CASE src WHEN ? THEN srcradiosector ELSE dstradiosector END) AS srcradiosector,
				(CASE src WHEN ? THEN dstradiosector ELSE srcradiosector END) AS dstradiosector
				FROM netlinks WHERE src = ? OR dst = ?)
			UNION
				(SELECT linktype AS type,
					id,
					linkspeed AS speed, linktechnology AS technology,
					port AS srcport,
					NULL AS dstport,
					linkradiosector AS srcradiosector,
					NULL AS dstradiosector
				FROM nodes WHERE netdev = ? AND ownerid IS NOT NULL)
			ORDER BY srcport', array($dev2['id'], $dev2['id'], $dev2['id'], $dev2['id'], $dev2['id'],
                    $dev2['id'], $dev2['id'], $dev2['id']));

            $DB->BeginTrans();

            $DB->Execute('UPDATE netdevices SET netnodeid = ?, ownerid = ?, address_id = ?, latitude = ?, longitude = ?
				WHERE id = ?', array($dev1['netnodeid'], $dev1['ownerid'], $dev1['address_id'], $dev1['latitude'], $dev1['longitude'], $dev2['id']));
            $DB->Execute('UPDATE netdevices SET netnodeid = ?, ownerid = ?, address_id = ?, latitude = ?, longitude = ?
				WHERE id = ?', array($dev2['netnodeid'], $dev2['ownerid'], $dev2['address_id'], $dev2['latitude'], $dev2['longitude'], $dev1['id']));

            if ($SYSLOG) {
                $args = array(
                    SYSLOG::RES_NETDEV => $dev2['id'],
                    'location' => $dev1['location'],
                    'latitude' => $dev1['latitude'],
                    'longitude' => $dev1['longitude'],
                );
                $SYSLOG->AddMessage(SYSLOG::RES_NETDEV, SYSLOG::OPER_UPDATE, $args);
                $args = array(
                    SYSLOG::RES_NETDEV => $dev1['id'],
                    'location' => $dev2['location'],
                    'latitude' => $dev2['latitude'],
                    'longitude' => $dev2['longitude'],
                );
                $SYSLOG->AddMessage(SYSLOG::RES_NETDEV, SYSLOG::OPER_UPDATE, $args);
            }

            $LMS->NetDevDelLinks($dev1['id']);
            $LMS->NetDevDelLinks($dev2['id']);

            $ports = array();
            // przypisujemy urzadzenia/komputer, probujac zachowac numeracje portow
            if ($links1) {
                foreach ($links1 as $row) {
                    $sport = $row['srcport'];
                    if ($sport) {
                        if ($sport > $dev2['ports']) {
                            for ($i = 1; $i <= $dev2['ports']; $i++) {
                                if (!isset($ports[$sport])) {
                                    $sport = $i;
                                    break;
                                }
                            }
                        }

                        $ports[$sport] = $sport;
                    }

                    if (isset($row['dstport'])) { // device
                        $LMS->NetDevLink($dev2['id'], $row['id'], array(
                            'type' => $row['type'],
                            'srcradiosector' => $row['srcradiosector'],
                            'dstradiosector' => $row['dstradiosector'],
                            'technology' => $row['technology'],
                            'speed' => $row['speed'],
                            'srcport' => $sport,
                            'dstport' => $row['dstport'],
                        ));
                    } else { // node
                        $LMS->NetDevLinkNode($row['id'], $dev2['id'], array(
                            'type' => $row['type'],
                            'radiosector' => $row['srcradiosector'],
                            'technology' => $row['technology'],
                            'speed' => $row['speed'],
                            'port' => $sport,
                        ));
                    }
                }
            }

            $ports = array();
            if ($links2) {
                foreach ($links2 as $row) {
                    $sport = $row['srcport'];
                    if ($sport) {
                        if ($sport > $dev1['ports']) {
                            for ($i = 1; $i <= $dev1['ports']; $i++) {
                                if (!isset($ports[$sport])) {
                                    $sport = $i;
                                    break;
                                }
                            }
                        }

                        $ports[$sport] = $sport;
                    }

                    if (isset($row['dstport'])) { // device
                        $LMS->NetDevLink($dev1['id'], $row['id'], array(
                            'type' => $row['type'],
                            'srcradiosector' => $row['srcradiosector'],
                            'dstradiosector' => $row['dstradiosector'],
                            'technology' => $row['technology'],
                            'speed' => $row['speed'],
                            'srcport' => $sport,
                            'dstport' => $row['dstport']
                        ));
                    } else { // node
                        $LMS->NetDevLinkNode($row['id'], $dev1['id'], array(
                            'type' => $row['type'],
                            'radiosector' => $row['srcradiosector'],
                            'technology' => $row['technology'],
                            'speed' => $row['speed'],
                            'port' => $sport,
                        ));
                    }
                }
            }

            $DB->CommitTrans();

            $SESSION->redirect('?m=netdevinfo&id=' . $_GET['id']);
        }

        break;

    case 'disconnect':
        $LMS->NetDevUnLink($_GET['id'], $_GET['devid']);
        $SESSION->redirect('?m=netdevinfo&id=' . $_GET['id']);
        break;
    case 'disconnectnode':
        $LMS->NetDevLinkNode($_GET['nodeid'], 0);
        $SESSION->redirect('?m=netdevinfo&id=' . $_GET['id']);
        break;
    case 'connect':
        $linktype = !empty($_GET['linktype']) && ctype_digit($_GET['linktype']) ? intval($_GET['linktype']) : null;
        $srcradiosector = $linktype == LINKTYPE_WIRELESS && ctype_digit($_GET['srcradiosector']) ? intval($_GET['srcradiosector']) : null;
        $dstradiosector = $linktype == LINKTYPE_WIRELESS && ctype_digit($_GET['dstradiosector']) ? intval($_GET['dstradiosector']) : null;
        $linktechnology = !empty($_GET['linktechnology']) && ctype_digit($_GET['linktechnology']) ? intval($_GET['linktechnology']) : null;
        $linkspeed = !empty($_GET['linkspeed']) && ctype_digit($_GET['linkspeed']) ? intval($_GET['linkspeed']) : null;
        $dev['srcport'] = !empty($_GET['srcport']) ? intval($_GET['srcport']) : '0';
        $dev['dstport'] = !empty($_GET['dstport']) ? intval($_GET['dstport']) : '0';
        $dev['id'] = !empty($_GET['netdev']) ? intval($_GET['netdev']) : '0';
        $routetype = !empty($_GET['routetype']) && ctype_digit($_GET['routetype']) ? intval($_GET['routetype']) : null;
        $linecount = !empty($_GET['linecount']) && ctype_digit($_GET['linecount']) ? intval($_GET['linecount']) : null;
        $usedlines = isset($_GET['usedlines']) && ctype_digit($_GET['usedlines']) ? intval($_GET['usedlines']) : null;
        $availablelines = isset($_GET['availablelines']) && ctype_digit($_GET['availablelines']) ? intval($_GET['availablelines']) : null;

        $ports1 = $DB->GetOne('SELECT ports FROM netdevices WHERE id = ?', array($_GET['id']));
        $takenports1 = $LMS->CountNetDevLinks($_GET['id']);

        $ports2 = $DB->GetOne('SELECT ports FROM netdevices WHERE id = ?', array($dev['id']));
        $takenports2 = $LMS->CountNetDevLinks($dev['id']);

        if ($ports1 <= $takenports1 || $ports2 <= $takenports2) {
            $error['srcport'] = trans('No free ports on device!');
        } else {
            if ($dev['srcport']) {
                if (!preg_match('/^[0-9]+$/', $dev['srcport']) || $dev['srcport'] > $ports2) {
                    $error['srcport'] = trans('Incorrect port number!');
                } elseif ($DB->GetOne('SELECT id FROM vnodes WHERE netdev=? AND port=? AND ownerid IS NOT NULL', array($dev['id'], $dev['srcport']))
                        || $DB->GetOne('SELECT 1 FROM netlinks WHERE (src = ? OR dst = ?)
					AND (CASE src WHEN ? THEN srcport ELSE dstport END) = ?', array($dev['id'], $dev['id'], $dev['id'], $dev['srcport']))) {
                    $error['srcport'] = trans('Selected port number is taken by other device or node!');
                }
            }

            if ($dev['dstport']) {
                if (!preg_match('/^[0-9]+$/', $dev['dstport']) || $dev['dstport'] > $ports1) {
                    $error['dstport'] = trans('Incorrect port number!');
                } elseif ($DB->GetOne('SELECT id FROM vnodes WHERE netdev=? AND port=? AND ownerid IS NOT NULL', array($_GET['id'], $dev['dstport']))
                        || $DB->GetOne('SELECT 1 FROM netlinks WHERE (src = ? OR dst = ?)
					AND (CASE src WHEN ? THEN srcport ELSE dstport END) = ?', array($_GET['id'], $_GET['id'], $_GET['id'], $dev['dstport']))) {
                    $error['dstport'] = trans('Selected port number is taken by other device or node!');
                }
            }
        }

        if (isset($usedlines)) {
            if (isset($linecount)) {
                if ($linecount < $usedlines) {
                    $error['usedlines'] = trans('Used fiber/line count cannot be greater than fiber/line count!');
                }
            } else {
                $error['linecount'] = trans('Fiber/line count cannot be empty when entered used fiber/line count!');
            }
        }

        if (isset($availablelines)) {
            if (isset($linecount)) {
                if ($linecount < $availablelines) {
                    $error['availablelines'] = trans('Available fiber/line count cannot be greater than fiber/line count!');
                }
            } else {
                $error['linecount'] = trans('Fiber/line count cannot be empty when entered available fiber/line count!');
            }
        }

        if (isset($linecount, $usedlines, $availablelines) && $linecount < $usedlines + $availablelines) {
            $error['linecount'] = $error['usedlines'] = $error['availablelines'] = trans('Sum of used and available fiber/line count cannot be greater than fiber/line count!');
        }

        $SESSION->save('devlinktype', $linktype);
        $SESSION->save('devlinksrcradiosector', $srcradiosector);
        $SESSION->save('devlinkdstradiosector', $dstradiosector);
        $SESSION->save('devlinktechnology', $linktechnology);
        $SESSION->save('devlinkspeed', $linkspeed);
        $SESSION->save('devlinkroutetype', $routetype);
        $SESSION->save('devlinklinecount', $linecount);
        $SESSION->save('devlinkusedlines', $usedlines);
        $SESSION->save('devlinkavailablelines', $availablelines);

        if (!$error) {
            $LMS->NetDevLink($dev['id'], $_GET['id'], array(
                'type' => $linktype,
                'srcradiosector' => $srcradiosector,
                'dstradiosector' => $dstradiosector,
                'technology' => $linktechnology,
                'speed' => $linkspeed,
                'srcport' => $dev['srcport'],
                'dstport' => $dev['dstport'],
                'routetype' => $routetype,
                'linecount' => $linecount,
                'usedlines' => $usedlines,
                'availablelines' => $availablelines,
            ));
            $SESSION->redirect('?m=netdevinfo&id=' . $_GET['id']);
        }

        $SMARTY->assign('connect', $dev);

        break;

    case 'connectnode':
        $linktype = isset($_GET['linktype']) && ctype_digit($_GET['linktype']) ? intval($_GET['linktype']) : null;
        $linkradiosector = $linktype == LINKTYPE_WIRELESS && ctype_digit($_GET['radiosector']) ? intval($_GET['radiosector']) : null;
        $linktechnology = !empty($_GET['linktechnology']) && ctype_digit($_GET['linktechnology']) ? intval($_GET['linktechnology']) : null;
        $linkspeed = !empty($_GET['linkspeed']) && ctype_digit($_GET['linkspeed']) ? intval($_GET['linkspeed']) : null;
        $node['port'] = !empty($_GET['port']) ? intval($_GET['port']) : '0';
        $node['id'] = !empty($_GET['nodeid']) ? intval($_GET['nodeid']) : '0';

        $ports = $DB->GetOne('SELECT ports FROM netdevices WHERE id = ?', array($_GET['id']));
        $takenports = $LMS->CountNetDevLinks($_GET['id']);

        if ($ports <= $takenports) {
            $error['linknode'] = trans('No free ports on device!');
        } elseif ($node['port']) {
            if (!preg_match('/^[0-9]+$/', $node['port']) || $node['port'] > $ports) {
                $error['port'] = trans('Incorrect port number!');
            } elseif ($DB->GetOne('SELECT id FROM vnodes WHERE netdev=? AND port=? AND ownerid IS NOT NULL', array($_GET['id'], $node['port']))
                    || $DB->GetOne('SELECT 1 FROM netlinks WHERE (src = ? OR dst = ?)
				AND (CASE src WHEN ? THEN srcport ELSE dstport END) = ?', array($_GET['id'], $_GET['id'], $_GET['id'], $node['port']))) {
                $error['port'] = trans('Selected port number is taken by other device or node!');
            }
        }

        $SESSION->save('nodelinktype', $linktype);
        $SESSION->save('nodelinkradiosector', $linkradiosector);
        $SESSION->save('nodelinktechnology', $linktechnology);
        $SESSION->save('nodelinkspeed', $linkspeed);

        if (!$error) {
            $LMS->NetDevLinkNode($node['id'], $_GET['id'], array(
                'type' => $linktype,
                'radiosector' => $linkradiosector,
                'technology' => $linktechnology,
                'speed' => $linkspeed,
                'port' => $node['port']
            ));
            $SESSION->redirect('?m=netdevinfo&id=' . $_GET['id']);
        }

        $SMARTY->assign('connectnode', $node);

        break;

    case 'addip':
        $subtitle = trans('New IP address');
        $nodeipdata['access'] = 1;

        $mac = $LMS->getNetDevMacs($id, 1);
        $macAddress = (!empty($mac) ? $mac[0]['mac'] : '');
        $macs = array(0 => $macAddress);

        $nodeipdata['macs'] = array_filter(
            $macs,
            function ($mac) use ($netdev_empty_mac) {
                return $mac != $netdev_empty_mac;
            }
        );

        $SMARTY->assign('nodeipdata', $nodeipdata);
        $edit = 'addip';
        break;

    case 'editip':
        $nodeipdata = $LMS->GetNode($_GET['ip']);
        $nodeipdata['ipaddr'] = $nodeipdata['ip'];
        $nodeipdata['ipaddr_pub'] = $nodeipdata['ip_pub'];
        $subtitle = trans('IP address edit');

        $nodeipdata['macs'] = array_filter(
            array_map(
                function ($mac) {
                    return $mac['mac'];
                },
                $nodeipdata['macs']
            ),
            function ($mac) use ($netdev_empty_mac) {
                return $mac != $netdev_empty_mac;
            }
        );

        $SMARTY->assign('nodeipdata', $nodeipdata);
        $edit = 'ip';
        break;

    case 'ipdel':
        if (!empty($_GET['ip'])) {
            if ($SYSLOG) {
                $args = array(
                    SYSLOG::RES_NODE => $_GET['ip'],
                    SYSLOG::RES_NETDEV => $_GET['id'],
                );
                $SYSLOG->AddMessage(SYSLOG::RES_NODE, SYSLOG::OPER_UPDATE, $args);
            }
            $DB->Execute('DELETE FROM nodes WHERE id = ? AND ownerid IS NULL', array($_GET['ip']));
        }

        $SESSION->redirect('?m=netdevinfo&id=' . $_GET['id']);
        break;

    case 'ipset':
        if (!empty($_GET['ip'])) {
            if ($SYSLOG) {
                $access = $DB->GetOne(
                    'SELECT access FROM vnodes WHERE id = ? AND ownerid IS NULL',
                    array($_GET['ip'])
                );
                $args = array(
                    SYSLOG::RES_NODE => $_GET['ip'],
                    SYSLOG::RES_NETDEV => $_GET['id'],
                    'access' => intval(!$access),
                );
                $SYSLOG->AddMessage(SYSLOG::RES_NODE, SYSLOG::OPER_UPDATE, $args);
            }
            $DB->Execute('UPDATE nodes SET access = (CASE access WHEN 1 THEN 0 ELSE 1 END)
				WHERE id = ? AND ownerid IS NULL', array($_GET['ip']));
        } else {
            $LMS->IPSetU($_GET['id'], $_GET['access']);
        }

        $SESSION->redirect_to_history_entry();
        break;

    case 'formaddip':
        $subtitle = trans('New IP address');

        if (!isset($_POST['ipadd'])) {
            $edit = 'addip';
            break;
        }

        $nodeipdata = $_POST['ipadd'];
        $nodeipdata['ownerid'] = null;
        if (!empty($nodeipdata['macs'])) {
            foreach ($nodeipdata['macs'] as $key => $value) {
                $nodeipdata['macs'][$key] = str_replace('-', ':', $value);
            }
        }

        $nodeipdata = trim_rec($nodeipdata);

        if ($nodeipdata['ipaddr'] == '' && $nodeipdata['name'] == '' && $nodeipdata['passwd'] == '') {
            $SESSION->redirect('?m=netdevedit&action=addip&id=' . $_GET['id']);
        }

        if ($nodeipdata['name'] == '') {
            $error['ipname'] = trans('Address field is required!');
        } elseif (strlen($nodeipdata['name']) > 32) {
            $error['ipname'] = trans('Specified name is too long (max. $a characters)!', '32');
        } elseif ($LMS->GetNodeIDByName($nodeipdata['name']) || $LMS->GetNodeIDByNetName($nodeipdata['name'])) {
            $error['ipname'] = trans('Specified name is in use!');
        } elseif (!preg_match('/' . ConfigHelper::getConfig('netdevices.ip_name_regexp', '^[_a-z0-9\-\.]+$') . '/i', $nodeipdata['name'])) {
            $error['ipname'] = trans('<!node>Name contains forbidden characters!');
        }

        if ($nodeipdata['ipaddr'] == '') {
            $error['ipaddr'] = trans('IP address is required!');
        } elseif (!check_ip($nodeipdata['ipaddr'])) {
            $error['ipaddr'] = trans('Incorrect IP address!');
        } elseif (!$LMS->IsIPValid($nodeipdata['ipaddr'])) {
            $error['ipaddr'] = trans('Specified address does not belong to any network!');
        } else {
            if (empty($nodeipdata['netid'])) {
                $nodeipdata['netid'] = $DB->GetOne(
                    'SELECT id FROM networks WHERE INET_ATON(?) & INET_ATON(mask) = address ORDER BY id LIMIT 1',
                    array($nodeipdata['ipaddr'])
                );
            }
            if (!$LMS->IsIPInNetwork($nodeipdata['ipaddr'], $nodeipdata['netid'])) {
                $error['ipaddr'] = trans('Specified IP address doesn\'t belong to selected network!');
            } else if (!$LMS->IsIPFree($nodeipdata['ipaddr'], $nodeipdata['netid'])) {
                $error['ipaddr'] = trans('Specified IP address is in use!');
            } else if ($LMS->IsIPGateway($nodeipdata['ipaddr'])) {
                $error['ipaddr'] = trans('Specified IP address is network gateway!');
            }
        }

        if ($nodeipdata['ipaddr_pub'] != '0.0.0.0' && $nodeipdata['ipaddr_pub'] != '') {
            if (!check_ip($nodeipdata['ipaddr_pub'])) {
                $error['ipaddr_pub'] = trans('Incorrect IP address!');
            } elseif (!$LMS->IsIPValid($nodeipdata['ipaddr_pub'])) {
                $error['ipaddr_pub'] = trans('Specified address does not belongs to any network!');
            } elseif (!$LMS->IsIPFree($nodeipdata['ipaddr_pub'])) {
                $error['ipaddr_pub'] = trans('Specified IP address is in use!');
            }
        } else {
            $nodeipdata['ipaddr_pub'] = '0.0.0.0';
        }

        $macs = array();
        if (!empty($nodeipdata['macs'])) {
            foreach ($nodeipdata['macs'] as $key => $value) {
                if (check_mac($value)) {
                    if (in_array($value, $macs)) {
                        $error['mac-input-' . $key] = trans('MAC address is in use!');
                    } elseif ($value != '00:00:00:00:00:00' && !ConfigHelper::checkConfig('phpui.allow_mac_sharing')) {
                        if ($LMS->GetNodeIDByMAC($value)) {
                            $error['mac-input-' . $key] = trans('MAC address is in use!');
                        }
                    }
                    $macs[] = $value;
                } elseif ($value != '') {
                    $error['mac-input-' . $key] = trans('Incorrect MAC address!');
                }
            }
        }

        $nodeipdata['macs'] = array_filter(
            $macs,
            function ($mac) use ($netdev_empty_mac) {
                return $mac != $netdev_empty_mac;
            }
        );

        if (strlen($nodeipdata['passwd']) > 32) {
            $error['passwd'] = trans('Password is too long (max. 32 characters)!');
        }

        if (!isset($nodeipdata['chkmac'])) {
            $nodeipdata['chkmac'] = 0;
        }
        if (!isset($nodeipdata['halfduplex'])) {
            $nodeipdata['halfduplex'] = 0;
        }
        if (!isset($nodeipdata['nas'])) {
            $nodeipdata['nas'] = 0;
        }

        $authtype = 0;
        if (isset($nodeipdata['authtype'])) {
            foreach ($nodeipdata['authtype'] as $value) {
                $authtype |= intval($value);
            }
        }
        $nodeipdata['authtype'] = $authtype;

        if (!$error) {
            $nodeipdata['warning'] = 0;
            $nodeipdata['location'] = '';
            $nodeipdata['netdev'] = $_GET['id'];

            $LMS->NodeAdd($nodeipdata);
            $SESSION->redirect('?m=netdevinfo&id=' . $_GET['id']);
        }

        $SMARTY->assign('nodeipdata', $nodeipdata);
        $edit = 'addip';
        break;

    case 'formeditip':
        $subtitle = trans('IP address edit');
        $nodeipdata = $_POST['ipadd'];
        $nodeipdata['ownerid'] = null;
        if (!empty($nodeipdata['macs'])) {
            foreach ($nodeipdata['macs'] as $key => $value) {
                $nodeipdata['macs'][$key] = str_replace('-', ':', $value);
            }
        }

        foreach ($nodeipdata as $key => $value) {
            if (isset($value) && !is_array($value)) {
                $nodeipdata[$key] = trim($value);
            }
        }

        if ($nodeipdata['ipaddr'] == '' && $nodeipdata['name'] == '' && $nodeipdata['passwd'] == '') {
            $SESSION->redirect('?m=netdevedit&action=editip&id=' . $_GET['id'] . '&ip=' . $_GET['ip']);
        }

        if ($nodeipdata['name'] == '') {
            $error['ipname'] = trans('Address field is required!');
        } elseif (strlen($nodeipdata['name']) > 32) {
            $error['ipname'] = trans('Specified name is too long (max. $a characters)!', '32');
        } elseif (($LMS->GetNodeIDByName($nodeipdata['name']) || $LMS->GetNodeIDByNetName($nodeipdata['name']))
                && strtoupper($LMS->GetNodeName($_GET['ip'])) != strtoupper($nodeipdata['name'])) {
            $error['ipname'] = trans('Specified name is in use!');
        } elseif (!preg_match('/' . ConfigHelper::getConfig('netdevices.ip_name_regexp', '^[_a-z0-9\-\.]+$') . '/i', $nodeipdata['name'])) {
            $error['ipname'] = trans('<!node>Name contains forbidden characters!');
        }

        if ($nodeipdata['ipaddr'] == '') {
            $error['ipaddr'] = trans('IP address is required!');
        } elseif (!check_ip($nodeipdata['ipaddr'])) {
            $error['ipaddr'] = trans('Incorrect IP address!');
        } elseif (!$LMS->IsIPValid($nodeipdata['ipaddr'])) {
            $error['ipaddr'] = trans('Specified address does not belong to any network!');
        } else {
            if (empty($nodeipdata['netid'])) {
                $nodeipdata['netid'] = $DB->GetOne(
                    'SELECT id FROM networks WHERE INET_ATON(?) & INET_ATON(mask) = address ORDER BY id LIMIT 1',
                    array($nodeipdata['ipaddr'])
                );
            }
            if (!$LMS->IsIPInNetwork($nodeipdata['ipaddr'], $nodeipdata['netid'])) {
                $error['ipaddr'] = trans('Specified IP address doesn\'t belong to selected network!');
            } else if (!$LMS->IsIPFree($nodeipdata['ipaddr'], $nodeipdata['netid']) &&
                $LMS->GetNodeIPByID($_GET['ip']) != $nodeipdata['ipaddr']) {
                $error['ipaddr'] = trans('IP address is in use!');
            } else if ($LMS->IsIPGateway($nodeipdata['ipaddr'])) {
                $error['ipaddr'] = trans('Specified IP address is network gateway!');
            }
        }

        if ($nodeipdata['ipaddr_pub'] != '0.0.0.0' && $nodeipdata['ipaddr_pub'] != '') {
            if (check_ip($nodeipdata['ipaddr_pub'])) {
                if ($LMS->IsIPValid($nodeipdata['ipaddr_pub'])) {
                    $ip = $LMS->GetNodePubIPByID($nodeipdata['id']);
                    if ($ip != $nodeipdata['ipaddr_pub'] && !$LMS->IsIPFree($nodeipdata['ipaddr_pub'])) {
                        $error['ipaddr_pub'] = trans('Specified IP address is in use!');
                    }
                } else {
                    $error['ipaddr_pub'] = trans('Specified IP address doesn\'t overlap with any network!');
                }
            } else {
                $error['ipaddr_pub'] = trans('Incorrect IP address!');
            }
        } else {
            $nodeipdata['ipaddr_pub'] = '0.0.0.0';
        }

        $macs = array();
        foreach ($nodeipdata['macs'] as $key => $value) {
            if (check_mac($value)) {
                if (in_array($value, $macs)) {
                    $error['mac-input-' . $key] = trans('MAC address is in use!');
                } elseif ($value != '00:00:00:00:00:00' && !ConfigHelper::checkConfig('phpui.allow_mac_sharing')) {
                    if (($nodeid = $LMS->GetNodeIDByMAC($value)) != null && $nodeid != $_GET['ip']) {
                        $error['mac-input-' . $key] = trans('MAC address is in use!');
                    }
                }
                $macs[] = $value;
            } elseif ($value != '') {
                $error['mac-input-' . $key] = trans('Incorrect MAC address!');
            }
        }

        $nodeipdata['macs'] = array_filter(
            $macs,
            function ($mac) use ($netdev_empty_mac) {
                return $mac != $netdev_empty_mac;
            }
        );

        if (strlen($nodeipdata['passwd']) > 32) {
            $error['passwd'] = trans('Password is too long (max. 32 characters)!');
        }

        if (!isset($nodeipdata['chkmac'])) {
            $nodeipdata['chkmac'] = 0;
        }
        if (!isset($nodeipdata['halfduplex'])) {
            $nodeipdata['halfduplex'] = 0;
        }
        if (!isset($nodeipdata['nas'])) {
            $nodeipdata['nas'] = 0;
        }

        $authtype = 0;
        if (isset($nodeipdata['authtype'])) {
            foreach ($nodeipdata['authtype'] as $value) {
                $authtype |= intval($value);
            }
        }
        $nodeipdata['authtype'] = $authtype;

        if (!$error) {
            $nodeipdata['warning'] = 0;
            $nodeipdata['location'] = '';
            $nodeipdata['netdev'] = $_GET['id'];

            $LMS->NodeUpdate($nodeipdata);
            $SESSION->redirect('?m=netdevinfo&id=' . $_GET['id']);
        }

        $SMARTY->assign('nodeipdata', $nodeipdata);
        $edit = 'ip';
        break;
    case 'authtype':
        $DB->Execute('UPDATE nodes SET authtype=? WHERE id=?', array($_GET['authtype'], $_GET['ip']));
        if ($SYSLOG) {
            $args = array(
                SYSLOG::RES_NODE => $_GET['ip'],
                SYSLOG::RES_CUST => $customerid,
                'authtype' => intval($_GET['authtype']),
            );
            $SYSLOG->AddMessage(SYSLOG::RES_NODE, SYSLOG::OPER_UPDATE, $args);
        }
        $SESSION->redirect('?m=netdevinfo&id=' . $_GET['id'].'&ip='.$_GET['ip']);
        break;
    default:
        $edit = 'data';
        break;
}

if (isset($netdev)) {
    $netdev['id'] = $id;

    $error = array();

    $netdev['name'] = trim($netdev['name']);
    if ($netdev['name'] == '') {
        $error['name'] = trans('Device name is required!');
    } elseif (strlen($netdev['name']) > 100) {
        $error['name'] = trans('Specified name is too long (max. $a characters)!', '60');
    }

    $netdev['ports'] = intval($netdev['ports']);

    if ($netdev['ports'] < $LMS->CountNetDevLinks($id)) {
        $error['ports'] = trans('Connected devices number exceeds number of ports!');
    }

    if (!empty($netdev['ownerid']) && !$LMS->CustomerExists($netdev['ownerid'])) {
        $error['ownerid'] = trans('Customer doesn\'t exist!');
    }

    if (!$api) {
        $netdev['clients'] = (empty($netdev['clients'])) ? 0 : intval($netdev['clients']);

        $netdev['purchasetime'] = intval($netdev['purchasetime']);
        if ($netdev['purchasetime'] && time() < $netdev['purchasetime']) {
            $error['purchasetime'] = trans('Date from the future not allowed!');
        }

        if ($netdev['guaranteeperiod'] != 0 && !$netdev['purchasetime']) {
            $error['purchasetime'] = trans('Purchase date cannot be empty when guarantee period is set!');
        }
    }

    $gps_coordinates_required = ConfigHelper::getConfig('phpui.netdev_gps_coordinates_required', 'none');

    $longitude = filter_var($netdev['longitude'], FILTER_VALIDATE_FLOAT);
    $latitude = filter_var($netdev['latitude'], FILTER_VALIDATE_FLOAT);

    if (strlen($netdev['longitude']) && $longitude === false) {
        $error['longitude'] = trans('Invalid longitude format!');
    }
    if (strlen($netdev['latitude']) && $latitude === false) {
        $error['latitude'] = trans('Invalid latitude format!');
    }

    if (!strlen($netdev['longitude']) != !strlen($netdev['latitude'])) {
        if (!isset($error['longitude'])) {
            $error['longitude'] = trans('Longitude and latitude cannot be empty!');
        }
        if (!isset($error['latitude'])) {
            $error['latitude'] = trans('Longitude and latitude cannot be empty!');
        }
    }

    if ($gps_coordinates_required != 'none'
        && ($gps_coordinates_required == 'warning'
            || $gps_coordinates_required == 'error'
            || ConfigHelper::checkValue($gps_coordinates_required))) {
        if ($gps_coordinates_required != 'warning' && $gps_coordinates_required != 'error') {
            $gps_coordinates_required = 'error';
        }
        if (!isset($error['longitude']) && !strlen($netdev['longitude'])) {
            if ($gps_coordinates_required == 'error' || ConfigHelper::checkValue($gps_coordinates_required)) {
                $error['longitude'] = trans('Longitude is required!');
            } elseif ($gps_coordinates_required == 'warning' && !isset($warnings['netdev-longitude-'])) {
                $warning['netdev[longitude]'] = trans('Longitude should not be empty!');
            }
        }
        if (!isset($error['latitude']) && !strlen($netdev['latitude'])) {
            if ($gps_coordinates_required == 'error' || ConfigHelper::checkValue($gps_coordinates_required)) {
                $error['latitude'] = trans('Latitude is required!');
            } elseif ($gps_coordinates_required == 'warning' && !isset($warnings['netdev-latitude-'])) {
                $warning['netdev[latitude]'] = trans('Latitude should not be empty!');
            }
        }
    }

    if (!strlen($netdev['projectid']) && !empty($netdev['project'])) {
        $project = $LMS->GetProjectByName($netdev['project']);
        if (empty($project)) {
            $netdev['projectid'] = -1;
        } else {
            $netdev['projectid'] = $project['id'];
        }
    }

    if (empty($netdev['ownerid']) && !ConfigHelper::checkPrivilege('full_access')
        && ConfigHelper::checkConfig('phpui.teryt_required')
        && !empty($netdev['location_city_name']) && ($netdev['location_country_id'] == 2 || empty($netdev['location_country_id']))
        && (!isset($netdev['teryt']) || empty($netdev['location_city'])) && $LMS->isTerritState($netdev['location_state_name'])) {
        $error['netdev[teryt]'] = trans('TERYT address is required!');
    }

    $allow_empty_streets = ConfigHelper::checkConfig('teryt.allow_empty_streets', true);
    $allow_empty_building_numbers = ConfigHelper::checkConfig('teryt.allow_empty_building_numbers', true);
    if (empty($netdev['ownerid']) && !empty($netdev['teryt'])) {
        if ($allow_empty_streets && empty($netdev['location_street']) && $LMS->isCityWithStreets($netdev['location_city'])
            || $allow_empty_building_numbers && !strlen($netdev['location_house'])) {
            if (!strlen($netdev['longitude'])) {
                $error['netdev[longitude]'] = trans('Longitude and latitude cannot be empty!');
            }
            if (!strlen($netdev['latitude'])) {
                $error['netdev[latitude]'] = trans('Longitude and latitude cannot be empty!');
            }
        }
    }

    if (!empty($netdev['location_country_id'])) {
        Localisation::setSystemLanguage($LMS->getCountryCodeById($netdev['location_country_id']));
    }
    if (empty($netdev['ownerid']) && $netdev['location_zip'] && !check_zip($netdev['location_zip'])) {
        $error['location_zip'] = trans('Incorrect ZIP code!');
    }
    Localisation::resetSystemLanguage();

    $hook_data = $LMS->executeHook(
        'netdevedit_validation_before_submit',
        array(
            'netdevdata' => $netdev,
            'error' => $error
        )
    );
    $netdev = $hook_data['netdevdata'];
    $error = $hook_data['error'];

    if (!$error) {
        if (!$api) {
            if ($netdev['guaranteeperiod'] == -1) {
                $netdev['guaranteeperiod'] = null;
            }

            if (!isset($netdev['shortname'])) {
                $netdev['shortname'] = '';
            }
            if (!isset($netdev['login'])) {
                $netdev['login'] = '';
            }
            if (!isset($netdev['secret'])) {
                $netdev['secret'] = '';
            }
            if (!isset($netdev['community'])) {
                $netdev['community'] = '';
            }
            if (!isset($netdev['nastype'])) {
                $netdev['nastype'] = 0;
            }
        }

        if ($netdev['projectid'] == -1) {
            $netdev['projectid'] = $LMS->AddProject($netdev);
        } elseif (empty($netdev['projectid'])) {
            $netdev['projectid'] = null;
        }

        // no net node selected
        if ($netdev['netnodeid'] == '-1') {
            $netdev['netnodeid'] = null;
        }

        $result = $LMS->NetDevUpdate($netdev);
        $LMS->CleanupProjects();

        if ($api) {
            if ($result) {
                header('Content-Type: application-json');
                echo json_encode(array('id' => $id));
            }
            $SESSION->close();
            die;
        }

        $hook_data = $LMS->executeHook(
            'netdevedit_after_update',
            array(
                'smarty' => $SMARTY,
                'netdevdata' => $netdev,
            )
        );
        $SESSION->redirect('?m=netdevinfo&id=' . $id);
    } elseif ($api) {
        header('Content-Type: application-json');
        echo json_encode($error);
        $SESSION->close();
        die;
    }

    if (!empty($netdev['ownerid'])) {
        $netdev['address_id'] = $netdev['customer_address_id'];
    }
} else {
    $netdev = $LMS->GetNetDev($id);

    if (isset($netdev['producer'])) {
        $netdev['pagetitle'] = trans('Device Edit: $a ($b)', $netdev['name'], $netdev['producer'] . ($netdev['model'] ? ' ' . $netdev['model'] : ''));
    } else {
        $netdev['pagetitle'] = trans('Device Edit: $a', $netdev['name']);
    }

    if (isset($netdev['producerid']) && preg_match('/^[0-9]+$/', $netdev['producerid'])
        && isset($netdev['modelid']) && preg_match('/^[0-9]+$/', $netdev['modelid'])) {
        $netdev['producer'] = $netdev['producerid'];
        $netdev['model'] = $netdev['modelid'];
    }

    $attachmenttype = 'netdevid';
    $attachmentresourceid = $id;
    $SMARTY->assign('attachmenttype', $attachmenttype);
    $SMARTY->assign('attachmentresourceid', $attachmentresourceid);

    $filecontainers = array(
        'netdevid' => array(
            'id' => $id,
            'prefix' => trans('Device attachments'),
            'containers' => $LMS->GetFileContainers('netdevid', $id),
        ),
        'netdevmodelid' => array(
            'id' => $netdev['modelid'],
            'prefix' => trans('Model attachments'),
            'containers' => $LMS->GetFileContainers('netdevmodelid', $netdev['model']),
        ),
    );
    $SMARTY->assign('filecontainers', $filecontainers);

    include(MODULES_DIR . DIRECTORY_SEPARATOR . 'attachments.php');

    if ($netdev['purchasetime']) {
        $netdev['purchasedate'] = date('Y/m/d', $netdev['purchasetime']);
    }

    if (($netdev['location_city'] || $netdev['location_street']) && !$netdev['ownerid']) {
        $netdev['teryt'] = true;
    }
}

$netdev['id'] = $id;

if (!empty($netdev['ownerid'])) {
    $assignments = $LMS->GetCustomerAssignments($netdev['ownerid'], true, false);
    $assignments = $LMS->GetNetDevCustomerAssignments($id, $assignments);
    $SMARTY->assign(array(
        'assignments' => $assignments,
        'customerinfo' => array(
            'id' => $netdev['ownerid'],
        )
    ));
}

$netdevips       = $LMS->GetNetDevIPs($id);
$nodelist        = $netdev['ports'] > ($netdev['takenports'] ?? $LMS->CountNetDevLinks($id)) ? $LMS->GetUnlinkedNodes() : array();
$netdevconnected = $LMS->GetNetDevConnectedNames($id);
$netcomplist     = $LMS->GetNetDevLinkedNodes($id);
$netdevlist      = $LMS->GetNotConnectedDevices($id);

unset($netdevlist['total']);
unset($netdevlist['order']);
unset($netdevlist['direction']);

$layout['pagetitle'] = $netdev['pagetitle'];

$hook_data = $LMS->executeHook(
    'netdevedit_before_display',
    array(
        'netdevdata' => $netdev,
        'smarty' => $SMARTY,
    )
);
$netdev = $hook_data['netdevdata'];

if ($subtitle) {
    $layout['pagetitle'] .= ' - ' . $subtitle;
}

$SMARTY->assign('divisions', $LMS->GetDivisions());
$SMARTY->assign('NNprojects', $LMS->GetProjects());
$SMARTY->assign('NNnodes', $LMS->GetNetNodes());
$SMARTY->assign('producers', $LMS->GetProducers());
$SMARTY->assign('models', $LMS->GetModels());

$SMARTY->assign('error', $error);
$SMARTY->assign('netdev', $netdev);
$SMARTY->assign('objectid', $netdev['id']);
$SMARTY->assign('netdevlist', $netdevconnected);
$SMARTY->assign('netcomplist', $netcomplist);
$SMARTY->assign('nodelist', $nodelist);
$SMARTY->assign('mgmurls', $LMS->GetManagementUrls(LMSNetDevManager::NETDEV_URL, $netdev['id']));
$SMARTY->assign('radiosectors', $LMS->GetRadioSectors($netdev['id']));
$SMARTY->assign('netdevcontype', $netdevcontype ?? null);
$SMARTY->assign('netdevauthtype', $netdevauthtype ?? null);
$SMARTY->assign('netdevips', $netdevips);
$SMARTY->assign('restnetdevlist', $netdevlist);
$SMARTY->assign('devlinktype', $SESSION->get('devlinktype'));
$SMARTY->assign('devlinksrcradiosector', $SESSION->get('devlinksrcradiosector'));
$SMARTY->assign('devlinkdstradiosector', $SESSION->get('devlinkdstradiosector'));
$SMARTY->assign('devlinktechnology', $SESSION->get('devlinktechnology'));
$SMARTY->assign('devlinkspeed', $SESSION->get('devlinkspeed'));
$SMARTY->assign('devlinkroutetype', $SESSION->get('devlinkroutetype'));
$SMARTY->assign('devlinklinecount', $SESSION->get('devlinklinecount'));
$SMARTY->assign('devlinkusedlines', $SESSION->get('devlinkusedlines'));
$SMARTY->assign('devlinkavailablelines', $SESSION->get('devlinkavailablelines'));

if ($SESSION->is_set('nodelinktype')) {
    $nodelinktype = $SESSION->get('nodelinktype');
} else {
    $nodelinktype = intval(ConfigHelper::getConfig('phpui.default_linktype', LINKTYPE_WIRE));
}
$SMARTY->assign('nodelinktype', $nodelinktype);

if ($SESSION->is_set('nodelinktechnology')) {
    $nodelinktechnology = $SESSION->get('nodelinktechnology');
} else {
    $nodelinktechnology = intval(ConfigHelper::getConfig('phpui.default_linktechnology', 0));
}
$SMARTY->assign('nodelinktechnology', $nodelinktechnology);

if ($SESSION->is_set('nodelinkspeed')) {
    $nodelinkspeed = $SESSION->get('nodelinkspeed');
} else {
    $nodelinkspeed = intval(ConfigHelper::getConfig('phpui.default_linkspeed', 100000));
}
$SMARTY->assign('nodelinkspeed', $nodelinkspeed);

$SMARTY->assign('nodelinkradiosector', $SESSION->get('nodelinkradiosector'));
$SMARTY->assign('nastypes', $LMS->GetNAStypes());
$SMARTY->assign('macs', $LMS->GetNetdevMacs($netdev['id']));
$SMARTY->assign('maclabels', $LMS->GetNetdevsMacLabels());

if (!ConfigHelper::checkConfig('phpui.big_networks')) {
    $SMARTY->assign('customers', $LMS->GetCustomerNames());
}

$LMS->InitXajax();
include(MODULES_DIR . DIRECTORY_SEPARATOR . 'netdevxajax.inc.php');
include(MODULES_DIR . DIRECTORY_SEPARATOR . 'geocodexajax.inc.php');
$SMARTY->assign('xajax', $LMS->RunXajax());

switch ($edit) {
    case 'data':
        if (ConfigHelper::checkConfig('phpui.ewx_support')) {
            $SMARTY->assign('channels', $DB->GetAll('SELECT id, name FROM ewx_channels ORDER BY name'));
        }

        if (!empty($netdev['ownerid'])) {
            $addresses = $LMS->getCustomerAddresses($netdev['ownerid']);
            $LMS->determineDefaultCustomerAddress($addresses);
            $SMARTY->assign('addresses', $addresses);
        }

        $SMARTY->assign('netdevedit_sortable_order', $SESSION->get_persistent_setting('netdevedit-sortable-order'));
        $SMARTY->display('netdev/netdevedit.html');
        break;
    case 'ip':
        $SMARTY->assign('networks', $LMS->GetNetworks());
        $SMARTY->assign('nodesessions', $LMS->GetNodeSessions($_GET['ip']));
        $SMARTY->assign('netdevvipedit_sortable_order', $SESSION->get_persistent_setting('netdevipedit-sortable-order'));

        $SMARTY->assign('routednetworks', $LMS->getNodeRoutedNetworks($_GET['ip']));
        $SMARTY->assign('notroutednetworks', $LMS->getNodeNotRoutedNetworks($_GET['ip']));
        $SMARTY->assign('nodeid', $_GET['ip']);

        $SMARTY->display('netdev/netdevipedit.html');
        break;
    case 'addip':
        $SMARTY->assign('networks', $LMS->GetNetworks());
        $SMARTY->assign('netdevvipadd_sortable_order', $SESSION->get_persistent_setting('netdevipadd-sortable-order'));
        $SMARTY->display('netdev/netdevipadd.html');
        break;
    default:
        $SMARTY->display('netdev/netdevinfo.html');
        break;
}
