Overhaul reCaptcha support. Ported from nZEDb. Remove old recaptchalib.

This commit is contained in:
Darko
2015-05-11 11:14:56 +02:00
parent 9cd720b341
commit 8d523b32f6
19 changed files with 1012 additions and 397 deletions
@@ -0,0 +1,97 @@
<?php
/**
* This is a PHP library that handles calling reCAPTCHA.
*
* @copyright Copyright (c) 2015, Google Inc.
* @link http://www.google.com/recaptcha
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace ReCaptcha;
/**
* reCAPTCHA client.
*/
class ReCaptcha
{
/**
* Version of this client library.
* @const string
*/
const VERSION = 'php_1.1.1';
/**
* Shared secret for the site.
* @var type string
*/
private $secret;
/**
* Method used to communicate with service. Defaults to POST request.
* @var RequestMethod
*/
private $requestMethod;
/**
* Create a configured instance to use the reCAPTCHA service.
*
* @param string $secret shared secret between site and reCAPTCHA server.
* @param RequestMethod $requestMethod method used to send the request. Defaults to POST.
*/
public function __construct($secret, RequestMethod $requestMethod = null)
{
if (empty($secret)) {
throw new \RuntimeException('No secret provided');
}
if (!is_string($secret)) {
throw new \RuntimeException('The provided secret must be a string');
}
$this->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);
}
}
@@ -0,0 +1,42 @@
<?php
/**
* This is a PHP library that handles calling reCAPTCHA.
*
* @copyright Copyright (c) 2015, Google Inc.
* @link http://www.google.com/recaptcha
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace ReCaptcha;
/**
* Method used to send the request to the service.
*/
interface RequestMethod
{
/**
* Submit the request with the specified parameters.
*
* @param RequestParameters $params Request parameters
* @return string Body of the reCAPTCHA response
*/
public function submit(RequestParameters $params);
}
@@ -0,0 +1,70 @@
<?php
/**
* This is a PHP library that handles calling reCAPTCHA.
*
* @copyright Copyright (c) 2015, Google Inc.
* @link http://www.google.com/recaptcha
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace ReCaptcha\RequestMethod;
use ReCaptcha\RequestMethod;
use ReCaptcha\RequestParameters;
/**
* Sends POST requests to the reCAPTCHA service.
*/
class Post implements RequestMethod
{
/**
* URL to which requests are POSTed.
* @const string
*/
const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
/**
* 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)
{
/**
* PHP 5.6.0 changed the way you specify the peer name for SSL context options.
* Using "CN_name" will still work, but it will raise deprecated errors.
*/
$peer_key = version_compare(PHP_VERSION, '5.6.0', '<') ? 'CN_name' : 'peer_name';
$options = array(
'http' => 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);
}
}
@@ -0,0 +1,104 @@
<?php
/**
* This is a PHP library that handles calling reCAPTCHA.
*
* @copyright Copyright (c) 2015, Google Inc.
* @link http://www.google.com/recaptcha
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace ReCaptcha\RequestMethod;
/**
* Convenience wrapper around native socket and file functions to allow for
* mocking.
*/
class Socket
{
private $handle = null;
/**
* fsockopen
*
* @see http://php.net/fsockopen
* @param string $hostname
* @param int $port
* @param int $errno
* @param string $errstr
* @param float $timeout
* @return resource
*/
public function fsockopen($hostname, $port = -1, &$errno = 0, &$errstr = '', $timeout = null)
{
$this->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);
}
}
@@ -0,0 +1,120 @@
<?php
/**
* This is a PHP library that handles calling reCAPTCHA.
*
* @copyright Copyright (c) 2015, Google Inc.
* @link http://www.google.com/recaptcha
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace ReCaptcha\RequestMethod;
use ReCaptcha\RequestMethod;
use ReCaptcha\RequestParameters;
/**
* Sends a POST request to the reCAPTCHA service, but makes use of fsockopen()
* instead of get_file_contents(). This is to account for people who may be on
* servers where allow_furl_open is disabled.
*/
class SocketPost implements RequestMethod
{
/**
* reCAPTCHA service host.
* @const string
*/
const RECAPTCHA_HOST = 'www.google.com';
/**
* @const string reCAPTCHA service path
*/
const SITE_VERIFY_PATH = '/recaptcha/api/siteverify';
/**
* @const string Bad request error
*/
const BAD_REQUEST = '{"success": false, "error-codes": ["invalid-request"]}';
/**
* @const string Bad response error
*/
const BAD_RESPONSE = '{"success": false, "error-codes": ["invalid-response"]}';
/**
* Socket to the reCAPTCHA service
* @var Socket
*/
private $socket;
/**
* Constructor
*
* @param \ReCaptcha\RequestMethod\Socket $socket optional socket, injectable for testing
*/
public function __construct(Socket $socket = null)
{
if (!is_null($socket)) {
$this->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;
}
}
@@ -0,0 +1,103 @@
<?php
/**
* This is a PHP library that handles calling reCAPTCHA.
*
* @copyright Copyright (c) 2015, Google Inc.
* @link http://www.google.com/recaptcha
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace ReCaptcha;
/**
* Stores and formats the parameters for the request to the reCAPTCHA service.
*/
class RequestParameters
{
/**
* Site secret.
* @var string
*/
private $secret;
/**
* Form response.
* @var string
*/
private $response;
/**
* Remote user's IP address.
* @var string
*/
private $remoteIp;
/**
* Client version.
* @var string
*/
private $version;
/**
* Initialise parameters.
*
* @param string $secret Site secret.
* @param string $response Value from g-captcha-response form field.
* @param string $remoteIp User's IP address.
* @param string $version Version of this client library.
*/
public function __construct($secret, $response, $remoteIp = null, $version = null)
{
$this->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(), '', '&');
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
/**
* This is a PHP library that handles calling reCAPTCHA.
*
* @copyright Copyright (c) 2015, Google Inc.
* @link http://www.google.com/recaptcha
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace ReCaptcha;
/**
* The response returned from the service.
*/
class Response
{
/**
* Succes or failure.
* @var boolean
*/
private $success = false;
/**
* Error code strings.
* @var array
*/
private $errorCodes = array();
/**
* Build the response from the expected JSON returned by the service.
*
* @param string $json
* @return \ReCaptcha\Response
*/
public static function fromJson($json)
{
$responseData = json_decode($json, true);
if (!$responseData) {
return new Response(false, array('invalid-json'));
}
if (isset($responseData['success']) && $responseData['success'] == true) {
return new Response(true);
}
if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) {
return new Response(false, $responseData['error-codes']);
}
return new Response(false);
}
/**
* Constructor.
*
* @param boolean $success
* @param array $errorCodes
*/
public function __construct($success, array $errorCodes = array())
{
$this->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;
}
}
@@ -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.
*
@@ -0,0 +1,205 @@
<?php
namespace newznab\controllers;
use newznab\db\DB;
use ReCaptcha\ReCaptcha;
class Captcha {
/**
* @var \newznab\db\DB
*/
private $pdo;
/**
* ReCaptcha Site Key from the
* settings database.
*
* @var bool|string
*/
private $sitekey;
/**
* ReCaptcha Secret Key from the
* settings database.
*
* @var bool|string
*/
private $secretkey;
/**
* ReCaptcha instance if enabled.
*
* @var \ReCaptcha\ReCaptcha
*/
private $recaptcha;
/**
* Contains the error output if ReCaptcha
* validation fails.
*
* @var string|bool
*/
private $error = false;
/**
* List of page routes to apply the captcha.
*
* @todo Find a better way to enumerate this, I hate literals.
* @var array
*/
private $captcha_pages = [
'login',
'register',
'contact-us',
'forgottenpassword'
];
/**
* $_POST key for the user-supplied ReCaptcha response.
*/
const RECAPTCHA_POSTKEY = 'g-recaptcha-response';
/**
* Error literal constants.
*/
const RECAPTCHA_ERROR_MISSING_SECRET = 'missing-input-secret';
const RECAPTCHA_ERROR_INVALID_SECRET = 'invalid-input-secret';
const RECAPTCHA_ERROR_MISSING_RESPONSE = 'missing-input-response';
const RECAPTCHA_ERROR_INVALID_RESPONSE = 'invalid-input-response';
/**
* Construct.
*
* @param array $options Class instances.
*/
public function __construct(array $options = []) {
$defaults = [
'Settings' => 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;
}
}
@@ -1,274 +0,0 @@
<?php
/*
* This is a PHP library that handles calling reCAPTCHA.
* - Documentation and latest version
* http://recaptcha.net/plugins/php/
* - Get a reCAPTCHA API Key
* https://www.google.com/recaptcha/admin/create
* - Discussion group
* http://groups.google.com/group/recaptcha
*
* Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
* AUTHORS:
* Mike Crawford
* Ben Maurer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* The reCAPTCHA server URL's
*/
define("RECAPTCHA_API_SERVER", "http://www.google.com/recaptcha/api");
define("RECAPTCHA_API_SECURE_SERVER", "https://www.google.com/recaptcha/api");
define("RECAPTCHA_VERIFY_SERVER", "www.google.com");
/**
* Encodes the given data into a query string format
* @param $data - array of string elements to be encoded
* @return string - encoded request
*/
function _recaptcha_qsencode ($data) {
$req = "";
foreach ( $data as $key => $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 <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
}
if ($use_ssl) {
$server = RECAPTCHA_API_SECURE_SERVER;
} else {
$server = RECAPTCHA_API_SERVER;
}
$errorpart = "";
if ($error) {
$errorpart = "&amp;error=" . $error;
}
return '<script type="text/javascript" src="'. $server . '/challenge?k=' . $pubkey . $errorpart . '"></script>
<noscript>
<iframe src="'. $server . '/noscript?k=' . $pubkey . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
</noscript>';
}
/**
* 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 <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
}
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 <a href='http://www.google.com/recaptcha/mailhide/apikey'>http://www.google.com/recaptcha/mailhide/apikey</a>");
}
$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]) . "<a href='" . htmlentities ($url) .
"' onclick=\"window.open('" . htmlentities ($url) . "', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;\" title=\"Reveal this e-mail address\">...</a>@" . htmlentities ($emailparts [1]);
}
+16 -18
View File
@@ -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<br />\r\n";
while (list ($key, $val) = each($_POST)) {
if ($key != "submit") {
$mailbody .= "$key : $val<br />\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", "<h2 style='padding-top:25px;'>Thanks for getting in touch with ".$page->site->title.".</h2>");
$page->smarty->assign("msg", "<h2 style='padding-top:25px;'>Thanks for getting in touch with " . $page->site->title . ".</h2>");
}
}
$page->title = "Contact ".$page->site->title;
$page->meta_title = "Contact ".$page->site->title;
$page->meta_keywords = "contact us,contact,get in touch,email";
+33 -42
View File
@@ -7,23 +7,18 @@ if ($users->isLoggedIn())
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'view';
switch($action)
{
switch($action) {
case "reset":
if (!isset($_REQUEST['guid']))
{
if (!isset($_REQUEST['guid'])) {
$page->smarty->assign('error', "No reset code provided.");
break;
}
$ret = $users->getByPassResetGuid($_REQUEST['guid']);
if (!$ret)
{
if (!$ret) {
$page->smarty->assign('error', "Bad reset code provided.");
break;
}
else
{
} else {
//
// reset the password, inform the user, send out the email
//
@@ -32,8 +27,8 @@ switch($action)
$users->updatePassword($ret["id"], $newpass);
$to = $ret["email"];
$subject = $page->site->title." Password Reset";
$contents = "Your password has been reset to ".$newpass;
$subject = $page->site->title . " Password Reset";
$contents = "Your password has been reset to " . $newpass;
Utility::sendEmail($to, $subject, $contents, $page->site->email);
$page->smarty->assign('confirmed', "true");
@@ -44,43 +39,39 @@ switch($action)
break;
case 'submit':
$page->smarty->assign('email', $_POST['email']);
if ($page->captcha->getError() === false) {
$page->smarty->assign('email', $_POST['email']);
if ($_POST['email'] =="")
{
$page->smarty->assign('error', "Missing Email");
}
else
{
//
// Check users exists and send an email
//
$ret = $users->getByEmail($_POST['email']);
if (!$ret)
{
$page->smarty->assign('error', "The email address is not recognised.");
break;
}
else
{
if ($_POST['email'] == "") {
$page->smarty->assign('error', "Missing Email");
} else {
//
// Generate a forgottenpassword guid, store it in the user table
// Check users exists and send an email
//
$guid = md5(uniqid());
$users->updatePassResetGuid($ret["id"], $guid);
$ret = $users->getByEmail($_POST['email']);
if (!$ret) {
$page->smarty->assign('error', "The email address is not recognised.");
break;
} else {
//
// Generate a forgottenpassword guid, store it in the user table
//
$guid = md5(uniqid());
$users->updatePassResetGuid($ret["id"], $guid);
//
// Send the email
//
$to = $ret["email"];
$subject = $page->site->title." Forgotten Password Request";
$contents = "Someone has requested a password reset for this email address. To reset the password use the following link.\n\n ".$page->serverurl."forgottenpassword?action=reset&guid=".$guid;
$page->smarty->assign('sent', "true");
Utility::sendEmail($to, $subject, $contents, $page->site->email);
break;
//
// Send the email
//
$to = $ret["email"];
$subject = $page->site->title . " Forgotten Password Request";
$contents = "Someone has requested a password reset for this email address. To reset the password use the following link.\n\n " . $page->serverurl . "forgottenpassword?action=reset&guid=" . $guid;
$page->smarty->assign('sent', "true");
Utility::sendEmail($to, $subject, $contents, $page->site->email);
break;
}
}
break;
}
break;
}
$page->title = "Forgotten Password";
+2 -3
View File
@@ -2,10 +2,9 @@
if ($page->isPostBack())
{
if (!isset($_POST["username"]) || !isset($_POST["password"]))
if (!isset($_POST["username"]) || !isset($_POST["password"])){
$page->smarty->assign('error', "Please enter your username and password.");
else
{
} elseif ($page->captcha->getError() === false) {
$username = htmlspecialchars($_POST["username"]);
$page->smarty->assign('username', $username);
$users = new Users();
+61 -53
View File
@@ -16,11 +16,11 @@ elseif ($page->site->registerstatus == Sites::REGISTER_STATUS_INVITE && (!isset(
$showregister = 0;
}
// Use recaptcha?
if ($page->site->registerrecaptcha == 1)
// Use recaptcha? 11.05.2015 - Old code
/*if ($page->site->registerrecaptcha == 1)
{
$page->smarty->assign('recaptcha', recaptcha_get_html($page->site->recaptchapublickey, null, $page->secure_connection));
}
}*/
if ($showregister == 0)
{
@@ -30,61 +30,69 @@ else {
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'view';
//Be sure to persist the invite code in the event of multiple form submissions. (errors)
if (isset($_REQUEST['invitecode'])) {
$page->smarty->assign('invite_code_query', '&invitecode='.htmlspecialchars($_REQUEST["invitecode"]));
} else {
$page->smarty->assign('invite_code_query', '');
}
switch ($action) {
case 'submit':
if ($page->captcha->getError() === false) {
$username = htmlspecialchars($_POST['username']);
$password = htmlspecialchars($_POST['password']);
$confirmpassword = htmlspecialchars($_POST['confirmpassword']);
$email = htmlspecialchars($_POST['email']);
$invitecode = htmlspecialchars($_POST['invitecode']);
$username = htmlspecialchars($_POST['username']);
$password = htmlspecialchars($_POST['password']);
$confirmpassword = htmlspecialchars($_POST['confirmpassword']);
$email = htmlspecialchars($_POST['email']);
$invitecode = htmlspecialchars($_POST['invitecode']);
$page->smarty->assign('username', $username);
$page->smarty->assign('password', $password);
$page->smarty->assign('confirmpassword', $confirmpassword);
$page->smarty->assign('email', $email);
$page->smarty->assign('invitecode', $invitecode);
$page->smarty->assign('username', $username);
$page->smarty->assign('password', $password);
$page->smarty->assign('confirmpassword', $confirmpassword);
$page->smarty->assign('email', $email);
$page->smarty->assign('invitecode', $invitecode);
//
// check uname/email isnt in use, password valid.
// if all good create new user account and redirect back to home page
//
if ($password != $confirmpassword) {
$page->smarty->assign('error', "Password Mismatch");
} else {
//get the default user role
$userdefault = $users->getDefaultRole();
$ret = $users->signup($username, $password, $email, $_SERVER['REMOTE_ADDR'], $userdefault['id'], "", $userdefault['defaultinvites'], $invitecode, false, isset($_POST['recaptcha_challenge_field']) ? $_POST['recaptcha_challenge_field'] : null, isset($_POST['recaptcha_response_field']) ? $_POST['recaptcha_response_field'] : null);
if ($ret > 0) {
$users->login($ret, $_SERVER['REMOTE_ADDR']);
header("Location: " . WWW_TOP . "/");
//
// check uname/email isnt in use, password valid.
// if all good create new user account and redirect back to home page
//
if ($password != $confirmpassword) {
$page->smarty->assign('error', "Password Mismatch");
} else {
switch ($ret) {
case Users::ERR_SIGNUP_BADUNAME:
$page->smarty->assign('error', "Your username must be longer than three characters.");
break;
case Users::ERR_SIGNUP_BADPASS:
$page->smarty->assign('error', "Your password must be longer than five characters.");
break;
case Users::ERR_SIGNUP_BADEMAIL:
$page->smarty->assign('error', "Your email is not a valid format.");
break;
case Users::ERR_SIGNUP_UNAMEINUSE:
$page->smarty->assign('error', "Sorry, the username is already taken.");
break;
case Users::ERR_SIGNUP_EMAILINUSE:
$page->smarty->assign('error', "Sorry, the email is already in use.");
break;
case Users::ERR_SIGNUP_BADINVITECODE:
$page->smarty->assign('error', "Sorry, the invite code is old or has been used.");
break;
case Users::ERR_SIGNUP_BADCAPTCHA:
$page->smarty->assign('error', "Sorry, your captcha code was incorrect.");
break;
default:
$page->smarty->assign('error', "Failed to register.");
break;
//get the default user role
$userdefault = $users->getDefaultRole();
$ret = $users->signup($username, $password, $email, $_SERVER['REMOTE_ADDR'], $userdefault['id'], "", $userdefault['defaultinvites'], $invitecode, false, isset($_POST['recaptcha_challenge_field']) ? $_POST['recaptcha_challenge_field'] : null, isset($_POST['recaptcha_response_field']) ? $_POST['recaptcha_response_field'] : null);
if ($ret > 0) {
$users->login($ret, $_SERVER['REMOTE_ADDR']);
header("Location: " . WWW_TOP . "/");
} else {
switch ($ret) {
case Users::ERR_SIGNUP_BADUNAME:
$page->smarty->assign('error', "Your username must be longer than three characters.");
break;
case Users::ERR_SIGNUP_BADPASS:
$page->smarty->assign('error', "Your password must be longer than five characters.");
break;
case Users::ERR_SIGNUP_BADEMAIL:
$page->smarty->assign('error', "Your email is not a valid format.");
break;
case Users::ERR_SIGNUP_UNAMEINUSE:
$page->smarty->assign('error', "Sorry, the username is already taken.");
break;
case Users::ERR_SIGNUP_EMAILINUSE:
$page->smarty->assign('error', "Sorry, the email is already in use.");
break;
case Users::ERR_SIGNUP_BADINVITECODE:
$page->smarty->assign('error', "Sorry, the invite code is old or has been used.");
break;
case Users::ERR_SIGNUP_BADCAPTCHA:
$page->smarty->assign('error', "Sorry, your captcha code was incorrect.");
break;
default:
$page->smarty->assign('error', "Failed to register.");
break;
}
}
}
}
@@ -0,0 +1,7 @@
{if $showCaptcha == true}
<div class="g-recaptcha" data-sitekey="{$sitekey}"></div>
<script type="text/javascript"
src="https://www.google.com/recaptcha/api.js?hl=en">
</script>
<br/>
{/if}
@@ -1,4 +1,4 @@
<h1>{$page->title}</h1>
<h2>Getting in touch</h2>
@@ -13,7 +13,7 @@
<p>
Alternatively use our contact form to get in touch.
</p>
<form method="post" action="contact-us">
<table>
<tr>
@@ -37,6 +37,7 @@
<tr>
<td></td>
<td>
{$page->smarty->fetch('captcha.tpl')}
<input type="submit" value="Submit" />
</td>
</tr>
@@ -1,4 +1,4 @@
<h1>{$page->title}</h1>
{if $error != ''}
@@ -18,7 +18,7 @@
<input id="email" autocomplete="off" name="email" value="{$email}" type="email"/>
</td>
</tr>
<tr><th></th><td><input type="submit" value="Request Password Reset" /><div style="float:right;" class="hint"><em>*</em> Indicates mandatory field.</div></td></tr>
<tr><th></th><td>{$page->smarty->fetch('captcha.tpl')}<input type="submit" value="Request Password Reset" /><div style="float:right;" class="hint"><em>*</em> Indicates mandatory field.</div></td></tr>
</table>
</form>
{elseif $sent != ''}
@@ -17,7 +17,7 @@
<input style="width:150px;" id="password" name="password" type="password"/>
</td></tr>
<tr><th><label for="rememberme">Remember Me:</label></th><td><input id="rememberme" {if $rememberme == 1}checked="checked"{/if} name="rememberme" type="checkbox"/></td>
<tr><th></th><td><input type="submit" value="Login"/></td></tr>
<tr><th></th><td>{$page->smarty->fetch('captcha.tpl')}<input type="submit" value="Login"/></td></tr>
</table>
</form>
<br/>
@@ -6,7 +6,7 @@
{/if}
{if $showregister != "0"}
<form method="post" action="register?action=submit">
<form method="post" action="register?action=submit{$invite_code_query}">
<table style="width:500px;" class="data">
<tr><th width="75px;"><label for="username">Username:</label> <em>*</em></th>
@@ -27,7 +27,7 @@
</table>
<table style="width:500px; margin-top:10px;" class="data">
<tr><th width="75px;"></th><td><input type="submit" value="Register"/><div style="float:right;" class="hint"><em>*</em> Indicates mandatory field.</div></td></tr>
<tr><th width="75px;"></th><td>{$page->smarty->fetch('captcha.tpl')}<input type="submit" value="Register"/><div style="float:right;" class="hint"><em>*</em> Indicates mandatory field.</div></td></tr>
</table>
</form>