From eb4ab0c4bfaf1b46b87cff21adfe025076358141 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Nov 2025 21:43:13 +0000 Subject: [PATCH 01/10] feat: Add Motorola IMEI lookup page Co-authored-by: sifan99sifan99 --- index.html | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..fae25f7 --- /dev/null +++ b/index.html @@ -0,0 +1,250 @@ + + + + + + استعلام بيانات IMEI لهواتف Motorola + + + +
+

استعلام بيانات Motorola عبر IMEI

+ + + +
+
+ + + + + + + + + +
IMEI
Model
Unlock Code
Lock 4
Lock 5
Message
+
+ +
+ + + + From 5a3f32a3f51347d98dcf99c6151026cb4d2ac64d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Nov 2025 22:00:33 +0000 Subject: [PATCH 02/10] feat: Add proxy to handle API requests Co-authored-by: sifan99sifan99 --- index.html | 4 ++-- proxy.php | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 proxy.php diff --git a/index.html b/index.html index fae25f7..857496d 100644 --- a/index.html +++ b/index.html @@ -178,7 +178,7 @@

استعلام بيانات Motorola عبر IMEI

message: document.getElementById('res-message') }; - const apiBase = 'https://general-unlocker.com/testsifan/index.php?Data='; + const apiBase = 'proxy.php?imei='; const setStatus = (message, type = '') => { statusBox.textContent = message; @@ -219,7 +219,7 @@

استعلام بيانات Motorola عبر IMEI

setStatus('جارٍ جلب البيانات ...'); try { - const response = await fetch(apiBase + imei, {cache: 'no-store'}); + const response = await fetch(apiBase + encodeURIComponent(imei), {cache: 'no-store'}); if (!response.ok) { throw new Error('تعذر الوصول إلى الخدمة'); } diff --git a/proxy.php b/proxy.php new file mode 100644 index 0000000..92b601d --- /dev/null +++ b/proxy.php @@ -0,0 +1,37 @@ + 'imei parameter must be a 15-digit number.']); + exit; +} + +$remoteUrl = 'https://general-unlocker.com/testsifan/index.php?Data=' . $imei; + +$ch = curl_init($remoteUrl); +curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_TIMEOUT => 25, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_HTTPHEADER => [ + 'Accept: application/json', + 'Cache-Control: no-cache', + ], +]); + +$response = curl_exec($ch); +$errno = curl_errno($ch); +$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); +curl_close($ch); + +if ($errno !== 0 || $httpCode !== 200 || empty($response)) { + echo json_encode(['error' => 'Remote service is unavailable right now.']); + exit; +} + +echo $response; From d6fc041baecf7e4c0b859c8d19121a716192b3d0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Nov 2025 22:09:43 +0000 Subject: [PATCH 03/10] Refactor: Improve UI and add multi-IMEI support Co-authored-by: sifan99sifan99 --- index.html | 388 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 295 insertions(+), 93 deletions(-) diff --git a/index.html b/index.html index 857496d..41b4685 100644 --- a/index.html +++ b/index.html @@ -21,18 +21,18 @@ } .card { - width: min(480px, 92%); - background: rgba(10, 26, 43, 0.85); - border-radius: 18px; - padding: 32px 28px; + width: min(860px, 95%); + background: rgba(10, 26, 43, 0.88); + border-radius: 22px; + padding: 34px 32px; box-shadow: 0 25px 65px rgba(3, 9, 15, 0.7); - backdrop-filter: blur(8px); + backdrop-filter: blur(10px); } h1 { - margin-top: 0; + margin: 0 0 18px 0; font-weight: 600; - font-size: 1.6rem; + font-size: 1.65rem; text-align: center; letter-spacing: 0.03em; } @@ -44,35 +44,53 @@ font-weight: 500; } - input { + textarea { width: 100%; - padding: 12px 14px; - border-radius: 10px; + min-height: 130px; + padding: 14px 16px; + border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.15); - background: rgba(15, 31, 51, 0.9); + background: rgba(15, 31, 51, 0.92); color: #f7f8fa; font-size: 1rem; direction: ltr; + resize: vertical; } - input:focus { + textarea:focus { outline: none; border-color: #4db5ff; box-shadow: 0 0 0 3px rgba(77, 181, 255, 0.25); } + .input-hint { + margin-top: 6px; + font-size: 0.84rem; + color: #9fbed3; + } + + .controls { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin-top: 18px; + } + + .controls button { + flex: 1; + min-width: 160px; + } + button { - width: 100%; padding: 12px; - margin-top: 16px; border: none; border-radius: 10px; background: linear-gradient(135deg, #4db5ff, #31a3f0); color: #041322; - font-size: 1.05rem; + font-size: 1.02rem; font-weight: 600; cursor: pointer; - transition: transform 0.15s ease, box-shadow 0.15s ease; + transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.2s ease; } button:disabled { @@ -85,6 +103,17 @@ box-shadow: 0 10px 25px rgba(62, 150, 230, 0.4); } + .ghost-btn { + background: transparent; + border: 1px solid rgba(255, 255, 255, 0.25); + color: #dce9fb; + box-shadow: none; + } + + .ghost-btn:not(:disabled):hover { + box-shadow: 0 0 0 1px rgba(77, 181, 255, 0.4); + } + .status { min-height: 24px; margin-top: 18px; @@ -101,35 +130,106 @@ } .result { - margin-top: 20px; - border-radius: 12px; - background: rgba(4, 19, 34, 0.7); - padding: 18px; + margin-top: 22px; + border-radius: 16px; + background: rgba(4, 19, 34, 0.78); + padding: 20px 18px; + } + + .result[hidden] { display: none; } - .result table { + .result-toolbar { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 12px; + } + + .result-toolbar span { + font-size: 0.95rem; + color: #9fbed3; + } + + .result-list { + margin-top: 14px; + display: flex; + flex-direction: column; + gap: 12px; + } + + .result-card { + border-radius: 12px; + border: 1px solid rgba(255, 255, 255, 0.06); + background: rgba(3, 11, 19, 0.9); + padding: 16px; + } + + .result-head { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 12px; + justify-content: space-between; + } + + .result-head-info { + display: flex; + align-items: center; + gap: 8px; + font-size: 1rem; + } + + .imei-label { + border-radius: 8px; + padding: 4px 10px; + background: rgba(77, 181, 255, 0.18); + color: #b9dfff; + font-size: 0.85rem; + } + + .imei-value { + direction: ltr; + letter-spacing: 0.04em; + } + + .result-body { + margin-top: 14px; + } + + .result-message { + margin: 0 0 10px 0; + font-size: 0.95rem; + color: #9fbed3; + } + + .result-message.error { + color: #ff8b8b; + } + + .result-body table { width: 100%; border-collapse: collapse; - font-size: 0.95rem; + font-size: 0.92rem; } - .result th, - .result td { - padding: 10px 8px; + .result-body th, + .result-body td { + padding: 8px 6px; border-bottom: 1px solid rgba(255, 255, 255, 0.08); - text-align: left; direction: ltr; } - .result th { + .result-body th { text-align: right; color: #9fbed3; width: 40%; font-weight: 500; } - .result td { + .result-body td { color: #f5f8fc; font-weight: 600; } @@ -145,38 +245,36 @@

استعلام بيانات Motorola عبر IMEI

- - - + + +

يمكنك إدخال عدة IMEI مفصولين بمسافة، فاصلة، أو سطر جديد.

+
+ + +
-
- - - - - - - - - -
IMEI
Model
Unlock Code
Lock 4
Lock 5
Message
+
From 14382315015aab7b87effefe45abdc1f51097380 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Nov 2025 22:25:31 +0000 Subject: [PATCH 04/10] Refactor: Improve Motorola IMEI lookup and display This commit updates the proxy.php to use the Motorola SOAP API for IMEI lookups and enhances the frontend to display the detailed results. Co-authored-by: sifan99sifan99 --- index.html | 160 +++++++++++++++++++++++++++++++++++++++++++++++++---- proxy.php | 123 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 265 insertions(+), 18 deletions(-) diff --git a/index.html b/index.html index 41b4685..cadda23 100644 --- a/index.html +++ b/index.html @@ -215,25 +215,67 @@ font-size: 0.92rem; } - .result-body th, - .result-body td { + .summary-table, + .detail-table { + width: 100%; + border-collapse: collapse; + font-size: 0.92rem; + } + + .summary-table th, + .summary-table td, + .detail-table th, + .detail-table td { padding: 8px 6px; border-bottom: 1px solid rgba(255, 255, 255, 0.08); direction: ltr; } - .result-body th { + .summary-table th, + .detail-table th { text-align: right; color: #9fbed3; width: 40%; font-weight: 500; } - .result-body td { + .summary-table td, + .detail-table td { color: #f5f8fc; font-weight: 600; } + .mini-title { + margin: 16px 0 6px; + font-size: 0.9rem; + color: #9fbed3; + font-weight: 600; + } + + details { + margin-top: 16px; + } + + details summary { + cursor: pointer; + color: #b9dfff; + font-size: 0.9rem; + font-weight: 600; + margin-bottom: 8px; + } + + details summary::marker { + color: #4db5ff; + } + + code { + font-family: "Fira Code", "Consolas", monospace; + font-size: 0.85rem; + background: rgba(255, 255, 255, 0.08); + padding: 2px 4px; + border-radius: 4px; + } + .footer-note { margin-top: 18px; font-size: 0.8rem; @@ -277,6 +319,52 @@

استعلام بيانات Motorola عبر IMEI

const resultCount = document.getElementById('result-count'); const apiBase = 'proxy.php?imei='; + const summaryFieldDefs = [ + {key: 'model', label: 'موديل الجهاز'}, + {key: 'carrier', label: 'شركة / ناقل الجهاز'}, + {key: 'country', label: 'الدولة'}, + {key: 'unlock_code', label: 'كود الفتح'}, + {key: 'lock_4', label: 'Lock 4'}, + {key: 'lock_5', label: 'Lock 5'}, + {key: 'status', label: 'حالة الضمان'}, + {key: 'device_type', label: 'نوع الجهاز'}, + {key: 'apc', label: 'APC'}, + ]; + const customerLabels = { + ship_to: 'Ship To', + sold_to: 'Sold To', + direct_ship: 'Direct Ship', + }; + const countryCodeLabels = { + ship: 'Country To Ship', + direct: 'Direct Ship Code', + warranty: 'Warranty Country', + }; + const detailLabels = { + response_code: 'رمز الاستجابة', + response_message: 'رسالة الاستجابة', + serial_no: 'Serial Number', + serial_no_type: 'نوع الرقم', + status_code: 'رمز الحالة', + master_service_lock: 'Master Service Lock', + master_lock_code: 'Master Lock Code', + onetime_lock: 'One-Time Lock', + lock_4: 'Lock 4', + lock_5: 'Lock 5', + deviceunlockcode: 'Device Unlock Code', + service_password: 'Service Password', + device_type: 'نوع الجهاز', + carrier_model_info: 'Carrier Model Info', + external_marketing_name: 'اسم التسويق', + country_to_ship: 'Country to Ship', + direct_ship_country_code: 'Direct Ship Country Code', + warranty_country_code: 'Warranty Country Code', + ship_to_cust_name: 'Ship To Customer', + sold_to_cust_name: 'Sold To Customer', + direct_ship_cust_name: 'Direct Ship Customer', + apc: 'APC', + status: 'Status', + }; const setStatus = (message, type = '') => { statusBox.textContent = message; @@ -302,6 +390,22 @@

استعلام بيانات Motorola عبر IMEI

}[char])); }; + const formatKey = (key) => detailLabels[key] ?? key.replace(/_/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase()); + + const stringifyValue = (value) => { + if (value === null || value === undefined || value === '') { + return '-'; + } + if (Array.isArray(value) || typeof value === 'object') { + try { + return JSON.stringify(value, null, 2); + } catch (error) { + return String(value); + } + } + return String(value); + }; + const parseImeis = (raw) => { const matches = raw.match(/\d{15}/g); if (!matches) { @@ -352,15 +456,51 @@

استعلام بيانات Motorola عبر IMEI

}; const renderSuccess = (cardObj, data) => { + const summaryRows = summaryFieldDefs + .map(({key, label}) => { + const value = data[key]; + if (value === null || value === undefined || value === '') { + return ''; + } + return `${label}${escapeHtml(value).replace(/\n/g, '
')}`; + }) + .filter(Boolean) + .join(''); + + const customerRows = data.customer + ? Object.entries(data.customer) + .filter(([, value]) => value) + .map(([key, value]) => `${customerLabels[key] ?? key}${escapeHtml(value)}`) + .join('') + : ''; + + const countryRows = data.country_codes + ? Object.entries(data.country_codes) + .filter(([, value]) => value) + .map(([key, value]) => `${countryCodeLabels[key] ?? key}${escapeHtml(value)}`) + .join('') + : ''; + + const rawEntries = data.raw && typeof data.raw === 'object' + ? Object.entries(data.raw) + : []; + + const detailRows = rawEntries + .map(([key, value]) => { + const printable = escapeHtml(stringifyValue(value)).replace(/\n/g, '
'); + return `${formatKey(key)}${printable}`; + }) + .join(''); + cardObj.body.innerHTML = ` -

${escapeHtml(data.message ?? 'Success (API)')}

- +

${escapeHtml(data.message ?? 'تم جلب البيانات بنجاح.')}

+
- - - - + ${summaryRows || ''}
IMEI${escapeHtml(data.imei)}
Model${escapeHtml(data.model)}
Unlock Code${escapeHtml(data.code)}
Lock 4${escapeHtml(data.lock_4)}
Lock 5${escapeHtml(data.lock_5)}
لا توجد بيانات إضافية.
+ ${customerRows ? `

بيانات العميل

${customerRows}
` : ''} + ${countryRows ? `

رموز الدول

${countryRows}
` : ''} + ${detailRows ? `
عرض ${rawEntries.length} حقل (استجابة كاملة)${detailRows}
` : ''} `; }; diff --git a/proxy.php b/proxy.php index 92b601d..262d0af 100644 --- a/proxy.php +++ b/proxy.php @@ -10,17 +10,67 @@ exit; } -$remoteUrl = 'https://general-unlocker.com/testsifan/index.php?Data=' . $imei; +function mungXml(string $xml): string +{ + $obj = @simplexml_load_string($xml); + if ($obj === false) { + return $xml; + } -$ch = curl_init($remoteUrl); + $namespaces = $obj->getNamespaces(true); + if (empty($namespaces)) { + return $xml; + } + + foreach (array_keys($namespaces) as $key) { + $pattern = '#(<\/?)' . preg_quote($key, '#') . ':#'; + $xml = preg_replace($pattern, '$1' . $key . '_', $xml); + } + + return $xml; +} + +function xmlToArray(string $xmlString): array +{ + $simple = @simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA); + if ($simple === false) { + return []; + } + + return json_decode(json_encode($simple), true) ?? []; +} + +$soapPayload = << + + + MST + NS1xpq#Y + IMEI + {$imei} + + Y + + + + + +XML; + +$ch = curl_init('https://wsgw.motorola.com/IQS_WARRANTY_CHECK_1.0'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, - CURLOPT_TIMEOUT => 25, - CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_TIMEOUT => 30, + CURLOPT_CONNECTTIMEOUT => 12, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $soapPayload, CURLOPT_HTTPHEADER => [ - 'Accept: application/json', - 'Cache-Control: no-cache', + 'Authorization: Basic bGVub3ZvMjAxODpMZW5vdm9AMjAxOA==', + 'Content-Type: text/xml; charset=utf-8', + 'Accept: text/xml', + 'Host: wsgw.motorola.com', + 'Content-Length: ' . strlen($soapPayload), ], ]); @@ -30,8 +80,65 @@ curl_close($ch); if ($errno !== 0 || $httpCode !== 200 || empty($response)) { - echo json_encode(['error' => 'Remote service is unavailable right now.']); + echo json_encode(['error' => 'Motorola service is unavailable right now.']); exit; } -echo $response; +$cleanXml = mungXml(trim($response)); +$parsed = xmlToArray($cleanXml); +$body = $parsed['Envelope']['Body'] ?? null; + +$resultNode = null; +if (is_array($body)) { + if (!empty($body['serviceResponse']['device_unlock_code_result'])) { + $resultNode = $body['serviceResponse']['device_unlock_code_result']; + } elseif (!empty($body['service_newResponse']['device_unlock_code_result'])) { + $resultNode = $body['service_newResponse']['device_unlock_code_result']; + } +} + +if (!is_array($resultNode)) { + echo json_encode(['error' => 'Unable to parse Motorola response.']); + exit; +} + +$derived = [ + 'imei' => $resultNode['serial_no'] ?? $imei, + 'serial_no_type' => $resultNode['serial_no_type'] ?? 'IMEI', + 'model' => $resultNode['carrier_model_info'] ?? $resultNode['external_marketing_name'] ?? null, + 'carrier' => $resultNode['ship_to_cust_name'] + ?? $resultNode['sold_to_cust_name'] + ?? $resultNode['direct_ship_cust_name'] + ?? $resultNode['carrier_model_info'] + ?? null, + 'country' => $resultNode['country_to_ship'] + ?? $resultNode['direct_ship_country_code'] + ?? $resultNode['warranty_country_code'] + ?? null, + 'unlock_code' => $resultNode['master_service_lock'] + ?? $resultNode['master_lock_code'] + ?? $resultNode['deviceunlockcode'] + ?? null, + 'lock_4' => $resultNode['lock_4'] ?? $resultNode['master_service_lock'] ?? null, + 'lock_5' => $resultNode['lock_5'] ?? null, + 'status' => $resultNode['status'] ?? $resultNode['status_code'] ?? null, + 'device_type' => $resultNode['device_type'] ?? null, + 'apc' => $resultNode['apc'] ?? null, + 'message' => $resultNode['response_message'] ?? null, +]; + +$payload = array_merge($derived, [ + 'customer' => [ + 'ship_to' => $resultNode['ship_to_cust_name'] ?? null, + 'sold_to' => $resultNode['sold_to_cust_name'] ?? null, + 'direct_ship' => $resultNode['direct_ship_cust_name'] ?? null, + ], + 'country_codes' => [ + 'ship' => $resultNode['country_to_ship'] ?? null, + 'direct' => $resultNode['direct_ship_country_code'] ?? null, + 'warranty' => $resultNode['warranty_country_code'] ?? null, + ], + 'raw' => $resultNode, +]); + +echo json_encode($payload, JSON_UNESCAPED_UNICODE); From 37e3945400c6c8efe5d5d11bbbc856a40852895a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Nov 2025 22:31:24 +0000 Subject: [PATCH 05/10] Refactor XML parsing and node finding logic Co-authored-by: sifan99sifan99 --- proxy.php | 58 +++++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/proxy.php b/proxy.php index 262d0af..0ab39ec 100644 --- a/proxy.php +++ b/proxy.php @@ -10,34 +10,38 @@ exit; } -function mungXml(string $xml): string +function xmlToArray(string $xmlString): array { - $obj = @simplexml_load_string($xml); - if ($obj === false) { - return $xml; - } - - $namespaces = $obj->getNamespaces(true); - if (empty($namespaces)) { - return $xml; + $simple = @simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA); + if ($simple === false) { + return []; } - foreach (array_keys($namespaces) as $key) { - $pattern = '#(<\/?)' . preg_quote($key, '#') . ':#'; - $xml = preg_replace($pattern, '$1' . $key . '_', $xml); - } + return json_decode(json_encode($simple), true) ?? []; +} - return $xml; +function stripNamespace(string $key): string +{ + return strpos($key, ':') !== false ? substr($key, strrpos($key, ':') + 1) : $key; } -function xmlToArray(string $xmlString): array +function findNode(array $data, string $targetKey): ?array { - $simple = @simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA); - if ($simple === false) { - return []; + foreach ($data as $key => $value) { + $plainKey = is_string($key) ? stripNamespace($key) : $key; + if ($plainKey === $targetKey && is_array($value)) { + return $value; + } + + if (is_array($value)) { + $found = findNode($value, $targetKey); + if ($found !== null) { + return $found; + } + } } - return json_decode(json_encode($simple), true) ?? []; + return null; } $soapPayload = << 'Unable to parse Motorola response.']); exit; } From 3c012da25af4be29c21ae1db36ad5ba3b4ca28d3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Nov 2025 22:38:25 +0000 Subject: [PATCH 06/10] Refactor: Improve XML parsing and key normalization Co-authored-by: sifan99sifan99 --- proxy.php | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/proxy.php b/proxy.php index 0ab39ec..e650d10 100644 --- a/proxy.php +++ b/proxy.php @@ -25,23 +25,29 @@ function stripNamespace(string $key): string return strpos($key, ':') !== false ? substr($key, strrpos($key, ':') + 1) : $key; } -function findNode(array $data, string $targetKey): ?array +function normalizeKeys($data) { + if (!is_array($data)) { + return $data; + } + + $normalized = []; + foreach ($data as $key => $value) { - $plainKey = is_string($key) ? stripNamespace($key) : $key; - if ($plainKey === $targetKey && is_array($value)) { - return $value; - } + $newValue = normalizeKeys($value); + $newKey = is_string($key) ? stripNamespace($key) : $key; - if (is_array($value)) { - $found = findNode($value, $targetKey); - if ($found !== null) { - return $found; + if (array_key_exists($newKey, $normalized)) { + if (!is_array($normalized[$newKey]) || array_keys($normalized[$newKey]) !== range(0, count($normalized[$newKey]) - 1)) { + $normalized[$newKey] = [$normalized[$newKey]]; } + $normalized[$newKey][] = $newValue; + } else { + $normalized[$newKey] = $newValue; } } - return null; + return $normalized; } $soapPayload = <<{'device_unlock_code_result'})) { + $resultNode = $body['service']['device_unlock_code_result']; + } +} + +if (is_array($resultNode) && isset($resultNode[0])) { + $resultNode = $resultNode[0]; +} -if ($resultNode === null) { +if (!is_array($resultNode)) { echo json_encode(['error' => 'Unable to parse Motorola response.']); exit; } From c893d066aabf4beecfb3b185adf7ac8ed70513c7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Nov 2025 22:43:45 +0000 Subject: [PATCH 07/10] Refactor: Improve device unlock code result parsing Co-authored-by: sifan99sifan99 --- proxy.php | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/proxy.php b/proxy.php index e650d10..3e5b6d8 100644 --- a/proxy.php +++ b/proxy.php @@ -50,6 +50,30 @@ function normalizeKeys($data) return $normalized; } +function findDeviceResult($data) +{ + if (!is_array($data)) { + return null; + } + + foreach ($data as $key => $value) { + $plainKey = is_string($key) ? stripNamespace($key) : $key; + if ($plainKey === 'device_unlock_code_result') { + if (is_array($value) && isset($value[0]) && is_array($value[0])) { + return $value[0]; + } + return is_array($value) ? $value : null; + } + + $found = findDeviceResult($value); + if ($found !== null) { + return $found; + } + } + + return null; +} + $soapPayload = << @@ -96,22 +120,7 @@ function normalizeKeys($data) $parsed = xmlToArray(trim($response)); $normalized = normalizeKeys($parsed); -$body = $normalized['Envelope']['Body'] ?? null; -$resultNode = null; - -if (is_array($body)) { - if (!empty($body['serviceResponse']['device_unlock_code_result'])) { - $resultNode = $body['serviceResponse']['device_unlock_code_result']; - } elseif (!empty($body['service_newResponse']['device_unlock_code_result'])) { - $resultNode = $body['service_newResponse']['device_unlock_code_result']; - } elseif (!empty($body['service']->{'device_unlock_code_result'})) { - $resultNode = $body['service']['device_unlock_code_result']; - } -} - -if (is_array($resultNode) && isset($resultNode[0])) { - $resultNode = $resultNode[0]; -} +$resultNode = findDeviceResult($normalized); if (!is_array($resultNode)) { echo json_encode(['error' => 'Unable to parse Motorola response.']); From aacfe97b3d98f1dce07ef8ea536f764e233b33e1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Nov 2025 22:48:53 +0000 Subject: [PATCH 08/10] Refactor: Simplify XML parsing and error handling Co-authored-by: sifan99sifan99 --- proxy.php | 74 ++++++++++--------------------------------------------- 1 file changed, 13 insertions(+), 61 deletions(-) diff --git a/proxy.php b/proxy.php index 3e5b6d8..f7ba610 100644 --- a/proxy.php +++ b/proxy.php @@ -5,73 +5,27 @@ $imei = $_GET['imei'] ?? ''; +$baseResponse = ['imei' => $imei]; + if (!preg_match('/^\d{15}$/', $imei)) { - echo json_encode(['error' => 'imei parameter must be a 15-digit number.']); + echo json_encode($baseResponse + ['error' => 'imei parameter must be a 15-digit number.']); exit; } -function xmlToArray(string $xmlString): array +function extractDeviceResult(string $xml): ?array { - $simple = @simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA); + libxml_use_internal_errors(true); + $simple = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); if ($simple === false) { - return []; - } - - return json_decode(json_encode($simple), true) ?? []; -} - -function stripNamespace(string $key): string -{ - return strpos($key, ':') !== false ? substr($key, strrpos($key, ':') + 1) : $key; -} - -function normalizeKeys($data) -{ - if (!is_array($data)) { - return $data; - } - - $normalized = []; - - foreach ($data as $key => $value) { - $newValue = normalizeKeys($value); - $newKey = is_string($key) ? stripNamespace($key) : $key; - - if (array_key_exists($newKey, $normalized)) { - if (!is_array($normalized[$newKey]) || array_keys($normalized[$newKey]) !== range(0, count($normalized[$newKey]) - 1)) { - $normalized[$newKey] = [$normalized[$newKey]]; - } - $normalized[$newKey][] = $newValue; - } else { - $normalized[$newKey] = $newValue; - } - } - - return $normalized; -} - -function findDeviceResult($data) -{ - if (!is_array($data)) { return null; } - foreach ($data as $key => $value) { - $plainKey = is_string($key) ? stripNamespace($key) : $key; - if ($plainKey === 'device_unlock_code_result') { - if (is_array($value) && isset($value[0]) && is_array($value[0])) { - return $value[0]; - } - return is_array($value) ? $value : null; - } - - $found = findDeviceResult($value); - if ($found !== null) { - return $found; - } + $nodes = $simple->xpath('//*[local-name()="device_unlock_code_result"]'); + if (!$nodes || !isset($nodes[0])) { + return null; } - return null; + return json_decode(json_encode($nodes[0]), true) ?? null; } $soapPayload = << 'Motorola service is unavailable right now.']); + echo json_encode($baseResponse + ['error' => 'Motorola service is unavailable right now.']); exit; } -$parsed = xmlToArray(trim($response)); -$normalized = normalizeKeys($parsed); -$resultNode = findDeviceResult($normalized); +$resultNode = extractDeviceResult($response); if (!is_array($resultNode)) { - echo json_encode(['error' => 'Unable to parse Motorola response.']); + echo json_encode($baseResponse + ['error' => 'Unable to parse Motorola response.']); exit; } From ca755549c05d1050ad17b5dbdb844dd9ae09bbb4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Nov 2025 23:01:18 +0000 Subject: [PATCH 09/10] Add copy IMEI + code button and improve data extraction Co-authored-by: sifan99sifan99 --- index.html | 37 +++++++++++++++- proxy.php | 122 ++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 127 insertions(+), 32 deletions(-) diff --git a/index.html b/index.html index cadda23..a6ee3e9 100644 --- a/index.html +++ b/index.html @@ -195,6 +195,16 @@ letter-spacing: 0.04em; } + .result-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + } + + .result-actions button { + flex: 1 1 140px; + } + .result-body { margin-top: 14px; } @@ -444,7 +454,17 @@

استعلام بيانات Motorola عبر IMEI

copyBtn.textContent = 'نسخ النتيجة'; copyBtn.disabled = true; - head.append(headInfo, copyBtn); + const copyPairBtn = document.createElement('button'); + copyPairBtn.type = 'button'; + copyPairBtn.className = 'ghost-btn'; + copyPairBtn.textContent = 'نسخ IMEI + الكود'; + copyPairBtn.disabled = true; + + const actions = document.createElement('div'); + actions.className = 'result-actions'; + actions.append(copyBtn, copyPairBtn); + + head.append(headInfo, actions); card.appendChild(head); const body = document.createElement('div'); @@ -452,7 +472,7 @@

استعلام بيانات Motorola عبر IMEI

body.textContent = 'جارٍ الاستعلام ...'; card.appendChild(body); - return {card, copyBtn, body}; + return {card, copyBtn, copyPairBtn, body}; }; const renderSuccess = (cardObj, data) => { @@ -517,6 +537,16 @@

استعلام بيانات Motorola عبر IMEI

} }; + const copyImeiCode = async (data) => { + const content = `IMEI: ${data.imei}\nMaster Service Lock: ${data.unlock_code}`; + try { + await navigator.clipboard.writeText(content); + setStatus('تم نسخ IMEI + الكود.', 'success'); + } catch (error) { + setStatus('تعذر نسخ البيانات. يرجى السماح بالوصول إلى الحافظة.', 'error'); + } + }; + const fetchForImei = async (imei) => { const response = await fetch(apiBase + encodeURIComponent(imei), {cache: 'no-store'}); if (!response.ok) { @@ -553,12 +583,15 @@

استعلام بيانات Motorola عبر IMEI

renderSuccess(cardObj, data); cardObj.copyBtn.disabled = false; cardObj.copyBtn.addEventListener('click', () => copyJson(data, `نتيجة IMEI ${imei}`)); + cardObj.copyPairBtn.disabled = false; + cardObj.copyPairBtn.addEventListener('click', () => copyImeiCode(data)); } catch (error) { const errPayload = {imei, error: error.message || 'خطأ غير معروف'}; collected.push(errPayload); renderError(cardObj, errPayload.error); cardObj.copyBtn.disabled = false; cardObj.copyBtn.addEventListener('click', () => copyJson(errPayload, `نتيجة IMEI ${imei}`)); + cardObj.copyPairBtn.disabled = true; } } diff --git a/proxy.php b/proxy.php index f7ba610..2c8086a 100644 --- a/proxy.php +++ b/proxy.php @@ -4,7 +4,6 @@ header('Content-Type: application/json; charset=utf-8'); $imei = $_GET['imei'] ?? ''; - $baseResponse = ['imei' => $imei]; if (!preg_match('/^\d{15}$/', $imei)) { @@ -28,6 +27,59 @@ function extractDeviceResult(string $xml): ?array return json_decode(json_encode($nodes[0]), true) ?? null; } +function fieldValue(array $source, string $key): ?string +{ + if (!array_key_exists($key, $source)) { + return null; + } + $value = $source[$key]; + + if (is_array($value)) { + $value = array_filter($value, static function ($item) { + if (is_array($item)) { + return !empty(array_filter($item, static fn($nested) => $nested !== null && $nested !== '' && $nested !== [])); + } + return $item !== null && $item !== '' && $item !== []; + }); + + if (empty($value)) { + return null; + } + + $value = reset($value); + } + + if ($value === null) { + return null; + } + + $value = trim((string)$value); + + return $value === '' ? null : $value; +} + +function orNo(?string $value): string +{ + return $value === null ? 'NO' : $value; +} + +function normalizeRawValue($value) +{ + if (is_array($value)) { + if (empty($value)) { + return 'NO'; + } + $normalized = []; + foreach ($value as $k => $v) { + $normalized[$k] = normalizeRawValue($v); + } + return $normalized; + } + + $value = trim((string)$value); + return $value === '' ? 'NO' : $value; +} + $soapPayload = << @@ -79,43 +131,53 @@ function extractDeviceResult(string $xml): ?array exit; } +$model = fieldValue($resultNode, 'carrier_model_info') ?? fieldValue($resultNode, 'external_marketing_name'); +$carrier = fieldValue($resultNode, 'ship_to_cust_name') + ?? fieldValue($resultNode, 'sold_to_cust_name') + ?? fieldValue($resultNode, 'direct_ship_cust_name') + ?? $model; +$country = fieldValue($resultNode, 'country_to_ship') + ?? fieldValue($resultNode, 'direct_ship_country_code') + ?? fieldValue($resultNode, 'warranty_country_code'); + +$rawUnlock = fieldValue($resultNode, 'master_service_lock') + ?? fieldValue($resultNode, 'master_lock_code') + ?? fieldValue($resultNode, 'deviceunlockcode'); + +if ($rawUnlock !== null && strpos($imei, $rawUnlock) === 0 && strlen($rawUnlock) === 8) { + $rawUnlock = 'No Code'; +} + +$lock4 = fieldValue($resultNode, 'lock_4') ?? $rawUnlock; +$lock5 = fieldValue($resultNode, 'lock_5'); + $derived = [ - 'imei' => $resultNode['serial_no'] ?? $imei, - 'serial_no_type' => $resultNode['serial_no_type'] ?? 'IMEI', - 'model' => $resultNode['carrier_model_info'] ?? $resultNode['external_marketing_name'] ?? null, - 'carrier' => $resultNode['ship_to_cust_name'] - ?? $resultNode['sold_to_cust_name'] - ?? $resultNode['direct_ship_cust_name'] - ?? $resultNode['carrier_model_info'] - ?? null, - 'country' => $resultNode['country_to_ship'] - ?? $resultNode['direct_ship_country_code'] - ?? $resultNode['warranty_country_code'] - ?? null, - 'unlock_code' => $resultNode['master_service_lock'] - ?? $resultNode['master_lock_code'] - ?? $resultNode['deviceunlockcode'] - ?? null, - 'lock_4' => $resultNode['lock_4'] ?? $resultNode['master_service_lock'] ?? null, - 'lock_5' => $resultNode['lock_5'] ?? null, - 'status' => $resultNode['status'] ?? $resultNode['status_code'] ?? null, - 'device_type' => $resultNode['device_type'] ?? null, - 'apc' => $resultNode['apc'] ?? null, - 'message' => $resultNode['response_message'] ?? null, + 'imei' => orNo(fieldValue($resultNode, 'serial_no') ?? $imei), + 'serial_no_type' => orNo(fieldValue($resultNode, 'serial_no_type') ?? 'IMEI'), + 'model' => orNo($model), + 'carrier' => orNo($carrier), + 'country' => orNo($country), + 'unlock_code' => orNo($rawUnlock), + 'lock_4' => orNo($lock4), + 'lock_5' => orNo($lock5), + 'status' => orNo(fieldValue($resultNode, 'status') ?? fieldValue($resultNode, 'status_code')), + 'device_type' => orNo(fieldValue($resultNode, 'device_type')), + 'apc' => orNo(fieldValue($resultNode, 'apc')), + 'message' => orNo(fieldValue($resultNode, 'response_message')), ]; $payload = array_merge($derived, [ 'customer' => [ - 'ship_to' => $resultNode['ship_to_cust_name'] ?? null, - 'sold_to' => $resultNode['sold_to_cust_name'] ?? null, - 'direct_ship' => $resultNode['direct_ship_cust_name'] ?? null, + 'ship_to' => orNo(fieldValue($resultNode, 'ship_to_cust_name')), + 'sold_to' => orNo(fieldValue($resultNode, 'sold_to_cust_name')), + 'direct_ship' => orNo(fieldValue($resultNode, 'direct_ship_cust_name')), ], 'country_codes' => [ - 'ship' => $resultNode['country_to_ship'] ?? null, - 'direct' => $resultNode['direct_ship_country_code'] ?? null, - 'warranty' => $resultNode['warranty_country_code'] ?? null, + 'ship' => orNo(fieldValue($resultNode, 'country_to_ship')), + 'direct' => orNo(fieldValue($resultNode, 'direct_ship_country_code')), + 'warranty' => orNo(fieldValue($resultNode, 'warranty_country_code')), ], - 'raw' => $resultNode, + 'raw' => normalizeRawValue($resultNode), ]); echo json_encode($payload, JSON_UNESCAPED_UNICODE); From 8896ef52ea89d38ac1a69776c66b095d09291f69 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 22 Nov 2025 00:04:53 +0000 Subject: [PATCH 10/10] Refactor: Improve result text formatting and copying Co-authored-by: sifan99sifan99 --- index.html | 54 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index a6ee3e9..56fdbe6 100644 --- a/index.html +++ b/index.html @@ -528,9 +528,53 @@

استعلام بيانات Motorola عبر IMEI

cardObj.body.innerHTML = `

${escapeHtml(message)}

`; }; - const copyJson = async (payload, label) => { + const formatResultText = (payload) => { + if (!payload || typeof payload !== 'object') { + return JSON.stringify(payload, null, 2); + } + + if (payload.error) { + return `IMEI: ${payload.imei ?? '-'}\nError: ${payload.error}`; + } + + const lines = [ + `IMEI: ${payload.imei ?? '-'}`, + `Model: ${payload.model ?? '-'}`, + `Carrier: ${payload.carrier ?? '-'}`, + `Country: ${payload.country ?? '-'}`, + `Master Service Lock: ${payload.unlock_code ?? '-'}`, + `Lock 4: ${payload.lock_4 ?? '-'}`, + `Lock 5: ${payload.lock_5 ?? '-'}`, + `Status: ${payload.status ?? '-'}`, + `Device Type: ${payload.device_type ?? '-'}`, + `APC: ${payload.apc ?? '-'}`, + `Message: ${payload.message ?? '-'}` + ]; + + const formatSection = (title, obj) => { + if (!obj || typeof obj !== 'object') return; + const entries = Object.entries(obj).filter(([, value]) => value && value !== 'NO'); + if (!entries.length) return; + lines.push(``, `${title}:`); + entries.forEach(([key, value]) => { + lines.push(` - ${key.replace(/_/g, ' ')}: ${value}`); + }); + }; + + formatSection('Customer', payload.customer); + formatSection('Country Codes', payload.country_codes); + + return lines.join('\n'); + }; + + const copyResultText = async (payload, label) => { try { - await navigator.clipboard.writeText(JSON.stringify(payload, null, 2)); + const text = Array.isArray(payload) + ? payload + .map((item, index) => `#${index + 1}\n${formatResultText(item)}`) + .join('\n\n------------------------------\n\n') + : formatResultText(payload); + await navigator.clipboard.writeText(text); setStatus(`تم نسخ ${label}.`, 'success'); } catch (error) { setStatus('تعذر نسخ البيانات. يرجى السماح بالوصول إلى الحافظة.', 'error'); @@ -582,7 +626,7 @@

استعلام بيانات Motorola عبر IMEI

collected.push(data); renderSuccess(cardObj, data); cardObj.copyBtn.disabled = false; - cardObj.copyBtn.addEventListener('click', () => copyJson(data, `نتيجة IMEI ${imei}`)); + cardObj.copyBtn.addEventListener('click', () => copyResultText(data, `نتيجة IMEI ${imei}`)); cardObj.copyPairBtn.disabled = false; cardObj.copyPairBtn.addEventListener('click', () => copyImeiCode(data)); } catch (error) { @@ -590,7 +634,7 @@

استعلام بيانات Motorola عبر IMEI

collected.push(errPayload); renderError(cardObj, errPayload.error); cardObj.copyBtn.disabled = false; - cardObj.copyBtn.addEventListener('click', () => copyJson(errPayload, `نتيجة IMEI ${imei}`)); + cardObj.copyBtn.addEventListener('click', () => copyResultText(errPayload, `نتيجة IMEI ${imei}`)); cardObj.copyPairBtn.disabled = true; } } @@ -598,7 +642,7 @@

استعلام بيانات Motorola عبر IMEI

if (collected.length) { resultCount.textContent = `عدد النتائج: ${collected.length}`; copyAllBtn.hidden = false; - copyAllBtn.onclick = () => copyJson(collected, 'كل النتائج'); + copyAllBtn.onclick = () => copyResultText(collected, 'كل النتائج'); setStatus('اكتملت العملية.', 'success'); } else { setStatus('لم يتم العثور على بيانات.', 'error');