Author: Jim Winstead (jimwins) Committer: GitHub (web-flow) Pusher: jimwins Date: 2024-09-20T14:22:01-07:00
Commit: https://github.com/php/web-news/commit/fe67cf8d29785228e2a3f11d018b5f008ae8d79b Raw diff: https://github.com/php/web-news/commit/fe67cf8d29785228e2a3f11d018b5f008ae8d79b.diff Add a subscription form to each group page with radio for standard/digest/nomail (#27) Changed paths: A subscribe.php M group.php M lib/common.php M phpcs.xml M style.css Diff: diff --git a/group.php b/group.php index c8848e6..4884b1c 100644 --- a/group.php +++ b/group.php @@ -91,8 +91,34 @@ } $subscription_address = htmlspecialchars(get_subscribe_address($group)); ?> + <form class="subscription-form" method="POST" action="/subscribe.php"> + <input type="hidden" name="group" value="<?= clean($group) ?>"> + <div> + <label for="email">Email address:</label> + <input type="email" id="email" name="email" required> + </div> + <div> + <label> + <input type="radio" name="type" value="" checked> + Standard + </label> + <label> + <input type="radio" name="type" value="digest"> + Digest + </label> + <label> + <input type="radio" name="type" value="nomail"> + No emails + </label> + </div> + <div> + <button type="submit"> + Subscribe + </button> + </div> + </form> <p> - Subscribe to this list by sending a blank email to + You can also subscribe to this list by sending a blank email to <a href="mailto:<?= $subscription_address ?>"> <?= $subscription_address ?> </a> diff --git a/lib/common.php b/lib/common.php index 8f6f446..7ce3ee8 100644 --- a/lib/common.php +++ b/lib/common.php @@ -13,6 +13,91 @@ function error($str) die(); } +/* Borrowed from web-php repo. */ +function clean($var) +{ + return htmlspecialchars($var, \ENT_QUOTES); +} + +// Try to check that this email address is valid +function is_emailable_address($email) +{ + $email = filter_var($email, FILTER_VALIDATE_EMAIL); + // No email, no validation + if (!$email) { + return false; + } + + $host = substr($email, strrpos($email, '@') + 1); + // addresses from our mailing-list servers + $host_regex = "!(lists\.php\.net|chek[^.*]\.com)!i"; + if (preg_match($host_regex, $host)) { + return false; + } + + return true; +} + +// Returns the real IP address of the user +function i2c_realip() +{ + // No IP found (will be overwritten by for + // if any IP is found behind a firewall) + $ip = false; + + // If HTTP_CLIENT_IP is set, then give it priority + if (!empty($_SERVER["HTTP_CLIENT_IP"])) { + $ip = $_SERVER["HTTP_CLIENT_IP"]; + } + + // User is behind a proxy and check that we discard RFC1918 IP addresses + // if they are behind a proxy then only figure out which IP belongs to the + // user. Might not need any more hackin if there is a squid reverse proxy + // infront of apache. + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + // Put the IP's into an array which we shall work with shortly. + $ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']); + if ($ip) { + array_unshift($ips, $ip); + $ip = false; + } + + for ($i = 0; $i < count($ips); $i++) { + // Skip RFC 1918 IP's 10.0.0.0/8, 172.16.0.0/12 and + // 192.168.0.0/16 + // Also skip RFC 6598 IP's + $regex = '/^(?:10|100\.(?:6[4-9]|[7-9]\d|1[01]\d|12[0-7])|172\.(?:1[6-9]|2\d|3[01])|192\.168)\./'; + if (!preg_match($regex, $ips[$i]) && ip2long($ips[$i])) { + $ip = $ips[$i]; + break; + } + } + } + + // Return with the found IP or the remote address + return $ip ?: $_SERVER['REMOTE_ADDR']; +} + +/* + This code is used to post data to the central server which + can store the data in database and/or mail notices or requests + to PHP.net stuff and servers +*/ +function posttohost($url, $data) +{ + $data = http_build_query($data); + + $opts = [ + 'method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'content' => $data, + ]; + + $ctx = stream_context_create(['http' => $opts]); + + return file_get_contents($url, false, $ctx); +} + function head($title = "PHP Mailing Lists (PHP News)") { header("Content-type: text/html; charset=utf-8"); @@ -216,10 +301,10 @@ function format_date($d) } /* - * Translate a group name to a subscription address for the list. It's almost + * Translate a group name to the email address for the list. It's almost * easy but then we have a bunch of special cases. */ -function get_subscribe_address($group) +function get_list_address($group) { $address = str_replace('.', '-', $group); // php.internals -> php-internals $address = str_replace('php-doc-', 'doc-', $address); // php-doc-fr -> doc-fr @@ -243,5 +328,10 @@ function get_subscribe_address($group) $address = $special[$address]; } - return $address . '+subscr...@lists.php.net'; + return $address; +} + +function get_subscribe_address($group, $mode = '') +{ + return get_list_address($group) . '+subscribe' . ($mode ? '-' . $mode : '') . '@lists.php.net'; } diff --git a/phpcs.xml b/phpcs.xml index 7602b0d..e38808b 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -12,5 +12,6 @@ <file>getpart.php</file> <file>group.php</file> <file>index.php</file> + <file>subscribe.php</file> <file>lib</file> </ruleset> diff --git a/style.css b/style.css index 92ab9e3..d8f91ce 100644 --- a/style.css +++ b/style.css @@ -381,6 +381,12 @@ table.standard th.subr { .signature a:active { color: #ff9999; } .signature a:visited { color: #ff66ff; } +/* Subscription form */ +form.subscription-form { + display: flex; + gap: 1em; +} + @media screen and (max-width: 760px) { .welcome { display: none; diff --git a/subscribe.php b/subscribe.php new file mode 100644 index 0000000..fbb620f --- /dev/null +++ b/subscribe.php @@ -0,0 +1,77 @@ +<?php + +require 'common.php'; +head("Subscribe to a group"); +echo '<section class="content">'; + +// No error found yet +$error = ""; + +// Check email address +if ( + empty($_POST['email']) || + $_POST['email'] == 'u...@example.com' || + $_POST['email'] == 'f...@from.net' || + !is_emailable_address($_POST['email']) +) { + $error = "You forgot to specify an email address to be added to the list, or specified an invalid address." . + "<br>Please go back and try again."; + +// Check if any mailing list was selected +} elseif (empty($_POST['group'])) { + $error = "You need to select a group subscribe to." . + "<br>Please go back and try again."; + +// Check if type of subscription makes sense +} elseif (!in_array($_POST['type'], [ '', 'digest', 'nomail' ])) { + $error = "The subscription type you specified is not valid." . + "<br>Please go back and try again."; + +// Seems to be a valid email address +} else { + $remote_addr = i2c_realip(); + $maillist = get_list_address($_POST['group']); + if ($_POST['type'] != '') { + $maillist .= '-' . $_POST['type']; + } + + if ($maillist) { + // Get in contact with main server to subscribe the user + $result = posttohost( + "https://main.php.net/entry/subscribe.php", + [ + "request" => 'subscribe', + "email" => $_POST['email'], + "maillist" => $maillist, + "remoteip" => $remote_addr, + "referer" + => 'http' . (@$_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] . + '/' . $_POST['group'], + ], + ); + + // Provide error if unable to subscribe + if ($result) { + $error = "We were unable to subscribe you due to some technical problems.<br>" . + "Please try again later."; + } + } else { + $error = "That's not a group that we can handle.<br>" . + "Please try again later."; + } +} + +// Give error information or success report +if (!empty($error)) { + echo "<p class=\"formerror\">$error</p>"; +} else { + ?> + <p> + A request has been entered into the mailing list processing queue. + You should receive an email at <?php echo clean($_POST['email']); ?> shortly + describing how to complete your request. + </p> + <?php +} +echo '</section>'; +foot();