403Webshell
Server IP : 80.211.21.168  /  Your IP : 216.73.217.143
Web Server : Apache
System : Linux alex-webdesign.it 3.10.0-1160.119.1.el7.tuxcare.els22.x86_64 #1 SMP Mon Aug 18 06:07:12 UTC 2025 x86_64
User : admin_japan ( 10003)
PHP Version : 8.2.32
Disable Function : opcache_get_status
MySQL : OFF  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /var/www/vhosts/cerchijapan.it/ai4dealers.com/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/vhosts/cerchijapan.it/ai4dealers.com/chat_fetch.php
<?php
session_start();
ini_set('display_errors','1');
error_reporting(E_ALL);

/** =========================
 *  CONFIG BASE + API KEY
 *  ========================= */
$API_URL = "https://api.openai.com/v1/chat/completions";
$MODEL   = "gpt-5-mini-2025-08-07";
$TEMP    = 1;

// ⚠️ API key hardcoded SOLO per test (RUOTALA DOPO)
$HARDCODED_API_KEY = "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";

// Limiti fetch URL
const FETCH_TIMEOUT_SEC = 10;
const FETCH_MAX_REDIRECTS = 3;
const FETCH_MAX_BYTES = 1572864; // ≈1.5MB
const TOOL_TEXT_LIMIT = 120000;  // ≈120k chars verso il modello

/** Estensioni minime */
foreach (['curl','json','mbstring','openssl'] as $ext) {
  if (!extension_loaded($ext)) {
    $_SESSION['flash_error'] = "Estensione PHP mancante: {$ext}. Attivala nel php.ini.";
  }
}

/** Gestione API key: form -> sessione -> env var -> hardcoded */
if (isset($_POST['api_key'])) {
  $_SESSION['api_key'] = trim((string)$_POST['api_key']);
}
$API_KEY = $_SESSION['api_key'] ?? getenv('OPENAI_API_KEY') ?? $HARDCODED_API_KEY;

/** Reset chat */
if (isset($_POST['reset'])) {
  unset($_SESSION['messages'], $_SESSION['last_api_raw'], $_SESSION['flash_error']);
  header("Location: ".$_SERVER['PHP_SELF']);
  exit;
}

/** Inizializza conversazione (system) */
if (!isset($_SESSION['messages'])) {
  $_SESSION['messages'] = [[
    "role" => "system",
    "content" =>
      "Se l'utente invia un URL, usa il tool fetch_url per leggerlo prima di rispondere. ".
      "Se il fetch fallisce, spiega brevemente che rispondi senza leggere il link."
  ]];
}

/** Tool dichiarato (ORA implementato lato server) */
$tools = [[
  "type" => "function",
  "function" => [
    "name" => "fetch_url",
    "description" => "Scarica una pagina web e restituisce testo pulito per il modello.",
    "parameters" => [
      "type" => "object",
      "properties" => [
        "url" => ["type"=>"string", "description"=>"URL http/https da leggere"]
      ],
      "required" => ["url"]
    ]
  ]
]];

/** =========================
 *  UTIL: maschera e debug
 *  ========================= */
function mask($s) {
  if (!$s) return '—';
  $n = strlen($s);
  if ($n <= 8) return str_repeat('•', $n);
  return substr($s, 0, 4) . str_repeat('•', $n - 8) . substr($s, -4);
}

function save_last_api_raw($code, $body) {
  $_SESSION['last_api_raw'] = "HTTP {$code}\n".(string)$body;
}

/** =========================
 *  OPENAI CALL
 *  ========================= */
function call_openai(array $payload, string $API_URL, string $API_KEY): array {
  if (!$API_KEY) throw new RuntimeException("API key mancante.");
  $ch = curl_init($API_URL);
  curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
      "Authorization: Bearer {$API_KEY}",
      "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS     => json_encode($payload, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 60,
  ]);
  $resp = curl_exec($ch);
  if ($resp === false) throw new RuntimeException("cURL error: ".curl_error($ch));
  $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  save_last_api_raw($code, $resp);
  if ($code < 200 || $code >= 300) {
    $snippet = mb_substr($resp ?? '', 0, 1500);
    throw new RuntimeException("HTTP {$code} dalla OpenAI API. Body: {$snippet}");
  }
  $data = json_decode($resp, true);
  if (!is_array($data)) throw new RuntimeException("Risposta non valida dall'API (JSON decode fallito).");
  return $data;
}

/** =========================
 *  FETCH URL (con protezioni)
 *  ========================= */
function is_public_ip(string $ip): bool {
  return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
}

function resolve_host_ips(string $host): array {
  $ips = [];
  // IPv4
  $a = @gethostbynamel($host);
  if (is_array($a)) $ips = array_merge($ips, $a);
  // IPv6
  $aaaa = @dns_get_record($host, DNS_AAAA);
  if (is_array($aaaa)) {
    foreach ($aaaa as $r) if (!empty($r['ipv6'])) $ips[] = $r['ipv6'];
  }
  return array_values(array_unique($ips));
}

