// RV_AUTH_IMAP_EARLY_WARNING_GUARD_V14_20260528 // Prevent optional IMAP mailbox warnings from breaking auth pages. if (!defined('RV_AUTH_IMAP_EARLY_WARNING_GUARD_V14')) { define('RV_AUTH_IMAP_EARLY_WARNING_GUARD_V14', true); set_error_handler(static function (int $severity, string $message, string $file = '', int $line = 0): bool { $m = strtolower($message); if ( str_contains($m, 'imap') || str_contains($m, 'authenticationfailed') || str_contains($m, 'can not authenticate to imap server') || str_contains($m, 'cannot authenticate to imap server') ) { if (function_exists('imap_errors')) { @imap_errors(); } if (function_exists('imap_alerts')) { @imap_alerts(); } return true; } return false; }); register_shutdown_function(static function (): void { try { if (function_exists('imap_errors')) { @imap_errors(); } if (function_exists('imap_alerts')) { @imap_alerts(); } } catch (Throwable $e) { } }); } // RV_AUTH_JOIN_FRONT_CONTROLLER_PATH_RESCUE_V2B_20260528 // Some legacy rewrite/canonical rules can turn /join into /news/article/join. // Normalize it before CodeIgniter routing. $__rvJoinUri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/'; if ($__rvJoinUri === '/news/article/join') { $_SERVER['REQUEST_URI'] = '/join' . ((isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] !== '') ? '?' . $_SERVER['QUERY_STRING'] : ''); } elseif ($__rvJoinUri === '/news/article/create-account') { $_SERVER['REQUEST_URI'] = '/create-account' . ((isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] !== '') ? '?' . $_SERVER['QUERY_STRING'] : ''); } unset($__rvJoinUri); /* ROOVET SEO CANONICAL PREBOOT START */ if ( PHP_SAPI !== 'cli' && in_array(strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')), ['GET', 'HEAD'], true) ) { $canonicalHost = 'roovet.com'; $host = strtolower((string) ($_SERVER['HTTP_HOST'] ?? $canonicalHost)); $host = preg_replace('~:\d+$~', '', $host) ?: $canonicalHost; $https = (! empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off') || strtolower((string) ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '')) === 'https' || ((int) ($_SERVER['SERVER_PORT'] ?? 0) === 443) || ( ! empty($_SERVER['HTTP_CF_VISITOR']) && stripos((string) $_SERVER['HTTP_CF_VISITOR'], 'https') !== false ); $requestUri = (string) ($_SERVER['REQUEST_URI'] ?? '/'); $parts = parse_url($requestUri); $originalPath = (string) ($parts['path'] ?? '/'); $rawQuery = (string) ($parts['query'] ?? ''); $path = '/' . ltrim($originalPath, '/'); $path = preg_replace('~/+~', '/', $path) ?: '/'; // Collapse duplicate front-controller URLs: // /index.php // /index.php/ // /index.php/search $path = preg_replace('~^/index\.php(?:/|$)~i', '/', $path) ?: '/'; $isCi4PhysicalFolderPath = preg_match('~^/(ads|drive)(?:/|$)~i', $path) === 1; if ($path !== '/' && ! $isCi4PhysicalFolderPath) { $path = rtrim($path, '/'); } $params = []; if ($rawQuery !== '') { parse_str($rawQuery, $params); } $noise = [ 'fbclid', 'gclid', 'gbraid', 'wbraid', 'msclkid', 'mc_cid', 'mc_eid', 'igshid', 'yclid', '_hsenc', '_hsmi', 'sscid', 'spm', ]; $clean = []; foreach ($params as $key => $value) { $key = trim((string) $key); $lower = strtolower($key); if ($key === '') { continue; } if (str_starts_with($lower, 'utm_')) { continue; } if (in_array($lower, $noise, true)) { continue; } if ($lower === 'page' && (string) $value === '1') { continue; } if ($value === null || $value === '') { continue; } $clean[$key] = $value; } if ($clean !== []) { ksort($clean); } $query = $clean === [] ? '' : http_build_query($clean, '', '&', PHP_QUERY_RFC3986); $target = 'https://' . $canonicalHost . $path . ($query !== '' ? '?' . $query : ''); $currentScheme = $https ? 'https' : 'http'; $current = $currentScheme . '://' . $host . $originalPath . ($rawQuery !== '' ? '?' . $rawQuery : ''); if ($current !== $target) { header('Location: ' . $target, true, 301); exit; } } /* ROOVET SEO CANONICAL PREBOOT END */ /* ROOVET SEO NEWS ARTICLE CANONICAL FIX START */ if ( PHP_SAPI !== 'cli' && strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')) === 'GET' ) { $requestUri = (string) ($_SERVER['REQUEST_URI'] ?? '/'); $parts = parse_url($requestUri); $path = '/' . ltrim((string) ($parts['path'] ?? '/'), '/'); $path = preg_replace('~/+~', '/', $path) ?: '/'; $path = preg_replace('~^/index\.php(?:/|$)~i', '/', $path) ?: '/'; if ($path !== '/') { $path = rtrim($path, '/'); } // RV_ACCOUNT_INFO_NEWS_REDIRECT_BYPASS_V9_20260524 $rvPrebootPathForAccountEditor = trim((string) parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH), '/'); $rvPrebootIsAccountEditor = (bool) preg_match('#^(?:account(?:/(?:info|profile|settings))?|profile-editor)$#i', $rvPrebootPathForAccountEditor); if (preg_match('~^/news/article/([A-Za-z0-9][A-Za-z0-9-]{1,180})$~', $path, $m)) { $canonical = 'https://roovet.com/news/article/' . rawurlencode($m[1]); ob_start(static function (string $html) use ($canonical): string { if ($html === '' || stripos($html, '') === false) { return $html; } $tag = '' . "\n"; if (preg_match('~]*rel=["\']canonical["\'][^>]*>\s*~i', $html)) { $html = preg_replace('~]*rel=["\']canonical["\'][^>]*>\s*~i', $tag, $html); } else { $html = preg_replace('~~i', ' ' . $tag . '', $html, 1); } return $html; }); } } /* ROOVET SEO NEWS ARTICLE CANONICAL FIX END */ /* ROOVET SEO SOFT404 SEARCH PREBOOT START */ if ( PHP_SAPI !== 'cli' && in_array(strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')), ['GET', 'HEAD'], true) ) { $requestUri = (string) ($_SERVER['REQUEST_URI'] ?? '/'); $parts = parse_url($requestUri); $path = '/' . ltrim((string) ($parts['path'] ?? '/'), '/'); $rawQuery = (string) ($parts['query'] ?? ''); $path = preg_replace('~/+~', '/', $path) ?: '/'; $path = preg_replace('~^/index\.php(?:/|$)~i', '/', $path) ?: '/'; if ($path !== '/') { $path = rtrim($path, '/'); } $isPublicSearchPath = ($path === '/search' || str_starts_with($path, '/search/')); if ($isPublicSearchPath) { $params = []; if ($rawQuery !== '') { parse_str($rawQuery, $params); } $q = trim((string) ($params['q'] ?? '')); $qDecoded = trim(rawurldecode(rawurldecode($q))); $isSpamSearchQuery = static function (string $q, string $rawQuery, string $requestUri): bool { $haystack = strtolower($rawQuery . ' ' . $requestUri . ' ' . $q); // Common ad/template placeholders and malformed injected query tokens. if (preg_match('~(%25s|%2525s|%s|\{searchterms?\}|\{search_terms?\}|\{keyword\}|\[keyword\]|__keyword__|%%)~i', $haystack)) { return true; } $qClean = strtolower(trim(preg_replace('~\s+~', ' ', $q) ?: '')); if ($qClean === '') { return false; } // Very long search-result URLs are usually crawler traps, not useful landing pages. if (strlen($qClean) > 180) { return true; } $tokens = preg_split('~[^a-z0-9]+~i', $qClean, -1, PREG_SPLIT_NO_EMPTY); $tokens = is_array($tokens) ? array_values(array_filter($tokens, static fn ($t) => strlen((string) $t) > 1)) : []; $tokenCount = count($tokens); if ($tokenCount > 24) { return true; } if ($tokenCount >= 8) { $freq = array_count_values($tokens); arsort($freq); $maxRepeat = (int) reset($freq); $unique = count($freq); // Keyword-stuffed repeated words like: // news news news reviews reviews official site latest latest... if ($maxRepeat >= 4) { return true; } if ($unique > 0 && ($unique / max(1, $tokenCount)) <= 0.38) { return true; } $spamWords = ['news', 'reviews', 'review', 'latest', 'official', 'site']; $spamHits = 0; foreach ($tokens as $token) { if (in_array($token, $spamWords, true)) { $spamHits++; } } if ($spamHits >= 6 && $spamHits >= (int) floor($tokenCount * 0.6)) { return true; } } if (substr_count($qClean, 'official site') >= 2) { return true; } return false; }; if ($isSpamSearchQuery($qDecoded, $rawQuery, $requestUri)) { http_response_code(410); header('Content-Type: text/html; charset=UTF-8', true); header('X-Robots-Tag: noindex, nofollow', true); header('Cache-Control: no-store, no-cache, must-revalidate', true); if (strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')) !== 'HEAD') { echo '
This search URL is no longer available.
'; } exit; } // Valid internal search pages can be used by people, but should not become indexed landing pages. header('X-Robots-Tag: noindex, follow', true); } } /* ROOVET SEO SOFT404 SEARCH PREBOOT END */ /* ROOVET SEO LEGACY 404 CLEANUP PREBOOT START */ if ( PHP_SAPI !== 'cli' && in_array(strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')), ['GET', 'HEAD'], true) ) { $requestUri = (string) ($_SERVER['REQUEST_URI'] ?? '/'); $parts = parse_url($requestUri); $host = strtolower((string) ($_SERVER['HTTP_HOST'] ?? 'roovet.com')); $host = preg_replace('~:\d+$~', '', $host) ?: 'roovet.com'; $path = '/' . ltrim((string) ($parts['path'] ?? '/'), '/'); $rawQuery = (string) ($parts['query'] ?? ''); $path = preg_replace('~/+~', '/', $path) ?: '/'; $path = preg_replace('~^/index\.php(?:/|$)~i', '/', $path) ?: '/'; if ($path !== '/') { $path = rtrim($path, '/'); } $pathLower = strtolower($path); $isHead = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')) === 'HEAD'; $params = []; if ($rawQuery !== '') { parse_str($rawQuery, $params); } $appendQuery = static function (string $target, string $query): string { $query = trim($query); if ($query === '') { return $target; } return $target . (str_contains($target, '?') ? '&' : '?') . $query; }; $moved = static function (string $target): void { header('Location: ' . $target, true, 301); exit; }; $gone = static function (string $message = 'This legacy URL is no longer available.') use ($isHead): void { http_response_code(410); header('Content-Type: text/html; charset=UTF-8', true); header('X-Robots-Tag: noindex, nofollow', true); header('Cache-Control: no-store, no-cache, must-revalidate', true); if (! $isHead) { echo '' . htmlspecialchars($message, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '
'; } exit; }; $notIndexable404 = static function (string $message = 'This URL is not available for indexing.') use ($isHead): void { http_response_code(404); header('Content-Type: text/html; charset=UTF-8', true); header('X-Robots-Tag: noindex, nofollow', true); if (! $isHead) { echo '' . htmlspecialchars($message, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '
'; } exit; }; $encodePathPreservingSlashes = static function (string $value): string { $value = trim(rawurldecode($value), "/ \t\n\r\0\x0B"); if ($value === '') { return ''; } $segments = explode('/', $value); $encoded = []; foreach ($segments as $segment) { $segment = trim($segment); if ($segment === '') { continue; } // MediaWiki titles prefer underscores. $segment = str_replace(' ', '_', $segment); $encoded[] = rawurlencode($segment); } return implode('/', $encoded); }; /* * MIGRATION: roovet.com/social moved to the new Social homepage. * The old Social content was deleted, so all legacy /social paths should land * on the new social.roovet.com homepage instead of preserving old dead paths. */ if ($path === '/social' || str_starts_with($path, '/social/')) { $moved('https://social.roovet.com/'); } /* * MIGRATION: roovet.com/sound moved to the new Sound homepage. * The old Sound content was deleted, so all legacy /sound paths should land * on the new sound.roovet.com homepage instead of preserving old dead paths. */ if ($path === '/sound' || str_starts_with($path, '/sound/')) { $moved('https://sound.roovet.com/'); } /* * MIGRATION: roovet.com/articles moved to articles.roovet.com * Preserve normal pretty titles and MediaWiki index.php query URLs. */ if ($path === '/articles') { $moved('https://articles.roovet.com/'); } if ($path === '/articles/index.php') { $moved($appendQuery('https://articles.roovet.com/index.php', $rawQuery)); } if (str_starts_with($path, '/articles/')) { $targetPath = substr($path, strlen('/articles/')); $targetPath = $encodePathPreservingSlashes($targetPath); $moved('https://articles.roovet.com/' . $targetPath); } /* * If old wiki.roovet.com URLs ever hit this CI4 app, send them to Articles. */ if ($host === 'wiki.roovet.com') { if ($path === '/' || $path === '') { $moved('https://articles.roovet.com/'); } if ($path === '/index.php') { $moved($appendQuery('https://articles.roovet.com/index.php', $rawQuery)); } $moved($appendQuery('https://articles.roovet.com' . $path, $rawQuery)); } /* * MIGRATION: old WordPress root slugs moved to CI4 news article URLs. * * Example: * /904-area-code/ -> /news/article/904-area-code * * This only catches single-segment slug URLs and avoids current app sections. */ $reservedRootSlugs = [ '_csrf', 'a', 'about', 'account', 'admin', 'ads', 'ai', 'api', 'app', 'articles', 'assets', 'atk-vs-kta-jacksonvilles-deadly-rapper-beef', 'auth', 'authors', 'books', 'careers', 'cart', 'categories', 'chat', 'checkout', 'contact', 'crew', 'crm', 'dashboard', 'dev', 'devportal', 'docs', 'download', 'drive', 'flow', 'forgot', 'help', 'images', 'img', 'links', 'login', 'logout', 'mail', 'medical', 'news', 'newsroom', 'notifications', 'onboarding', 'pay', 'portal', 'privacy', 'pro', 'products', 'profile', 'pros', 'register', 'reset', 'robots.txt', 'search', 'seo', 'settings', 'shop', 'signup', 'sitemap', 'sitemap-news.xml', 'sitemap.xml', 'sitemap_news.xml', 'social', 'sound', 'store', 'stories', 'suite', 'support', 'tag', 'talk', 'terms', 'tribalbrown', 'twerking', 'user', 'users', 'verification', 'verified', 'verify-phone', 'video', 'videos', 'wallet', 'walletpass', 'wishlist', 'wjxt-tv-march-15-2009-15k-take-to-streets-in-15k-river', 'wp-admin', 'wp-content', 'wp-includes', 'x', ]; $singleSlug = trim($path, '/'); /* * HARD EXEMPTION: real CI4 root feature pages. * These must never be migrated to /news/article/{slug}. */ $realCi4RootSlugs = [ 'about', 'pro', 'pros', 'ads', 'drive', 'shop', 'video', 'stories', 'search', 'wallet', 'mail', 'ai', 'chat', 'docs', 'help', 'contact', 'careers', 'verification', 'verified', ]; if (isset($reservedRootSlugs) && is_array($reservedRootSlugs)) { $reservedRootSlugs = array_values(array_unique(array_merge($reservedRootSlugs, $realCi4RootSlugs))); } if ( $singleSlug !== '' && ! str_contains($singleSlug, '/') && preg_match('~^[A-Za-z0-9][A-Za-z0-9-]{1,180}$~', $singleSlug) === 1 && ! in_array(strtolower($singleSlug), $reservedRootSlugs, true) && ! in_array(strtolower($singleSlug), $realCi4RootSlugs ?? [], true) ) { $moved('https://roovet.com/news/article/' . rawurlencode($singleSlug)); } /* * Old WordPress category/tag/date archives. Keep these out of the index. * Tags can go to news tag pages if your current News controller supports them. */ if (preg_match('~^/tag/([^/]+)$~i', $path, $m)) { $moved('https://roovet.com/news/tag/' . rawurlencode($m[1])); } if (preg_match('~^/category/([^/]+)$~i', $path, $m)) { $moved('https://roovet.com/news/category/' . rawurlencode($m[1])); } /* * WordPress/AMP/probe/crawler junk with no useful new destination. */ if ( preg_match('~/(wp-admin|wp-content|wp-includes|wlwmanifest\.xml)$~i', $path) === 1 || preg_match('~/(wp-admin|wp-content|wp-includes)/~i', $path) === 1 || preg_match('~/amp$~i', $path) === 1 || $path === '/xmlrpc.php' ) { $gone('This old WordPress/probe URL is no longer available.'); } /* * Old image proxy URLs. If the underlying upload file is missing, return 410. */ if ($path === '/img/local') { $p = trim((string) ($params['p'] ?? '')); if ($p === '' || str_contains($p, '..') || ! is_file(__DIR__ . '/' . ltrim($p, '/'))) { $gone('This old image URL is no longer available.'); } } /* * Old deleted ad script endpoints from pre-CI4/external embeds. */ if ( $path === '/ads/show-ads.php' || $path === '/ads/ppc-contact-us.php' || $host === 'adwords.roovet.com' ) { $gone('This old ads URL is no longer available.'); } /* * Old account/API/dashboard URLs should not be indexed if they are unavailable. * Do not redirect private/account URLs to the homepage. */ if ( preg_match('~^/(api|account|password|dashboard|wallet)/(.*)$~i', $path) === 1 && ! str_starts_with($pathLower, '/api/search') ) { // RV_ACCOUNT_INFO_INDEX_PREBOOT_ALLOW_V9_20260524 // Let real account editor routes reach CodeIgniter instead of the private/API indexing 404. $rvAccountEditorPath = trim((string) parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH), '/'); if (! preg_match('#^account(?:/(?:info|profile|settings|profile-save|info/save|profile/update))?$#i', $rvAccountEditorPath)) { // RV_ACCOUNT_PROFILE_SAVE_ROUTE_V14_20260524 // Account profile editor + save endpoints are private app routes, not SEO fallback URLs. $rvAccountProfileSavePath = trim((string) parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH), '/'); if (! preg_match('#^account(?:/(?:info|profile|settings|profile-save|info/save|profile/update))?$#i', $rvAccountProfileSavePath)) { $notIndexable404('This private or API URL is not available for indexing.'); } } } } /* ROOVET SEO LEGACY 404 CLEANUP PREBOOT END */ use CodeIgniter\Boot; use Config\Paths; $minPhpVersion = '8.1'; if (version_compare(PHP_VERSION, $minPhpVersion, '<')) { header('HTTP/1.1 503 Service Unavailable.', true, 503); echo sprintf('Your PHP version must be %s or higher to run CodeIgniter. Current version: %s', $minPhpVersion, PHP_VERSION); exit(1); } define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR); if (getcwd() . DIRECTORY_SEPARATOR !== FCPATH) { chdir(FCPATH); } /* app is inside webroot now */ $pathsPath = FCPATH . 'app/Config/Paths.php'; if (!is_file($pathsPath)) { header('HTTP/1.1 503 Service Unavailable.', true, 503); echo 'Invalid $pathsPath: ' . htmlspecialchars($pathsPath); exit(1); } require $pathsPath; $paths = new Paths(); require rtrim($paths->systemDirectory, "/ \t\n\r\0\x0B") . '/Boot.php'; exit(Boot::bootWeb($paths));