$oldMask = umask(0077); try { $stateHandle = @fopen($stateFile, 'c+'); } finally { umask($oldMask); } if (!$stateHandle || !flock($stateHandle, LOCK_EX)) { throw new RuntimeException('state file unavailable'); } $raw = stream_get_contents($stateHandle); $state = trim($raw) === '' ? [] : json_decode($raw, true, 512, JSON_THROW_ON_ERROR); if (!is_array($state)) throw new RuntimeException('invalid state'); // Remove stale request history. A blocked user does not extend the ban. foreach ($state as $savedIp => $history) { if (!filter_var($savedIp, FILTER_VALIDATE_IP) || !is_array($history)) { unset($state[$savedIp]); continue; } $state[$savedIp] = array_values(array_filter($history, static function ($t) use ($now) { return (is_int($t) || is_float($t)) && $t > $now - 300 && $t <= $now; })); if ($state[$savedIp] === []) unset($state[$savedIp]); } $blockHandle = @fopen($blockFile, 'r+'); if (!$blockHandle || !flock($blockHandle, LOCK_EX)) { throw new RuntimeException('blocked_ips.txt unavailable'); } $oldBlocks = stream_get_contents($blockHandle); $blocks = koreaMainRateParseBlocks($oldBlocks, $epoch); if (isset($blocks[$ip])) { $result['blocked'] = true; $result['retry_after'] = max(1, $blocks[$ip]['until'] - $epoch); $result['reason'] = $blocks[$ip]['reason']; unset($state[$ip]); } elseif ($isMain) { $check = koreaMainRateEvaluate($state[$ip] ?? [], $now); if ($check['seconds'] > 0) { $blocks[$ip] = ['created' => $date($epoch), 'until' => $epoch + $check['seconds'], 'reason' => $check['reason']]; unset($state[$ip]); $result['blocked'] = true; $result['retry_after'] = $check['seconds']; $result['reason'] = $check['reason']; } else { $state[$ip] = $check['times']; } } $newBlocks = ''; foreach ($blocks as $savedIp => $block) { $newBlocks .= $block['created'].'|'.$savedIp.'|'.$block['until'].'|'.$block['reason']."\n"; } if ($newBlocks !== $oldBlocks) koreaMainRateWrite($blockHandle, $newBlocks); koreaMainRateWrite($stateHandle, json_encode($state, JSON_THROW_ON_ERROR)); // Preserve the existing 3-field format used by api/count_api.php. $visitorText = ''; foreach ($state as $savedIp => $history) { $recent = array_values(array_filter($history, static fn($t) => $t > $now - 60)); if ($recent !== []) { $visitorText .= $date(max($recent)).'|'.$savedIp.'|'.count($recent)."\n"; } } $visitorHandle = @fopen($visitorFile, 'r+'); if ($visitorHandle) { try { if (!flock($visitorHandle, LOCK_EX)) throw new RuntimeException('visitor lock failed'); koreaMainRateWrite($visitorHandle, $visitorText); } finally { flock($visitorHandle, LOCK_UN); fclose($visitorHandle); } } else { error_log('[MAIN-RATE] visitors.txt unavailable'); } } catch (Throwable $error) { // Storage failure must not lock every visitor out of the live site. error_log('[MAIN-RATE] '.$error->getMessage()); $result['available'] = false; } finally { foreach ([$blockHandle, $stateHandle] as $handle) { if (is_resource($handle)) { flock($handle, LOCK_UN); fclose($handle); } } } if ($isMain && $result['blocked']) { header('Cache-Control: no-store, private, max-age=0'); header('Retry-After: '.$result['retry_after']); header('Location: /wait.php', true, 302); exit; } return $result; } if (!defined('KOREA_MAIN_RATE_LIBRARY_ONLY')) { $mainRateStatus = koreaMainRateRun(); } 충남 구인정보 게시판 2페이지 - 실시간 채용공고·일자리 | 114114korea 구인구직 플랫폼