function normalize_url(string $url): string {
  $url = trim($url);
  // Assicuriamoci che ci sia schema
  if (!preg_match('~^https?://~i', $url)) {
    throw new InvalidArgumentException("URL non valido (manca schema http/https).");
  }
  $parts = parse_url($url);
  if (!$parts || empty($parts['host'])) throw new InvalidArgumentException("URL non valido.");
  $scheme = strtolower($parts['scheme'] ?? '');
  if (!in_array($scheme, ['http','https'], true)) throw new InvalidArgumentException("Schema non consentito.");
  return $url;
}

function assert_safe_host(string $host): void {
  // Se host è già un IP, controlla subito
  if (filter_var($host, FILTER_VALIDATE_IP)) {
    if (!is_public_ip($host)) throw new RuntimeException("IP non pubblico bloccato.");
    return;
  }
  // Altrimenti risolvi DNS e verifica tutti gli IP
  $ips = resolve_host_ips($host);
  if (!$ips) throw new RuntimeException("Impossibile risolvere DNS per {$host}.");
  foreach ($ips as $ip) {
    if (!is_public_ip($ip)) throw new RuntimeException("Risolto IP non pubblico/riservato. Bloccato.");
  }
}

function parse_headers_from_string(string $rawHeaders): array {
  $headers = [];
  $lines = preg_split("/\r\n|\n|\r/", $rawHeaders);
  foreach ($lines as $line) {
    if (strpos($line, ':') !== false) {
      [$k,$v] = explode(':', $line, 2);
      $headers[strtolower(trim($k))] = trim($v);
    }
  }
  return $headers;
}

function abs_url(string $base, string $rel): string {
  // semplice risoluzione Location relativa
  if (preg_match('~^https?://~i', $rel)) return $rel;
  $bp = parse_url($base);
  if (!$bp) return $rel;
  $scheme = $bp['scheme'] ?? 'https';
  $host   = $bp['host'] ?? '';
  $port   = isset($bp['port']) ? ":{$bp['port']}" : '';
  if (strpos($rel, '/') === 0) {
    return "{$scheme}://{$host}{$port}{$rel}";
  }
  $path = $bp['path'] ?? '/';
  $path = preg_replace('~/[^/]*$~', '/', $path);
  return "{$scheme}://{$host}{$port}{$path}{$rel}";
}

function fetch_once(string $url): array {
  $ch = curl_init($url);
  $headers = [
    "User-Agent: WhatsApp-GPT/1.0 (+chat_web.php)",
    "Accept: text/html,application/json,text/plain;q=0.9,*/*;q=0.8",
    "Accept-Language: it-IT,it;q=0.9,en;q=0.8",
    "Range: bytes=0-".(FETCH_MAX_BYTES-1),
  ];
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HEADER         => true,
    CURLOPT_FOLLOWLOCATION => false, // gestiamo noi i redirect
    CURLOPT_TIMEOUT        => FETCH_TIMEOUT_SEC,
    CURLOPT_CONNECTTIMEOUT => 6,
    CURLOPT_HTTPHEADER     => $headers,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
  ]);
  $resp = curl_exec($ch);
  if ($resp === false) {
    throw new RuntimeException("Fetch cURL error: ".curl_error($ch));
  }
  $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  $rawHeaders = substr($resp, 0, $headerSize);
  $body       = substr($resp, $headerSize);
  $hdrs = parse_headers_from_string($rawHeaders);
  $ct = $hdrs['content-type'] ?? '';
  return [
    'status'       => $status,
    'headers'      => $hdrs,
    'body'         => $body,
    'content_type' => $ct,
    'url'          => $url,
  ];
}

function fetch_with_redirects(string $url, int $maxRedirects = FETCH_MAX_REDIRECTS): array {
  $visited = 0;
  $current = $url;

  while (true) {
    $norm = normalize_url($current);
    $parts = parse_url($norm);
    assert_safe_host($parts['host']);
    $r = fetch_once($norm);

    if (in_array($r['status'], [301,302,303,307,308], true) && isset($r['headers']['location'])) {
      if ($visited >= $maxRedirects) {
        throw new RuntimeException("Troppi redirect.");
      }
      $next = abs_url($norm, $r['headers']['location']);
      // ricontrolla sicurezza su host target al prossimo giro
      $visited++;
      $current = $next;
      continue;
    }

    return $r;
  }
}

