| Server IP : 188.114.96.3 / Your IP : 216.73.216.143 Web Server : Apache System : Linux sv058.home.net.pl 6.8.2 husak_dirty_pages_fix #1 SMP PREEMPT_DYNAMIC Tue Apr 2 22:06:51 CEST 2024 x86_64 User : ( 3194769) PHP Version : 8.5.12 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /polana/includes/ |
Upload File : |
<?php
declare(strict_types=1);
/** POLANA INDEXNOW V17.7: same-domain, deduplicated queue, safe fail-open CMS hooks. */
function pin77_host(): string { return 'mojapolanasmakow.pl'; }
function pin77_base(): string { return 'https://' . pin77_host(); }
function pin77_key(): string { return trim(setting('polana_indexnow_key', '')); }
function pin77_enabled(): bool { return setting('polana_indexnow_enabled', '0') === '1'; }
function pin77_key_url(): string { return pin77_base() . '/' . pin77_key() . '.txt'; }
function pin77_tables_exist(): bool {
try {
db()->query('SELECT 1 FROM polana_indexnow_queue LIMIT 1');
return true;
} catch (Throwable $e) { return false; }
}
function pin77_valid_url(string $url): ?string {
$url = trim($url);
if ($url === '' || strlen($url) > 1500 || preg_match('/[\x00-\x20\x7f]/', $url)) return null;
if (str_starts_with($url, '/') && !str_starts_with($url, '//')) $url = pin77_base() . $url;
$parts = parse_url($url);
if (!is_array($parts) || strtolower((string)($parts['scheme'] ?? '')) !== 'https'
|| strtolower((string)($parts['host'] ?? '')) !== pin77_host()
|| isset($parts['port']) || isset($parts['user']) || isset($parts['pass']) || isset($parts['fragment'])) return null;
$path = $parts['path'] ?? '/';
if ($path === '') $path = '/';
if (str_starts_with($path, '//') || preg_match('~/(admin|includes|storage|uploads/private)(/|$)~i', $path)
|| preg_match('~\.(php|txt|xml|json|sql|bak|zip|log|pdf)$~i', $path)
|| (isset($parts['query']) && !(preg_match('~^/(?:en/|de/|es/)?menu-seo$~',$path) && preg_match('/^kind=(?:soft|wine)$/D',(string)$parts['query'])))) return null;
if (in_array(rtrim(strtolower($path), '/'), ['/newsletter', '/rezerwacja', '/polityka-prywatnosci', '/en/privacy', '/de/privacy', '/es/privacy'], true)) return null;
return pin77_base() . $path . (isset($parts['query']) ? '?'.(string)$parts['query'] : '');
}
function pin77_enqueue(string $url, bool $force = true): bool {
if (!pin77_enabled() || !pin77_tables_exist()) return false;
$url = pin77_valid_url($url);
if ($url === null) return false;
$hash = hash('sha256', $url);
if ($force) {
$sql = "INSERT INTO polana_indexnow_queue (url_hash,url,status,attempts,next_attempt,updated_at)
VALUES (?,?,'pending',0,NOW(),NOW())
ON DUPLICATE KEY UPDATE url=VALUES(url),status='pending',attempts=0,next_attempt=NOW(),updated_at=NOW()";
} else {
$sql = "INSERT IGNORE INTO polana_indexnow_queue (url_hash,url,status,attempts,next_attempt,updated_at)
VALUES (?,?,'pending',0,NOW(),NOW())";
}
db()->prepare($sql)->execute([$hash,$url]);
return true;
}
function pin77_http(string $url, ?string $post = null, int $timeout = 10): array {
// URL is ALWAYS one of two hard-coded HTTPS endpoints, never a user-supplied host.
if (!in_array($url, [pin77_base().'/sitemap.xml', 'https://api.indexnow.org/indexnow'], true)) {
throw new RuntimeException('Niedozwolony endpoint.');
}
if (function_exists('curl_init')) {
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>$timeout,
CURLOPT_CONNECTTIMEOUT=>5, CURLOPT_FOLLOWLOCATION=>false, CURLOPT_MAXREDIRS=>0,
CURLOPT_SSL_VERIFYPEER=>true, CURLOPT_SSL_VERIFYHOST=>2,
CURLOPT_USERAGENT=>'MojaPolana-IndexNow/17.7',
CURLOPT_HTTPHEADER=> $post === null ? ['Accept: application/xml'] : ['Content-Type: application/json; charset=utf-8']]);
if ($post !== null) { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); }
$body = curl_exec($ch);
$status = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$error = curl_error($ch);
curl_close($ch);
return [$status, is_string($body) ? $body : '', $error];
}
$opts = ['http'=>['method'=>$post===null?'GET':'POST','timeout'=>$timeout,'ignore_errors'=>true,
'follow_location'=>0, 'max_redirects'=>0,
'header'=>$post===null?"Accept: application/xml\r\n":"Content-Type: application/json; charset=utf-8\r\n",
'content'=>$post ?? ''], 'ssl'=>['verify_peer'=>true,'verify_peer_name'=>true]];
$body = @file_get_contents($url, false, stream_context_create($opts));
$status = 0;
foreach (($http_response_header ?? []) as $line) if (preg_match('~^HTTP/\S+\s+(\d{3})~', $line, $m)) $status = (int)$m[1];
return [$status, is_string($body)?$body:'', $body===false?'Brak odpowiedzi HTTPS':''];
}
function pin77_import_sitemap(): array {
[$code,$body,$error] = pin77_http(pin77_base().'/sitemap.xml',null,10);
if ($code !== 200) throw new RuntimeException('Mapa XML nie odpowiada HTTP 200 ('.$code.'). '.$error);
if (strlen($body) > 3*1024*1024 || !preg_match('~<(?:\w+:)?urlset\b~i', $body)) throw new RuntimeException('Odpowiedź nie jest poprawną mapą urlset XML.');
if (!preg_match_all('~<(?:\w+:)?loc>\s*(.*?)\s*</(?:\w+:)?loc>~si', $body, $matches)) throw new RuntimeException('Brak adresów <loc> w mapie.');
$accepted=0; $rejected=0;
foreach (array_unique($matches[1]) as $raw) {
$url = html_entity_decode(trim(strip_tags($raw)), ENT_QUOTES | ENT_XML1, 'UTF-8');
if (pin77_enqueue($url, false)) $accepted++; else $rejected++;
}
return ['accepted'=>$accepted,'rejected'=>$rejected];
}
function pin77_send_queue(int $limit = 25): array {
if (!pin77_enabled()) throw new RuntimeException('Włącz IndexNow w ustawieniach.');
$key = pin77_key();
if (!preg_match('/^[A-Za-z0-9-]{8,128}$/D', $key)) throw new RuntimeException('Niepoprawny klucz IndexNow.');
$file = dirname(__DIR__) . '/' . $key . '.txt';
if (!is_file($file) || trim((string)@file_get_contents($file)) !== $key) throw new RuntimeException('Brak pliku klucza w katalogu strony.');
$limit = max(1,min(100,$limit));
$stmt = db()->query("SELECT id,url FROM polana_indexnow_queue
WHERE status IN ('pending','error') AND attempts < 8 AND (next_attempt IS NULL OR next_attempt<=NOW())
ORDER BY updated_at ASC LIMIT ".$limit);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!$rows) return ['count'=>0,'http'=>null,'message'=>'Kolejka jest pusta lub czeka na ponowienie.'];
$payload = json_encode(['host'=>pin77_host(),'key'=>$key,'keyLocation'=>pin77_key_url(),
'urlList'=>array_column($rows,'url')], JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE|JSON_THROW_ON_ERROR);
[$http,$body,$error] = pin77_http('https://api.indexnow.org/indexnow',$payload,12);
$ok = in_array($http,[200,202],true);
$reason = $ok ? ($http===202?'Przyjęto do weryfikacji klucza':'Adresy przyjęte przez IndexNow')
: ($error!=='' ? $error : 'HTTP '.$http.' '.substr(trim(strip_tags($body)),0,100));
$status = $ok?'sent':'error';
$update = db()->prepare("UPDATE polana_indexnow_queue SET status=?,attempts=attempts+1,
last_http=?,last_error=?,last_attempt=NOW(),
sent_at=IF(?='sent',NOW(),sent_at),
next_attempt=IF(?='sent',NULL,DATE_ADD(NOW(),INTERVAL 60 MINUTE)) WHERE id=?");
foreach ($rows as $row) $update->execute([$status,$http?:null,$ok?null:substr($reason,0,240),$status,$status,(int)$row['id']]);
try {
db()->prepare('INSERT INTO polana_indexnow_log (url_count,http_code,result,created_at) VALUES (?,?,?,NOW())')
->execute([count($rows),$http?:null,substr($reason,0,240)]);
} catch (Throwable $ignored) {}
return ['count'=>count($rows),'http'=>$http,'message'=>$reason];
}
function pin77_blog_public(array $post): bool {
if (($post['status'] ?? '') !== 'published') return false;
$when = (string)($post['publish_at'] ?? '');
return $when === '' || strtotime($when) <= time();
}
function pin77_blog_urls(array $post): array {
$slug = trim((string)($post['slug'] ?? ''));
if (!preg_match('/^[a-z0-9-]+$/D', $slug)) return [];
$paths=['/blog/'.$slug];
foreach (['en','de','es'] as $lang) {
if (!empty($post['title_'.$lang]) && !empty($post['content_'.$lang])) $paths[]='/'.$lang.'/blog/'.$slug;
}
return array_map(fn($p)=>pin77_base().$p, $paths);
}
function pin77_after_blog_save(?array $old, int $id): void {
try {
if (!pin77_enabled()) return;
$stmt=db()->prepare('SELECT * FROM blog_posts WHERE id=? LIMIT 1');
$stmt->execute([$id]); $new=$stmt->fetch(PDO::FETCH_ASSOC);
if (!$new) return;
if ($old && pin77_blog_public($old)) foreach (pin77_blog_urls($old) as $url) {
if (!pin77_blog_public($new) || !in_array($url,pin77_blog_urls($new),true)) pin77_enqueue($url);
}
if (pin77_blog_public($new)) foreach (pin77_blog_urls($new) as $url) pin77_enqueue($url);
pin77_enqueue(pin77_base().'/blog');
pin77_send_queue(12);
} catch (Throwable $e) { error_log('POLANA IndexNow blog save: '.$e->getMessage()); }
}
function pin77_after_blog_delete(array $old): void {
try {
if (!pin77_enabled() || !pin77_blog_public($old)) return;
foreach (pin77_blog_urls($old) as $url) pin77_enqueue($url);
pin77_enqueue(pin77_base().'/blog'); pin77_send_queue(12);
} catch (Throwable $e) { error_log('POLANA IndexNow blog delete: '.$e->getMessage()); }
}
function pin77_after_menu_change(): void {
try {
if (!pin77_enabled()) return;
/* POLANA V18: /menu is intentionally noindex. Only notify indexable /menu-seo pages from sitemap. */
require_once __DIR__.'/polana_v166_seo.php';
$map=p164_sitemap_data();
foreach (($map['urls']??[]) as $entry) {
$url=(string)($entry['loc']??'');
$parts=parse_url($url);
$path=(string)($parts['path']??'');
if (preg_match('~^/(?:en/|de/|es/)?menu-seo$~',$path)) pin77_enqueue($url);
}
pin77_send_queue(12);
} catch (Throwable $e) { error_log('POLANA IndexNow menu: '.$e->getMessage()); }
}