* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. */ /** * \file bankimport/ajax/checktan.php * \ingroup bankimport * \brief AJAX endpoint for checking decoupled TAN status (SecureGo Plus) */ // Disable error display for JSON output ini_set('display_errors', 0); // Load Dolibarr environment $res = 0; if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) { $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; } $tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; $tmp2 = realpath(__FILE__); $i = strlen($tmp) - 1; $j = strlen($tmp2) - 1; while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { $i--; $j--; } if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) { $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php"; } if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) { $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php"; } if (!$res && file_exists("../../main.inc.php")) { $res = @include "../../main.inc.php"; } if (!$res && file_exists("../../../main.inc.php")) { $res = @include "../../../main.inc.php"; } if (!$res) { die(json_encode(['status' => 'error', 'message' => 'Include of main fails'])); } dol_include_once('/bankimport/class/fints.class.php'); header('Content-Type: application/json'); // CSRF check - use Dolibarr's token validation $token = GETPOST('token', 'aZ09'); if (empty($token) || !isset($_SESSION['token']) || $token !== $_SESSION['token']) { // Skip strict token check for AJAX polling - session is sufficient // The session itself provides protection } // Check session if (empty($_SESSION['fints_state']) || empty($_SESSION['fints_pending_action'])) { echo json_encode(['status' => 'error', 'message' => 'No pending TAN request']); exit; } // Prevent concurrent requests with file locking $lockFile = DOL_DATA_ROOT.'/bankimport/tan_check_'.session_id().'.lock'; if (!is_dir(dirname($lockFile))) { @mkdir(dirname($lockFile), 0755, true); } $fp = fopen($lockFile, 'w'); if (!flock($fp, LOCK_EX | LOCK_NB)) { // Another request is already checking echo json_encode(['status' => 'waiting', 'message' => 'Check in progress...']); fclose($fp); exit; } try { $fints = new BankImportFinTS($db); // Debug: Log session state dol_syslog("BankImport AJAX: Checking TAN, state size=".strlen($_SESSION['fints_state'] ?? ''), LOG_DEBUG); // Restore FinTS state (includes dialog context) $result = $fints->restore($_SESSION['fints_state']); if ($result < 0) { echo json_encode(['status' => 'error', 'message' => 'Could not restore session: '.$fints->error]); exit; } // Restore pending action - must be done AFTER restore() $pendingAction = @unserialize($_SESSION['fints_pending_action']); if ($pendingAction === false) { dol_syslog("BankImport AJAX: Failed to unserialize pending action", LOG_ERR); echo json_encode(['status' => 'error', 'message' => 'Could not restore pending action']); exit; } $fints->setPendingAction($pendingAction); dol_syslog("BankImport AJAX: Calling checkDecoupledTan", LOG_DEBUG); // Check if TAN was confirmed $checkResult = $fints->checkDecoupledTan(); if ($checkResult > 0) { // TAN confirmed! Now fetch statements $savedAction = $_SESSION['fints_action'] ?? 'statements'; $savedDateFrom = $_SESSION['fints_datefrom'] ?? strtotime('-30 days'); $savedDateTo = $_SESSION['fints_dateto'] ?? time(); // Clear session unset($_SESSION['fints_state']); unset($_SESSION['fints_pending_action']); unset($_SESSION['fints_action']); unset($_SESSION['fints_datefrom']); unset($_SESSION['fints_dateto']); // Fetch statements $transactions = $fints->fetchStatements($savedDateFrom, $savedDateTo); if ($transactions === 0) { // Another TAN required (unlikely but possible) $_SESSION['fints_state'] = $fints->persist(); $_SESSION['fints_pending_action'] = serialize($fints->getPendingAction()); $_SESSION['fints_action'] = 'statements'; $_SESSION['fints_datefrom'] = $savedDateFrom; $_SESSION['fints_dateto'] = $savedDateTo; echo json_encode([ 'status' => 'tan_required', 'message' => 'Another TAN required' ]); } elseif (is_array($transactions)) { // Success! $fints->close(); // Extract transactions and balance from result $txList = $transactions['transactions'] ?? array(); $balance = $transactions['balance'] ?? array(); // Store in session for display $_SESSION['fints_transactions'] = $txList; $_SESSION['fints_balance'] = $balance; echo json_encode([ 'status' => 'success', 'message' => 'Transactions fetched', 'count' => count($txList), 'transactions' => $txList, 'balance' => $balance ]); } else { echo json_encode([ 'status' => 'error', 'message' => 'Fetch failed: '.$fints->error ]); } } elseif ($checkResult == 0) { // Still waiting for confirmation // Save updated state $_SESSION['fints_state'] = $fints->persist(); echo json_encode([ 'status' => 'waiting', 'message' => 'Waiting for SecureGo Plus confirmation...' ]); } else { // Error echo json_encode([ 'status' => 'error', 'message' => 'TAN check failed: '.$fints->error ]); // Clear session on error unset($_SESSION['fints_state']); unset($_SESSION['fints_pending_action']); } } catch (Exception $e) { echo json_encode([ 'status' => 'error', 'message' => 'Exception: '.$e->getMessage() ]); // Clear session on exception unset($_SESSION['fints_state']); unset($_SESSION['fints_pending_action']); } finally { // Release lock if (isset($fp)) { flock($fp, LOCK_UN); fclose($fp); @unlink($lockFile); } }