xref: /plugin/captcha/IpCounter.php (revision 563fb5668f85b2cd8c67a6d8af2d4597cbcfdaa7)
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