diff --git a/lib/copy_this/libs/ReCaptcha/ReCaptcha.php b/lib/copy_this/libs/ReCaptcha/ReCaptcha.php
new file mode 100644
index 000000000..523c4aae4
--- /dev/null
+++ b/lib/copy_this/libs/ReCaptcha/ReCaptcha.php
@@ -0,0 +1,97 @@
+secret = $secret;
+
+ if (!is_null($requestMethod)) {
+ $this->requestMethod = $requestMethod;
+ } else {
+ $this->requestMethod = new RequestMethod\Post();
+ }
+ }
+
+ /**
+ * Calls the reCAPTCHA siteverify API to verify whether the user passes
+ * CAPTCHA test.
+ *
+ * @param string $response The value of 'g-recaptcha-response' in the submitted form.
+ * @param string $remoteIp The end user's IP address.
+ * @return Response Response from the service.
+ */
+ public function verify($response, $remoteIp = null)
+ {
+ // Discard empty solution submissions
+ if (empty($response)) {
+ $recaptchaResponse = new Response(false, array('missing-input-response'));
+ return $recaptchaResponse;
+ }
+
+ $params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION);
+ $rawResponse = $this->requestMethod->submit($params);
+ return Response::fromJson($rawResponse);
+ }
+}
diff --git a/lib/copy_this/libs/ReCaptcha/RequestMethod.php b/lib/copy_this/libs/ReCaptcha/RequestMethod.php
new file mode 100644
index 000000000..fc4dde59c
--- /dev/null
+++ b/lib/copy_this/libs/ReCaptcha/RequestMethod.php
@@ -0,0 +1,42 @@
+ array(
+ 'header' => "Content-type: application/x-www-form-urlencoded\r\n",
+ 'method' => 'POST',
+ 'content' => $params->toQueryString(),
+ // Force the peer to validate (not needed in 5.6.0+, but still works
+ 'verify_peer' => true,
+ // Force the peer validation to use www.google.com
+ $peer_key => 'www.google.com',
+ ),
+ );
+ $context = stream_context_create($options);
+ return file_get_contents(self::SITE_VERIFY_URL, false, $context);
+ }
+}
diff --git a/lib/copy_this/libs/ReCaptcha/RequestMethod/Socket.php b/lib/copy_this/libs/ReCaptcha/RequestMethod/Socket.php
new file mode 100644
index 000000000..0d5dfb460
--- /dev/null
+++ b/lib/copy_this/libs/ReCaptcha/RequestMethod/Socket.php
@@ -0,0 +1,104 @@
+handle = fsockopen($hostname, $port, $errno, $errstr, (is_null($timeout) ? ini_get("default_socket_timeout") : $timeout));
+
+ if ($this->handle != false && $errno === 0 && $errstr === '') {
+ return $this->handle;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * fwrite
+ *
+ * @see http://php.net/fwrite
+ * @param string $string
+ * @param int $length
+ * @return int | bool
+ */
+ public function fwrite($string, $length = null)
+ {
+ return fwrite($this->handle, $string, (is_null($length) ? strlen($string) : $length));
+ }
+
+ /**
+ * fgets
+ *
+ * @see http://php.net/fgets
+ * @param int $length
+ */
+ public function fgets($length = null)
+ {
+ return fgets($this->handle, $length);
+ }
+
+ /**
+ * feof
+ *
+ * @see http://php.net/feof
+ * @return bool
+ */
+ public function feof()
+ {
+ return feof($this->handle);
+ }
+
+ /**
+ * fclose
+ *
+ * @see http://php.net/fclose
+ * @return bool
+ */
+ public function fclose()
+ {
+ return fclose($this->handle);
+ }
+}
diff --git a/lib/copy_this/libs/ReCaptcha/RequestMethod/SocketPost.php b/lib/copy_this/libs/ReCaptcha/RequestMethod/SocketPost.php
new file mode 100644
index 000000000..ba9036f69
--- /dev/null
+++ b/lib/copy_this/libs/ReCaptcha/RequestMethod/SocketPost.php
@@ -0,0 +1,120 @@
+socket = $socket;
+ } else {
+ $this->socket = new Socket();
+ }
+ }
+
+ /**
+ * Submit the POST request with the specified parameters.
+ *
+ * @param RequestParameters $params Request parameters
+ * @return string Body of the reCAPTCHA response
+ */
+ public function submit(RequestParameters $params)
+ {
+ $errno = 0;
+ $errstr = '';
+
+ if ($this->socket->fsockopen('ssl://' . self::RECAPTCHA_HOST, 443, $errno, $errstr, 30) !== false) {
+ $content = $params->toQueryString();
+
+ $request = "POST " . self::SITE_VERIFY_PATH . " HTTP/1.1\r\n";
+ $request .= "Host: " . self::RECAPTCHA_HOST . "\r\n";
+ $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
+ $request .= "Content-length: " . strlen($content) . "\r\n";
+ $request .= "Connection: close\r\n\r\n";
+ $request .= $content . "\r\n\r\n";
+
+ $this->socket->fwrite($request);
+ $response = '';
+
+ while (!$this->socket->feof()) {
+ $response .= $this->socket->fgets(4096);
+ }
+
+ $this->socket->fclose();
+
+ if (0 === strpos($response, 'HTTP/1.1 200 OK')) {
+ $parts = preg_split("#\n\s*\n#Uis", $response);
+ return $parts[1];
+ }
+
+ return self::BAD_RESPONSE;
+ }
+
+ return self::BAD_REQUEST;
+ }
+}
diff --git a/lib/copy_this/libs/ReCaptcha/RequestParameters.php b/lib/copy_this/libs/ReCaptcha/RequestParameters.php
new file mode 100644
index 000000000..cb66f26cf
--- /dev/null
+++ b/lib/copy_this/libs/ReCaptcha/RequestParameters.php
@@ -0,0 +1,103 @@
+secret = $secret;
+ $this->response = $response;
+ $this->remoteIp = $remoteIp;
+ $this->version = $version;
+ }
+
+ /**
+ * Array representation.
+ *
+ * @return array Array formatted parameters.
+ */
+ public function toArray()
+ {
+ $params = array('secret' => $this->secret, 'response' => $this->response);
+
+ if (!is_null($this->remoteIp)) {
+ $params['remoteip'] = $this->remoteIp;
+ }
+
+ if (!is_null($this->version)) {
+ $params['version'] = $this->version;
+ }
+
+ return $params;
+ }
+
+ /**
+ * Query string representation for HTTP request.
+ *
+ * @return string Query string formatted parameters.
+ */
+ public function toQueryString()
+ {
+ return http_build_query($this->toArray(), '', '&');
+ }
+}
diff --git a/lib/copy_this/libs/ReCaptcha/Response.php b/lib/copy_this/libs/ReCaptcha/Response.php
new file mode 100644
index 000000000..d2d8a8bf7
--- /dev/null
+++ b/lib/copy_this/libs/ReCaptcha/Response.php
@@ -0,0 +1,102 @@
+success = $success;
+ $this->errorCodes = $errorCodes;
+ }
+
+ /**
+ * Is success?
+ *
+ * @return boolean
+ */
+ public function isSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Get error codes.
+ *
+ * @return array
+ */
+ public function getErrorCodes()
+ {
+ return $this->errorCodes;
+ }
+}
diff --git a/lib/copy_this/newznab/controllers/BasePage.php b/lib/copy_this/newznab/controllers/BasePage.php
index 97b8ef7ea..656e1e317 100644
--- a/lib/copy_this/newznab/controllers/BasePage.php
+++ b/lib/copy_this/newznab/controllers/BasePage.php
@@ -2,6 +2,8 @@
require_once SMARTY_DIR . 'Smarty.class.php';
require_once NN_LIB . 'utility' . DS . 'SmartyUtils.php';
+use newznab\controllers\Captcha;
+
class BasePage
{
/**
@@ -9,6 +11,13 @@ class BasePage
*/
public $settings = null;
+ /**
+ * Public access to Captcha object for error checking.
+ *
+ * @var \newznab\controllers\Captcha
+ */
+ public $captcha;
+
/**
* @var Users
*/
@@ -30,6 +39,9 @@ class BasePage
public $secure_connection = false;
+ /**
+ * Set up session / smarty / user variables.
+ */
public function __construct()
{
@session_start();
@@ -52,6 +64,7 @@ class BasePage
// Buffer settings/DB connection.
$this->settings = new newznab\db\DB();
$this->smarty = new Smarty();
+ $this->captcha = new Captcha(['Settings' => $this->settings]);
if ($this->site->style != "default")
$this->smarty->addTemplateDir(WWW_DIR.'templates/'.$this->site->style.'/views/frontend', 'style_frontend');
@@ -128,12 +141,41 @@ class BasePage
$this->smarty->assign('isadmin',"false");
$this->smarty->assign('loggedin',"false");
$this->floodCheck();
+ $this->handleCaptcha();
+
}
$this->smarty->assign('site', $this->site);
$this->smarty->assign('page', $this);
}
+ /**
+ * Allow display on pages that require captcha
+ * and handle captcha responses.
+ *
+ * @notes Optimized for speed over code brevity since it's
+ * executed on every singe page.
+ * Instantiating Captcha() doesn't initialize the underlying libraries.
+ * shouldDisplay() does it if applicable.
+ */
+ private function handleCaptcha() {
+ if ($this->captcha->shouldDisplay($this->page)) {
+ $this->smarty->assign('showCaptcha', true);
+ $this->smarty->assign('sitekey', $this->captcha->getSiteKey());
+
+ if ($this->isPostBack()) {
+ if (!$this->captcha->processCaptcha($_POST, $_SERVER['REMOTE_ADDR'])) {
+ $this->smarty->assign('error', $this->captcha->getError());
+ }
+ //Delete this key after using so it doesn't interfere with normal $_POST
+ //processing. (i.e. contact-us)
+ unset($_POST[Captcha::RECAPTCHA_POSTKEY]);
+ }
+ } else {
+ $this->smarty->assign('showCaptcha', false);
+ }
+ }
+
/**
* Unquotes quoted strings recursively in an array.
*
diff --git a/lib/copy_this/newznab/controllers/Captcha.php b/lib/copy_this/newznab/controllers/Captcha.php
new file mode 100644
index 000000000..7986f6cb8
--- /dev/null
+++ b/lib/copy_this/newznab/controllers/Captcha.php
@@ -0,0 +1,205 @@
+ null
+ ];
+ $options += $defaults;
+
+ $this->pdo = ($options['Settings'] instanceof DB ? $options['Settings'] : new DB());
+ $s = new \Sites();
+ $this->site = $s->get();
+ }
+
+ /**
+ * If site admin setup keys properly,
+ * allow display of recaptcha.
+ *
+ * @param string|bool $page
+ * @return bool
+ */
+ public function shouldDisplay($page = false) {
+ if ($page !== false) {
+ if (in_array($page, $this->captcha_pages) && $this->_bootstrapCaptcha()) {
+ return true;
+ }
+ } elseif ($this->_bootstrapCaptcha()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return formatted error messages.
+ *
+ * @return string
+ */
+ public function getError() {
+ return $this->error;
+ }
+
+ /**
+ * Return sitekey for captcha html display.
+ *
+ * @return bool|string
+ */
+ public function getSiteKey() {
+ return $this->sitekey;
+ }
+
+ /**
+ * Process the submitted captcha and validate.
+ *
+ * @param array $response
+ * @param string $ip
+ * @return bool
+ */
+ public function processCaptcha($response, $ip) {
+ if (isset($response[self::RECAPTCHA_POSTKEY])) {
+ $post_response = $response[self::RECAPTCHA_POSTKEY];
+ } else {
+ $post_response = '';
+ }
+
+ $verify_response = $this->recaptcha->verify($post_response, $ip);
+
+ if (!$verify_response->isSuccess()) {
+ $this->_handleErrors($verify_response->getErrorCodes());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Build formatted error string for output using
+ * Google's reCaptcha error codes.
+ *
+ * @param array $codes
+ */
+ private function _handleErrors($codes) {
+ $rc_error = 'ReCaptcha Failed: ';
+
+ foreach ($codes as $c) {
+ switch($c) {
+ case self::RECAPTCHA_ERROR_MISSING_SECRET:
+ $rc_error .= 'Missing Secret Key';
+ break;
+ case self::RECAPTCHA_ERROR_INVALID_SECRET:
+ $rc_error .= 'Invalid Secret Key';
+ break;
+ case self::RECAPTCHA_ERROR_MISSING_RESPONSE:
+ $rc_error .= 'No Response!';
+ break;
+ case self::RECAPTCHA_ERROR_INVALID_RESPONSE:
+ $rc_error .= 'Invalid response! You are a bot!';
+ break;
+ default:
+ $rc_error .= 'Unknown Error!';
+ }
+ }
+
+ $this->error = $rc_error;
+ }
+
+ /**
+ * Instantiate the ReCaptcha library and store it.
+ * Return bool on success/failure.
+ *
+ * @return bool
+ */
+ private function _bootstrapCaptcha() {
+ if ($this->recaptcha instanceof ReCaptcha) {
+ return true;
+ }
+
+ $this->sitekey = $this->site->recaptchapublickey;
+ $this->secretkey = $this->site->recaptchaprivatekey;
+
+ if ($this->sitekey != false && $this->sitekey != '') {
+ if ($this->secretkey != false && $this->secretkey != '') {
+ $this->recaptcha = new ReCaptcha($this->secretkey);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/lib/copy_this/newznab/controllers/recaptchalib.php b/lib/copy_this/newznab/controllers/recaptchalib.php
deleted file mode 100644
index f285e3200..000000000
--- a/lib/copy_this/newznab/controllers/recaptchalib.php
+++ /dev/null
@@ -1,274 +0,0 @@
- $value )
- $req .= $key . '=' . urlencode( stripslashes($value) ) . '&';
-
- // Cut the last '&'
- $req=substr($req,0,strlen($req)-1);
- return $req;
-}
-
-
-
-/**
- * Submits an HTTP POST to a reCAPTCHA server
- * @param string $host
- * @param string $path
- * @param array $data
- * @param int port
- * @return array response
- */
-function _recaptcha_http_post($host, $path, $data, $port = 80) {
-
- $req = _recaptcha_qsencode ($data);
-
- $http_request = "POST $path HTTP/1.0\r\n";
- $http_request .= "Host: $host\r\n";
- $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
- $http_request .= "Content-Length: " . strlen($req) . "\r\n";
- $http_request .= "User-Agent: reCAPTCHA/PHP\r\n";
- $http_request .= "\r\n";
- $http_request .= $req;
-
- $response = '';
- if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) {
- die ('Could not open socket');
- }
-
- fwrite($fs, $http_request);
-
- while ( !feof($fs) )
- $response .= fgets($fs, 1160); // One TCP-IP packet
- fclose($fs);
- $response = explode("\r\n\r\n", $response, 2);
-
- return $response;
-}
-
-
-
-/**
- * Gets the challenge HTML (javascript and non-javascript version).
- * This is called from the browser, and the resulting reCAPTCHA HTML widget
- * is embedded within the HTML form it was called from.
- * @param string $pubkey A public key for reCAPTCHA
- * @param string $error The error given by reCAPTCHA (optional, default is null)
- * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false)
-
- * @return string - The HTML to be embedded in the user's form.
- */
-function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false)
-{
- if ($pubkey == null || $pubkey == '') {
- die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create");
- }
-
- if ($use_ssl) {
- $server = RECAPTCHA_API_SECURE_SERVER;
- } else {
- $server = RECAPTCHA_API_SERVER;
- }
-
- $errorpart = "";
- if ($error) {
- $errorpart = "&error=" . $error;
- }
- return '
-
- ';
-}
-
-
-
-
-/**
- * A ReCaptchaResponse is returned from recaptcha_check_answer()
- */
-class ReCaptchaResponse {
- var $is_valid;
- var $error;
-}
-
-
-/**
- * Calls an HTTP POST function to verify if the user's guess was correct
- * @param string $privkey
- * @param string $remoteip
- * @param string $challenge
- * @param string $response
- * @param array $extra_params an array of extra variables to post to the server
- * @return ReCaptchaResponse
- */
-function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array())
-{
- if ($privkey == null || $privkey == '') {
- die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create");
- }
-
- if ($remoteip == null || $remoteip == '') {
- die ("For security reasons, you must pass the remote ip to reCAPTCHA");
- }
-
-
-
- //discard spam submissions
- if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) {
- $recaptcha_response = new ReCaptchaResponse();
- $recaptcha_response->is_valid = false;
- $recaptcha_response->error = 'incorrect-captcha-sol';
- return $recaptcha_response;
- }
-
- $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify",
- array (
- 'privatekey' => $privkey,
- 'remoteip' => $remoteip,
- 'challenge' => $challenge,
- 'response' => $response
- ) + $extra_params
- );
-
- $answers = explode ("\n", $response [1]);
- $recaptcha_response = new ReCaptchaResponse();
-
- if (trim ($answers [0]) == 'true') {
- $recaptcha_response->is_valid = true;
- }
- else {
- $recaptcha_response->is_valid = false;
- $recaptcha_response->error = $answers [1];
- }
- return $recaptcha_response;
-
-}
-
-/**
- * gets a URL where the user can sign up for reCAPTCHA. If your application
- * has a configuration page where you enter a key, you should provide a link
- * using this function.
- * @param string $domain The domain where the page is hosted
- * @param string $appname The name of your application
- */
-function recaptcha_get_signup_url ($domain = null, $appname = null) {
- return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname));
-}
-
-function _recaptcha_aes_pad($val) {
- $block_size = 16;
- $numpad = $block_size - (strlen ($val) % $block_size);
- return str_pad($val, strlen ($val) + $numpad, chr($numpad));
-}
-
-/* Mailhide related code */
-
-function _recaptcha_aes_encrypt($val,$ky) {
- if (! function_exists ("mcrypt_encrypt")) {
- die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed.");
- }
- $mode=MCRYPT_MODE_CBC;
- $enc=MCRYPT_RIJNDAEL_128;
- $val=_recaptcha_aes_pad($val);
- return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
-}
-
-
-function _recaptcha_mailhide_urlbase64 ($x) {
- return strtr(base64_encode ($x), '+/', '-_');
-}
-
-/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */
-function recaptcha_mailhide_url($pubkey, $privkey, $email) {
- if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) {
- die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " .
- "you can do so at http://www.google.com/recaptcha/mailhide/apikey");
- }
-
-
- $ky = pack('H*', $privkey);
- $cryptmail = _recaptcha_aes_encrypt ($email, $ky);
-
- return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail);
-}
-
-/**
- * gets the parts of the email to expose to the user.
- * eg, given johndoe@example,com return ["john", "example.com"].
- * the email is then displayed as john...@example.com
- */
-function _recaptcha_mailhide_email_parts ($email) {
- $arr = preg_split("/@/", $email );
-
- if (strlen ($arr[0]) <= 4) {
- $arr[0] = substr ($arr[0], 0, 1);
- } else if (strlen ($arr[0]) <= 6) {
- $arr[0] = substr ($arr[0], 0, 3);
- } else {
- $arr[0] = substr ($arr[0], 0, 4);
- }
- return $arr;
-}
-
-/**
- * Gets html to display an email address given a public an private key.
- * to get a key, go to:
- *
- * http://www.google.com/recaptcha/mailhide/apikey
- */
-function recaptcha_mailhide_html($pubkey, $privkey, $email) {
- $emailparts = _recaptcha_mailhide_email_parts ($email);
- $url = recaptcha_mailhide_url ($pubkey, $privkey, $email);
-
- return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]);
-
-}
\ No newline at end of file
diff --git a/lib/copy_this/www/pages/contact-us.php b/lib/copy_this/www/pages/contact-us.php
index 349341e9a..588ee86ba 100644
--- a/lib/copy_this/www/pages/contact-us.php
+++ b/lib/copy_this/www/pages/contact-us.php
@@ -2,33 +2,31 @@
use newznab\utility\Utility;
-if (isset($_POST["useremail"]))
-{
+if (isset($_POST["useremail"])) {
//
// send the contact info and report back to user.
//
- $email = $_POST["useremail"];
- $mailto = $page->site->email;
- $mailsubj = "Contact Form Submitted";
- $mailhead = "From: $email\n";
- $mailbody = "Values submitted from contact form:\n";
+ if ($page->captcha->getError() === false) {
+ $email = $_POST["useremail"];
+ $mailto = $page->site->email;
+ $mailsubj = "Contact Form Submitted";
+ $mailhead = "From: $email\n";
+ $mailbody = "Values submitted from contact form:\n";
- while (list ($key, $val) = each ($_POST))
- {
- if ($key != "submit") {
- $mailbody .= "$key : $val
\r\n";
+ while (list ($key, $val) = each($_POST)) {
+ if ($key != "submit") {
+ $mailbody .= "$key : $val
\r\n";
+ }
}
- }
- if (!preg_match("/\n/i",$_POST["useremail"]))
- {
- Utility::sendEmail($mailto, $mailsubj, $mailbody, $email);
- }
+ if (!preg_match("/\n/i", $_POST["useremail"])) {
+ Utility::sendEmail($mailto, $mailsubj, $mailbody, $email);
+ }
- $page->smarty->assign("msg", "
Alternatively use our contact form to get in touch.
- + {elseif $sent != ''} diff --git a/lib/copy_this/www/templates/nntmux/views/frontend/login.tpl b/lib/copy_this/www/templates/nntmux/views/frontend/login.tpl index aabe6f05e..fafa2f19a 100644 --- a/lib/copy_this/www/templates/nntmux/views/frontend/login.tpl +++ b/lib/copy_this/www/templates/nntmux/views/frontend/login.tpl @@ -17,7 +17,7 @@