PK 3m}P
LICENSE.mdnu W+A Copyright (c) 2019-2020, Laminas Foundation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of Laminas Foundation nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PK 3m}P%'\ \ COPYRIGHT.mdnu W+A Copyright (c) 2019-2020, Laminas Foundation.
All rights reserved. (https://getlaminas.org/)
PK 3m}PAé .travis.ymlnu W+A language: php
cache:
directories:
- $HOME/.composer/cache
env:
global:
- COMPOSER_ARGS="--no-interaction"
- COVERAGE_DEPS="php-coveralls/php-coveralls"
matrix:
fast_finish: true
include:
- php: 5.6
env:
- DEPS=lowest
- php: 5.6
env:
- DEPS=latest
- php: 7
env:
- DEPS=lowest
- php: 7
env:
- DEPS=latest
- php: 7.1
env:
- DEPS=lowest
- php: 7.1
env:
- DEPS=latest
- CS_CHECK=true
- TEST_COVERAGE=true
- php: 7.2
env:
- DEPS=lowest
- php: 7.2
env:
- DEPS=latest
- php: 7.3
env:
- DEPS=lowest
- php: 7.3
env:
- DEPS=latest
before_install:
- if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi
install:
- travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs
- if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi
- if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi
- if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi
- if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi
- stty cols 120 && composer show
script:
- if [[ $TEST_COVERAGE == 'true' ]]; then composer test-coverage ; else composer test ; fi
- if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi
after_script:
- if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry php vendor/bin/php-coveralls -v ; fi
notifications:
email: false
PK 3m}Px= = .coveralls.ymlnu W+A coverage_clover: clover.xml
json_path: coveralls-upload.json
PK 3m}Px]" " phpcs.xmlnu W+A
srctest
PK 3m}P"Ȁ README.mdnu W+A # Laminas\ReCaptcha component
[![Build Status](https://travis-ci.com/laminas/laminas-recaptcha.svg?branch=master)](https://travis-ci.com/laminas/laminas-recaptcha)
[![Coverage Status](https://coveralls.io/repos/github/laminas/laminas-recaptcha/badge.svg?branch=master)](https://coveralls.io/github/laminas/laminas-recaptcha?branch=master)
## Install
You can install using [Composer][1]:
```
composer require laminas/laminas-recaptcha
```
## Documentation
Documentation is on the Laminas website:
- [https://docs.laminas.dev/laminas-recaptcha][2]
[1]: https://getcomposer.org/download/
[2]: https://docs.laminas.dev/laminas-recaptcha
PK 3m}P@ݫ
.gitignorenu W+A /clover.xml
/composer.lock
/coveralls-upload.json
/docs/html/
/laminas-mkdoc-theme.tgz
/laminas-mkdoc-theme/
/phpunit.xml
/vendor/
PK 3m}P{fH' H' src/MailHide.phpnu W+A requireMcrypt();
// If options is a traversable, we want to convert it to an array
// so we can merge it with the default options
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
/* Merge if needed */
if (is_array($options)) {
$options = array_merge($this->getDefaultOptions(), $options);
} else {
$options = $this->getDefaultOptions();
}
parent::__construct($publicKey, $privateKey, null, $options);
if ($email !== null) {
$this->setEmail($email);
}
}
/**
* Get emailValidator
*
* @return ValidatorInterface
*/
public function getEmailValidator()
{
if (null === $this->emailValidator) {
$this->setEmailValidator(new EmailAddressValidator());
}
return $this->emailValidator;
}
/**
* Set email validator
*
* @param ValidatorInterface $validator
* @return MailHide
*/
public function setEmailValidator(ValidatorInterface $validator)
{
$this->emailValidator = $validator;
return $this;
}
/**
* See if the mcrypt extension is available
*
* @throws MailHideException
*/
protected function requireMcrypt()
{
if (! extension_loaded('mcrypt')) {
throw new MailHideException(sprintf(
'Use of the %s component requires the mcrypt extension to be enabled in PHP',
__CLASS__
));
}
}
/**
* Serialize as string
*
* When the instance is used as a string it will display the email address. Since we can't
* throw exceptions within this method we will trigger a user warning instead.
*
* @return string
*/
public function __toString()
{
try {
$return = $this->getHtml();
} catch (\Exception $e) {
$return = '';
trigger_error($e->getMessage(), E_USER_WARNING);
}
return $return;
}
/**
* Get the default set of parameters
*
* @return array
*/
public function getDefaultOptions()
{
return [
'encoding' => 'UTF-8',
'linkTitle' => 'Reveal this e-mail address',
'linkHiddenText' => '...',
'popupWidth' => 500,
'popupHeight' => 300,
];
}
/**
* Set the private key property
*
* Override the parent method to store a binary representation of the private key as well.
*
* Note that we use the nomenclature "private key" as this is what MailHide's API
* uses, even though the parent ReCaptcha API uses "secret key"
*
* @param string $privateKey
* @return MailHide
*/
public function setPrivateKey($privateKey)
{
parent::setSecretKey($privateKey);
/* Pack the private key into a binary string */
$this->privateKeyPacked = pack('H*', $this->getSecretKey());
return $this;
}
/**
* get the private key property
*
* Note that we use the nomenclature "private key" as this is what MailHide's API
* uses, even though the parent ReCaptcha API uses "secret key"
*
* @return string
*/
public function getPrivateKey()
{
return parent::getSecretKey();
}
/**
* set the public key property
*
* Note that we use the nomenclature "public key" as this is what MailHide's API
* uses, even though the parent ReCaptcha API uses "site key"
*
* @param string $publicKey
*/
public function setPublicKey($publicKey)
{
return parent::setSiteKey($publicKey);
}
/**
* Get the public key property
*
* Note that we use the nomenclature "public key" as this is what MailHide's API
* uses, even though the parent ReCaptcha API uses "site key"
*
* @return string
*/
public function getPublicKey()
{
return parent::getSiteKey();
}
/**
* Set the email property
*
* This method will set the email property along with the local and domain parts
*
* @param string $email
* @return MailHide
*/
public function setEmail($email)
{
$this->email = $email;
$validator = $this->getEmailValidator();
if (! $validator->isValid($email)) {
throw new MailHideException('Invalid email address provided');
}
$emailParts = explode('@', $email, 2);
/* Decide on how much of the local part we want to reveal */
if (strlen($emailParts[0]) <= 4) {
$emailParts[0] = substr($emailParts[0], 0, 1);
} elseif (strlen($emailParts[0]) <= 6) {
$emailParts[0] = substr($emailParts[0], 0, 3);
} else {
$emailParts[0] = substr($emailParts[0], 0, 4);
}
$this->emailLocalPart = $emailParts[0];
$this->emailDomainPart = $emailParts[1];
return $this;
}
/**
* Get the email property
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Get the local part of the email address
*
* @return string
*/
public function getEmailLocalPart()
{
return $this->emailLocalPart;
}
/**
* Get the domain part of the email address
*
* @return string
*/
public function getEmailDomainPart()
{
return $this->emailDomainPart;
}
/**
* Get the HTML code needed for the mail hide
*
* @param string $email
* @return string
* @throws MailHideException
*/
public function getHtml($email = null)
{
if ($email !== null) {
$this->setEmail($email);
} elseif (null === ($email = $this->getEmail())) {
throw new MailHideException('Missing email address');
}
if ($this->getPublicKey() === null) {
throw new MailHideException('Missing public key');
}
if ($this->getPrivateKey() === null) {
throw new MailHideException('Missing private key');
}
/* Generate the url */
$url = $this->getUrl();
$enc = $this->getOption('encoding');
/* Genrate the HTML used to represent the email address */
$html = htmlentities($this->getEmailLocalPart(), ENT_COMPAT, $enc)
. '' . $this->options['linkHiddenText'] . '@'
. htmlentities($this->getEmailDomainPart(), ENT_COMPAT, $enc);
return $html;
}
/**
* Get the url used on the "hidden" part of the email address
*
* @return string
*/
protected function getUrl()
{
/* Figure out how much we need to pad the email */
$numPad = self::ENCRYPTION_BLOCK_SIZE - (strlen($this->email) % self::ENCRYPTION_BLOCK_SIZE);
/* Pad the email */
$emailPadded = str_pad($this->email, strlen($this->email) + $numPad, chr($numPad));
/* Encrypt the email */
$emailEncrypted = mcrypt_encrypt(
self::ENCRYPTION_CIPHER,
$this->privateKeyPacked,
$emailPadded,
self::ENCRYPTION_MODE,
self::ENCRYPTION_IV
);
/* Return the url */
return sprintf(
'%s?k=%s&c=%s',
self::MAILHIDE_SERVER,
$this->getSiteKey(),
strtr(base64_encode($emailEncrypted), '+/', '-_')
);
}
}
PK 3m}PZ3@ @ src/Response.phpnu W+A setStatus($status);
}
if (! empty($errorCodes)) {
$this->setErrorCodes($errorCodes);
}
if ($httpResponse !== null) {
$this->setFromHttpResponse($httpResponse);
}
}
/**
* Set the status
*
* @param bool $status
* @return self
*/
public function setStatus($status)
{
$this->status = (bool) $status;
return $this;
}
/**
* Get the status
*
* @return bool
*/
public function getStatus()
{
return $this->status;
}
/**
* Alias for getStatus()
*
* @return bool
*/
public function isValid()
{
return $this->getStatus();
}
/**
* Set the error codes
*
* @param array $errorCodes
* @return self
*/
public function setErrorCodes($errorCodes)
{
if (is_string($errorCodes)) {
$errorCodes = [$errorCodes];
}
$this->errorCodes = $errorCodes;
return $this;
}
/**
* Get the error codes
*
* @return array
*/
public function getErrorCodes()
{
return $this->errorCodes;
}
/**
* Populate this instance based on a Laminas_Http_Response object
*
* @param HTTPResponse $response
* @return self
*/
public function setFromHttpResponse(HTTPResponse $response)
{
$body = $response->getBody();
$parts = json_decode($body, true);
$status = false;
$errorCodes = [];
if (is_array($parts) && array_key_exists('success', $parts)) {
$status = $parts['success'];
if (array_key_exists('error-codes', $parts)) {
$errorCodes = $parts['error-codes'];
}
}
$this->setStatus($status);
$this->setErrorCodes($errorCodes);
return $this;
}
}
PK 3m}PSrnr r src/MailHideException.phpnu W+A false, /* Includes the
HTML;
}
return $return;
}
/**
* Gets a solution to the verify server
*
* @param string $responseField
* @return \Laminas\Http\Response
* @throws Exception
*/
protected function post($responseField)
{
if ($this->secretKey === null) {
throw new Exception('Missing secret key');
}
if ($this->ip === null) {
throw new Exception('Missing ip address');
}
/* Fetch an instance of the http client */
$httpClient = $this->getHttpClient();
$params = [
'secret' => $this->secretKey,
'remoteip' => $this->ip,
'response' => $responseField
];
$request = new HttpRequest();
$request->setUri(self::VERIFY_SERVER);
$request->getPost()->fromArray($params);
$request->setMethod(HttpRequest::METHOD_POST);
$httpClient->setEncType($httpClient::ENC_URLENCODED);
return $httpClient->send($request);
}
/**
* Verify the user input
*
* This method calls up the post method and returns a
* \Laminas\ReCaptcha\Response object.
*
* @param string $responseField
* @return \Laminas\ReCaptcha\Response
*/
public function verify($responseField)
{
$response = $this->post($responseField);
return new Response(null, null, $response);
}
}
PK 3m}P+ej j src/Exception.phpnu W+A publicKey = getenv('TESTS_LAMINAS_SERVICE_RECAPTCHA_MAILHIDE_PUBLIC_KEY');
$this->privateKey = getenv('TESTS_LAMINAS_SERVICE_RECAPTCHA_MAILHIDE_PRIVATE_KEY');
if (! extension_loaded('mcrypt')) {
$this->markTestSkipped('Laminas\ReCaptcha tests skipped due to missing mcrypt extension');
}
if (empty($this->publicKey)
|| $this->publicKey == 'public mailhide key'
|| empty($this->privateKey)
|| $this->privateKey == 'private mailhide key'
) {
$this->markTestSkipped('Laminas\ReCaptcha\MailHide tests skipped due to missing keys');
}
$this->mailHide = new ReCaptcha\MailHide();
}
public function testSetGetPrivateKey()
{
$this->mailHide->setPrivateKey($this->privateKey);
$this->assertSame($this->privateKey, $this->mailHide->getPrivateKey());
}
public function testSetGetEmail()
{
$mail = 'mail@example.com';
$this->mailHide->setEmail($mail);
$this->assertSame($mail, $this->mailHide->getEmail());
$this->assertSame('example.com', $this->mailHide->getEmailDomainPart());
}
public function testEmailLocalPart()
{
$this->mailHide->setEmail('abcd@example.com');
$this->assertSame('a', $this->mailHide->getEmailLocalPart());
$this->mailHide->setEmail('abcdef@example.com');
$this->assertSame('abc', $this->mailHide->getEmailLocalPart());
$this->mailHide->setEmail('abcdefg@example.com');
$this->assertSame('abcd', $this->mailHide->getEmailLocalPart());
}
public function testConstructor()
{
$mail = 'mail@example.com';
$options = [
'theme' => 'black',
'lang' => 'no',
];
$config = new \Laminas\Config\Config($options);
$mailHide = new ReCaptcha\MailHide($this->publicKey, $this->privateKey, $mail, $config);
$_options = $mailHide->getOptions();
$this->assertSame($this->publicKey, $mailHide->getPublicKey());
$this->assertSame($this->privateKey, $mailHide->getPrivateKey());
$this->assertSame($mail, $mailHide->getEmail());
$this->assertSame($options['theme'], $_options['theme']);
$this->assertSame($options['lang'], $_options['lang']);
}
protected function checkHtml($html)
{
$server = ReCaptcha\MailHide::MAILHIDE_SERVER;
$pubKey = $this->publicKey;
$this->assertEquals(2, substr_count($html, 'k=' . $pubKey));
$this->assertRegExp('/c\=[a-zA-Z0-9_=-]+"/', $html);
$this->assertRegExp('/c\=[a-zA-Z0-9_=-]+\\\'/', $html);
}
public function testGetHtml()
{
$mail = 'mail@example.com';
$this->mailHide->setEmail($mail);
$this->mailHide->setPublicKey($this->publicKey);
$this->mailHide->setPrivateKey($this->privateKey);
$html = $this->mailHide->getHtml();
$this->checkHtml($html);
}
public function testGetHtmlWithNoEmail()
{
$this->expectException(MailHideException::class);
$html = $this->mailHide->getHtml();
}
public function testGetHtmlWithMissingPublicKey()
{
$mail = 'mail@example.com';
$this->mailHide->setEmail($mail);
$this->mailHide->setPrivateKey($this->privateKey);
$this->expectException(MailHideException::class);
$html = $this->mailHide->getHtml();
}
public function testGetHtmlWithMissingPrivateKey()
{
$this->expectException(MailHideException::class);
$mail = 'mail@example.com';
$this->mailHide->setEmail($mail);
$this->mailHide->setPublicKey($this->publicKey);
$html = $this->mailHide->getHtml();
}
public function testGetHtmlWithParamter()
{
$mail = 'mail@example.com';
$this->mailHide->setPublicKey($this->publicKey);
$this->mailHide->setPrivateKey($this->privateKey);
$html = $this->mailHide->getHtml($mail);
$this->checkHtml($html);
}
}
PK 3m}PCE test/ReCaptchaTest.phpnu W+A siteKey = getenv('TESTS_LAMINAS_SERVICE_RECAPTCHA_SITE_KEY');
$this->secretKey = getenv('TESTS_LAMINAS_SERVICE_RECAPTCHA_SECRET_KEY');
if (empty($this->siteKey) || empty($this->siteKey)) {
$this->markTestSkipped('Laminas\ReCaptcha\ReCaptcha tests skipped due to missing keys');
}
$httpClient = new HttpClient(
null,
[
'adapter' => Curl::class,
]
);
$this->reCaptcha = new ReCaptcha(null, null, null, null, null, $httpClient);
}
public function testSetAndGet()
{
/* Set and get IP address */
$ip = '127.0.0.1';
$this->reCaptcha->setIp($ip);
$this->assertSame($ip, $this->reCaptcha->getIp());
/* Set and get site key */
$this->reCaptcha->setSiteKey($this->siteKey);
$this->assertSame($this->siteKey, $this->reCaptcha->getSiteKey());
/* Set and get secret key */
$this->reCaptcha->setSecretKey($this->secretKey);
$this->assertSame($this->secretKey, $this->reCaptcha->getSecretKey());
}
public function testSingleParam()
{
$key = 'ssl';
$value = true;
$this->reCaptcha->setParam($key, $value);
$this->assertSame($value, $this->reCaptcha->getParam($key));
}
public function testGetNonExistingParam()
{
$this->assertNull($this->reCaptcha->getParam('foobar'));
}
public function testMultipleParams()
{
$params = [
'ssl' => true,
];
$this->reCaptcha->setParams($params);
$_params = $this->reCaptcha->getParams();
$this->assertSame($params['ssl'], $_params['ssl']);
}
public function testSingleOption()
{
$key = 'theme';
$value = 'dark';
$this->reCaptcha->setOption($key, $value);
$this->assertSame($value, $this->reCaptcha->getOption($key));
}
public function testGetNonExistingOption()
{
$this->assertNull($this->reCaptcha->getOption('foobar'));
}
public function testMultipleOptions()
{
$options = [
'theme' => 'dark',
'hl' => 'en',
];
$this->reCaptcha->setOptions($options);
$_options = $this->reCaptcha->getOptions();
$this->assertSame($options['theme'], $_options['theme']);
$this->assertSame($options['hl'], $_options['hl']);
}
public function testSetMultipleParamsFromLaminasConfig()
{
$params = [
'ssl' => true,
];
$config = new Config\Config($params);
$this->reCaptcha->setParams($config);
$_params = $this->reCaptcha->getParams();
$this->assertSame($params['ssl'], $_params['ssl']);
}
public function testSetInvalidParams()
{
$this->expectException(Exception::class);
$var = 'string';
$this->reCaptcha->setParams($var);
}
public function testSetMultipleOptionsFromLaminasConfig()
{
$options = [
'theme' => 'dark',
'hl' => 'en',
];
$config = new Config\Config($options);
$this->reCaptcha->setOptions($config);
$_options = $this->reCaptcha->getOptions();
$this->assertSame($options['theme'], $_options['theme']);
$this->assertSame($options['hl'], $_options['hl']);
}
public function testSetInvalidOptions()
{
$this->expectException(Exception::class);
$var = 'string';
$this->reCaptcha->setOptions($var);
}
public function testConstructor()
{
$params = [
'noscript' => true,
];
$options = [
'theme' => 'dark',
'hl' => 'en',
];
$ip = '127.0.0.1';
$reCaptcha = new ReCaptcha($this->siteKey, $this->secretKey, $params, $options, $ip);
$_params = $reCaptcha->getParams();
$_options = $reCaptcha->getOptions();
$this->assertSame($this->siteKey, $reCaptcha->getSiteKey());
$this->assertSame($this->secretKey, $reCaptcha->getSecretKey());
$this->assertSame($params['noscript'], $_params['noscript']);
$this->assertSame($options['theme'], $_options['theme']);
$this->assertSame($options['hl'], $_options['hl']);
$this->assertSame($ip, $reCaptcha->getIp());
}
public function testConstructorWithNoIp()
{
// Fake the _SERVER value
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$reCaptcha = new ReCaptcha(null, null, null, null, null);
$this->assertSame($_SERVER['REMOTE_ADDR'], $reCaptcha->getIp());
unset($_SERVER['REMOTE_ADDR']);
}
public function testGetHtmlWithNoPublicKey()
{
$this->expectException(Exception::class);
$this->reCaptcha->getHtml();
}
public function testVerify()
{
$this->reCaptcha->setSiteKey($this->siteKey);
$this->reCaptcha->setSecretKey($this->secretKey);
$this->reCaptcha->setIp('127.0.0.1');
$adapter = new Test();
$client = new HttpClient(null, [
'adapter' => $adapter
]);
$this->reCaptcha->setHttpClient($client);
$resp = $this->reCaptcha->verify('responseField');
// See if we have a valid object and that the status is false
$this->assertInstanceOf(ReCaptchaResponse::class, $resp);
$this->assertFalse($resp->getStatus());
}
public function testGetHtml()
{
$this->reCaptcha->setSiteKey($this->siteKey);
$html = $this->reCaptcha->getHtml();
// See if the options for the captcha exist in the string
$this->assertNotFalse(strstr($html, sprintf('data-sitekey="%s"', $this->siteKey)));
// See if the js/iframe src is correct
$this->assertNotTrue(strstr($html, '