Compare commits

...

24 Commits

Author SHA1 Message Date
Hamza-Ayed
752bbf3a63 Fix #23: JWT storage consistency across all Flutter apps
- siro_admin: added FlutterSecureStorage write alongside GetStorage
- siro_service: added FlutterSecureStorage write in login + guest JWT flows
- siro_rider: added FlutterSecureStorage write in guest + token-refresh flows
  (full-credential login already wrote to both)
- siro_driver: already wrote to both (no change needed)
- All apps now write JWT to both GetStorage and FlutterSecureStorage
2026-06-17 08:03:19 +03:00
Hamza-Ayed
a8748cf4c9 Fix #22: Medium-severity fixes (M-01 through M-07)
M-01: Host header injection - replaced HTTP_HOST with APP_DOMAIN
M-02: Unauthenticated CRUD - ownership checks on carDrivers add/delete
M-03: MD5 tracking token - replaced md5() with hash_hmac sha256
M-04: Webhook SMS - absolute log path instead of relative
M-05: Weak 3-digit OTP - already noted as requirement (Fix #5)
M-06: Redis without auth - added password + prefix to cancel_ride_by_driver
M-07: SSRF bypass - str_ends_with -> strict equality in allowlist
2026-06-17 07:58:21 +03:00
Hamza-Ayed
3543fdd2cd Fix #21: High-severity fixes (H-01 through H-06)
H-01: Egypt document uploads - added path traversal prevention (basename),
       replaced HTTP_HOST with APP_DOMAIN env var
H-02: 7 remaining hardcoded /home/siro-api/ paths replaced with env vars
       (ENV_FILE_PATH, INTERNAL_SOCKET_KEY_PATH, WEBHOOK_SECRET_KEY_PATH)
H-03: serviceapp/updateDriver.php - added ownership check (user_id must match
       driverID or user must be admin); non-admins blocked from changing
       password/status/email/phone
H-04: ggg.php - replaced weak client-supplied phone auth with proper admin
       JWT authentication via JwtService
H-05: Static IV fallback in encrypt_decrypt.php already documented as legacy
H-06: Wallet shared password noted as design limitation (mitigated by
       fingerprint verification + short token TTL)
- Also fixed functions.php log message (removed hardcoded path)
2026-06-17 07:56:57 +03:00
Hamza-Ayed
50a5308f43 Fix #20: DDL removal from register.php, CORS policy, secret leak
- Removed ALTER TABLE DDL statements from Admin/auth/register.php (belongs in migration scripts)
- Added validated CORS with configurable allowed origins via CORS_ALLOWED_ORIGINS env var
- Removed  assignment in load_env.php (secrets no longer exposed in superglobal)
2026-06-17 07:51:01 +03:00
Hamza-Ayed
2d607d9e90 Fix #19: Plaintext OTP hashing + hardcoded server paths
- Changed OTP storage in Admin/auth/login.php from plaintext to sha256 hash
- Updated Admin/auth/verify_login.php to hash user input before comparison
- Replaced hardcoded /home/siro-api/ paths with environment variables:
  - ERROR_LOG_PATH, ENV_FILE_PATH, SECRET_KEY_PAY_PATH, SECRET_KEY_PATH
  - Falls back to __DIR__-relative paths when env vars are unset
2026-06-17 07:49:46 +03:00
Hamza-Ayed
790d58aaa2 remove temp fix script 2026-06-17 07:48:34 +03:00
Hamza-Ayed
72eeb24cd7 Fix #18: Exception leak remediation across 87 PHP files
- Replaced all client-facing $e->getMessage() with generic error messages
- Added error_log() with filename prefix to all catch blocks
- Covered jsonError(), echo, and json_encode() response patterns
- Also fixed 2 remaining display_errors=1 and add_invoice.php leak
- Script-assisted fix for 75 files, manual fix for 12 remaining edge cases
2026-06-17 07:48:31 +03:00
Hamza-Ayed
e51d266a0f Fix #17: SQL injection + mass data exposure (backend)
- Fixed SQL injection in ride/license/get.php (interpolated variable → parameterized query)
- Added admin role checks to all 3 mass data endpoints (driver tokens, passenger tokens, phones+tokens)
- Added pagination (50/page) to all 4 mass data endpoints
- Fixed LIMIT to use placeholders with type binding
2026-06-17 07:45:35 +03:00
Hamza-Ayed
f528e1d3c5 Fix #16: SSL pinning in all 4 Flutter apps
- Created ssl_pinning.dart with SHA-256 DER hash pinning for intaleq.xyz and siromove.com
- Replaced http.post/http.get with pinned client in all CRUD classes
- Added crypto dependency to siro_admin and siro_driver pubspec
2026-06-17 07:40:43 +03:00
Hamza-Ayed
0e28814e7d Fix #15: PCI-DSS compliance - remove persistent CVV storage from Flutter apps 2026-06-17 07:26:27 +03:00
Hamza-Ayed
16331bd35d Fix #14: Remove unused privateKeyFCM (Firebase service account key) from Flutter apps 2026-06-17 07:21:18 +03:00
Hamza-Ayed
623d66a3d8 Fix #13: Remove hardcoded PII from Flutter apps, enable root detection in siro_admin 2026-06-17 07:13:18 +03:00
Hamza-Ayed
1a9619f9f8 fix(security): fix login AND logic to OR, add signup input validation, separate OTP rate limit keys 2026-06-17 07:05:58 +03:00
Hamza-Ayed
70c06edd71 fix(security): fix host header injection in upload_audio, email header injection, add SSL verify to MTN curl 2026-06-17 06:57:56 +03:00
Hamza-Ayed
75aeb73f27 fix(security): fix openssl_sign key resource in MTN initiate, add google-services.json to gitignore 2026-06-17 06:55:36 +03:00
Hamza-Ayed
1d3ea597f4 fix(security): wallet balance check with FOR UPDATE, remove user-supplied ID in signup, hardcoded IP to env 2026-06-17 06:53:00 +03:00
Hamza-Ayed
3dad979eb5 fix(security): remove JWT role extraction without signature, add OTP replay protection, fix user enumeration 2026-06-17 06:45:53 +03:00
Hamza-Ayed
81376a2245 fix(security): remove SSL bypass + hardcoded creds in face_detect, rider debug CA overrides, fix siro_service manifest 2026-06-17 06:36:26 +03:00
Hamza-Ayed
c82b0071bb fix(security): wallet race conditions - FOR UPDATE + atomic claims on payments, webhooks, bonuses 2026-06-17 06:34:51 +03:00
Hamza-Ayed
0ceb67ee56 fix(security): fix SQL injection in updatePaymetToPaid, OTP random_int, static IV encryption, storage mismatch 2026-06-17 06:31:13 +03:00
Hamza-Ayed
8c6dea5d96 fix(security): add auth to FCM relay, HMAC to shamcash webhook, fix jwtconnect webhook bypass 2026-06-17 06:27:07 +03:00
Hamza-Ayed
d6f29802e0 fix(security): fix pervasive IDOR - force JWT user identity in 9 endpoints, fix host injection, exception leaks, wallet auth 2026-06-17 06:22:41 +03:00
Hamza-Ayed
4a9e6b22c5 fix(security): add role checks to 7 admin endpoints, fix undefined vars in admin_update_passenger, add input validation to send_whatsapp 2026-06-17 06:19:47 +03:00
Hamza-Ayed
9bbda24d4a fix(security): add .gitignore, remove PEM keys and debug endpoints from tracking 2026-06-17 06:17:03 +03:00
196 changed files with 1297 additions and 1036 deletions

97
.gitignore vendored Normal file
View File

@@ -0,0 +1,97 @@
# ============================================================
# Siro Project - .gitignore
# ============================================================
# --- Environment & Secrets ---
.env
.env.*
!.env.example
**/*.env
**/private_key.pem
**/public_key.pem
*.pem
service-account.json
**/service-account.json
# --- IDE & OS ---
.DS_Store
Thumbs.db
*.swp
*.swo
*~
.vscode/
.idea/
*.iml
.ruby-lsp/
.kilo/
# --- Build Artifacts ---
node_modules/
vendor/
**/vendor/
build/
dist/
*.js.map
*.css.map
# --- Flutter/Dart ---
.dart_tool/
.packages
.pub-cache/
pubspec.lock
*.g.dart
**/env.g.dart
*.freezed.dart
*.config.dart
# --- Android ---
*.apk
*.aab
*.dex
*.class
*.keystore
local.properties
android/.gradle/
android/captures/
# --- iOS ---
*.ipa
*.dSYM.zip
*.dSYM
Pods/
DerivedData/
*.xcworkspace
xcuserdata/
# --- Composer / PHP ---
/composer.lock
**/composer.lock
# --- Logs ---
*.log
logs/
**/logs/
# --- Uploads ---
uploads/
**/uploads/
portrate_captain_image/
card_image/
imageForUsingApp/
new_driver_car/
upload_audio/
# --- Python ---
__pycache__/
*.pyc
.venv/
venv/
# --- Firebase ---
google-services.json
.google-services.json
GoogleService-Info.plist
# --- Audit/Scan Output ---
semgrep_*.json
nuclei_results.txt

View File

@@ -1,6 +1,12 @@
<?php
require_once __DIR__ . '/../../connect.php';
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
$sql = "SELECT
`driver`.`id`,
`driver`.`phone`,
@@ -48,9 +54,14 @@ $sql = "SELECT
) AS passengerToken
FROM `driver`
ORDER BY passengerAverageRating DESC
LIMIT 10";
LIMIT :lim OFFSET :off";
$stmt = $con->prepare($sql);
$page = max(1, (int) filterRequest('page'));
$limit = 10;
$offset = ($page - 1) * $limit;
$stmt->bindValue(':lim', $limit, PDO::PARAM_INT);
$stmt->bindValue(':off', $offset, PDO::PARAM_INT);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
@@ -67,8 +78,16 @@ foreach ($result as &$row) {
$row['maritalStatus'] = $encryptionHelper->decryptData($row['maritalStatus']);
}
$countStmt = $con->query("SELECT COUNT(*) FROM `driver`");
$total = $countStmt->fetchColumn();
if (count($result) > 0) {
jsonSuccess($result);
jsonSuccess([
'data' => $result,
'total' => (int) $total,
'page' => $page,
'pages' => (int) ceil($total / $limit),
]);
} else {
jsonError("No records found");
}

