diff --git a/api/protocols.php b/api/protocols.php index 5652d25..1e1d8bd 100644 --- a/api/protocols.php +++ b/api/protocols.php @@ -42,6 +42,21 @@ require_once __DIR__.'/../class/netdiagmeasurement.class.php'; // GET: einzelnes Protokoll mit Geräten und Messungen // ========================================================================= if ($_SERVER['REQUEST_METHOD'] === 'GET') { + // Leserecht prüfen. Der POST-Zweig weiter unten verlangt seit jeher + // 'protocol write', der GET-Zweig prüfte bis 16.08.2026 gar nichts: jeder + // angemeldete Benutzer konnte mit einer geratenen ID ein fremdes Protokoll + // samt Geräteliste, IP-Adressen und offenen Ports abrufen. Das ist beim + // Kunden erhobene Netzstruktur — sie gehört hinter ein Recht. + // + // 'write' wird bewusst mit akzeptiert: In Dolibarr sind die Rechte einzeln + // vergebbar, ein Techniker-Benutzer mit Schreib- aber ohne ausdrückliches + // Leserecht ist möglich. Der würde sonst genau die Protokolle nicht mehr + // abrufen können, die er selbst hochgeladen hat — die App wäre nach dem + // Update kaputt, und zwar erst beim Kunden. Wer schreiben darf, darf lesen. + if (!$user->hasRight('netdiag', 'protocol', 'read') + && !$user->hasRight('netdiag', 'protocol', 'write')) { + netdiag_api_error('Keine Leseberechtigung', 403); + } $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; if ($id <= 0) { netdiag_api_error('Parameter id fehlt', 400); @@ -217,9 +232,17 @@ foreach ($measIn as $m) { $meas->fk_protocol = $protocolId; $dcid = isset($m['deviceClientId']) ? (string) $m['deviceClientId'] : ''; $meas->fk_device = ($dcid !== '' && isset($deviceIdMap[$dcid])) ? $deviceIdMap[$dcid] : null; - $meas->tool = isset($m['tool']) ? (string) $m['tool'] : 'unknown'; - $meas->category = isset($m['category']) ? (string) $m['category'] : ''; - $meas->label = isset($m['label']) ? (string) $m['label'] : ''; + // Auf die Spaltenlängen kürzen (varchar 64/32/255, siehe + // sql/llx_netdiag_measurement.sql). Ohne das kippt bei striktem SQL-Modus + // EIN zu langes Label den gesamten Sync: create() schlägt fehl, es folgt + // rollback() — und der Techniker steht beim Kunden mit einem Protokoll da, + // das sich nicht abschließen lässt, obwohl nur eine Beschriftung zu lang + // war. Die App setzt Labels durch Verketten zusammen (Raumname + SSID + + // Geschwindigkeit …), 255 Zeichen sind dabei erreichbar. Bei den Geräten + // wird längst so verfahren (found_via, netdiag_join_list weiter oben). + $meas->tool = substr(isset($m['tool']) ? (string) $m['tool'] : 'unknown', 0, 64); + $meas->category = substr(isset($m['category']) ? (string) $m['category'] : '', 0, 32); + $meas->label = substr(isset($m['label']) ? (string) $m['label'] : '', 0, 255); $meas->params = isset($m['params']) ? json_encode($m['params'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null; $meas->result = isset($m['result']) ? json_encode($m['result'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null; // Nur bekannte Bewertungen uebernehmen. Ein unbekannter Wert landete sonst diff --git a/lib/netdiag.lib.php b/lib/netdiag.lib.php index 6df1097..bb94ed0 100644 --- a/lib/netdiag.lib.php +++ b/lib/netdiag.lib.php @@ -378,14 +378,32 @@ function netdiagKundenfelder() 'ssid' => array('WLAN-Name', ''), 'raum' => array('Raum / Dose', ''), 'iface' => array('Schnittstelle', ''), - 'typ' => array('Anschlussart', ''), + // Anschlussart. ACHTUNG: hier stand bis 16.08.2026 'typ' — ein Schlüssel, + // den kein einziges Werkzeug erzeugt. Die App liefert das LinkInfo-Objekt + // durch, und dort heißt das Feld 'type' (app/src/lib/types.ts). Folge: + // im Kundendokument fehlte ausgerechnet beim IP-Test an der Dose die + // Angabe, ob per LAN oder WLAN gemessen wurde — bei einem Abnahmebeleg + // die halbe Aussage. Rohwerte sind englisch und werden in + // netdiagKundentext() übersetzt. + 'type' => array('Anschlussart', ''), + // Empfangspegel: bei einer WLAN-Dose IST das der Messwert. Wurde bisher + // verworfen, weil nicht in der Liste. + 'rssi' => array('Empfangspegel', 'dBm'), 'neu' => array('Neu hinzugekommen', ''), 'nichtMehrErreichbar' => array('Nicht mehr erreichbar', ''), 'demodaten' => array('DEMODATEN — keine echte Messung', ''), + // Bewusst NICHT aufgenommen (Messgeräte-Interna, für den Kunden ohne + // Aussage): 'bssid', 'isUsbEthernet', 'isDefault', 'dns'. // Fehler/Hinweise — immer zeigen, sonst bliebe eine gelbe oder rote Ampel // im Kundendokument ohne jede Erklärung stehen. Der Text läuft durch // netdiagKundentext() und wird dort von Messgeräte-Interna befreit. 'fehler' => array('Fehler', ''), + // 'error' ist die englische Schreibweise desselben Feldes. Die App + // vereinheitlicht auf 'fehler', aber in bereits ausgelieferten + // Protokollen steht 'error' — und Altdaten lassen sich nicht ändern. + // Ohne diesen Eintrag stünde dort eine rote Ampel ohne einen Buchstaben + // Erklärung. + 'error' => array('Fehler', ''), 'hinweis' => array('Hinweis', ''), 'kundenhinweis' => array('Hinweis', ''), ); @@ -407,9 +425,18 @@ function netdiagDauerLesbar($sek) return round($sek / 60).' min'; } if ($sek < 86400) { - return floor($sek / 3600).' Std '.round(($sek % 3600) / 60).' min'; + $std = floor($sek / 3600); + $min = round(($sek % 3600) / 60); + // Volle Stunde nicht als „2 Std 0 min" ausgeben + return $std.' Std'.($min > 0 ? ' '.$min.' min' : ''); } - return floor($sek / 86400).' Tage '.round(($sek % 86400) / 3600).' Std'; + // (int) nicht weglassen: floor() liefert einen Float, und 1.0 === 1 ist in + // PHP false — die Einzahl unten hätte sonst nie gegriffen. + $tage = (int) floor($sek / 86400); + $std = (int) round(($sek % 86400) / 3600); + // Einzahl/Mehrzahl und die glatte Angabe: eine DHCP-Lease von genau einem + // Tag las sich vorher als „1 Tage 0 Std". + return $tage.($tage === 1 ? ' Tag' : ' Tage').($std > 0 ? ' '.$std.' Std' : ''); } /** @@ -434,6 +461,16 @@ function netdiagFeldFuerKunde($key, $val) $flat[] = is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : (string) $item; } $val = implode(', ', $flat); + // Leeres Array = das gute Ergebnis: kein offener Port, kein IP-Konflikt, + // keine Veränderung seit dem letzten Scan. Bis 16.08.2026 stand hier im + // Kundendokument „Offene Ports: " ohne Wert — die Leerprüfung weiter + // unten ist ein elseif und wurde nach diesem Zweig nie erreicht. + // „keine" statt Weglassen, weil die Zeile sonst kommentarlos + // verschwindet und der Kunde nicht unterscheiden kann, ob nichts + // gefunden oder nichts geprüft wurde. + if ($val === '') { + $val = 'keine'; + } } elseif (is_bool($val)) { $val = $val ? 'ja' : 'nein'; } elseif ($val === null || $val === '') { @@ -466,6 +503,21 @@ function netdiagKundentext($text) if (stripos($text, '/proc/net/arp') !== false || stripos($text, 'braucht root') !== false) { return 'Mit dem eingesetzten Messgerät nicht prüfbar — die Aussage bleibt offen.'; } + // Anschlussart: Android liefert die Rohwerte englisch. Im Abnahmeprotokoll + // eines deutschen Handwerksbetriebs hat „Anschlussart: ethernet" nichts zu + // suchen. Exakter Vergleich statt stripos, damit nicht versehentlich ein + // Fließtext getroffen wird, in dem das Wort vorkommt. + $anschluss = array( + 'wifi' => 'WLAN', + 'ethernet' => 'LAN (Kabel)', + 'cellular' => 'Mobilfunk', + 'vpn' => 'VPN', + 'other' => 'sonstige', + ); + $k = strtolower(trim($text)); + if (isset($anschluss[$k])) { + return $anschluss[$k]; + } return $text; }