![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/public_html/api/ |
<?php
/**
* Link Preview API Endpoint
* Fetches Open Graph metadata from URLs for link previews in messages
*/
header('Content-Type: application/json');
session_start();
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../includes/security.php';
// Check authentication
if (!isset($_SESSION['user_id'])) {
echo json_encode(['success' => false, 'message' => 'Authentication required']);
exit;
}
$url = $_GET['url'] ?? '';
if (empty($url)) {
echo json_encode(['success' => false, 'message' => 'URL is required']);
exit;
}
// Validate URL
if (!filter_var($url, FILTER_VALIDATE_URL)) {
echo json_encode(['success' => false, 'message' => 'Invalid URL']);
exit;
}
// Domain restriction - set to true to only allow your domain, false to allow all domains
$restrict_to_domain = false; // Change to true to only allow soundstudiopro.com links
$parsed_url = parse_url($url);
if ($restrict_to_domain) {
$allowed_domains = ['soundstudiopro.com', 'www.soundstudiopro.com'];
$host = strtolower($parsed_url['host'] ?? '');
// Remove www. prefix for comparison
$host = preg_replace('/^www\./', '', $host);
if (!in_array($host, array_map(function($d) { return preg_replace('/^www\./', '', strtolower($d)); }, $allowed_domains))) {
echo json_encode(['success' => false, 'message' => 'Domain not allowed']);
exit;
}
}
// Function to fetch OG metadata from URL
function fetchOGMetadata($url) {
$result = [
'title' => '',
'description' => '',
'image' => '',
'url' => $url,
'site_name' => ''
];
$parsed_url = parse_url($url);
$host = strtolower($parsed_url['host'] ?? '');
$isFacebook = strpos($host, 'facebook.com') !== false;
try {
// For Facebook links, try multiple approaches
if ($isFacebook) {
// First, try the mobile version which sometimes has better OG tags
$mobileUrl = str_replace('www.facebook.com', 'm.facebook.com', $url);
$mobileUrl = str_replace('facebook.com', 'm.facebook.com', $mobileUrl);
// Try mobile version first
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $mobileUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language: en-US,en;q=0.9'
]);
$mobileHtml = curl_exec($ch);
$mobileHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Try to extract OG tags from mobile version
if ($mobileHtml && $mobileHttpCode == 200) {
$dom = new DOMDocument();
@$dom->loadHTML(mb_convert_encoding($mobileHtml, 'HTML-ENTITIES', 'UTF-8'));
$xpath = new DOMXPath($dom);
$imageNodes = $xpath->query("//meta[@property='og:image']");
if ($imageNodes->length > 0) {
$imageUrl = trim($imageNodes->item(0)->getAttribute('content'));
if (!empty($imageUrl)) {
$result['image'] = $imageUrl;
}
}
$titleNodes = $xpath->query("//meta[@property='og:title']");
if ($titleNodes->length > 0) {
$ogTitle = trim($titleNodes->item(0)->getAttribute('content'));
if (!empty($ogTitle) && stripos($ogTitle, 'log in') === false) {
$result['title'] = $ogTitle;
}
}
$descNodes = $xpath->query("//meta[@property='og:description']");
if ($descNodes->length > 0) {
$ogDesc = trim($descNodes->item(0)->getAttribute('content'));
if (!empty($ogDesc) && stripos($ogDesc, 'log in') === false) {
$result['description'] = $ogDesc;
}
}
// If we got good data from mobile, return it
if (!empty($result['image']) || (!empty($result['title']) && $result['title'] !== 'Facebook' && stripos($result['title'], 'log in') === false)) {
if (empty($result['site_name'])) {
$result['site_name'] = 'Facebook';
}
return $result;
}
}
// Fallback to tryFacebookOEmbed function
$fbResult = tryFacebookOEmbed($url);
if ($fbResult && !empty($fbResult['title'])) {
// Merge results, prioritizing OG data
if (empty($result['image']) && !empty($fbResult['image'])) {
$result['image'] = $fbResult['image'];
}
if (empty($result['title']) && !empty($fbResult['title'])) {
$result['title'] = $fbResult['title'];
}
if (empty($result['description']) && !empty($fbResult['description'])) {
$result['description'] = $fbResult['description'];
}
if (empty($result['site_name'])) {
$result['site_name'] = $fbResult['site_name'];
}
}
}
// Set up cURL with proper headers and timeout for regular URL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 8);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
// Enhanced headers - use Facebook's bot user agent for Facebook links
if ($isFacebook) {
$headers = [
'User-Agent: facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)',
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language: en-US,en;q=0.9'
];
} else {
$headers = [
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'Accept-Language: en-US,en;q=0.9',
'Accept-Encoding: gzip, deflate, br',
'Connection: keep-alive',
'Upgrade-Insecure-Requests: 1',
'Sec-Fetch-Dest: document',
'Sec-Fetch-Mode: navigate',
'Sec-Fetch-Site: none',
'Cache-Control: max-age=0'
];
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate, br');
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
$html = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
// Check if we got redirected to a login page (common with Facebook)
$isLoginPage = $isFacebook && (
stripos($html, 'log in') !== false ||
stripos($html, 'Log Into Facebook') !== false ||
stripos($html, 'login') !== false ||
stripos($html, 'must log in') !== false ||
stripos($html, 'sign up') !== false
);
if ($isLoginPage) {
// Try to extract basic info even from login page
$fbInfo = tryFacebookOEmbed($url);
if (!empty($fbInfo['title'])) {
return $fbInfo;
}
// Fallback
$result['title'] = 'Facebook';
$result['site_name'] = 'Facebook';
$result['description'] = 'View on Facebook (login required)';
return $result;
}
if ($error || ($httpCode !== 200 && $httpCode !== 301 && $httpCode !== 302) || empty($html)) {
// If Facebook link failed, return basic info
if ($isFacebook) {
$result['title'] = 'Facebook';
$result['site_name'] = 'Facebook';
$result['description'] = 'View on Facebook';
}
return $result;
}
// Parse HTML to extract OG tags
$dom = new DOMDocument();
@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$xpath = new DOMXPath($dom);
// Extract OG tags
$ogTags = [
'og:title' => 'title',
'og:description' => 'description',
'og:image' => 'image',
'og:site_name' => 'site_name',
'og:url' => 'url'
];
foreach ($ogTags as $ogProperty => $resultKey) {
$nodes = $xpath->query("//meta[@property='{$ogProperty}']");
if ($nodes->length > 0) {
$content = $nodes->item(0)->getAttribute('content');
if (!empty($content)) {
$result[$resultKey] = trim($content);
}
}
}
// Fallback to regular meta tags if OG tags not found
if (empty($result['title'])) {
$titleNodes = $xpath->query("//title");
if ($titleNodes->length > 0) {
$result['title'] = trim($titleNodes->item(0)->textContent);
}
}
if (empty($result['description'])) {
$descNodes = $xpath->query("//meta[@name='description']");
if ($descNodes->length > 0) {
$result['description'] = trim($descNodes->item(0)->getAttribute('content'));
}
}
// Make image URL absolute if it's relative
if (!empty($result['image']) && !preg_match('/^https?:\/\//', $result['image'])) {
$urlParts = parse_url($url);
$baseUrl = ($urlParts['scheme'] ?? 'https') . '://' . ($urlParts['host'] ?? '');
if (isset($urlParts['port'])) {
$baseUrl .= ':' . $urlParts['port'];
}
if (strpos($result['image'], '/') === 0) {
$result['image'] = $baseUrl . $result['image'];
} else {
$path = dirname($urlParts['path'] ?? '/');
$result['image'] = $baseUrl . $path . '/' . $result['image'];
}
}
// Extract domain name for site_name if not set
if (empty($result['site_name'])) {
$urlParts = parse_url($url);
$result['site_name'] = $urlParts['host'] ?? 'SoundStudioPro';
}
} catch (Exception $e) {
error_log("Error fetching OG metadata: " . $e->getMessage());
}
return $result;
}
// Function to try Facebook oEmbed API and Graph API
function tryFacebookOEmbed($url) {
$result = [
'title' => '',
'description' => '',
'image' => '',
'url' => $url,
'site_name' => 'Facebook'
];
// Extract page/group identifier from URL
$pageId = null;
$isGroup = false;
$isPage = false;
if (preg_match('/\/groups\/([^\/\?]+)/', $url, $matches)) {
$pageId = urldecode($matches[1]);
$isGroup = true;
$groupName = str_replace(['-', '_'], ' ', $pageId);
$result['title'] = 'Facebook Group: ' . ucwords($groupName);
$result['description'] = 'Join this Facebook group';
} elseif (preg_match('/\/pages\/([^\/\?]+)/', $url, $matches)) {
$pageId = urldecode($matches[1]);
$isPage = true;
$pageName = str_replace(['-', '_'], ' ', $pageId);
$result['title'] = 'Facebook Page: ' . ucwords($pageName);
$result['description'] = 'Visit this Facebook page';
} elseif (preg_match('/facebook\.com\/([^\/\?]+)/', $url, $matches)) {
$pageId = urldecode($matches[1]);
if ($pageId !== 'www' && $pageId !== 'groups' && $pageId !== 'pages' && !preg_match('/^[0-9]+$/', $pageId)) {
$pageName = str_replace(['-', '_'], ' ', $pageId);
$result['title'] = 'Facebook: ' . ucwords($pageName);
$result['description'] = 'View on Facebook';
$isPage = true;
}
}
// Try to fetch OG data using Facebook's sharing debugger endpoint (public, no auth needed)
if ($pageId) {
$debugUrl = 'https://developers.facebook.com/tools/debug/echo/?q=' . urlencode($url);
try {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . '?sk=about'); // Try about page which sometimes has more public info
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language: en-US,en;q=0.9'
]);
$html = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Try to extract OG tags even from login page (sometimes they're still there)
if ($html && $httpCode == 200) {
$dom = new DOMDocument();
@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$xpath = new DOMXPath($dom);
// Try to get OG image
$imageNodes = $xpath->query("//meta[@property='og:image']");
if ($imageNodes->length > 0) {
$imageUrl = trim($imageNodes->item(0)->getAttribute('content'));
if (!empty($imageUrl) && strpos($imageUrl, 'facebook.com') !== false) {
$result['image'] = $imageUrl;
}
}
// Try to get OG title
$titleNodes = $xpath->query("//meta[@property='og:title']");
if ($titleNodes->length > 0) {
$ogTitle = trim($titleNodes->item(0)->getAttribute('content'));
if (!empty($ogTitle) && stripos($ogTitle, 'log in') === false) {
$result['title'] = $ogTitle;
}
}
// Try to get OG description
$descNodes = $xpath->query("//meta[@property='og:description']");
if ($descNodes->length > 0) {
$ogDesc = trim($descNodes->item(0)->getAttribute('content'));
if (!empty($ogDesc) && stripos($ogDesc, 'log in') === false) {
$result['description'] = $ogDesc;
}
}
}
} catch (Exception $e) {
error_log("Error fetching Facebook OG data: " . $e->getMessage());
}
}
// Fallback images if no image found
if (empty($result['image'])) {
if ($isGroup) {
$result['image'] = 'https://static.xx.fbcdn.net/rsrc.php/v3/yx/r/9M3lGjPJ9qK.png';
} elseif ($isPage) {
// Try a generic Facebook page image
$result['image'] = 'https://static.xx.fbcdn.net/rsrc.php/v3/yz/r/ujTY9Bound_j.png';
} else {
$result['image'] = 'https://static.xx.fbcdn.net/rsrc.php/yb/r/hLRJ1GG_y0J.svg';
}
}
return $result;
}
$metadata = fetchOGMetadata($url);
echo json_encode([
'success' => true,
'preview' => $metadata
]);