"0 DHCP-Server (!)" in Rot: In der Produktionsdatenbank stehen drei
ausgelieferte Messungen des entfernten Werkzeugs dhcpcheck mit Ergebnis
{"count":0,"server":[],"hinweis":""} und Status 2. Bei Kabelverbindung gibt
Android die DHCP-Angaben aber grundsaetzlich nicht heraus (nur DhcpInfo, nur
WLAN, deprecated) - gemessen wurde also nichts, "0 gefunden" war der
Rueckgabewert fuer "nicht ermittelbar". netdiagAltlastKorrektur() zeigt diese
Messungen jetzt als "nicht messbar" mit Erklaerung, in PDF und
Technikeransicht. Rohdaten bleiben unangetastet, korrigiert wird nur die
Darstellung - und die wird bei jedem Abruf neu erzeugt. Die Ergebniszahlen
werden dabei unterdrueckt: "Gefundene Geraete: 0" widerspraeche der Aussage
direkt darueber.
"Lease-Dauer: 864000 s s": aeltere App-Fassungen haben die Einheit in den Wert
geschrieben, die Feldtabelle haengt sie erneut an (Prod-Messung #42).
Adress- und Lease-Felder mit dem Wert 0 erscheinen jetzt als "nicht
ermittelbar" statt als "0" - das war nie eine Messung, sondern das
"nichts ermittelt" der alten Android-API.
Neu in der Whitelist: dhcpQuelle (system = LinkProperties ab Android 11 fuer
jeden Anschlusstyp, wlan = alter Weg nur fuer WLAN). Damit ist klaerbar, ob
eine fehlende DHCP-Angabe an der Leitung lag oder an der Android-Version.
Geprueft gegen die lokale Testinstanz mit einer Kopie der echten
Prod-Messungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
287 lines
11 KiB
PHP
287 lines
11 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* \file netdiag/netdiagprotocol_card.php
|
|
* \ingroup netdiag
|
|
* \brief Detailansicht eines Diagnose-Protokolls (Backend)
|
|
*/
|
|
|
|
// Dolibarr-Umgebung laden
|
|
$res = 0;
|
|
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
|
|
$res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
|
|
}
|
|
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
|
|
$tmp2 = realpath(__FILE__);
|
|
$i = strlen($tmp) - 1;
|
|
$j = strlen($tmp2) - 1;
|
|
while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
|
|
$i--;
|
|
$j--;
|
|
}
|
|
if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
|
|
$res = @include substr($tmp, 0, ($i + 1))."/main.inc.php";
|
|
}
|
|
if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) {
|
|
$res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php";
|
|
}
|
|
if (!$res && file_exists("../main.inc.php")) {
|
|
$res = @include "../main.inc.php";
|
|
}
|
|
if (!$res && file_exists("../../main.inc.php")) {
|
|
$res = @include "../../main.inc.php";
|
|
}
|
|
if (!$res) {
|
|
die("Include of main fails");
|
|
}
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
|
|
require_once __DIR__.'/class/netdiagprotocol.class.php';
|
|
require_once __DIR__.'/class/netdiagdevice.class.php';
|
|
require_once __DIR__.'/class/netdiagmeasurement.class.php';
|
|
require_once __DIR__.'/lib/netdiag.lib.php';
|
|
require_once __DIR__.'/lib/netdiag_pdf.lib.php';
|
|
|
|
/**
|
|
* @var Conf $conf
|
|
* @var DoliDB $db
|
|
* @var Translate $langs
|
|
* @var User $user
|
|
*/
|
|
|
|
$langs->loadLangs(array("netdiag@netdiag", "companies", "orders", "other"));
|
|
|
|
if (!$user->hasRight('netdiag', 'protocol', 'read')) {
|
|
accessforbidden();
|
|
}
|
|
|
|
$id = GETPOSTINT('id');
|
|
$action = GETPOST('action', 'aZ09');
|
|
|
|
$object = new NetDiagProtocol($db);
|
|
if ($id <= 0 || $object->fetch($id) <= 0) {
|
|
accessforbidden('Protocol not found');
|
|
}
|
|
|
|
// Aktion: PDF generieren
|
|
if ($action == 'builddoc' && $user->hasRight('netdiag', 'protocol', 'read')) {
|
|
$result = netdiagGeneratePdf($db, $object, $langs);
|
|
if ($result > 0) {
|
|
setEventMessages($langs->trans("FileGenerated"), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($object->error, $object->errors, 'errors');
|
|
}
|
|
header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id);
|
|
exit;
|
|
}
|
|
|
|
// Aktion: löschen
|
|
if ($action == 'confirm_delete' && GETPOST('confirm') == 'yes' && $user->hasRight('netdiag', 'protocol', 'delete')) {
|
|
if ($object->delete($user) > 0) {
|
|
setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs');
|
|
header("Location: ".dol_buildpath('/netdiag/netdiagindex.php', 1));
|
|
exit;
|
|
}
|
|
setEventMessages($object->error, $object->errors, 'errors');
|
|
}
|
|
|
|
/*
|
|
* Ansicht
|
|
*/
|
|
|
|
$form = new Form($db);
|
|
llxHeader('', $object->ref, '', '', 0, 0, '', '', '', 'mod-netdiag page-card');
|
|
|
|
$head = netdiagProtocolPrepareHead($object);
|
|
print dol_get_fiche_head($head, 'card', $langs->trans("NetDiagProtocol"), -1, 'fa-network-wired');
|
|
|
|
// Lösch-Bestätigung
|
|
if ($action == 'delete') {
|
|
print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id, $langs->trans("Delete"), $langs->trans("ConfirmDeleteObject"), "confirm_delete", '', '', 1);
|
|
}
|
|
|
|
$linkback = '<a href="'.dol_buildpath('/netdiag/netdiagindex.php', 1).'">'.$langs->trans("BackToList").'</a>';
|
|
dol_banner_tab($object, 'id', $linkback, 1, 'rowid', 'ref');
|
|
|
|
print '<div class="fichecenter">';
|
|
print '<div class="underbanner clearboth"></div>';
|
|
print '<table class="border centpercent tableforfield">';
|
|
|
|
print '<tr><td class="titlefield">'.$langs->trans("Label").'</td><td>'.dol_escape_htmltag($object->label).'</td></tr>';
|
|
|
|
$soc = new Societe($db);
|
|
print '<tr><td>'.$langs->trans("ThirdParty").'</td><td>';
|
|
if ($object->fk_soc > 0 && $soc->fetch($object->fk_soc) > 0) {
|
|
print $soc->getNomUrl(1);
|
|
}
|
|
print '</td></tr>';
|
|
|
|
print '<tr><td>'.$langs->trans("Order").'</td><td>';
|
|
if ($object->fk_commande > 0) {
|
|
$cmd = new Commande($db);
|
|
if ($cmd->fetch($object->fk_commande) > 0) {
|
|
print $cmd->getNomUrl(1);
|
|
}
|
|
}
|
|
print '</td></tr>';
|
|
|
|
print '<tr><td>'.$langs->trans("DateDiag").'</td><td>'.dol_print_date($object->date_diag, 'dayhour').'</td></tr>';
|
|
print '<tr><td>'.$langs->trans("Location").'</td><td>'.dol_escape_htmltag($object->standort).'</td></tr>';
|
|
print '<tr><td>'.$langs->trans("Subnet").'</td><td>'.dol_escape_htmltag($object->subnet).'</td></tr>';
|
|
print '<tr><td>'.$langs->trans("Status").'</td><td>'.$object->getLibStatut(4).'</td></tr>';
|
|
print '<tr><td>'.$langs->trans("Note").'</td><td>'.dol_nl2br(dol_escape_htmltag($object->note)).'</td></tr>';
|
|
|
|
print '</table>';
|
|
print '</div>';
|
|
|
|
print dol_get_fiche_end();
|
|
|
|
// Aktionsknöpfe
|
|
print '<div class="tabsAction">';
|
|
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=builddoc&token='.newToken().'">'.$langs->trans("BuildDoc").'</a>';
|
|
if ($user->hasRight('netdiag', 'protocol', 'delete')) {
|
|
print '<a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete&token='.newToken().'">'.$langs->trans("Delete").'</a>';
|
|
}
|
|
print '</div>';
|
|
|
|
// Vorhandene PDF-Dokumente
|
|
$upload_dir = netdiagGetOutputDir().'/'.dol_sanitizeFileName($object->ref);
|
|
if (is_dir($upload_dir)) {
|
|
$files = dol_dir_list($upload_dir, 'files', 0, '\.pdf$');
|
|
if (!empty($files)) {
|
|
print '<br><table class="noborder centpercent"><tr class="liste_titre"><th>'.$langs->trans("Documents").'</th></tr>';
|
|
foreach ($files as $f) {
|
|
$durl = DOL_URL_ROOT.'/document.php?modulepart=netdiag&file='.urlencode(dol_sanitizeFileName($object->ref).'/'.$f['name']);
|
|
print '<tr class="oddeven"><td>'.img_mime($f['name']).' <a href="'.$durl.'">'.dol_escape_htmltag($f['name']).'</a></td></tr>';
|
|
}
|
|
print '</table>';
|
|
}
|
|
}
|
|
|
|
// Geräteliste
|
|
$devObj = new NetDiagDevice($db);
|
|
$devices = $devObj->fetchAllByProtocol($object->id);
|
|
print '<br>';
|
|
print load_fiche_titre($langs->trans("NetDiagDevices").' ('.count($devices).')', '', 'fa-desktop');
|
|
print '<div class="div-table-responsive-no-min"><table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<th>'.$langs->trans("IpAddress").'</th><th>'.$langs->trans("MacAddress").'</th>';
|
|
print '<th>'.$langs->trans("Hostname").'</th><th>'.$langs->trans("NetDiagVendor").'</th><th>'.$langs->trans("DeviceType").'</th>';
|
|
print '<th>'.$langs->trans("NetDiagOpenPorts").'</th><th>'.$langs->trans("NetDiagFoundVia").'</th>';
|
|
print '</tr>';
|
|
// Fundweg als Klartext — "ping"/"port"/"arp"/"mdns" sagt sonst nur dem Entwickler etwas
|
|
$foundvialabels = array(
|
|
'ping' => $langs->trans("NetDiagFoundViaPing"),
|
|
'port' => $langs->trans("NetDiagFoundViaPort"),
|
|
'arp' => $langs->trans("NetDiagFoundViaArp"),
|
|
'mdns' => $langs->trans("NetDiagFoundViaMdns"),
|
|
);
|
|
foreach ($devices as $dev) {
|
|
// Anzeigename: eigener Name vor mDNS-/Host-/NetBIOS-Name (wie in der App)
|
|
$anzeige = '';
|
|
foreach (array($dev->custom_name, $dev->mdns_name, $dev->hostname, $dev->netbios_name) as $kandidat) {
|
|
if (!empty($kandidat)) {
|
|
$anzeige = $kandidat;
|
|
break;
|
|
}
|
|
}
|
|
print '<tr class="oddeven">';
|
|
print '<td>'.dol_escape_htmltag($dev->ip).'</td>';
|
|
print '<td>'.dol_escape_htmltag($dev->mac).'</td>';
|
|
print '<td>'.dol_escape_htmltag($anzeige);
|
|
// Weitere Bezeichner nur zeigen, wenn sie vom Anzeigenamen abweichen
|
|
$weitere = array();
|
|
if (!empty($dev->netbios_name) && $dev->netbios_name !== $anzeige) {
|
|
$weitere[] = 'NB: '.$dev->netbios_name;
|
|
}
|
|
if (!empty($dev->mdns_name) && $dev->mdns_name !== $anzeige) {
|
|
$weitere[] = 'mDNS: '.$dev->mdns_name;
|
|
}
|
|
if (!empty($weitere)) {
|
|
print '<br><span class="opacitymedium small">'.dol_escape_htmltag(implode(' · ', $weitere)).'</span>';
|
|
}
|
|
print '</td>';
|
|
print '<td>'.dol_escape_htmltag($dev->vendor).'</td>';
|
|
print '<td>'.dol_escape_htmltag($dev->devicetype).'</td>';
|
|
print '<td>'.dol_escape_htmltag($dev->open_ports).'</td>';
|
|
print '<td>'.dol_escape_htmltag($foundvialabels[$dev->found_via] ?? $dev->found_via).'</td>';
|
|
print '</tr>';
|
|
}
|
|
if (empty($devices)) {
|
|
print '<tr><td colspan="7" class="opacitymedium center">-</td></tr>';
|
|
}
|
|
print '</table></div>';
|
|
|
|
// Messungen
|
|
$measObj = new NetDiagMeasurement($db);
|
|
$measurements = $measObj->fetchAllByProtocol($object->id);
|
|
$statuslabels = array(
|
|
0 => 'NetDiagMeasureOk',
|
|
1 => 'NetDiagMeasureWarn',
|
|
2 => 'NetDiagMeasureFail',
|
|
3 => 'NetDiagMeasureUnmeasurable',
|
|
);
|
|
$statuscss = array(
|
|
0 => 'badge-status4',
|
|
1 => 'badge-status1',
|
|
2 => 'badge-status8',
|
|
3 => 'badge-status0', // grau: Test war nicht durchfuehrbar
|
|
);
|
|
print '<br>';
|
|
print load_fiche_titre($langs->trans("NetDiagMeasurements").' ('.count($measurements).')', '', 'fa-wave-square');
|
|
print '<div class="div-table-responsive-no-min"><table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<th>'.$langs->trans("DateMeasure").'</th><th>'.$langs->trans("ToolCategory").'</th>';
|
|
print '<th>'.$langs->trans("Tool").'</th><th>'.$langs->trans("Label").'</th>';
|
|
print '<th>'.$langs->trans("Result").'</th><th class="center">'.$langs->trans("MeasureStatus").'</th>';
|
|
print '</tr>';
|
|
foreach ($measurements as $m) {
|
|
// Dieselbe Korrektur wie im PDF, damit Technikeransicht und Kundendokument
|
|
// nicht auseinanderlaufen (netdiagAltlastKorrektur(), Werkzeug dhcpcheck).
|
|
$korr = netdiagAltlastKorrektur($m->tool, $m->measure_status, $m->label, $m->result);
|
|
$st = (int) $korr['status'];
|
|
// Unbekannte Werte nie als OK durchgehen lassen (Array-Zugriff waere sonst leer)
|
|
if ($st < 0 || $st > 3) {
|
|
$st = 1;
|
|
}
|
|
print '<tr class="oddeven">';
|
|
print '<td class="nowraponall">'.dol_print_date($m->date_measure, 'dayhour').'</td>';
|
|
print '<td>'.dol_escape_htmltag($m->category).'</td>';
|
|
print '<td>'.dol_escape_htmltag(netdiagToolName($m->tool)).'</td>';
|
|
print '<td>'.dol_escape_htmltag($korr['label']);
|
|
if ($korr['hinweis'] !== '') {
|
|
print '<br><span class="opacitymedium small">'.dol_escape_htmltag($korr['hinweis']).'</span>';
|
|
}
|
|
// Messparameter darunter: sonst ist nicht erkennbar, wogegen gemessen wurde
|
|
$params = netdiagFormatParams($m->params);
|
|
if ($params !== '') {
|
|
print '<br><span class="opacitymedium small">'.dol_escape_htmltag($params).'</span>';
|
|
}
|
|
print '</td>';
|
|
print '<td>'.netdiagFormatResult($m->result, $m->tool).'</td>';
|
|
print '<td class="center"><span class="badge '.$statuscss[$st].'">'.$langs->trans($statuslabels[$st]).'</span></td>';
|
|
print '</tr>';
|
|
}
|
|
if (empty($measurements)) {
|
|
print '<tr><td colspan="6" class="opacitymedium center">-</td></tr>';
|
|
}
|
|
print '</table></div>';
|
|
|
|
llxFooter();
|
|
$db->close();
|