diff --git a/ratelimit.php b/ratelimit.php new file mode 100644 index 0000000..1d1575b --- /dev/null +++ b/ratelimit.php @@ -0,0 +1,41 @@ + */ + +/** The rate limit abstract class */ +class ratelimit +{ + /** The maximum number of entries by specified unit time */ + public $maxEntries = 10; + /** The unit time in seconds */ + public $unittime = 60; + + /** The function set a rate-limit + @return bool true if the rate-limit is not overloaded + false if the rate-limit is overloaded */ + public function set ($name) + { + throw new Exception (dgettext("domframework", + "ratelimit : no valid ratelimiter available"), + 500); + } + + /** The function delete a rate-limit + @return bool */ + public function del ($name) + { + throw new Exception (dgettext("domframework", + "ratelimit : no valid ratelimiter available"), + 500); + } + + /** The function clean the storage + @return bool */ + public function clean () + { + throw new Exception (dgettext("domframework", + "ratelimit : no valid ratelimiter available"), + 500); + } +} diff --git a/ratelimitfile.php b/ratelimitfile.php new file mode 100644 index 0000000..6aef8eb --- /dev/null +++ b/ratelimitfile.php @@ -0,0 +1,117 @@ + */ + +require_once ("domframework/lockfile.php"); + +/** The rate limit with file storage */ +class ratelimitfile extends ratelimit +{ + /** The storage directory */ + public $storageDir = "data/ratelimit/"; + /** Debug the ratelimiting process to screen */ + public $debug = true; + + /** The function set a rate-limit + @return bool true if the rate-limit is not overloaded + false if the rate-limit is overloaded */ + public function set ($name) + { + $this->storageOK(); + $nameFile = rawurlencode ($name); + $file = $this->storageDir."/$nameFile"; + // Add the current timestamp + $currentTimeStamp = microtime (true)."\n"; + if ($this->debug) echo "Add entry : $currentTimeStamp"; + file_put_contents ($file, $currentTimeStamp, FILE_APPEND); + $lock = new lockfile (); + $lock->lockRW (); + $contents = file ($file); + // Look if there is deprecated entries in the file + foreach ($contents as $key=>$content) + { + $content = rtrim ($content); + if ($content + $this->unittime < microtime (true)) + { + if ($this->debug) echo "Remove deprecated entry : $content\n"; + unset ($contents[$key]); + } + else + { + if ($this->debug) echo "Active entry : $content\n"; + } + } + file_put_contents ($file, $contents); + $lock->unlockRW (); + unset ($lock); + if (count ($contents) > $this->maxEntries) + { + if ($this->debug) echo "Ratelimiting for '$name'\n"; + return FALSE; + } + return TRUE; + } + + /** The function delete a rate-limit + @return bool */ + public function del ($name) + { + $this->storageOK(); + $nameFile = rawurlencode ($name); + $file = $this->storageDir."/$nameFile"; + if (file_exists ($file)) + { + if ($this->debug) echo "Removing the rate-limiting for '$name'\n"; + unlink ($file); + } + return TRUE; + } + + /** The function clean the storage with expired entries + @return bool */ + public function clean () + { + $this->storageOK(); + $files = glob ($this->storageDir."/*"); + foreach ($files as $file) + { + if (filemtime ($file) + $this->unittime < time ()) + { + if ($this->debug) + echo "Removing the deprecated rate-limiting for '". + rawurldecode (basename ($file))."'\n"; + unlink ($file); + } + } + return TRUE; + } + + /** Check the storageDir state */ + private function storageOK () + { + if (file_exists ($this->storageDir)) + { + if (! is_dir ($this->storageDir)) + throw new Exception (sprintf (dgettext ("domframework", + "storageDir '%s' is not a directory"), + $this->storageDir), 500); + if (! is_readable ($this->storageDir)) + throw new Exception (sprintf (dgettext ("domframework", + "storageDir '%s' is not readable"), + $this->storageDir), 500); + if (! is_writeable ($this->storageDir)) + throw new Exception (sprintf (dgettext ("domframework", + "storageDir '%s' is not writeable"), + $this->storageDir), 500); + } + else + { + if (mkdir ($this->storageDir, 0777, true) === false) + throw new Exception (sprintf (dgettext ("domframework", + "Can't create '%s' storageDir"), + $this->storageDir), 500); + } + return TRUE; + } +}