function html_to_text_basic(string $html): string {
  // rimuovi script/style/noscript/svg/canvas
  $html = preg_replace('~<(script|style|noscript|svg|canvas)[^>]*>.*?</\1>~is', ' ', $html);
  // togli tag
  $text = strip_tags($html);
  // normalizza spazi
  $text = html_entity_decode($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
  $text = preg_replace('/[ \t]+/', ' ', $text);
  $text = preg_replace('/\R{2,}/', "\n\n", $text);
  return trim($text);
}

function pick_mime(string $ct): string {
  if (!$ct) return 'application/octet-stream';
  if (strpos($ct, ';') !== false) $ct = trim(strtolower(strtok($ct,';')));
  return strtolower($ct);
}

function convert_body_to_text(string $body, string $contentType): string {
  $mime = pick_mime($contentType);
  if (strpos($mime, 'text/html') === 0) {
    return html_to_text_basic($body);
  }
  if (strpos($mime, 'text/') === 0) {
    return trim($body);
  }
  if (strpos($mime, 'application/json') === 0) {
    // prova a prettificare
    $arr = json_decode($body, true);
    if (is_array($arr)) return substr(json_encode($arr, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE), 0, TOOL_TEXT_LIMIT);
    return substr($body, 0, TOOL_TEXT_LIMIT);
  }
  // PDF e altri: non processiamo qui
  return "[fetch_url] Tipo contenuto non gestito: {$mime}. Dimensione ricevuta: ".strlen($body)." bytes.";
}

function fetch_url_text_secure(string $url): string {
  try {
    $res = fetch_with_redirects($url);
    $text = convert_body_to_text($res['body'], $res['content_type']);
    // tronca per il modello
    return mb_substr($text, 0, TOOL_TEXT_LIMIT);
  } catch (Throwable $e) {
    return "[fetch_url][ERRORE] ".$e->getMessage();
  }
}

/** =========================
 *  HANDLER POST CHAT
 *  ========================= */
$hadError = false;
if (isset($_POST['user_message']) && trim((string)$_POST['user_message']) !== '') {
  $user = trim((string)$_POST['user_message']);
  $_SESSION['messages'][] = ["role" => "user", "content" => $user];

  try {
    // 1) Prima chiamata
    $req = [
      "model"       => $MODEL,
      "messages"    => $_SESSION['messages'],
      "tools"       => $tools,
      "tool_choice" => "auto",
      "temperature" => $TEMP
    ];
    $res = call_openai($req, $API_URL, $API_KEY);

    $choice = $res["choices"][0] ?? null;
    $assistantMsg = $choice["message"] ?? ["role" => "assistant", "content" => ""];
    $_SESSION['messages'][] = $assistantMsg;

    // 2) Eseguiamo le eventuali tool-calls fetch_url
    $toolCalls = $assistantMsg["tool_calls"] ?? [];
    if (is_array($toolCalls) && count($toolCalls) > 0) {
      foreach ($toolCalls as $tc) {
        $callId = $tc["id"] ?? "";
        $fname  = $tc["function"]["name"] ?? "";
        $args   = json_decode($tc["function"]["arguments"] ?? "{}", true);
        if ($fname === "fetch_url" && is_array($args) && !empty($args["url"])) {
          $toolContent = fetch_url_text_secure((string)$args["url"]);
        } else {
          $toolContent = "[fetch_url][ERRORE] Argomenti non validi o funzione sconosciuta.";
        }
        $_SESSION['messages'][] = [
          "role"         => "tool",
          "tool_call_id" => $callId,
          "content"      => $toolContent
        ];
      }

      // 2a) Seconda chiamata con output del tool
      $req2 = [
        "model"       => $MODEL,
        "messages"    => $_SESSION['messages'],
        "tools"       => $tools,
        "tool_choice" => "auto",
        "temperature" => $TEMP
      ];
      $res2 = call_openai($req2, $API_URL, $API_KEY);
      $choice2 = $res2["choices"][0] ?? null;
      $assistantMsg2 = $choice2["message"] ?? ["role" => "assistant", "content" => "(nessuna risposta)"];
      $_SESSION['messages'][] = $assistantMsg2;
    }

    // redirect solo se tutto ok
    header("Location: " . $_SERVER['PHP_SELF'] . (isset($_GET['debug']) ? '?debug=1' : ''));
    exit;

  } catch (Throwable $e) {
    $hadError = true;
    $_SESSION['flash_error'] = $e->getMessage();
    // niente redirect: mostriamo errore in pagina
  }
}

/** =========================
 *  VIEW
 *  ========================= */
$flashError = $_SESSION['flash_error'] ?? null;
unset($_SESSION['flash_error']);

$API_IN_USE = $API_KEY ?: '—';
$debugOn = isset($_GET['debug']);
$lastRaw = $debugOn ? ($_SESSION['last_api_raw'] ?? "—") : null;

?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mini Chat GPT — Chat Completions + fetch_url</title>
<style>
  body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 0; background:#f6f7f9; }
  header { background:#0f172a; color:#fff; padding:14px 16px; }
  main { max-width: 900px; margin: 0 auto; padding: 16px; }
  .bar { background:#fff; border:1px solid #e5e7eb; padding:12px; border-radius:10px; margin-bottom:16px; display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
  .bar input[type="password"] { width: 320px; max-width: 80vw; padding:8px; border:1px solid #e5e7eb; border-radius:8px; }
  .chat { background:#fff; border:1px solid #e5e7eb; border-radius: 12px; padding: 12px; min-height: 50vh; }
  .msg { margin: 10px 0; }
  .msg.user { text-align: right; }
  .bubble { display:inline-block; padding:10px 12px; border-radius:12px; max-width: 80%; white-space: pre-wrap; }
  .user .bubble { background:#dcfce7; border:1px solid #bbf7d0; }
  .assistant .bubble { background:#eef2ff; border:1px solid #c7d2fe; }
  .tool .bubble { background:#ffe4e6; border:1px solid #fecdd3; font-size: 0.95em; }
  form.send { display:flex; gap:10px; margin-top:12px; }
  form.send textarea { flex:1; padding:10px; border-radius:10px; border:1px solid #e5e7eb; min-height:60px; }
  form.send button { padding:10px 14px; border-radius:10px; border:0; background:#0ea5e9; color:#fff; cursor:pointer; }
  .small { color:#64748b; font-size:12px; }
  .danger { color:#b91c1c; }
  .error { background:#fee2e2; border:1px solid #fecaca; color:#991b1b; padding:8px 10px; border-radius:10px; margin-bottom:12px; }
  pre.debug { background:#0b1020; color:#d1e7ff; padding:12px; border-radius:10px; overflow:auto; max-height:300px; }
  a.btn { background:#111827; color:#fff; padding:6px 10px; border-radius:8px; text-decoration:none; }
</style>
</head>
<body>
<header>
  <strong>Mini Chat GPT</strong> — Chat Completions + tool <code>fetch_url</code> (attivo)
</header>
<main>
  <?php if ($flashError): ?>
    <div class="error"><?= htmlspecialchars($flashError, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></div>
  <?php endif; ?>

  <form class="bar" method="post" action="">
    <label>API key (opzionale override):&nbsp;
      <input type="password" name="api_key" placeholder="sk-..." value="" autocomplete="off">
    </label>
    <button type="submit">Salva</button>
    <span class="small">In uso: <?= htmlspecialchars(mask($API_IN_USE), ENT_QUOTES, 'UTF-8') ?></span>
    <button name="reset" value="1" style="margin-left:auto; background:#ef4444;color:#fff;padding:8px 12px;border-radius:8px;border:0;cursor:pointer">Reset chat</button>
    <?php if (!$debugOn): ?>
      <a class="btn" href="<?= htmlspecialchars($_SERVER['PHP_SELF'].'?debug=1', ENT_QUOTES) ?>">Debug: ON</a>
    <?php else: ?>
      <a class="btn" href="<?= htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES) ?>">Debug: OFF</a>
    <?php endif; ?>
  </form>

  <div class="chat">
    <?php
      foreach (($_SESSION['messages'] ?? []) as $m) {
        if (!in_array($m['role'], ['user','assistant','tool'], true)) continue;
        $cls = htmlspecialchars($m['role'], ENT_QUOTES, 'UTF-8');
        $content = $m['content'] ?? '';
        echo '<div class="msg '.$cls.'"><div class="bubble">'.nl2br(htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')).'</div></div>';
      }
    ?>
    <form class="send" method="post" action="">
      <textarea name="user_message" placeholder="Scrivi un messaggio... (incolla un link per farlo leggere al modello)"></textarea>
      <button type="submit">Invia</button>
    </form>
    <div class="small" style="margin-top:8px">
      Modello: <code><?= htmlspecialchars($MODEL, ENT_QUOTES, 'UTF-8') ?></code> — Temperature: <?= $TEMP ?> — Endpoint: <code>/v1/chat/completions</code>
    </div>
  </div>

  <?php if ($debugOn): ?>
    <h3>Debug ultima risposta API</h3>
    <pre class="debug"><?= htmlspecialchars($lastRaw ?? "—", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></pre>
    <p class="small">
      Suggerimento: se vedi <em>invalid_model</em> o simili, verifica che il nome del modello sia abilitato per Chat Completions.
    </p>
  <?php endif; ?>

  <p class="small danger" style="margin-top:10px">
    ⚠️ Sicurezza: key hardcoded. Non pubblicare questo file; ruota la chiave a fine test. Il fetch blocca IP privati/loopback e segue max <?= FETCH_MAX_REDIRECTS ?> redirect con ri-validazione ostile.
  </p>
</main>
</body>
</html>

Youez - 2016 - github.com/yon3zu
LinuXploit