317 lines
8.8 KiB
PHP
317 lines
8.8 KiB
PHP
<?php
|
|
// api/target/quickcheck.php
|
|
|
|
/**
|
|
* Diese Funktion wird von api/index.php aufgerufen.
|
|
* Sie liest die Eingaben, prüft Seriennummer + VID, und gibt ein Array zurück,
|
|
* das dann als JSON ausgegeben wird.
|
|
*/
|
|
function quickcheck_handle_request(): array
|
|
{
|
|
// JSON Body hat Priorität
|
|
$source = quickcheck_get_input();
|
|
|
|
$vid = isset($source['vid']) ? trim((string)$source['vid']) : '';
|
|
$pid = isset($source['pid']) ? trim((string)$source['pid']) : '';
|
|
$manufacturer = isset($source['manufacturer']) ? trim((string)$source['manufacturer']) : '';
|
|
$serial = isset($source['serial']) ? trim((string)$source['serial']) : '';
|
|
|
|
if ($serial === '') {
|
|
http_response_code(400);
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Missing required parameter: serial',
|
|
];
|
|
}
|
|
|
|
// Mini-VID-Datenbank (kannst du später aus Datei/DB laden)
|
|
$VID_DB = [
|
|
'0781' => 'SanDisk Corp.',
|
|
'0951' => 'Kingston Technology',
|
|
'054C' => 'Sony Corp.',
|
|
'1B1C' => 'Corsair',
|
|
'13FE' => 'Phison Electronics',
|
|
'8564' => 'Transcend Information, Inc.',
|
|
'090C' => 'Silicon Motion, Inc.',
|
|
'174C' => 'ASMedia Technology',
|
|
'0BC2' => 'Seagate',
|
|
// ... nach Bedarf ergänzen
|
|
];
|
|
|
|
$vendorInfo = quickcheck_lookup_vendor($vid, $VID_DB);
|
|
$serialAnalysis = quickcheck_analyze_serial($serial);
|
|
$consistencyInfo = quickcheck_evaluate_consistency($vendorInfo['vendor'] ?? null, $manufacturer, $serialAnalysis);
|
|
|
|
// Gesamtrating
|
|
$rating = 'ok';
|
|
if ($serialAnalysis['category'] === 'invalid') {
|
|
$rating = 'invalid';
|
|
} elseif ($serialAnalysis['category'] === 'very_suspicious' || $consistencyInfo['manufacturer_match'] === 'mismatch') {
|
|
$rating = 'suspicious';
|
|
} elseif ($serialAnalysis['category'] === 'suspicious') {
|
|
$rating = 'needs_review';
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'rating' => $rating,
|
|
'input' => [
|
|
'vid' => $vid,
|
|
'pid' => $pid,
|
|
'manufacturer' => $manufacturer,
|
|
'serial' => $serial,
|
|
],
|
|
'vendor_detected' => $vendorInfo,
|
|
'serial_analysis' => $serialAnalysis,
|
|
'consistency' => $consistencyInfo,
|
|
'messages' => [
|
|
'de' => [
|
|
'Hinweis: Diese Prüfung kann keine Echtheit garantieren, sondern bewertet nur Plausibilität und Auffälligkeiten.',
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Eingaben lesen: JSON-Body > POST > GET
|
|
*/
|
|
function quickcheck_get_input(): array
|
|
{
|
|
$contentType = $_SERVER['CONTENT_TYPE'] ?? $_SERVER['HTTP_CONTENT_TYPE'] ?? '';
|
|
$contentType = strtolower(trim(explode(';', $contentType)[0]));
|
|
|
|
if ($contentType === 'application/json') {
|
|
$raw = file_get_contents('php://input');
|
|
if ($raw !== false && $raw !== '') {
|
|
$data = json_decode($raw, true);
|
|
if (json_last_error() === JSON_ERROR_NONE && is_array($data)) {
|
|
return $data;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($_POST)) {
|
|
return $_POST;
|
|
}
|
|
if (!empty($_GET)) {
|
|
return $_GET;
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* VID normalisieren + Lookup
|
|
*/
|
|
function quickcheck_normalize_vid(string $vid): ?string
|
|
{
|
|
$vid = strtoupper(trim($vid));
|
|
$vid = preg_replace('/^0X/i', '', $vid);
|
|
if ($vid === '' || !preg_match('/^[0-9A-F]{1,4}$/', $vid)) {
|
|
return null;
|
|
}
|
|
return str_pad($vid, 4, '0', STR_PAD_LEFT);
|
|
}
|
|
|
|
function quickcheck_lookup_vendor(?string $vid, array $VID_DB): array
|
|
{
|
|
if (!$vid) {
|
|
return [
|
|
'vid' => null,
|
|
'found' => false,
|
|
'vendor' => null,
|
|
'confidence' => 0,
|
|
];
|
|
}
|
|
|
|
$norm = quickcheck_normalize_vid($vid);
|
|
if (!$norm) {
|
|
return [
|
|
'vid' => $vid,
|
|
'found' => false,
|
|
'vendor' => null,
|
|
'confidence' => 0,
|
|
'issue' => 'Invalid VID format',
|
|
];
|
|
}
|
|
|
|
if (isset($VID_DB[$norm])) {
|
|
return [
|
|
'vid' => $norm,
|
|
'found' => true,
|
|
'vendor' => $VID_DB[$norm],
|
|
'confidence' => 1.0,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'vid' => $norm,
|
|
'found' => false,
|
|
'vendor' => null,
|
|
'confidence' => 0.2,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Seriennummer grob analysieren (Plausibilität)
|
|
*/
|
|
function quickcheck_analyze_serial(string $serial): array
|
|
{
|
|
$serial = trim($serial);
|
|
$length = strlen($serial);
|
|
$issues = [];
|
|
$score = 100;
|
|
|
|
if ($length === 0) {
|
|
return [
|
|
'serial' => $serial,
|
|
'length' => 0,
|
|
'issues' => ['empty'],
|
|
'score' => 0,
|
|
'category' => 'invalid',
|
|
];
|
|
}
|
|
|
|
if ($length < 4) {
|
|
$issues[] = 'too_short_critical';
|
|
$score -= 60;
|
|
} elseif ($length < 8) {
|
|
$issues[] = 'short_suspicious';
|
|
$score -= 30;
|
|
}
|
|
|
|
if ($length > 32) {
|
|
$issues[] = 'very_long';
|
|
$score -= 10;
|
|
}
|
|
|
|
if (quickcheck_is_all_same_char($serial)) {
|
|
$issues[] = 'all_same_char';
|
|
$score -= 50;
|
|
}
|
|
|
|
$simplePatterns = [
|
|
'000000', '00000000', '000000000000',
|
|
'111111', '123456', '12345678', 'ABCDEF', 'AABBCC'
|
|
];
|
|
if (in_array(strtoupper($serial), $simplePatterns, true)) {
|
|
$issues[] = 'simple_pattern';
|
|
$score -= 40;
|
|
}
|
|
|
|
if (quickcheck_is_simple_sequence($serial)) {
|
|
$issues[] = 'sequence_pattern';
|
|
$score -= 25;
|
|
}
|
|
|
|
$isNumeric = preg_match('/^[0-9]+$/', $serial) === 1;
|
|
$isAlpha = preg_match('/^[A-Za-z]+$/', $serial) === 1;
|
|
$isHex = preg_match('/^[0-9A-Fa-f]+$/', $serial) === 1;
|
|
$isAlnum = preg_match('/^[A-Za-z0-9]+$/', $serial) === 1;
|
|
|
|
if ($isNumeric) {
|
|
$issues[] = 'numeric_only';
|
|
$score -= 10;
|
|
} elseif ($isAlpha) {
|
|
$issues[] = 'letters_only';
|
|
$score -= 10;
|
|
}
|
|
|
|
if ($isHex && !$isNumeric && $length >= 8 && $length <= 24) {
|
|
$issues[] = 'hex_pattern_plausible';
|
|
$score += 5;
|
|
}
|
|
|
|
if ($isAlnum && !$isNumeric && !$isAlpha && $length >= 8) {
|
|
$issues[] = 'alnum_mixed_plausible';
|
|
$score += 5;
|
|
}
|
|
|
|
if ($score > 100) $score = 100;
|
|
if ($score < 0) $score = 0;
|
|
|
|
if ($length === 0) {
|
|
$category = 'invalid';
|
|
} elseif ($score >= 80) {
|
|
$category = 'plausible';
|
|
} elseif ($score >= 50) {
|
|
$category = 'suspicious';
|
|
} else {
|
|
$category = 'very_suspicious';
|
|
}
|
|
|
|
return [
|
|
'serial' => $serial,
|
|
'length' => $length,
|
|
'issues' => array_values(array_unique($issues)),
|
|
'score' => $score,
|
|
'category' => $category,
|
|
];
|
|
}
|
|
|
|
function quickcheck_is_all_same_char(string $s): bool
|
|
{
|
|
return strlen($s) > 1 && preg_match('/^(.)\1+$/', $s) === 1;
|
|
}
|
|
|
|
function quickcheck_is_simple_sequence(string $s): bool
|
|
{
|
|
$seqs = [
|
|
'123456', '1234567', '12345678', '234567', '345678',
|
|
'ABCDEFG', 'ABCDEF', 'ABCDE'
|
|
];
|
|
$ls = strtoupper($s);
|
|
foreach ($seqs as $pattern) {
|
|
if (strpos($ls, $ls) !== false && strpos($ls, $pattern) !== false) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Herstellerangabe vs. Vendor-Name bewerten
|
|
*/
|
|
function quickcheck_evaluate_consistency(?string $vendorName, string $userManufacturer, array $serialAnalysis): array
|
|
{
|
|
$vendorName = $vendorName ? trim($vendorName) : '';
|
|
$userManufacturer = trim($userManufacturer);
|
|
|
|
$matchStatus = 'unknown';
|
|
$notes = [];
|
|
|
|
if ($vendorName !== '' && $userManufacturer !== '') {
|
|
$v = mb_strtolower($vendorName);
|
|
$u = mb_strtolower($userManufacturer);
|
|
|
|
if (strpos($v, $u) !== false || strpos($u, $v) !== false) {
|
|
$matchStatus = 'match';
|
|
$notes[] = 'Herstellerangabe passt zur ermittelten Vendor-ID.';
|
|
} else {
|
|
$matchStatus = 'mismatch';
|
|
$notes[] = 'Herstellerangabe weicht von der Vendor-ID ab.';
|
|
}
|
|
} elseif ($vendorName !== '') {
|
|
$notes[] = 'Hersteller über Vendor-ID bekannt, aber keine Herstellerangabe des Nutzers.';
|
|
} elseif ($userManufacturer !== '') {
|
|
$notes[] = 'Hersteller vom Nutzer angegeben, aber keine oder unbekannte Vendor-ID.';
|
|
} else {
|
|
$notes[] = 'Keine Herstellerinformationen vorhanden.';
|
|
}
|
|
|
|
$ratingHint = 'neutral';
|
|
if ($serialAnalysis['category'] === 'very_suspicious' || $matchStatus === 'mismatch') {
|
|
$ratingHint = 'suspicious';
|
|
} elseif ($serialAnalysis['category'] === 'plausible' && $matchStatus === 'match') {
|
|
$ratingHint = 'good';
|
|
}
|
|
|
|
return [
|
|
'manufacturer_match' => $matchStatus,
|
|
'notes' => $notes,
|
|
'consistency_hint' => $ratingHint,
|
|
];
|
|
}
|
|
|
|
|