1<?php 2 3namespace dokuwiki\plugin\captcha; 4 5/** 6 * A simple mechanism to count login failures for IP addresses 7 */ 8class IpCounter 9{ 10 protected $ip; 11 protected $store; 12 13 /** 14 * Initialize the counter 15 */ 16 public function __construct() 17 { 18 $this->ip = clientIP(true); 19 $this->store = getCacheName($this->ip, '.captchaip'); 20 } 21 22 /** 23 * Increases the counter by adding a byte 24 * 25 * @return void 26 */ 27 public function increment() 28 { 29 io_saveFile($this->store, '1', true); 30 } 31 32 /** 33 * Return the current counter 34 * 35 * @return int 36 */ 37 public function get() 38 { 39 return (int)@filesize($this->store); 40 } 41 42 /** 43 * Reset the counter to zero 44 * 45 * @return void 46 */ 47 public function reset() 48 { 49 @unlink($this->store); 50 } 51 52 /** 53 * Get timestamp of last failed attempt 54 * 55 * @return int Unix timestamp, 0 if no attempts 56 */ 57 public function getLastAttempt() 58 { 59 return (int)@filemtime($this->store); 60 } 61 62 /** 63 * Calculate required timeout in seconds based on failure count 64 * 65 * First failed attempt is okay, second requires $base seconds wait. 66 * 67 * @param int $base Base delay in seconds 68 * @param int $max Maximum delay in seconds 69 * @return int Timeout in seconds (0 if no failures) 70 */ 71 public function calculateTimeout($base = 5, $max = 3600) 72 { 73 $count = $this->get(); 74 if ($count < 1) return 0; 75 $timeout = $base * pow(2, $count - 1); // -1 because first failure is free 76 return (int)min($timeout, $max); 77 } 78 79 /** 80 * Get remaining wait time in seconds 81 * 82 * @param int $base Base delay in seconds 83 * @param int $max Maximum delay in seconds 84 * @return int Seconds remaining (0 if no wait needed) 85 */ 86 public function getRemainingTime($base = 5, $max = 3600) 87 { 88 $timeout = $this->calculateTimeout($base, $max); 89 if ($timeout === 0) return 0; 90 $elapsed = time() - $this->getLastAttempt(); 91 return max(0, $timeout - $elapsed); 92 } 93} 94