²ιΏ΄/±ΰΌ ΄ϊΒλ
ΔΪΘέ
<?php /** * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ * LANDING SYSTEM - URL PROCESSOR (Phase 2) * Handles redirect URL template processing with placeholders * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ class UrlProcessor { /** * Obfuscate a recipient ID for URL usage * Format: {4hex}{actual_id}{4HEX} * Example: a1b2abc123A1B2 */ public static function obfuscateId($id) { if (empty($id)) { return ''; } $prefix = substr(bin2hex(random_bytes(2)), 0, 4); // 4 lowercase hex $suffix = strtoupper(substr(bin2hex(random_bytes(2)), 0, 4)); // 4 uppercase hex return $prefix . $id . $suffix; } /** * Extract actual ID from obfuscated URL ID * Input: a1b2abc123A1B2 -> Output: abc123 */ public static function deobfuscateId($obfuscatedId) { if (strlen($obfuscatedId) < 9) { // 4 + 1 + 4 minimum return $obfuscatedId; } // Remove first 4 chars and last 4 chars return substr($obfuscatedId, 4, -4); } /** * Process redirect URL template with recipient data * * @param string $template URL template with ${placeholder} syntax * @param array $recipient Recipient data from database * @return string Processed URL */ public static function processRedirectUrl($template, $recipient) { if (empty($template)) { return ''; } $email = $recipient['email'] ?? ''; $domain = $recipient['domain'] ?? ''; // If domain not set, extract from email if (empty($domain) && !empty($email) && strpos($email, '@') !== false) { $domain = substr($email, strpos($email, '@') + 1); } // Build placeholder map $placeholders = [ // Email variations '${email}' => $email, '${emailb64}' => self::base64UrlEncode($email), '${emailmd5}' => md5(strtolower(trim($email))), '${emailsha1}' => sha1(strtolower(trim($email))), '${emailhex}' => bin2hex($email), '${emailurl}' => rawurlencode($email), // Recipient data '${id}' => $recipient['id'] ?? '', '${name}' => $recipient['name'] ?? '', '${firstname}' => $recipient['firstname'] ?? '', '${lastname}' => $recipient['lastname'] ?? '', '${company}' => $recipient['company'] ?? '', '${domain}' => $domain, // Timestamps '${timestamp}' => time(), '${date}' => date('Y-m-d'), '${datetime}' => date('Y-m-d_H-i-s'), '${unixtime}' => time(), // Random/unique values '${random}' => bin2hex(random_bytes(8)), '${uuid}' => self::generateUuid(), '${token}' => bin2hex(random_bytes(16)), // Hashed combinations (for verification) '${idhash}' => substr(md5(($recipient['id'] ?? '') . 'salt'), 0, 8), '${verify}' => self::generateVerifyToken($recipient), ]; // Custom data placeholders if (!empty($recipient['custom_data'])) { $customData = is_string($recipient['custom_data']) ? json_decode($recipient['custom_data'], true) : $recipient['custom_data']; if (is_array($customData)) { foreach ($customData as $key => $value) { $placeholders['${' . $key . '}'] = $value; $placeholders['${' . $key . '_b64}'] = self::base64UrlEncode($value); } } } // Replace all placeholders $url = $template; foreach ($placeholders as $placeholder => $value) { $url = str_replace($placeholder, $value, $url); } return $url; } /** * Get the redirect URL for a recipient * Uses per-recipient URL if set, otherwise uses global template * * @param array $recipient Recipient data * @return string Final redirect URL */ public static function getRedirectUrl($recipient) { // Check if recipient has custom redirect URL if (USE_RECIPIENT_REDIRECT && !empty($recipient['redirect_url'])) { $template = $recipient['redirect_url']; // Log source for debugging if (function_exists('log_info')) { log_info("URL source: recipient redirect_url (" . strlen($template) . " chars)"); } } else { $template = FINAL_REDIRECT_URL; // Log source for debugging if (function_exists('log_info')) { $reason = !USE_RECIPIENT_REDIRECT ? 'USE_RECIPIENT_REDIRECT=false' : 'recipient redirect_url empty'; log_info("URL source: FINAL_REDIRECT_URL (reason: $reason)"); } } // Process placeholders $result = self::processRedirectUrl($template, $recipient); // Log template processing result if (function_exists('log_info') && $result !== $template) { log_info("URL template processed: placeholders replaced"); } return $result; } /** * URL-safe Base64 encoding * Replaces + with -, / with _, removes = * * @param string $data Data to encode * @return string URL-safe base64 string */ public static function base64UrlEncode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } /** * URL-safe Base64 decoding * * @param string $data Base64 URL-safe string * @return string Decoded data */ public static function base64UrlDecode($data) { return base64_decode(strtr($data, '-_', '+/')); } /** * Generate UUID v4 * * @return string UUID string */ public static function generateUuid() { $data = random_bytes(16); $data[6] = chr(ord($data[6]) & 0x0f | 0x40); $data[8] = chr(ord($data[8]) & 0x3f | 0x80); return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); } /** * Generate verification token for recipient * Can be used to verify redirect authenticity * * @param array $recipient Recipient data * @return string Verification token */ public static function generateVerifyToken($recipient) { $data = ($recipient['id'] ?? '') . ($recipient['email'] ?? '') . date('Y-m-d') . API_SECRET; return substr(hash('sha256', $data), 0, 16); } /** * Validate a verification token * * @param string $token Token to validate * @param array $recipient Recipient data * @return bool True if valid */ public static function validateVerifyToken($token, $recipient) { $expected = self::generateVerifyToken($recipient); return hash_equals($expected, $token); } /** * Validate URL format and scheme * * @param string $url URL to validate * @return bool True if valid */ public static function isValidUrl($url) { if (!filter_var($url, FILTER_VALIDATE_URL)) { return false; } $scheme = parse_url($url, PHP_URL_SCHEME); return in_array(strtolower($scheme), ['http', 'https']); } /** * Obfuscate URL for logging (hide sensitive parts) * * @param string $url URL to obfuscate * @return string Obfuscated URL */ public static function obfuscateUrl($url) { $parsed = parse_url($url); $result = ($parsed['scheme'] ?? 'https') . '://'; $result .= $parsed['host'] ?? 'unknown'; if (!empty($parsed['path'])) { $result .= preg_replace('/[a-zA-Z0-9]{20,}/', '***', $parsed['path']); } if (!empty($parsed['query'])) { $result .= '?***'; } return $result; } } /** * Quick function to get redirect URL */ function get_redirect_url($recipient) { return UrlProcessor::getRedirectUrl($recipient); } /** * Quick function for URL-safe base64 encode */ function base64url_encode($data) { return UrlProcessor::base64UrlEncode($data); } /** * Quick function for URL-safe base64 decode */ function base64url_decode($data) { return UrlProcessor::base64UrlDecode($data); }