View File

@@ -1,6 +1,16 @@
<?php
require_once __DIR__ . '/../../connect.php';
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
$page = max(1, (int) filterRequest('page'));
$limit = 50;
$offset = ($page - 1) * $limit;
$sql = "
SELECT
d.phone,
@@ -11,13 +21,18 @@ FROM
`driver` d
LEFT JOIN driverToken dt ON
dt.captain_id = d.id
LIMIT :lim OFFSET :off
";
$stmt = $con->prepare($sql);
$stmt->bindValue(':lim', $limit, PDO::PARAM_INT);
$stmt->bindValue(':off', $offset, PDO::PARAM_INT);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// فك التشفير للحقول الحساسة
$countStmt = $con->query("SELECT COUNT(*) FROM `driver`");
$total = $countStmt->fetchColumn();
foreach ($result as &$row) {
$row['phone'] = $encryptionHelper->decryptData($row['phone']);
if (!empty($row['token'])) {
@@ -26,8 +41,12 @@ foreach ($result as &$row) {
}
if ($stmt->rowCount() > 0) {
jsonSuccess($result);
jsonSuccess([
'data' => $result,
'total' => (int) $total,
'page' => $page,
'pages' => (int) ceil($total / $limit),
]);
} else {
jsonError("No records found");
}
?>

View File

@@ -49,6 +49,6 @@ try {
}
} catch (Exception $e) {
error_log("[Staff Activate Error] " . $e->getMessage());
jsonError("خطأ في السيرفر: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
exit();

View File

@@ -96,5 +96,5 @@ try {
} catch (Exception $e) {
error_log("[Staff Add Error] " . $e->getMessage());
jsonError("Server error: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -37,6 +37,6 @@ try {
} catch (Exception $e) {
error_log("[Staff Pending Error] " . $e->getMessage());
jsonError("خطأ في السيرفر: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
exit();

View File

@@ -61,5 +61,5 @@ try {
}
echo "<h1>Initialization Successful</h1>";
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
echo "An internal error occurred";
}

View File

@@ -40,7 +40,7 @@ try {
}
} catch (Exception $e) {
error_log("[Admin Add Error] " . $e->getMessage());
jsonError("Database error: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -1,7 +1,7 @@
<?php
// عرض كافة الأخطاء
ini_set('display_errors', 1);
ini_set('display_errors', 0);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
@@ -83,6 +83,6 @@ try {
echo json_encode([
'status' => 'error',
'message' => "Database error: $errorMsg"
'message' => "Database error occurred"
]);
}

View File

@@ -22,7 +22,7 @@ try {
} catch (PDOException $e) {
echo json_encode([
"status" => "error",
"message" => "Database error: " . $e->getMessage()
"message" => "An internal error occurred"
]);
}
?>

View File

@@ -44,5 +44,5 @@ try {
} catch (Exception $e) {
error_log("[Approve Admin Error] " . $e->getMessage());
jsonError("Server Error: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -29,5 +29,5 @@ try {
} catch (Exception $e) {
error_log("[List Pending Admins Error] " . $e->getMessage());
jsonError("Server Error: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -140,11 +140,12 @@ try {
$success = sendWhatsAppFromServer($phone, $messageBody);
if ($success) {
// حفظ الرمز كما هو في قاعدة البيانات (بدون تشفير)
// تخزين هاش للـ OTP بدلاً من النص الصريح
$otpHash = hash('sha256', (string)$otp);
$stmt = $con->prepare("INSERT INTO token_verification_admin (phone_number, token, expiration_time)
VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 10 MINUTE))
ON DUPLICATE KEY UPDATE token = VALUES(token), expiration_time = VALUES(expiration_time)");
$stmt->execute([$encryptedPhone, $otp]);
$stmt->execute([$encryptedPhone, $otpHash]);
// إخفاء جزء من الرقم في الاستجابة للأمان
$maskedPhone = substr($phone, 0, 4) . '****' . substr($phone, -3);

View File

@@ -22,12 +22,13 @@ if ($admin->role !== 'admin' && $admin->role !== 'super_admin') {
}
try {
// جلب المفتاح المشترك لسيرفر المحفظة
$payKeyPath = '/home/siro-api/.secret_key_pay';
$payKey = file_exists($payKeyPath) ? trim(file_get_contents($payKeyPath)) : getenv('SECRET_KEY_PAY');
// جلب المفتاح المشترك لسيرفر المحفظة من متغير البيئة أو الملف
$payKeyPath = getenv('SECRET_KEY_PAY_PATH');
$payKey = ($payKeyPath && file_exists($payKeyPath)) ? trim(file_get_contents($payKeyPath)) : getenv('SECRET_KEY_PAY');
if (empty($payKey)) {
$payKey = trim(@file_get_contents('/home/siro-api/.secret_key'));
$fallbackPath = getenv('SECRET_KEY_PATH');
$payKey = ($fallbackPath && file_exists($fallbackPath)) ? trim(file_get_contents($fallbackPath)) : null;
}
if (empty($payKey)) {
@@ -89,5 +90,5 @@ try {
} catch (Exception $e) {
error_log("[Admin Wallet SSO Error] " . $e->getMessage());
jsonError("Server Error: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -24,5 +24,5 @@ try {
echo json_encode(["status" => "success", "message" => "Columns already exist."]);
}
} catch (Exception $e) {
echo json_encode(["status" => "error", "message" => $e->getMessage()]);
echo json_encode(["status" => "error", "message" => "An internal error occurred"]);
}

View File

@@ -77,7 +77,7 @@ foreach ($tables as $table => $columns) {
}
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
echo "Skipped $table due to error: " . $e->getMessage() . "\n";
echo "An internal error occurred" . "\n";
continue;
}

View File

@@ -53,12 +53,6 @@ try {
$encPhone = $encPhoneInput;
$encFp = $encryptionHelper->encryptData($fingerprint);
// التأكد من وجود عمود phone و status في الجدول
try {
$con->exec("ALTER TABLE adminUser ADD COLUMN phone VARCHAR(255) NULL AFTER name");
$con->exec("ALTER TABLE adminUser ADD COLUMN status VARCHAR(50) DEFAULT 'pending' AFTER role");
} catch (Exception $e) { /* الأعمدة موجودة مسبقاً */ }
// 4. الإدخال في قاعدة البيانات بحالة pending
$sql = "INSERT INTO adminUser (id, fingerprint, fingerprint_hash, name, phone, password, role, status, created_at)
VALUES (:id, :fp, :fp_hash, :name, :phone, :pass, 'admin', 'pending', NOW())";
@@ -80,7 +74,7 @@ try {
} catch (Exception $e) {
error_log("[Admin Register Error] " . $e->getMessage());
jsonError("خطأ في السيرفر: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
exit();

View File

@@ -39,14 +39,14 @@ try {
// فك تشفيره لو احتجنا إرساله أو عرضه، لكن هنا نحن نحتاج المشفر للبحث
// $phone = $encryptionHelper->decryptData($encryptedPhone);
// تشفير الرمز (OTP) القادم من التطبيق للمقارنة
$encryptedOtp = $encryptionHelper->encryptData((string)$otp);
// هاش الرمز (OTP) القادم من التطبيق للمقارنة
$otpHash = hash('sha256', (string)$otp);
// 3. التحقق من الـ OTP (باستخدام القيم المشفرة)
// 3. التحقق من الـ OTP
$stmt = $con->prepare("SELECT * FROM token_verification_admin
WHERE phone_number = ? AND token = ?
AND expiration_time >= NOW()");
$stmt->execute([$encryptedPhone, $encryptedOtp]);
$stmt->execute([$encryptedPhone, $otpHash]);
if ($stmt->rowCount() === 0) {
jsonError("رمز التحقق غير صالح أو منتهي الصلاحية.");
@@ -83,5 +83,5 @@ try {
} catch (Exception $e) {
error_log("[Admin Verify OTP Error] " . $e->getMessage());
jsonError("خطأ في السيرفر: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -1,10 +0,0 @@
# 🔒 SECURITY: Block all access to debug files
# This directory contains sensitive debugging scripts
# DO NOT remove this file in production
<RequireAll>
Require all denied
</RequireAll>
# Alternative for older Apache:
# Deny from all

View File

@@ -1,13 +0,0 @@
<?php
require_once 'connect.php';
try {
$stmt = $con->query("SELECT phone FROM driver LIMIT 10");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
echo "Raw: " . $row['phone'] . " | Decrypted: " . $encryptionHelper->decryptData($row['phone']) . "\n";
}
} catch (Exception $e) {
echo "An error occurred.";
}
?>

View File

@@ -1,11 +0,0 @@
<?php
require_once 'connect.php';
try {
$stmt = $con->query("DESCRIBE users");
$cols = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($cols, JSON_PRETTY_PRINT);
} catch (Exception $e) {
echo "An error occurred.";
}
?>

View File

@@ -1,23 +0,0 @@
<?php
require_once __DIR__ . '/connect.php';
$searchPhone = '0992952235';
echo "Searching for: $searchPhone\n";
$variants = [$searchPhone, '963' . substr($searchPhone, 1), '+963' . substr($searchPhone, 1)];
foreach ($variants as $v) {
echo "Checking variant: $v\n";
$enc = $encryptionHelper->encryptData($v);
$stmt = $con->prepare("SELECT id, phone, first_name FROM driver WHERE phone = ? OR phone = ?");
$stmt->execute([$v, $enc]);
$res = $stmt->fetch();
if ($res) {
echo "FOUND! ID: {$res['id']}, Name: {$res['first_name']}, Phone in DB: {$res['phone']}\n";
exit;
}
}
echo "NOT FOUND in driver table.\n";

View File

@@ -1,57 +0,0 @@
<?php
// env_test.php - أداة مخصصة لاختبار جميع متغيرات البيئة
require_once __DIR__ . '/core/bootstrap.php'; // لتحميل الـ .env
header('Content-Type: text/plain; charset=utf-8');
echo "=== فحص متغيرات البيئة (Environment Variables) ===\n\n";
$keysToCheck = [
'PASSENGER_SOCKET_URL',
'LOCATION_SOCKET_URL',
'INTERNAL_SOCKET_KEY_PATH',
'SECRET_KEY_PAY_PATH',
'SECRET_KEY_HMAC',
'allowed1',
'allowed2',
'passwordnewpassenger',
'FP_PEPPER'
];
foreach ($keysToCheck as $key) {
$val = getenv($key);
if ($val !== false && $val !== '') {
// إخفاء جزء من القيم الحساسة مثل كلمات المرور
if (strpos(strtolower($key), 'password') !== false || strpos(strtolower($key), 'secret') !== false || strpos(strtolower($key), 'hmac') !== false) {
$hiddenVal = substr($val, 0, 3) . '***' . substr($val, -3);
echo "[OK] $key = $hiddenVal\n";
} else {
echo "[OK] $key = $val\n";
}
} else {
echo "[ERROR] $key = (مفقود أو فارغ!)\n";
}
}
echo "\n\n=== فحص الملفات المباشرة ===\n\n";
$filesToCheck = [
'/home/siro-api/.internal_socket_key',
'/home/siro-api/.secret_key_pay'
];
foreach ($filesToCheck as $file) {
if (file_exists($file)) {
$content = trim(file_get_contents($file));
if (!empty($content)) {
$hidden = substr($content, 0, 3) . '***' . substr($content, -3);
echo "[OK] File ($file) exists and has content: $hidden\n";
} else {
echo "[WARNING] File ($file) exists but is EMPTY!\n";
}
} else {
echo "[ERROR] File ($file) DOES NOT EXIST!\n";
}
}
echo "\n=== انتهى الفحص ===\n";

View File

@@ -1,78 +0,0 @@
<?php
include 'connect.php';
// نضمن أن الرد دائماً JSON
header('Content-Type: application/json; charset=utf-8');
// 1) قراءة الـ body كـ JSON (من Flutter)
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!is_array($data)) {
// fallback لو أرسلت form-data أو x-www-form-urlencoded
$data = $_POST;
}
// 2) التحقق من رقم هاتف الأدمن المصرّح له
// قراءة الأرقام المسموح لها من الـ ENV
$phonesRaw = getenv('ADMIN_PHONE_NUMBERS') ?: '';
$ALLOWED_TOOL_PHONES = array_values(
array_filter(
array_map(function ($p) {
// إزالة أي رموز غير رقمية (مسافات، +، - إلخ)
return preg_replace('/\D+/', '', $p);
}, explode(',', $phonesRaw))
)
);
// رقم الهاتف القادم من Flutter (parameter جديد)
$adminPhoneParam = isset($data['admin_phone'])
? preg_replace('/\D+/', '', $data['admin_phone'])
: '';
// إذا لم يُرسل رقم أو لم يكن ضمن القائمة → منع الوصول
if ($adminPhoneParam === '' || !in_array($adminPhoneParam, $ALLOWED_TOOL_PHONES, true)) {
http_response_code(403);
echo json_encode([
'status' => 'error',
'message' => 'Access denied for this admin phone.',
]);
exit;
}
// 3) التحقق من بقية المدخلات (action + text)
$action = $data['action'] ?? '';
$text = trim($data['text'] ?? '');
if ($text === '' || ($action !== 'encrypt' && $action !== 'decrypt')) {
http_response_code(400);
echo json_encode([
'status' => 'error',
'message' => 'Invalid input: need action=encrypt|decrypt and non-empty text.',
]);
exit;
}
// 4) تنفيذ التشفير / الفك
try {
// require_once __DIR__ . '/encrypt_decrypt.php';
if ($action === 'encrypt') {
$result = $encryptionHelper->encryptData($text);
} else { // decrypt
$result = $encryptionHelper->decryptData($text);
}
echo json_encode([
'status' => 'success',
'action' => $action,
'result' => (string) $result,
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => 'Operation failed.',
]);
}

View File

@@ -1,23 +0,0 @@
<?php
require_once 'connect.php';
echo "--- ADMIN TABLE ---\n";
try {
$stmt = $con->prepare("SELECT id, name, role FROM admin");
$stmt->execute();
$admins = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($admins);
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
echo "\n--- DATABASES ---\n";
try {
$stmt = $con->prepare("SHOW DATABASES");
$stmt->execute();
$dbs = $stmt->fetchAll(PDO::FETCH_COLUMN);
print_r($dbs);
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
?>

View File

@@ -1,2 +0,0 @@
<?php
echo ini_get('error_log');

View File

@@ -1,13 +0,0 @@
<?php
require_once __DIR__ . '/../core/bootstrap.php';
require_once __DIR__ . '/../functions.php';
$con = Database::get('main');
$lat = 32.11171;
$lng = 36.06737;
$carType = 'Fixed Price';
echo "Testing findBestDrivers...\n";
$drivers = findBestDrivers($con, $lat, $lng, $carType);
print_r($drivers);
echo "Done.\n";

View File

@@ -1,10 +0,0 @@
<?php
require_once __DIR__ . '/../core/bootstrap.php';
$redis = getRedis(); // or however it's connected in bootstrap
if (!$redis) {
echo "No redis\n"; exit;
}
$redis->geoadd('geo:rides:waiting', 36.0, 32.0, 'test_ride');
$res = $redis->georadius('geo:rides:waiting', 36.0, 32.0, 10, 'km', ['WITHDIST' => true]);
print_r($res);
echo json_encode($res) . "\n";

View File

@@ -37,5 +37,6 @@ try {
}
} catch (PDOException $e) {
jsonError("Error: " . $e->getMessage());
error_log("[deleteCaptain.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -24,7 +24,7 @@ try {
}
} catch (PDOException $e) {
// Handle any SQL errors
jsonError("Error deleting records: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -51,5 +51,6 @@ try {
}
} catch (PDOException $e) {
jsonError("Error searching driver: " . $e->getMessage());
error_log("[find_driver_by_phone.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -23,5 +23,6 @@ try {
}
} catch (PDOException $e) {
jsonError("Error removing from blacklist: " . $e->getMessage());
error_log("[remove_from_blacklist.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -72,6 +72,7 @@ try {
jsonError("No records updated or driver not found.");
}
} catch (PDOException $e) {
jsonError("Error updating record: " . $e->getMessage());
error_log("[updateDriverFromAdmin.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -1,7 +1,7 @@
<?php
require_once __DIR__ . '/../connect.php';
// استلام البيانات من الطلب
// Allow any authenticated user to report errors, but validate input
$error = filterRequest("error");
$userId = filterRequest("userId");
$userType = filterRequest("userType");
@@ -9,8 +9,14 @@ $phone = filterRequest("phone");
$device = filterRequest("device");
$details = filterRequest("details");
// تسجيل الخطأ في ملف logs/app.log للمتابعة السريعة
$logMsg = "[$userType ID: $userId] Error: $error | Where: $device | Details: $details";
// Sanitize log input to prevent log injection
$safeError = str_replace(["\r", "\n"], ' ', substr($error ?? '', 0, 500));
$safeUserId = str_replace(["\r", "\n"], ' ', substr($userId ?? '', 0, 50));
$safeUserType = str_replace(["\r", "\n"], ' ', substr($userType ?? '', 0, 50));
$safeDevice = str_replace(["\r", "\n"], ' ', substr($device ?? '', 0, 200));
$safeDetails = str_replace(["\r", "\n"], ' ', substr($details ?? '', 0, 1000));
$logMsg = "[$safeUserType ID: $safeUserId] Error: $safeError | Where: $safeDevice | Details: $safeDetails";
appLog($logMsg, "APP_ERROR");
// جملة SQL لإدخال البيانات، مع إضافة الحقل الجديد

View File

@@ -31,7 +31,9 @@ try {
echo "The token does not have an expiration time.\n";
}
} catch (Facebook\Exceptions\FacebookResponseException $e) {
echo 'Graph API Error: ' . $e->getMessage();
error_log("[facebook.php] Graph API Error: " . $e->getMessage());
echo 'An error occurred while fetching Facebook data';
} catch (Facebook\Exceptions\FacebookSDKException $e) {
echo 'SDK Error: ' . $e->getMessage();
error_log("[facebook.php] SDK Error: " . $e->getMessage());
echo 'An error occurred while processing Facebook data';
}

View File

@@ -1,6 +1,12 @@
<?php
require_once __DIR__ . '/../../connect.php';
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
function normalize_phone($s) { return preg_replace('/\D+/', '', (string)$s); }
$id = filterRequest("id"); // أو
@@ -48,5 +54,5 @@ try {
jsonSuccess(null, "Passenger deleted and blacklisted");
} catch (Throwable $e) {
$con->rollBack();
jsonError("Failed: ".$e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -1,7 +1,11 @@
<?php
require_once __DIR__ . '/../../connect.php';
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
$id = filterRequest("id"); // مفضّل
@@ -9,38 +13,41 @@ $first_name = filterRequest("first_name");
$last_name = filterRequest("last_name");
$new_phone = filterRequest("phone");
if (empty($id) ) { jsonError("Provide id or phone_lookup"); exit; }
if (empty($id)) { jsonError("Passenger ID is required"); exit; }
if ($first_name === null && $last_name === null && $new_phone === null) {
jsonError("Nothing to update"); exit;
}
$sets = [];
$params = [];
$new_phone = $encryptionHelper->encryptData($new_phone);
$first_name = $encryptionHelper->encryptData($first_name);
$last_name = $encryptionHelper->encryptData($last_name);
$enc_norm = $encryptionHelper->encryptData($norm);
if ($first_name !== null) { $sets[] = "first_name = :first_name"; $params['first_name'] = trim($first_name); }
if ($last_name !== null) { $sets[] = "last_name = :last_name"; $params['last_name'] = trim($last_name); }
if ($first_name !== null) {
$encFirst = $encryptionHelper->encryptData($first_name);
$sets[] = "first_name = :first_name";
$params['first_name'] = trim($encFirst);
}
if ($last_name !== null) {
$encLast = $encryptionHelper->encryptData($last_name);
$sets[] = "last_name = :last_name";
$params['last_name'] = trim($encLast);
}
if ($new_phone !== null) {
$encPhone = $encryptionHelper->encryptData($new_phone);
$sets[] = "phone = :phone";
$params['phone'] = trim($new_phone);
$params['phone'] = trim($encPhone);
// منع تكرار الهاتف على راكب آخر
$q = $con->prepare("SELECT id FROM passengers WHERE phone = :ph LIMIT 1");
$q->execute(['ph' => $params['phone']]);
$row = $q->fetch(PDO::FETCH_ASSOC);
if ($row) {
if (!empty($id) && $row['id'] != $id) { jsonError("Phone already used by another passenger"); exit; }
if (empty($id) && $row['id'] != $phoneLookup) { jsonError("Phone already used by another passenger"); exit; }
if ($row && $row['id'] != $id) {
jsonError("Phone already used by another passenger");
exit;
}
}
$whereSql = "";
$whereParams = [];
if (!empty($id)) { $whereSql = "id = :pid"; $whereParams['pid'] = $id; }
else { $whereSql = "phone = :plk"; $whereParams['plk'] = $phoneLookup; }
$whereSql = "id = :pid";
$whereParams = ['pid' => $id];
$sql = "UPDATE passengers SET ".implode(", ", $sets).", updated_at = CURRENT_TIMESTAMP WHERE $whereSql";
$stmt = $con->prepare($sql);

View File

@@ -1,6 +1,12 @@
<?php
require_once __DIR__ . '/../../connect.php';
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
/**
* تطبيع رقم الهاتف ليتوافق مع التخزين في قاعدة البيانات
*/
@@ -174,5 +180,5 @@ try {
} catch (Throwable $e) {
error_log("[get_last_ride] Exception: " . $e->getMessage());
jsonError("Error: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -84,5 +84,5 @@ try {
jsonSuccess(['ride' => $ride, 'message' => 'Status updated']);
} catch (Throwable $e) {
if ($con->inTransaction()) $con->rollBack();
jsonError("Error: ".$e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -45,6 +45,7 @@ try {
}
} catch (PDOException $e) {
jsonError("Database Error: " . $e->getMessage());
error_log("[get_driver_live_pos.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -104,6 +104,7 @@ try {
jsonSuccess($data);
} catch (PDOException $e) {
jsonError("Database Error: " . $e->getMessage());
error_log("[get_rides_by_status.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -1,6 +1,12 @@
<?php
require_once __DIR__ . '/../../connect.php';
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
/**
* تطبيع رقم الهاتف ليتوافق مع التخزين في قاعدة البيانات
*/

View File

@@ -2,7 +2,13 @@
// File: send_whatsapp_message.php
// هذا السكربت يرسل رسالة واتساب فقط باستخدام RaseelPlus API
require_once __DIR__ . '/../connect.php'; // فقط إذا كنت تحتاج للوصول إلى environment
require_once __DIR__ . '/../connect.php';
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
error_log("--- [send_whatsapp_message.php] Script execution started ---");
@@ -16,6 +22,18 @@ if (empty($receiver) || empty($message)) {
exit();
}
// Validate phone number format (basic international format)
if (!preg_match('/^\+?[1-9]\d{6,14}$/', $receiver)) {
jsonError('Invalid phone number format.');
exit();
}
// Limit message length to prevent abuse
if (strlen($message) > 4096) {
jsonError('Message too long. Maximum 4096 characters.');
exit();
}
// بيانات Raseel
$instanceId = getenv("RASEEL_DRIVER_INSTANCE_ID");
$accessToken = getenv("RASEEL_DRIVER_ACCESS_TOKEN");

View File

@@ -38,6 +38,7 @@ try {
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
error_log("[driver_ranking.php] " . $e->getMessage());
echo json_encode(['status' => 'error', 'message' => 'An internal error occurred']);
}
?>

View File

@@ -53,6 +53,7 @@ try {
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
error_log("[growth.php] " . $e->getMessage());
echo json_encode(['status' => 'error', 'message' => 'An internal error occurred']);
}
?>

View File

@@ -47,6 +47,7 @@ try {
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
error_log("[revenue.php] " . $e->getMessage());
echo json_encode(['status' => 'error', 'message' => 'An internal error occurred']);
}
?>

View File

@@ -39,6 +39,7 @@ try {
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
error_log("[settlements.php] " . $e->getMessage());
echo json_encode(['status' => 'error', 'message' => 'An internal error occurred']);
}
?>

View File

@@ -29,6 +29,7 @@ try {
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
error_log("[stats.php] " . $e->getMessage());
echo json_encode(['status' => 'error', 'message' => 'An internal error occurred']);
}
?>

View File

@@ -97,6 +97,7 @@ try {
jsonError("Invalid action_type", 400);
} catch (Exception $e) {
jsonError("Blacklist action failed: " . $e->getMessage(), 500);
error_log("[blacklist_manager.php] " . $e->getMessage());
jsonError("Blacklist action failed. Please try again later.", 500);
}
?>

View File

@@ -100,6 +100,7 @@ try {
jsonSuccess($scorecard);
} catch (Exception $e) {
jsonError("Failed to fetch scorecard: " . $e->getMessage(), 500);
error_log("[driver_scorecard.php] " . $e->getMessage());
jsonError("Failed to fetch scorecard. Please try again later.", 500);
}
?>

View File

@@ -57,6 +57,7 @@ try {
echo json_encode($response);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
error_log("[realtime_dashboard.php] " . $e->getMessage());
echo json_encode(['status' => 'error', 'message' => 'An internal error occurred']);
}
?>

View File

@@ -72,6 +72,7 @@ try {
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
error_log("[smart_alerts.php] " . $e->getMessage());
echo json_encode(['status' => 'error', 'message' => 'An internal error occurred']);
}
?>

View File

@@ -5,13 +5,16 @@ header('Content-Type: application/json');
uploadLog("🚀 [EgyptDocuments/uploadEgyptIdBack.php] Egyptian ID back upload started.");
$driverID = filterRequest("driverID");
if (empty($driverID)) {
$rawDriverID = filterRequest("driverID");
if (empty($rawDriverID)) {
uploadLog("❌ Missing driverID parameter.", 'ERROR');
jsonError("driverID is required.");
exit;
}
// منع path traversal
$driverID = basename($rawDriverID);
if (isset($_FILES['image'])) {
uploadLog("$_FILES['image'] metadata", 'INFO', [
'name' => $_FILES['image']['name'] ?? 'unknown',
@@ -33,19 +36,16 @@ if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
$image_file = $_FILES['image'];
$allowed_extensions = ['jpg', 'jpeg', 'png'];
// Get file information
$image_name = $image_file['name'];
$image_size = $image_file['size'];
$image_extension = strtolower(pathinfo($image_name, PATHINFO_EXTENSION));
// Validate file extension
if (!in_array($image_extension, $allowed_extensions, true)) {
uploadLog("❌ Invalid image format extension: .$image_extension", 'ERROR');
jsonError("Invalid image format");
exit;
}
// Validate MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $image_file['tmp_name']);
finfo_close($finfo);
@@ -57,29 +57,25 @@ if (!in_array($mime_type, $allowed_mime_types, true)) {
exit;
}
// Generate a unique filename using driverID
$new_filename = $driverID . '.' . $image_extension;
// Set target directory for uploads
$target_dir = __DIR__ . "/card_image/";
if (!is_dir($target_dir)) {
mkdir($target_dir, 0755, true);
}
// Construct target file path
$target_file = $target_dir . $new_filename;
// Move the image file to the target location
if (!move_uploaded_file($image_file['tmp_name'], $target_file)) {
uploadLog("❌ Failed to save image to target file: $target_file", 'ERROR');
jsonError("Failed to save image");
exit;
}
// Resolve dynamic URL
$host = $_SERVER['HTTP_HOST'] ?? 'api.siromove.com';
// استخدام النطاق من البيئة بدلاً من Host header
$domain = getenv('APP_DOMAIN') ?: 'api.siromove.com';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
$image_url = "$protocol://$host/siro/EgyptDocuments/card_image/" . $new_filename;
$image_url = "$protocol://$domain/siro/EgyptDocuments/card_image/" . $new_filename;
uploadLog("✅ Egypt ID back uploaded successfully. URL: $image_url");
@@ -89,4 +85,3 @@ printSuccess([
"file_link" => $image_url,
"image_url" => $image_url
]);
?>

View File

@@ -5,13 +5,16 @@ header('Content-Type: application/json');
uploadLog("🚀 [EgyptDocuments/uploadEgyptidFront.php] Egyptian ID front upload started.");
$driverID = filterRequest("driverID");
if (empty($driverID)) {
$rawDriverID = filterRequest("driverID");
if (empty($rawDriverID)) {
uploadLog("❌ Missing driverID parameter.", 'ERROR');
jsonError("driverID is required.");
exit;
}
// منع path traversal
$driverID = basename($rawDriverID);
if (isset($_FILES['image'])) {
uploadLog("$_FILES['image'] metadata", 'INFO', [
'name' => $_FILES['image']['name'] ?? 'unknown',
@@ -33,19 +36,16 @@ if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
$image_file = $_FILES['image'];
$allowed_extensions = ['jpg', 'jpeg', 'png'];
// Get file information
$image_name = $image_file['name'];
$image_size = $image_file['size'];
$image_extension = strtolower(pathinfo($image_name, PATHINFO_EXTENSION));
// Validate file extension
if (!in_array($image_extension, $allowed_extensions, true)) {
uploadLog("❌ Invalid image format extension: .$image_extension", 'ERROR');
jsonError("Invalid image format");
exit;
}
// Validate MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $image_file['tmp_name']);
finfo_close($finfo);
@@ -57,29 +57,25 @@ if (!in_array($mime_type, $allowed_mime_types, true)) {
exit;
}
// Generate a unique filename using driverID
$new_filename = $driverID . '.' . $image_extension;
// Set target directory for uploads
$target_dir = __DIR__ . "/egypt/idFront/";
if (!is_dir($target_dir)) {
mkdir($target_dir, 0755, true);
}
// Construct target file path
$target_file = $target_dir . $new_filename;
// Move the image file to the target location
if (!move_uploaded_file($image_file['tmp_name'], $target_file)) {
uploadLog("❌ Failed to save image to target file: $target_file", 'ERROR');
jsonError("Failed to save image");
exit;
}
// Resolve dynamic URL
$host = $_SERVER['HTTP_HOST'] ?? 'api.siromove.com';
// استخدام النطاق من البيئة بدلاً من Host header
$domain = getenv('APP_DOMAIN') ?: 'api.siromove.com';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
$image_url = "$protocol://$host/siro/EgyptDocuments/egypt/idFront/" . $new_filename;
$image_url = "$protocol://$domain/siro/EgyptDocuments/egypt/idFront/" . $new_filename;
uploadLog("✅ Egypt ID front uploaded successfully. URL: $image_url");
@@ -89,4 +85,3 @@ printSuccess([
"file_link" => $image_url,
"image_url" => $image_url
]);
?>

View File

@@ -46,7 +46,7 @@ if (!move_uploaded_file($file['tmp_name'], $uploadPath)) {
exit;
}
$host = $_SERVER['HTTP_HOST'] ?? 'api-syria.siromove.com';
$host = getenv('APP_DOMAIN') ?: 'api-syria.siromove.com';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
$imageUrl = "$protocol://$host/siro/auth/uploads/documents/" . $uniqueName ;
$imageData = file_get_contents($uploadPath);

View File

@@ -6,8 +6,23 @@ $email = filterRequest('email');
$phone = filterRequest('phone');
$password = filterRequest('password');
// Hash the password
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
if (empty($phone) && empty($email)) {
echo json_encode(["status" => "Failure", "data" => "Phone or email is required."]);
exit;
}
// Build WHERE dynamically: support phone-only, email-only, or both
$conditions = [];
$params = [':password' => $password];
if (!empty($phone)) {
$conditions[] = "passengers.phone = :phone";
$params[':phone'] = $phone;
}
if (!empty($email)) {
$conditions[] = "passengers.email = :email";
$params[':email'] = $email;
}
$where = implode(' OR ', $conditions);
$sql = "SELECT
passengers.`id`,
@@ -29,11 +44,9 @@ FROM
`passengers`
LEFT JOIN email_verifications ON email_verifications.email = passengers.email
WHERE
passengers.phone = :phone AND passengers.email = :email ";
$where";
$stmt = $con->prepare($sql);
$stmt->bindParam(':email', $email);
$stmt->bindParam(':phone', $phone);
$stmt->execute();
$stmt->execute($params);
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
$count = $stmt->rowCount();
@@ -55,13 +68,11 @@ if ($count > 0) {
// jsonError("Incorrect password.");
}
} else {
// The user does not exist
echo json_encode([
"status" => "Failure",
"data" => "User does not exist."
"data" => "Invalid credentials."
]);
// jsonError("User does not exist.");
}
$conn->close();
$con = null;
?>

View File

@@ -18,17 +18,7 @@ if (empty($receiver)) {
$user_type = filterRequest("user_type");
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? (function_exists('apache_request_headers') ? (apache_request_headers()['Authorization'] ?? null) : null);
if (!empty($authHeader) && preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
$jwtToken = $matches[1];
$tokenParts = explode('.', $jwtToken);
if (count($tokenParts) === 3) {
$payload = json_decode(base64_decode($tokenParts[1]), true);
if (isset($payload['role'])) {
$user_type = $payload['role'];
}
}
}
// user_type is taken from request only (JWT not trusted without signature verification)
$country = filterRequest("country"); // Egypt | Syria | Jordan
$method = filterRequest("method"); // whatsapp | sms | voice | flash_call | bearer_send

View File

@@ -7,7 +7,7 @@ require_once __DIR__ . '/../../functions.php';
// 0. Rate Limiting: 3 محاولات OTP كل 5 دقائق لكل IP
$rateLimiter = new RateLimiter($redis);
$rateLimiter->enforce(RateLimiter::identifier(), 'otp');
$rateLimiter->enforce(RateLimiter::identifier(), 'otp_verify');
// 1. Fetch input parameters
$phone_number = filterRequest("phone_number");
@@ -23,17 +23,7 @@ if (empty($token_code)) {
$user_type = filterRequest("user_type");
$context = filterRequest("context"); // token_change | login (default)
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? (function_exists('apache_request_headers') ? (apache_request_headers()['Authorization'] ?? null) : null);
if (!empty($authHeader) && preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
$jwtToken = $matches[1];
$tokenParts = explode('.', $jwtToken);
if (count($tokenParts) === 3) {
$payload = json_decode(base64_decode($tokenParts[1]), true);
if (isset($payload['role'])) {
$user_type = $payload['role'];
}
}
}
// user_type is taken from request only (JWT not trusted without signature verification)
if (empty($phone_number)) {
jsonError("Phone number is required.");
@@ -75,7 +65,7 @@ try {
if ($user_type === 'admin') {
$sql = "SELECT * FROM token_verification_admin
WHERE phone_number = :phone AND token = :token
AND expiration_time >= NOW()";
AND expiration_time >= NOW() AND verified = 0";
$stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
@@ -103,7 +93,7 @@ try {
} elseif ($user_type === 'service') {
$sql = "SELECT `id` FROM `phone_verification_service`
WHERE `phone_number` = :phone AND `token_code` = :token
AND `expiration_time` > NOW()";
AND `expiration_time` > NOW() AND `is_verified` = 0";
$stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
@@ -124,7 +114,7 @@ try {
$sql = "SELECT `id` FROM `token_verification_driver`
WHERE `phone_number` = :phone
AND `token` = :token
AND `expiration_time` > NOW()";
AND `expiration_time` > NOW() AND `verified` = 0";
$stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
@@ -147,7 +137,7 @@ try {
$sql = "SELECT `id` FROM `phone_verification`
WHERE `phone_number` = :phone
AND `token_code` = :token
AND `expiration_time` > NOW()";
AND `expiration_time` > NOW() AND `is_verified` = 0";
$stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
@@ -172,7 +162,7 @@ try {
$sql = "SELECT `id` FROM `token_verification`
WHERE `phone_number` = :phone
AND `token` = :token
AND `expiration_time` > NOW()";
AND `expiration_time` > NOW() AND `verified` = 0";
$stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
@@ -195,7 +185,7 @@ try {
$sql = "SELECT `id` FROM `phone_verification_passenger`
WHERE `phone_number` = :phone
AND `token` = :token
AND `expiration_time` > NOW()";
AND `expiration_time` > NOW() AND `verified` = 0";
$stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);

View File

@@ -11,7 +11,24 @@ $password = filterRequest("password");
$gender = filterRequest("gender");
$birthdate = filterRequest("birthdate");
$site = filterRequest("site");
$id = filterRequest("id");
// --- Input Validation ---
if (empty($phone) || strlen(preg_replace('/\D+/', '', $phone)) < 8) {
jsonError("Valid phone number is required.");
exit;
}
if (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
jsonError("Valid email address is required.");
exit;
}
if (empty($password) || strlen($password) < 6) {
jsonError("Password must be at least 6 characters.");
exit;
}
if (empty($first_name) || empty($last_name)) {
jsonError("First name and last name are required.");
exit;
}
// تشفير البيانات الحساسة
$phone = $encryptionHelper->encryptData($phone);
@@ -39,14 +56,13 @@ try {
exit;
}
// إدخال البيانات الجديدة
// إدخال البيانات الجديدة (مع ID تلقائي)
$sql = "INSERT INTO passengers (
id, phone, email, password, gender, birthdate, site, first_name, last_name
) VALUES (
:id, :phone, :email, :password, :gender, :birthdate, :site, :first_name, :last_name
UUID_SHORT(), :phone, :email, :password, :gender, :birthdate, :site, :first_name, :last_name
)";
$stmt = $con->prepare($sql);
$stmt->bindParam(":id", $id);
$stmt->bindParam(":phone", $phone);
$stmt->bindParam(":email", $email);
$stmt->bindParam(":password", $hashedPassword);

View File

@@ -16,7 +16,7 @@ try {
exit;
}
$host = $_SERVER['HTTP_HOST'] ?? 'api-syria.siromove.com';
$host = getenv('APP_DOMAIN') ?: 'api-syria.siromove.com';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
$PUBLIC_BASE = "$protocol://$host/siro/auth/uploads/documents";
@@ -230,7 +230,7 @@ Therefore, do NOT assume a specific field is on the front or the back of a card.
$urlHost = parse_url($url, PHP_URL_HOST);
$allowed = false;
foreach ($allowedHosts as $host) {
if ($host && str_ends_with($urlHost, $host)) {
if ($host && $urlHost === $host) {
$allowed = true;
break;
}

View File

@@ -10,7 +10,7 @@ const MAX_FILE_MB = 5;
const ALLOWED_MIMES = ['image/jpeg','image/png','image/webp']; // فقط صور
const UPLOAD_ROOT = __DIR__ . "/../../private_uploads"; // مجلد خاص (غير عام)
const SIGN_SECRET = getenv('SECRET_KEY_HMAC'); // غيّرها واقرأها من .env
$host = $_SERVER['HTTP_HOST'] ?? 'api-syria.siromove.com';
$host = getenv('APP_DOMAIN') ?: 'api-syria.siromove.com';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
define('PUBLIC_BASE', "$protocol://$host/siro");
const SIGNED_TTL_SEC = 172800; // 2 days = 60*60*24

View File

@@ -3,7 +3,7 @@
require_once __DIR__ . '/../../../connect.php';
/* 1) توليد رمز التحقق (3 خانات) --------------------------------------------------- */
$otp = (string)rand(100, 999);
$otp = (string)random_int(100, 999);
$receiver = filterRequest("receiver");
if (empty($receiver)) {
@@ -69,19 +69,19 @@ if ($sentOK) {
");
$stmt->execute([$receiver_enc, $otp_enc, $exp, $now]);
// Also save to Redis for verify_otp compatibility
if ($redis) {
$redis->setex("otp:driver:$receiver", 300, $otp);
}
jsonSuccess(null, 'OTP sent and saved successfully');
} catch (PDOException $e) {
error_log("[send_otp_driver.php] " . $e->getMessage());
jsonError('OTP sent but failed to save to database');
}
} else {
$errMsg = $decoded['message'] ?? 'Unknown error';
jsonError('Failed to send OTP: ' . $errMsg);
jsonError('Failed to send OTP');
}
/* -----------------------------------------------------------------------
* أبقينا callAPI() فقط إذا كان يُستخدم في ملفات أخرى احذفه إن شئت.
* --------------------------------------------------------------------- */
function callAPI($method, $url, $data) { /* … */ }
?>

View File

@@ -77,5 +77,6 @@ try {
}
} catch (PDOException $e) {
error_log("[verify_otp_driver.php] " . $e->getMessage());
jsonError("Database error occurred.");
}

View File

@@ -3,7 +3,7 @@
require_once __DIR__ . '/../../connect.php';
/* 1) توليد رمز التحقق (3 خانات) */
$otp = (string)rand(100, 999);
$otp = (string)random_int(100, 999);
$receiver = filterRequest("receiver");
if (empty($receiver)) {
@@ -50,7 +50,7 @@ $decoded = json_decode((string)$res, true);
$sentOK = ($httpCode === 200 && ($decoded['success'] ?? false));
if ($sentOK) {
/* 3) تشفير البيانات وحفظ الرمز في قاعدة البيانات */
/* 3) حفظ الرمز في Redis + قاعدة البيانات */
$receiver_enc = $encryptionHelper->encryptData($receiver);
$otp_enc = $encryptionHelper->encryptData($otp);
@@ -58,6 +58,7 @@ if ($sentOK) {
$now = date('Y-m-d H:i:s');
try {
// Save to MySQL
$con->prepare("DELETE FROM token_verification WHERE phone_number = ?")
->execute([$receiver_enc]);
@@ -68,19 +69,20 @@ if ($sentOK) {
");
$stmt->execute([$receiver_enc, $otp_enc, $exp, $now]);
// Also save to Redis for verify_otp.php compatibility
if ($redis) {
$redis->setex("otp:passenger:$receiver", 300, $otp);
}
jsonSuccess(null, 'OTP sent and saved successfully');
} catch (PDOException $e) {
error_log("[send_otp.php] " . $e->getMessage());
jsonError('OTP sent but failed to save to database');
}
} else {
$errMsg = $decoded['message'] ?? 'Unknown error';
jsonError('Failed to send OTP: ' . $errMsg);
jsonError('Failed to send OTP');
}
/* -----------------------------------------------------------------
* يمكن حذف callAPI() تمامًا إن لم يعد مستخدمًا في أي ملف آخر.
* ---------------------------------------------------------------- */
function callAPI($method, $url, $data) { /* … (أبقِها أو احذفها) */ }
?>

View File

@@ -30,7 +30,7 @@ try {
$cachedOtp = $redis->get("otp:passenger:$phoneNumber");
if ($cachedOtp && $cachedOtp == $otp) {
if ($cachedOtp && $cachedOtp === $otp) {
// ننجح في التحقق ونحذف المفتاح من Redis لمنع استخدامه مرة أخرى (One-time use)
$redis->del("otp:passenger:$phoneNumber");

View File

@@ -64,14 +64,25 @@ class EncryptionHelper
}
// ─── تشفير/فك تشفير Binary (صور، ملفات) ───────────────
// تُستخدم الـ GCM مع IV عشوائي (كما في encryptData)
public function encryptBinary(string $data): string
{
return openssl_encrypt($data, self::ALGO_CBC, $this->key, OPENSSL_RAW_DATA, $this->cbcIv);
$iv = random_bytes(self::IV_LEN_GCM);
$tag = '';
$encrypted = openssl_encrypt($data, self::ALGO_GCM, $this->key, OPENSSL_RAW_DATA, $iv, $tag, "", self::TAG_LEN);
return base64_encode($iv . $tag . $encrypted);
}
public function decryptBinary(string $data): string|false
{
return openssl_decrypt($data, self::ALGO_CBC, $this->key, OPENSSL_RAW_DATA, $this->cbcIv);
$raw = base64_decode($data, true);
if ($raw === false || strlen($raw) < self::IV_LEN_GCM + self::TAG_LEN) return false;
$iv = substr($raw, 0, self::IV_LEN_GCM);
$tag = substr($raw, self::IV_LEN_GCM, self::TAG_LEN);
$cipher = substr($raw, self::IV_LEN_GCM + self::TAG_LEN);
return openssl_decrypt($cipher, self::ALGO_GCM, $this->key, OPENSSL_RAW_DATA, $iv, $tag);
}
// --------- دوال الـ Padding للـ CBC ----------

View File

@@ -20,10 +20,7 @@ if ($debugMode) {
ini_set('log_errors', '1');
// تحديد مسار اللوج بشكل ديناميكي (محلياً أو سيرفر)
$logPath = '/home/siro-api/logs/php_errors.log';
if (!file_exists(dirname($logPath)) || !is_writable(dirname($logPath))) {
$logPath = __DIR__ . '/../logs/php_errors.log';
}
$logPath = getenv('ERROR_LOG_PATH') ?: (__DIR__ . '/../logs/php_errors.log');
ini_set('error_log', $logPath);
header_remove('X-Powered-By');
@@ -37,7 +34,13 @@ header("Permissions-Policy: geolocation=(), microphone=(), camera=()");
header("X-XSS-Protection: 1; mode=block");
// CORS (يجب تخصيصه في endpoints مخصصة إن لزم، لكن هذا افتراضي)
// CORS مع التحقق من المصدر المسموح
$allowedOrigins = array_map('trim', explode(',', getenv('CORS_ALLOWED_ORIGINS') ?: 'https://siromove.com,https://admin.siromove.com'));
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins)) {
header("Access-Control-Allow-Origin: $origin");
header('Access-Control-Allow-Credentials: true');
}
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Device-FP, X-HMAC-Auth, X-Internal-Key');
@@ -54,10 +57,7 @@ if ($vendorPath) require_once $vendorPath;
require_once __DIR__ . '/helpers.php';
// تحديد مسار الـ .env بشكل ديناميكي
$envFile = '/home/siro-api/env/.env';
if (!file_exists($envFile)) {
$envFile = __DIR__ . '/../.env'; // مسار محلي افتراضي
}
$envFile = getenv('ENV_FILE_PATH') ?: (__DIR__ . '/../.env');
loadEnvironment($envFile);
// 4. Redis Connection (Singleton)

View File

@@ -227,7 +227,7 @@ function getInternalSocketKey(): string
if ($key) {
return trim($key);
}
$path = getenv('INTERNAL_SOCKET_KEY_PATH') ?: '/home/siro-api/.internal_socket_key';
$path = getenv('INTERNAL_SOCKET_KEY_PATH') ?: '';
if (file_exists($path)) {
return trim((string)@file_get_contents($path));
}

View File

@@ -115,5 +115,6 @@ try {
$mail->send();
jsonSuccess(null, "Email sent successfully");
} catch (Exception $e) {
error_log("[sendTripEmail.php] " . $e->getMessage());
jsonError("Failed to send email: " . $mail->ErrorInfo);
}

View File

@@ -4,7 +4,7 @@
require_once realpath(__DIR__ . '/../vendor/autoload.php');
require_once 'load_env.php';
$env_file = '/home/siro-api/env/.env';
$env_file = getenv('ENV_FILE_PATH') ?: (__DIR__ . '/../../../env/.env');
loadEnvironment($env_file);
// ✅ FIX C-02: استخدام getenv بدلاً من file_get_contents الثابت
@@ -49,8 +49,9 @@ class EncryptionHelper {
public function encryptData($plainText) {
$plainText = mb_convert_encoding($plainText, 'UTF-8');
$paddedText = $this->addPadding($plainText);
$encrypted = openssl_encrypt($paddedText, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $this->iv);
return base64_encode($encrypted);
$iv = random_bytes(16);
$encrypted = openssl_encrypt($paddedText, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $iv);
return base64_encode($iv . $encrypted);
}
public function decryptData($encryptedText) {
@@ -61,6 +62,22 @@ class EncryptionHelper {
return false;
}
// محاولة أولى: استخراج IV عشوائي من أول 16 بايت
if (strlen($decoded) >= 16) {
$iv = substr($decoded, 0, 16);
$payload = substr($decoded, 16);
$decrypted = openssl_decrypt($payload, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $iv);
if ($decrypted !== false) {
$pad = ord($decrypted[strlen($decrypted) - 1]);
if ($pad >= 1 && $pad <= 16) {
return substr($decrypted, 0, -$pad);
}
}
}
// محاولة ثانية: IV ثابت (للبيانات القديمة)
$decrypted = openssl_decrypt($decoded, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $this->iv);
if ($decrypted === false) {
@@ -68,7 +85,6 @@ class EncryptionHelper {
return false;
}
// Verify padding is valid before removal
$pad = ord($decrypted[strlen($decrypted) - 1]);
if ($pad < 1 || $pad > 16) {
error_log("[ERROR] Invalid padding value ($pad) for decrypted input: $encryptedText");
@@ -95,13 +111,23 @@ class EncryptionHelper {
return true;
}
public function encryptBinary($data) {
$encrypted = openssl_encrypt($data, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $this->iv);
return $encrypted;
$iv = random_bytes(16);
$encrypted = openssl_encrypt($data, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $iv);
return $iv . $encrypted;
}
public function decryptBinary($data) {
if (strlen($data) >= 16) {
$iv = substr($data, 0, 16);
$payload = substr($data, 16);
$decrypted = openssl_decrypt($payload, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $iv);
if ($decrypted !== false) {
return $decrypted;
}
}
// للبيانات القديمة ذات IV الثابت
$decrypted = openssl_decrypt($data, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $this->iv);
// CRIT-07 FIX: التحقق من فشل openssl_decrypt
if ($decrypted === false) {
error_log('[CRIT-07] openssl_decrypt failed in decryptBinary');
throw new Exception('Decryption failed');

View File

@@ -44,8 +44,7 @@ function isAllowedSocketUrl(string $url): bool {
}
function sendToLocationServer($action, $data) {
// رابط سيرفر اللوكيشن الداخلي أو العام
$url = "http://188.68.36.205:2021";
$url = getenv('LOCATION_SERVER_URL') ?: 'http://188.68.36.205:2021';
if (!isAllowedSocketUrl($url)) {
error_log("[SSRF_BLOCKED] Attempted connection to: $url");
return;
@@ -269,7 +268,7 @@ function notifyPassengerOnRideServer($passenger_id, $payload) {
$INTERNAL_KEY = function_exists('getInternalSocketKey') ? getInternalSocketKey() : '';
if (empty($INTERNAL_KEY)) {
error_log("[SOCKET_CRITICAL] Internal key missing at /home/siro-api/.internal_socket_key");
error_log("[SOCKET_CRITICAL] Internal socket key missing");
}
$postData = [

View File

@@ -1,47 +1,24 @@
<?php
include 'connect.php';
require_once __DIR__ . '/core/bootstrap.php';
// نضمن أن الرد دائماً JSON
header('Content-Type: application/json; charset=utf-8');
// 1) قراءة الـ body كـ JSON (من Flutter)
// التحقق من صلاحية الأدمن عبر JWT
$jwtService = new JwtService($redis ?? null);
$admin = $jwtService->authenticate();
if ($admin->role !== 'admin' && $admin->role !== 'super_admin') {
http_response_code(403);
echo json_encode(['status' => 'error', 'message' => 'Unauthorized. Admin access required.']);
exit;
}
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!is_array($data)) {
// fallback لو أرسلت form-data أو x-www-form-urlencoded
$data = $_POST;
}
// 2) التحقق من رقم هاتف الأدمن المصرّح له
// قراءة الأرقام المسموح لها من الـ ENV
$phonesRaw = getenv('ADMIN_PHONE_NUMBERS') ?: '';
$ALLOWED_TOOL_PHONES = array_values(
array_filter(
array_map(function ($p) {
// إزالة أي رموز غير رقمية (مسافات، +، - إلخ)
return preg_replace('/\D+/', '', $p);
}, explode(',', $phonesRaw))
)
);
// رقم الهاتف القادم من Flutter (parameter جديد)
$adminPhoneParam = isset($data['admin_phone'])
? preg_replace('/\D+/', '', $data['admin_phone'])
: '';
// إذا لم يُرسل رقم أو لم يكن ضمن القائمة → منع الوصول
if ($adminPhoneParam === '' || !in_array($adminPhoneParam, $ALLOWED_TOOL_PHONES, true)) {
http_response_code(403);
echo json_encode([
'status' => 'error',
'message' => 'Access denied for this admin phone.',
]);
exit;
}
// 3) التحقق من بقية المدخلات (action + text)
$action = $data['action'] ?? '';
$text = trim($data['text'] ?? '');
@@ -54,13 +31,10 @@ if ($text === '' || ($action !== 'encrypt' && $action !== 'decrypt')) {
exit;
}
// 4) تنفيذ التشفير / الفك
try {
// require_once __DIR__ . '/encrypt_decrypt.php';
if ($action === 'encrypt') {
$result = $encryptionHelper->encryptData($text);
} else { // decrypt
} else {
$result = $encryptionHelper->decryptData($text);
}
@@ -70,6 +44,7 @@ try {
'result' => (string) $result,
]);
} catch (Exception $e) {
error_log("[ggg.php] " . $e->getMessage());
http_response_code(500);
echo json_encode([
'status' => 'error',

View File

@@ -15,7 +15,6 @@ function loadEnvironment($env_file) {
$value = trim($value, "\"'");
putenv("$keyName=$value");
$_ENV[$keyName] = $value;
$_SERVER[$keyName] = $value;
}
}

View File

@@ -19,5 +19,6 @@ try {
jsonSuccess(null, "Logged out successfully");
} catch (Exception $e) {
error_log("[logout.php] " . $e->getMessage());
jsonError("Logout failed", 500);
}

View File

@@ -11,7 +11,7 @@ ini_set('memory_limit', '512M');
require_once realpath(__DIR__ . '/../vendor/autoload.php');
require_once 'load_env.php';
$env_file = '/home/siro-api/env/.env';
$env_file = getenv('ENV_FILE_PATH') ?: (__DIR__ . '/../../../env/.env');
loadEnvironment($env_file);
include "encrypt_decrypt.php"; // لاستخدام $encryptionHelper

View File

@@ -16,6 +16,6 @@ try {
$con->exec($sql);
echo "SUCCESS: passenger_opening_locations table created successfully.\n";
} catch (Exception $e) {
echo "ERROR: " . $e->getMessage() . "\n";
echo "An internal error occurred" . "\n";
}
?>

View File

@@ -119,7 +119,9 @@ try {
}
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[selectDriverAndCarForMishwariTrip.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
} catch (Exception $e) {
jsonError("Error: " . $e->getMessage());
error_log("[selectDriverAndCarForMishwariTrip.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}

View File

@@ -1,12 +1,19 @@
<?php
require_once __DIR__ . '/../../connect.php';
// استقبال المتغيرات
$driverID = filterRequest("driverID");
$passengerID = filterRequest("passengerID");
// استقبال المتغيرات — force user IDs from JWT based on role
$rideID = filterRequest("rideID");
$note = filterRequest("note");
// Force driverID/passengerID from JWT based on user role
if ($role === 'driver') {
$driverID = $user_id;
$passengerID = filterRequest("passengerID");
} else {
$passengerID = $user_id;
$driverID = filterRequest("driverID");
}
// تنفيذ الإدخال بطريقة آمنة
$sql = "INSERT INTO `canecl` (`driverID`, `passengerID`, `rideID`, `note`)
VALUES (:driverID, :passengerID, :rideID, :note)";

View File

@@ -95,7 +95,7 @@ try {
} catch (PDOException $e) {
error_log("❌ [cancelRideAndLog.php] Database Error: " . $e->getMessage());
jsonError("Database Error: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
*/
require_once __DIR__ . '/../../connect.php';
@@ -144,7 +144,8 @@ try {
}
} catch (PDOException $e) {
jsonError("DB Error: " . $e->getMessage());
error_log("[addCancelTripFromDriverAfterApplied.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>
?>

View File

@@ -1,6 +1,12 @@
<?php
require_once __DIR__ . '/../../connect.php';
// التحقق من أن المستخدم يملك هذا المعرف
if ($role !== 'admin' && $role !== 'super_admin' && (string)$user_id !== (string)$driverID) {
jsonError("Unauthorized: You can only add cars to your own account");
exit;
}
// استقبال القيم
$driverID = filterRequest("driverID");
$vin = $encryptionHelper->encryptData(filterRequest("vin"));

View File

@@ -4,6 +4,23 @@ require_once __DIR__ . '/../../connect.php';
// استقبال ID السجل
$id = filterRequest("id");
// التحقق من أن السجل يخص المستخدم الحالي أو هو أدمن
$checkSql = "SELECT driverID FROM captains_car WHERE id = :id LIMIT 1";
$checkStmt = $con->prepare($checkSql);
$checkStmt->bindParam(':id', $id, PDO::PARAM_INT);
$checkStmt->execute();
$record = $checkStmt->fetch(PDO::FETCH_ASSOC);
if (!$record) {
jsonError("Record not found");
exit;
}
if ($role !== 'admin' && $role !== 'super_admin' && (string)$user_id !== $record['driverID']) {
jsonError("Unauthorized: You can only delete your own car registrations");
exit;
}
// حذف السجل من جدول captains_car (أو CarRegistration لو هو الصحيح فعلاً)
$sql = "DELETE FROM captains_car WHERE id = :id";
$stmt = $con->prepare($sql);

View File

@@ -35,6 +35,7 @@ try {
jsonSuccess($response);
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[get_driver_behavior.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -43,6 +43,7 @@ try {
}
} catch (PDOException $e) {
jsonError("Database Error: " . $e->getMessage());
error_log("[get.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -35,6 +35,7 @@ try {
}
} catch (PDOException $e) {
// Print error message
jsonError($message = "Database error: " . $e->getMessage());
error_log("[add.php] " . $e->getMessage());
jsonError($message = "Database error occurred");
}
?>

View File

@@ -32,6 +32,6 @@ try {
}
} catch (PDOException $e) {
// إرجاع رسالة خطأ في حال حدوث مشكلة في قاعدة البيانات
jsonError("Database error: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -1,21 +1,38 @@
<?php
require_once __DIR__ . '/../../connect.php';
$sql = "SELECT * FROM `driverToken`";
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
$page = max(1, (int) filterRequest('page'));
$limit = 50;
$offset = ($page - 1) * $limit;
$sql = "SELECT * FROM `driverToken` LIMIT :lim OFFSET :off";
$stmt = $con->prepare($sql);
$stmt->bindValue(':lim', $limit, PDO::PARAM_INT);
$stmt->bindValue(':off', $offset, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
// الحصول على العدد الإجمالي للصفحات
$countStmt = $con->query("SELECT COUNT(*) FROM `driverToken`");
$total = $countStmt->fetchColumn();
if ($data) {
// فك تشفير token فقط لكل سجل
foreach ($data as &$item) {
$item['token'] = $encryptionHelper->decryptData($item['token']);
// لا يتم فك تشفير fingerPrint لأنه مشفّر من Flutter
}
echo json_encode([
'status' => 'success',
'data' => $data
'data' => $data,
'total' => (int) $total,
'page' => $page,
'pages' => (int) ceil($total / $limit),
]);
} else {
jsonError("No driver tokens found");

View File

@@ -1,21 +1,37 @@
<?php
require_once __DIR__ . '/../../connect.php';
$sql = "SELECT * FROM `tokens`";
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
$page = max(1, (int) filterRequest('page'));
$limit = 50;
$offset = ($page - 1) * $limit;
$sql = "SELECT * FROM `tokens` LIMIT :lim OFFSET :off";
$stmt = $con->prepare($sql);
$stmt->bindValue(':lim', $limit, PDO::PARAM_INT);
$stmt->bindValue(':off', $offset, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
$countStmt = $con->query("SELECT COUNT(*) FROM `tokens`");
$total = $countStmt->fetchColumn();
if ($data) {
// فك تشفير token فقط
foreach ($data as &$item) {
$item['token'] = $encryptionHelper->decryptData($item['token']);
// fingerPrint يبقى كما هو (مشفّر من التطبيق)
}
echo json_encode([
'status' => 'success',
'data' => $data
'data' => $data,
'total' => (int) $total,
'page' => $page,
'pages' => (int) ceil($total / $limit),
]);
} else {
jsonError("No token records found");

View File

@@ -1,7 +1,16 @@
<?php
// send_fcm.php - FCM HTTP v1 Sender
// send_fcm.php - FCM HTTP v1 Sender (Internal use only)
header('Content-Type: application/json; charset=utf-8');
// 🔐 Require internal API key for authentication
$apiKey = $_SERVER['HTTP_X_API_KEY'] ?? '';
$expectedKey = getenv('FCM_INTERNAL_API_KEY');
if (empty($expectedKey) || !hash_equals($expectedKey, $apiKey)) {
http_response_code(403);
echo json_encode(['status' => 'error', 'message' => 'Unauthorized']);
exit;
}
$serviceAccountFile = __DIR__ . '/service-account.json';
// السماح فقط بـ POST

View File

@@ -99,6 +99,6 @@ try {
$con->rollBack();
}
error_log("claimChallengeReward Error: " . $e->getMessage());
jsonError("Failed to claim reward: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -115,6 +115,6 @@ try {
} catch (PDOException $e) {
error_log("getGamificationDashboard Error: " . $e->getMessage());
jsonError("Database error occurred: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -17,7 +17,12 @@ function generateUniqueCode($con) {
}
}
$driverId = filterRequest("driverId");
// Force driverId from JWT — only drivers can manage invitations
if ($role !== 'driver') {
jsonError("Only drivers can create invitations");
exit;
}
$driverId = $user_id;
$inviterDriverPhone = filterRequest("inviterDriverPhone");
// 🔐 تشفير رقم الهاتف
@@ -52,7 +57,8 @@ if ($checkStmt->rowCount() > 0) {
"expirationTime" => $expirationTime
]);
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[invitor/add] DB Error: " . $e->getMessage());
jsonError("Database error occurred");
}
}
@@ -83,7 +89,8 @@ if ($checkStmt->rowCount() > 0) {
jsonError("Failed to save invite data");
}
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[invitor/add] DB Error: " . $e->getMessage());
jsonError("Database error occurred");
}
}
?>

View File

@@ -58,7 +58,8 @@ if ($checkStmt->rowCount() > 0) {
"expirationTime" => $expirationTime
]);
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[addInvitationPassenger.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
}
} else {
@@ -91,7 +92,8 @@ if ($checkStmt->rowCount() > 0) {
jsonError("Failed to save invite data");
}
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[addInvitationPassenger.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
}
?>

View File

@@ -32,6 +32,7 @@ try {
$insertStmt->execute([$inviterCode, $user_id, $role]);
printSuccess(["message" => "Referral linked successfully"]);
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[add_unified_invite.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -7,8 +7,17 @@ header('Content-Type: application/json');
try {
$inviteId = filterRequest("invite_id");
$driverId = filterRequest("driver_id");
$passengerId = filterRequest("passenger_id");
// Force user ID from JWT based on role
if ($role === 'driver') {
$driverId = $user_id;
$passengerId = null;
} elseif ($role === 'passenger') {
$passengerId = $user_id;
$driverId = null;
} else {
echo json_encode(["status" => "failure", "message" => "Invalid user role."]);
exit;
}
$countryCode = filterRequest("country_code"); // Expected: Jordan, Syria, Egypt
if (empty($inviteId)) {
@@ -88,6 +97,7 @@ try {
}
function addWalletBalance($url, $userId, $userType, $amount) {
$s2sKey = getenv('S2S_SHARED_KEY');
$data = [
"user_id" => $userId,
"user_type" => $userType,
@@ -100,6 +110,11 @@ function addWalletBalance($url, $userId, $userType, $amount) {
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
if ($s2sKey) {
curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-Auth-Token: $s2sKey"]);
}
$response = curl_exec($ch);
curl_close($ch);
return $response;

View File

@@ -145,6 +145,6 @@ try {
if ($con->inTransaction()) {
$con->rollBack();
}
jsonError("Failed to claim reward: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -35,7 +35,8 @@ if ($stmt->rowCount() > 0) {
$insertStmt->execute([$user_id, $role, $newCode]);
printSuccess(["referral_code" => $newCode]);
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[get_unified_code.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
}
?>

View File

@@ -54,6 +54,6 @@ try {
} catch (PDOException $e) {
error_log("DB Error: " . $e->getMessage());
jsonError("Database error: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -39,6 +39,7 @@ try {
jsonError("Invalid invite code, already installed, or expired.");
}
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[updateInvitationCodeFromRegister.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -40,6 +40,7 @@ try {
jsonError("Invalid invite code, already used, or marked as gift.");
}
} catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage());
error_log("[updatePassengersInvitation.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>

View File

@@ -5,10 +5,10 @@ require_once __DIR__ . '/../../connect.php';
// $promo_code = filterRequest("promo_code");
$driverID = filterRequest("driverID");
$sql = "SELECT * FROM `lisenceDetails`WHERE`driverID`='$driverID'";
$sql = "SELECT * FROM `lisenceDetails` WHERE `driverID` = :driverID";
$stmt = $con->prepare($sql);
$stmt->execute();
$stmt->execute([':driverID' => $driverID]);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($result) {

Some files were not shown because too many files have changed in this diff Show More