aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorarf20 <aruizfernandez05@gmail.com>2026-07-01 20:15:42 +0200
committerarf20 <aruizfernandez05@gmail.com>2026-07-01 20:15:42 +0200
commitb7ed54f91c373aef452b3540c8cd352d11305c30 (patch)
treea4cb7c87a02e29ee59696ce0f8c245aaa4cee525
parentfe649aed998af16df2af646bd0acfed0175f3aa9 (diff)
downloadarfnet2-lists-b7ed54f91c373aef452b3540c8cd352d11305c30.tar.gz
arfnet2-lists-b7ed54f91c373aef452b3540c8cd352d11305c30.zip
Add captcha
-rw-r--r--CaptchasDotNet.php309
-rw-r--r--index.php7
-rw-r--r--mlmmj.php28
-rw-r--r--subscribe.php20
4 files changed, 357 insertions, 7 deletions
diff --git a/CaptchasDotNet.php b/CaptchasDotNet.php
new file mode 100644
index 0000000..7b11409
--- /dev/null
+++ b/CaptchasDotNet.php
@@ -0,0 +1,309 @@
+<?php
+//
+// PHP module for easy utilization of the free captchas.net CAPTCHA service
+//
+// For documentation look at http://captchas.net/
+// https://github.com/captchasDotNet/captchas.net
+//
+// Written by
+// Sebastian Wilhelmi <seppi@seppi.de> and
+// Felix Holderied <felix@holderied.de>
+// This file is in the public domain.
+//
+
+class CaptchasDotNet
+{
+ function __construct($client, $secret,
+ $random_repository = '/tmp/captchasnet-random-strings',
+ $cleanup_time = 3600,
+ $alphabet = 'abcdefghijklmnopqrstuvwxyz',
+ $letters = 6,
+ $width = 240,
+ $height = 80,
+ $color = '000000')
+ {
+ $this->__client = $client;
+ $this->__secret = $secret;
+ $this->__random_repository = $random_repository;
+ $this->__cleanup_time = $cleanup_time;
+ $this->__time_stamp_file = $random_repository . '/__time_stamp__';
+ $this->__alphabet = $alphabet;
+ $this->__letters = $letters;
+ $this->__width = $width;
+ $this->__height = $height;
+ $this->__color = $color;
+ $this->__random_file = "";
+ }
+
+ function __random_string ()
+ {
+ // The random string shall consist of small letters, big letters
+ // and digits.
+ $letters = "abcdefghijklmnopqrstuvwxyz";
+ $letters .= strtoupper ($letters) . "0123456789";
+
+ // The random starts out empty, then 40 random possible characters
+ // are appended.
+ $random_string = '';
+ for ($i = 0; $i < 40; $i++)
+ {
+ $random_string .= $letters[rand (0, strlen ($letters) - 1)];
+ }
+
+ // Return the random string.
+ return $random_string;
+ }
+
+ // Create a new random string and register it.
+ function random ()
+ {
+ // If the repository directory is does not yet exist, create it.
+ if (!is_dir ($this->__random_repository))
+ {
+ mkdir ($this->__random_repository);
+ }
+
+ // If the time stamp file does not yet exist, create it.
+ if (!is_file ($this->__time_stamp_file))
+ {
+ touch ($this->__time_stamp_file);
+ }
+
+ // Get the current time.
+ $now = time ();
+
+ // Determine the time, before which to remove random strings.
+ $cleanup_time = $now - $this->__cleanup_time;
+
+ // If the last cleanup is older than specified, cleanup the
+ // directory.
+ if (filemtime ($this->__time_stamp_file) < $cleanup_time)
+ {
+ $handle = opendir ($this->__random_repository);
+ while (true)
+ {
+ $filename = readdir ($handle);
+ if (!$filename)
+ {
+ break;
+ }
+ if ($filename != '.' && $filename != '..')
+ {
+ $filename = $this->__random_repository . '/' . $filename;
+ if (filemtime ($filename) < $cleanup_time)
+ {
+ unlink ($filename);
+ }
+ }
+ }
+ closedir ($handle);
+
+ touch ($this->__time_stamp_file);
+ }
+
+ // loop until a valid random string has been found and registered,
+ // but at most 20 times. If no valid random has been found during
+ // that time, there is something really wrong. Also show the error
+ // in the last run.
+ for ($remaining = 20; $remaining > 0; $remaining--)
+ {
+ // generate a new random string.
+ $random = $this->__random_string ();
+
+ // open a file with the corresponding name in the repository
+ // directory in such a way, that the creation fails, when the
+ // file already exists. That should be near to impossible with
+ // good seeding of the random number generator, but it's better
+ // to play safe. If this is the last run, show the possible
+ // error message.
+ $filename = $this->__random_repository . '/' . $random;
+
+ if ($remaining == 1)
+ {
+ $file = fopen ($filename, 'x');
+ }
+ else
+ {
+ $file = @fopen ($filename, 'x');
+ }
+
+ if ($file)
+ {
+ fclose ($file);
+ break;
+ }
+
+ // if the file already existed, rerun the loop to try the next
+ // string.
+ }
+
+ // return the successfully registered random string.
+ $this->__random = $random;
+ return $random;
+ }
+
+ //
+ // Generates image-URL Parameters are only atached if different from default
+ //
+ function image_url ($random = False, $base = 'http://image.captchas.net/')
+ {
+ if (!$random)
+ {
+ $random = $this->__random;
+ }
+ $image_url = $base;
+ $image_url .= '?client=' . $this->__client;
+ $image_url .= '&amp;random=' . $random;
+ if ($this->__alphabet!='abcdefghijklmnopqrstuvwxyz') {$image_url .= '&amp;alphabet=' . $this->__alphabet;};
+ if ($this->__letters!=6) {$image_url .= '&amp;letters=' . $this->__letters;};
+ if ($this->__width!=240) {$image_url .= '&amp;width=' . $this->__width;};
+ if ($this->__height!=80) {$image_url .= '&amp;height=' . $this->__height;};
+ if ($this->__color!='000000') {$image_url .= '&amp;color=' . $this->__color;};
+ return $image_url;
+ }
+
+ //
+ // Same as image_url but without width and height
+ //
+ function audio_url ($random = False, $base = 'http://audio.captchas.net/')
+ {
+ if (!$random)
+ {
+ $random = $this->__random;
+ }
+ $audio_url = $base;
+ $audio_url .= '?client=' . $this->__client;
+ $audio_url .= '&amp;random=' . $random;
+ if ($this->__alphabet!='abcdefghijklmnopqrstuvwxyz') {$audio_url .= '&amp;alphabet=' . $this->__alphabet;};
+ if ($this->__letters!=6) {$audio_url .= '&amp;letters=' . $this->__letters;};
+ return $audio_url;
+ }
+
+ //
+ // Generates complete html-sample with javascript to reload image from
+ // backup server
+ //
+ function image ($random = False, $id = 'captchas.net')
+ {
+ $image = <<<EOT
+ <a href="http://captchas.net"><img
+ style="border: none; vertical-align: bottom"
+ id="@ID@" src="@URL@" width="@WIDTH@" height="@HEIGHT@"
+ alt="The Captcha image" /></a>
+ <script type="text/javascript">
+ <!--
+ function captchas_image_reload (imgId)
+ {
+ var image_url = document.getElementById(imgId).src;
+ image_url+= "&";
+ document.getElementById(imgId).src = image_url;
+ }
+
+ function captchas_image_error (image)
+ {
+ if (!image.timeout) return true;
+ image.src = image.src.replace (/^http:\/\/image\.captchas\.net/,
+ 'http://image.backup.captchas.net');
+ return captchas_image_loaded (image);
+ }
+
+ function captchas_image_loaded (image)
+ {
+ if (!image.timeout) return true;
+ window.clearTimeout (image.timeout);
+ image.timeout = false;
+ return true;
+ }
+
+ var image = document.getElementById ('@ID@');
+ image.onerror = function() {return captchas_image_error (image);};
+ image.onload = function() {return captchas_image_loaded (image);};
+ image.timeout
+ = window.setTimeout(
+ "captchas_image_error (document.getElementById ('@ID@'))",
+ 10000);
+ image.src = image.src;
+ //-->
+ </script>
+EOT;
+ $image = str_replace ('@HEIGHT@', $this->__height, $image);
+ $image = str_replace ('@WIDTH@', $this->__width, $image);
+ $image = str_replace ('@ID@', $id, $image);
+ $image = str_replace ('@URL@', $this->image_url (), $image);
+ return $image;
+ }
+
+ function validate ($random)
+ {
+ $this->__random = $random;
+
+ $file_name = $this->__random_repository . '/' . $random;
+
+ // Find out, whether the file exists
+ $result = is_file ($file_name);
+
+ // if the file exists, remember it.
+ if ($result)
+ {
+ $this->__random_file = $file_name;
+ }
+
+ // the random string was valid, if and only if the corresponding
+ // file existed.
+ return $result;
+ }
+
+ function verify ($input, $random = False)
+ {
+ if (!$random)
+ {
+ $random = $this->__random;
+ }
+ $password_letters = $this->__alphabet;
+ $password_length = $this->__letters;
+
+ // If the user input has the wrong lenght, it can't be correct.
+ if (strlen ($input) != $password_length)
+ {
+ return False;
+ }
+
+ // Calculate the MD5 digest of the concatenation of secret key and
+ // random string. The digest is a hex string.
+ $encryption_base = $this->__secret . $random;
+ // This extension is needed for secure use of optional parameters
+ // In case of standard use we do not append the values, to be
+ // compatible to existing implementations
+ if(($password_letters != 'abcdefghijklmnopqrstuvwxyz') || ($password_length != '6'))
+ {
+ $encryption_base = $encryption_base . ':' . $password_letters . ':' . $password_length;
+ }
+ $digest = md5 ($encryption_base);
+
+ // Check the password according to the rules from the first
+ // positions of the digest.
+ for ($pos = 0; $pos < $password_length; $pos++)
+ {
+ $letter_num
+ = hexdec (substr ($digest, 2 * $pos, 2)) % strlen ($password_letters);
+
+ // If the letter at the current position is wrong, the user
+ // input isn't correct.
+ if ($input[$pos] != $password_letters[$letter_num])
+ {
+ return False;
+ }
+ }
+
+ // if the file exists, remove it.
+ if ($this->__random_file)
+ {
+ unlink ($this->__random_file);
+ unset ($this->__random_file);
+ }
+
+ // The user input was correct.
+ return True;
+ }
+
+}
diff --git a/index.php b/index.php
index 8626ffb..b3a5718 100644
--- a/index.php
+++ b/index.php
@@ -29,14 +29,9 @@ $lists = array_diff($lists, array(".", ".."));
}
?>
</ul>
- <hr>
- <h2>Subscription</h2>
- <ul>
- <li>To subscribe to a list, send a message to list+subscribe@arf20.com or go to https://lists.arf20.com/subscribe.php?list=list</li>
- <li>To unsubscribe, send a message to list+unsubscribe@arf20.com or go to https://lists.arf20.com/subscribe.php?list=list</li>
- </ul>
</div>
</div>
</main>
</body>
</html>
+
diff --git a/mlmmj.php b/mlmmj.php
index c4ff7d3..70d6488 100644
--- a/mlmmj.php
+++ b/mlmmj.php
@@ -24,6 +24,8 @@
// error_reporting(E_ALL);
+require 'CaptchasDotNet.php';
+
class mlmmj
{
var $email;
@@ -53,7 +55,9 @@ class mlmmj
!isset($_POST["mailinglist"]) &&
!isset($_POST["job"]) &&
!isset($_POST["redirect_success"]) &&
- !isset($_POST["redirect_failure"]))
+ !isset($_POST["redirect_failure"]) &&
+ !isset($_POST["random"])) &&
+ !isset($_POST["captcha"]))
{
$this->errors = TRUE;
if(isset($_POST["redirect_failure"])) {
@@ -64,6 +68,28 @@ class mlmmj
die("An error occurred. Please check contrib/web/php-user/README for details.");
}
else {
+ $captchas = new CaptchasDotNet ('arf20', '7QOD8AEp5n9ib5bp',
+ '/tmp/captchasnet-random-strings','3600',
+ 'abcdefghkmnopqrstuvwxyz','6',
+ '240','80','000088');
+
+ // Check the random string to be valid and return an error message
+ // otherwise.
+ if (!$captchas->validate ($random_string))
+ {
+ $this->error(
+ "The session key (random) does not exist, please go back and reload form.<br/>"
+ ."In case you are the administrator of this page, "
+ ."please check if random keys are stored correct.<br/>"
+ ."See http://captchas.net/sample/php/ 'Problems with save mode'");
+ }
+ // Check, that the right CAPTCHA password has been entered and
+ // return an error message otherwise.
+ elseif (!$captchas->verify ($password))
+ {
+ $this->error("You entered the wrong password. Aren't you human? Please use back button and reload.";
+ }
+
if ($this->is_email($_POST["email"]))
$this->email = $_POST["email"];
else
diff --git a/subscribe.php b/subscribe.php
index f60c15b..f2e9d56 100644
--- a/subscribe.php
+++ b/subscribe.php
@@ -1,5 +1,12 @@
<?php
+require 'CaptchasDotNet.php';
+
+$captchas = new CaptchasDotNet ('arf20', '7QOD8AEp5n9ib5bp',
+ '/tmp/captchasnet-random-strings','3600',
+ 'abcdefghkmnopqrstuvwxyz','6',
+ '240','80','000088');
+
if (!isset($_GET["list"]) || empty($_GET["list"])) {
die("List required");
}
@@ -34,6 +41,8 @@ $domain = "arf20.com"
<input name="job" type="hidden" value="subscribe">
<input name="redirect_failure" type="hidden" value="/error.html">
<input name="redirect_success" type="hidden" value="/">
+ <input type="hidden" name="random" value="<?= $captchas->random () ?>" />
+ <input name="captcha" size="6" />
<br><input type="submit" value="Subscribe">
</form>
@@ -45,9 +54,20 @@ $domain = "arf20.com"
<input name="job" type="hidden" value="unsubscribe">
<input name="redirect_failure" type="hidden" value="/error.html">
<input name="redirect_success" type="hidden" value="/">
+ <input type="hidden" name="random" value="<?= $captchas->random () ?>" />
+ <input name="captcha" size="6" />
<br><input type="submit" value="Subscribe">
</form>
+ <?= $captchas->image () ?> <a href="javascript:captchas_image_reload('captchas.net')">Reload Image</a>
+
+ <hr>
+ <h2>E-mail based subscription</h2>
+ <ul>
+ <li>To subscribe to <?php echo $list; ?>, send a message to <?php echo $list; ?>+subscribe@arf20.com</li>
+ <li>To unsubscribe, send a message to <?php echo $list; ?>+unsubscribe@arf20.com</li>
+ </ul>
</div>
</main>
</body>
</html>
+