Initial commit - Typo3 11.5.41

This commit is contained in:
Matteo Gallo
2026-07-03 17:53:31 +02:00
commit 5ca4743197
6811 changed files with 568848 additions and 0 deletions

View File

@@ -0,0 +1,479 @@
2021-03-24 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* Resolves #93424: Audio Captcha without image is not accessible with tab
* Add extra array to composer.json
2020-12-26 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* Resolves #93107: Audio content is output twice
* Update version to 2.6.2
2020-11-22 Stanislas Rolland <typo32020(arobas)sjbr.ca>
* Update version to 2.6.1
2020-10-23 Stanislas Rolland <typo32020(arobas)sjbr.ca>
* Resolves #92608: PHP 7.4 issues deprecation notices
2020-06-15 Stanislas Rolland <typo32020(arobas)sjbr.ca>
* Resolves #90636: ConfigurationHelper Select EncryptionAlgorythm does not work
* Resolves #89735: RequestMiddlewares.php missing
* Update version to 2.5.4
* Resolves #91650: Adapt to TYPO3 10 LTS
* Update version to 2.6.0
2019-07-19 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves Security Ticket#201907155760000027
* Update version to 2.5.3
2018-11-02 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #86843: Syntax error in class EncryptionUtility
* Update version to 2.5.2
2018-10-24 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #86737: Fix syntax error in class PiBaseApi
* Update version to 2.5.1
2018-10-18 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #86688: Migrate to TYPO3 9 LTS
* Update version to 2.5.0
2018-10-18 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #86691: Incorrect keywords for composer
* Update version to 2.4.4
2018-07-24 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #85628: Error in Spanish localization file
* Resolves #85629: Remove dependency on TYPO3 ClientUtility
* Resolves #85499: Follow-up to migration from mcrypt to OpenSSL (Thanks to Marcus Bitzl)
* Update version to 2.4.3
2018-04-02 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #84576: Adapt font maker backend module to TYPO3 CMS 7 & 8
* Update version to 2.4.2
2018-04-01 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #83808: Allow translation of invalid input error (Thanks to Christian Toffolo)
2017-12-20 Stanislas Rolland <typo(arobas)sjbr.ca>
* Update manual
2017-12-19 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #80906: Unknown Exception thrown when domain model with validator is used in backend context
* Resolves #82716: Migrate from mcrypt to openssl (follow-up)
* Update version to 2.4.1
2017-10-16 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #82716: Migrate from mcrypt to openssl
* Resolves #81736: Add P3P header for IE 11 of Windows 7 when page opens in popup
2017-05-28 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #81354: Error code not localized
2017-01-07 Stanislas Rolland <typo(arobas)sjbr.ca>
* Change composer name to sjbr/sr-freecap
* Require TYPO3 CMS 7.6 LTS
* Resolves #79195: Require zend.multibyte for backend module
* Resolves #76062: add freeCap.js via includeJSlibs for compression
* Update version to 2.4.0
* Update manual
2016-02-13 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #73422: Cleanup in German word list
* Resolves #73423: Incorrect audio rendering when using word lists with non-ASCII characters
* Resolves #73220: Follow-up to PHP 5.5 warning when decrypting word with blowfish algorithm
* Update version to 2.3.1
2016-02-11 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #73394: Replace the English word list
2016-02-10 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #73220: PHP 5.5 warning when decrypting word with blowfish algorithm
2016-02-01 Stanislas Rolland <typo(arobas)sjbr.ca>
* Cleanup in ImageViewHelper
* Correction to composer.json
* Resolves #73075: Word list used when renewing captcha image is in wrong language
2015-11-02 Stanislas Rolland <typo(arobas)sjbr.ca>
* Resolves #70917: An end brace is in excess in TS setup.
* Resolves #66194: 500 error in access log
2015-10-11 Stanislas Rolland <typo3@sjbr.ca>
* Cleanup
* Add composer.json
* Move static template registration to TCA overrides
* Compatibility with TYPO3 CMS 7
* Improve template handling
* Cleanup references to versions of TYPO3 CMS lower than 6.2
* Delete directory mod1
* Update version to 2.3.0
2015-03-31 Stanislas Rolland <typo3@sjbr.ca>
* Resolves #64524: SJBR\SrFreecap\Domain\Model\Font raises warning on PHP 5.4+
2015-03-18 Stanislas Rolland <typo3@sjbr.ca>
* Resolves #65649: $this->freeCap->checkWord accepts empty postvar
* Resolves #64524: SJBR\SrFreecap\Domain\Model\Font raises warning on PHP 5.4+
2015-02-10 Stanislas Rolland <typo3@sjbr.ca>
* Resolves #64517: Revert CaptchaValidator::isValid should be protected
* Resolves #64972: New view configuration in TYPO3 CMS 6.2
* Require TYPO3 CMS 6.2
* Update version to 2.2.0
2015-02-04 Stanislas Rolland <typo3@sjbr.ca>
* Resolves #64847: Fatal error on CaptchaValidator::isValid() from context PiBaseApi
* Update version to 2.1.1
2015-01-28 Stanislas Rolland <typo3@sjbr.ca>
* Cleanup
2015-01-27 Stanislas Rolland <typo3@sjbr.ca>
* Resolves #64543: Extension does not handle vendorName
* Resolves #64525 (Follow-up): Extbase does not find the templates of backend Font Maker in TYPO3 7.0
* Resolves #64548: Font file not found in Font Maker on PHP 5.5
* Enable in TYPO3 CMS 7
* Update version to 2.1.0
2015-01-26 Stanislas Rolland <typo3@sjbr.ca>
* Resolves #64517: CaptchaValidator::isValid should be protected
* Resolves #64519: Validate annotation: class name TtfFileValidator should not start with backslash
* Resolves #64520: Extract module configuration from static template
* Resolves #64524: SJBR\SrFreecap\Domain\Model\Font raises warning on PHP 5.4+
* Resolves #64525: Extbase does not find the templates of backend Font Maker in TYPO3 7.0
2014-09-22 Stanislas Rolland <typo3@sjbr.ca>
* Resolves #61756: Storing session data prevents correct handling of session data in other extensions (Thanks to Daniel Minder)
* Update version to 2.0.10
2014-08-01 Stanislas Rolland <typo3@sjbr.ca>
* Update version to 2.0.9
2014-07-29 Stanislas Rolland <typo3@sjbr.ca>
* Correction to Spanish localization file
* Resolves #60536: Mallformed Last-Modified header (Thanks to Christian Ludwig)
2014-04-14 Stanislas Rolland <typo3@sjbr.ca>
* Correction to default css styles
2014-04-08 Stanislas Rolland <typo3@sjbr.ca>
* Localization of audio captcha may fail for lack of available TCA of pages_language_overlay
2014-04-02 Stanislas Rolland <typo3@sjbr.ca>
* Resolves #57543: French text in Italian language file
2014-02-22 Stanislas Rolland <typo3@sjbr.ca>
* Cleanup calls to deprecated methods
2014-02-21 Stanislas Rolland <typo3@sjbr.ca>
* Resolves #56045: Add random variable in captcha image src
* Update version to 2.0.8
2014-02-13 Stanislas Rolland <typo3@sjbr.ca>
* Enable in TYPO3 6.2
* Update version to 2.0.7
2014-02-07 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #55777: Object manager exceptions raised in TYPO3 6.2
2013-11-26 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #51900: Danish locallang file wrongly named
* Brazilian locallang file wrongly named
* Update version to 2.0.6
2013-08-08 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #50931: Error in autoload.php
* Update version to 2.0.5
2013-07-10 Stanislas Rolland <typo3@sjbr.ca>
* Follow-up to bug #47810: Audio Captcha not working under IE 8
* Update version to 2.0.4
2013-07-04 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #47810: Audio Captcha not working under IE 8
* Update version to 2.0.3
2013-06-19 Stanislas Rolland <typo3@sjbr.ca>
* Added feature #49272: Add support for mp3 audio captcha rendering
* Fixed bug #49274: Voice file for digit 3 in Portuguese is broken
2013-05-24 Stanislas Rolland <typo3@sjbr.ca>
* Fixed issue #48516: Replace some default fonts for better readability
* Fixed issue #48519: Correct/remove some words from French words list
* Updated manual
* Fixed textual errors in TS static template
* Require 6.0.5-6.1.99
* Update version to 2.0.1
2013-05-23 Stanislas Rolland <typo3@sjbr.ca>
* Fixed issue #48500: Remove superfluous class loader cache entries
* Fixed bug #48501: Audio captcha rendering and wordslist selection should apply language fallback rules
2013-05-07 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #46265: Problem with freecap 2.0.0 and postgreSQL (Thanks to Tilo Baller)
2013-04-22 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #46648: Filenames for *.wav causes potential problems
2013-04-20 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #47406: Cleanup in ext_autoload.php
* Fixed bug #47407: Keep domain object font ISO-8859-2-encoded
* Fixed bug #47408: Incorrect object manager creation
2013-02-14 Stanislas Rolland <typo3@sjbr.ca>
* Remove "site tag" feature
* Updated version to 2.0.0
2013-02-13 Stanislas Rolland <typo3@sjbr.ca>
* Add option to configure the encryption algorithm in Extension Manager
* Disable the audio feature on iOS devices
* Use object manager to create object instances
* Follow-up to use object manager to create object instances
2013-02-12 Stanislas Rolland <typo3@sjbr.ca>
* Cleanup audio file rendering
* Fix audio player in IE7, IE9 and Opera
* Fix audio player in IE8 (but may still be blocked depending on security settings)
2013-02-11 Stanislas Rolland <typo3@sjbr.ca>
* Update the manual
* Namespace the JavaScript functions
2013-02-07 Stanislas Rolland <typo3@sjbr.ca>
* Fix max attempts message
* Delete unused directories
* Fix model html template insert for pi_base plugin
2013-02-06 Stanislas Rolland <typo3@sjbr.ca>
* Add translation view helper with formal/informal salutation option
* Cleanup configuration files
* Add suggested HTML template insert (for pi_base)
* Add captcha image and audio icon view helpers
* Remove alias on SessionStorage class
* Cleanup translation view helper
* Require extension vhs 1.5.0
* Remove requirement for vhs extension
* Fix validation error translation
* Cleanup image generator
2012-12-15 Stanislas Rolland <typo3@sjbr.ca>
* Add captcha validator class
2012-12-13 Stanislas Rolland <typo3@sjbr.ca>
* Migrate backend GD font maker to extbase
2012-11-15 Stanislas Rolland <typo3@sjbr.ca>
* Move language labels to private resources directory
* Migrate captcha image and audio generation to extbase
2012-11-10 Stanislas Rolland <typo3@sjbr.ca>
* Move captcha media to private resources directory
* Move javascript to public resources directory
* Require TYPO3 6.0
2012-11-09 Stanislas Rolland <typo3@sjbr.ca>
* Move original freecap to Contrib directory
2012-11-06 Stanislas Rolland <typo3@sjbr.ca>
* Fix font maker script character encoding
2012-11-05 Stanislas Rolland <typo3@sjbr.ca>
* Migrate localization files to xliff
* Migrate font maker module to the use of typo3/mod.php
* Add ext_autoload
2012-03-18 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #32848: Captcha image expiry headers
* Fixed bug #34729: Problems with encrypt function (Thanks to Carlos Meyer)
* Updated version to 1.5.3
2011-12-15 Stanislas Rolland <typo3@sjbr.ca>
* Follow-up to bug #7066: IE embeded media player display
* Updated version to 1.5.0
* Fixed bug #32604: BE module broken with TYPO3 4.6
* Added feature #32610: Russian documentation (Thanks to Андрей Аксенов)
* Updated version to 1.5.1
* Follow-up to bug #32604: BE module broken with TYPO3 4.6
* Updated version to 1.5.2
2011-12-14 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #7066: IE embeded media player display
* Added feature #32567: Use HTML5 audio tag when available
* Follow-up to feature #32567: Use HTML5 audio tag when available
* Fixed bug #32579: Incorrect length in header of merged wave file
2011-12-13 Stanislas Rolland <typo3@sjbr.ca>
* Follow-up to bug #13428: pi1/captcha.php: PHP Fatal Error when called directly
* Added feature #32488: Require TYPO3 4.5 and PHP 5.3
* Added feature #32524: Remove random number generator seeding
* Added feature #32525: Use TYPO3 encryption key
* Follow-up to bug #26113: On enter, always the same image is loaded
2011-12-12 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #32477: Reference to tslib_pibase constructor in tx_srfreecap_pi2
* Fixed bug #13428: pi1/captcha.php: PHP Fatal Error when called directly
* Fixed bug #13669: ereg/ereg_replace deprecated since PHP 5.3
* Fixed bug #27000: error_reporting in pi1/class.tx_srfreecap_pi1.php
* Fixed bug #29276: function.mt-rand: max(1) is smaller than min(5)
* Fixed bug #26113: On enter, always the same image is loaded
* Fixed bug #32487: Remove reference to mediunDoc
2011-02-15 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #13072: Captcha not showing up when using MP from other domain (Thanks to Ernesto Baschny)
* Updated version to 1.4.1
2011-02-10 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #12921: Audio captcha should be accessible by TAB key (Thanks to Elliot Sawyer)
* Require PHP 5.1
* Updated manual
* Fixed bug #12938: "www.mydomain.org" is not RFC 2606 compilant
* Partially fixed bug #5628: Political (In-)Correctness of the german word list
* Updated version to 1.4.0
2011-02-01 Stanislas Rolland <typo3@sjbr.ca>
* Added feature #12714: Add TS constants to control darkness of the captcha text. Thanks to Julia Pardanaud.
* Added feature #12716: New default values of TS constants to increase CAPTCHA readability. Thanks to Julia Pardanaud.
2010-12-21 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #10223: BE module not shown anymore with TYPO3 4.4
* Fixed bug #8528: Typo in Dutch localization of label in pi2/locallang.xml
* Fixed bug #7987: maxWordLength problem
2010-04-16 Stanislas Rolland <typo3@sjbr.ca>
* Require TYPO3 4.3
* Manual updated.
* Updated ext_emconf from TER version 1.2.3
2010-04-14 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #7259: Use of deprecated function t3lib_div::makeInstanceClassName()
2009-10-20 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug: Mishandling of session data. Thanks to Wolo Wolski.
* Updated ext_emconf from TER version 1.2.2
* Updated manual.
2009-08-31 Stanislas Rolland <typo3@sjbr.ca>
* Added feature: new TS constant accessibleOutputImage and textual version.
* Manual updated.
* Updated ext_emconf from TER version 1.2.0
2009-08-29 Stanislas Rolland <typo3@sjbr.ca>
* Follow-up to #3056: Audio Captcha is not rendered in IE8
* Added feature: Two new TS constants: textHorizontalPosition and textVerticalPosition. Thanks to Julia Pardanaud.
* Added Turkish word list. Thanks to Zeynep Nurten Cinarcik.
2009-06-03 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #3056: Audio Captcha is not rendered in IE8
2009-06-01 Stanislas Rolland <typo3@sjbr.ca>
* Added feature #3526: Use eID API for graphical and audio captcha generation.
* Follow-up to #3526 for TYPO3 < 4.3: Use eID API for graphical and audio captcha generation.
* Fixed bug #3530: Morphed image blocks background type not working.
* Follow-up to feature #3526: Cleanup parameters of new captcha image
2009-05-29 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #3472: No image rendering in T3 V4.3 alpha3.
2009-05-07 Stanislas Rolland <typo3@sjbr.ca>
* Added feature #3244: User can set the width of the generated captcha-image. Thanks to Simon Schick.
2009-04-15 Stanislas Rolland <typo3@sjbr.ca>
* Added Russian translation. Thanks to Андрей Аксенов.
2009-04-07 Stanislas Rolland <typo3@sjbr.ca>
* Fixed bug #3052: "onclick" should be used instead of "onClick". Thanks to Gerald Schwarz.
* Fixed bug: Random number generator should be seeded.
2009-04-01 Stanislas Rolland <typo3@sjbr.ca>
* Removed some words from the default word list.
* Added partial Russian translation. Thanks to Esben Fest.
* Updated manual.
* Updated ext_emconf from TER version 1.1.2
2009-01-24 Stanislas Rolland <typo3@sjbr.ca>
* Updated manual.
* Updated ext_emconf from TER version 1.1.1
2009-01-23 Stanislas Rolland <typo3@sjbr.ca>
* Initial upload to TYPO3 Forge
* Cleanup: Use UNIX line separators in all files
* Fix for current TYPO3 trunk (4.3-dev) caching
* Update version number to 1.1.1
* Default to local installation
* Fixed bug #10132: sr_freecap seems to work only with default language. Thanks to Dan Grünzig.
* Follow-up to issue #10132: Adding comments
* Added Polish translation. Thanks to Daniel Ciunkiewicz.
* Correction to the manual. Thanks to Niklas Kanthak.
* Remove superfluous spaces in rendered html. Thanks to Niklas Kanthak.
* ###SR_FREECAP_ACCESSIBLE### marker should produce inline html. Audio object wrapper changed from div to span. Thanks to Niklas Kanthak.
* Updated manual: random background type does not exist anymore with freecap 1.4.
* Added missing images for Morphed image blocks option of backgroundType.
* Updated damaged images.

View File

@@ -0,0 +1,63 @@
<?php
namespace SJBR\SrFreecap\Configuration;
/*
* Copyright notice
*
* (c) 2013-2020 Stanislas Rolland <typo32020(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* Class providing configuration help for extension SrFreecap
*/
class ConfigurationHelper
{
/**
* Renders a select element that allows to choose the encryption algoritm to be used by the extension
*
* @param array $params: Field information to be rendered
* @param AbstractTagBasedViewHelper $pObj: The calling parent object.
* @return string The HTML select field
*/
public function buildEncryptionAlgorithmSelector(array $params, AbstractTagBasedViewHelper $pObj)
{
if (in_array('openssl', get_loaded_extensions())) {
$encryptionAlgorithms = openssl_get_cipher_methods(true);
if (!empty($encryptionAlgorithms)) {
$field = '<br /><select id="' . $params['propertyName'] . '" name="' . $params['fieldName'] . '" >' . LF;
foreach ($encryptionAlgorithms as $encryptionAlgorithm) {
$selected = $params['fieldValue'] == $encryptionAlgorithm ? 'selected="selected"' : '';
$field .= '<option name="' . $encryptionAlgorithm . '" value="' . $encryptionAlgorithm . '" ' . $selected . '>' . $encryptionAlgorithm . '</option>' . LF;
}
$field .= '</select><br /><br />' . LF;
} else {
$field = '<br />Available encryption algorithms could not be found. Algorithm AES-256-CBC will be used.<br />';
}
} else {
$field = '<br />PHP openssl extension is not available.<br />';
}
return $field;
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace SJBR\SrFreecap\Controller;
/*
* Copyright notice
*
* (c) 2012-2021 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\Domain\Repository\WordRepository;
use SJBR\SrFreecap\View\AudioPlayer\PlayMp3;
use SJBR\SrFreecap\View\AudioPlayer\PlayWav;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
/**
* Renders a wav audio version of the CAPTCHA
*/
class AudioPlayerController extends ActionController
{
/**
* @var string Name of the extension this controller belongs to
*/
protected $extensionName = 'SrFreecap';
/**
* @var WordRepository
*/
protected $wordRepository;
/**
* Dependency injection of the Word Repository
*
* @param WordRepository $wordRepository
*/
public function injectWordRepository(WordRepository $wordRepository)
{
$this->wordRepository = $wordRepository;
}
/**
* Play the audio catcha
*
* @return string Audio content to be sent to the client
*/
public function playAction()
{
$word = $this->wordRepository->getWord();
$format = $this->request->getFormat();
if ($format === 'mp3') {
$this->view = GeneralUtility::makeInstance(PlayMp3::class);
} else if ($format === 'wav') {
$this->view = GeneralUtility::makeInstance(PlayWav::class);
} else {
throw new \Exception('Unknow audio format ' . $format);
}
$this->view->assign('word', $word);
$this->view->render();
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace SJBR\SrFreecap\Controller;
/***************************************************************
* Copyright notice
*
* (c) 2012-2021 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use SJBR\SrFreecap\Domain\Model\Font;
use SJBR\SrFreecap\Domain\Repository\FontRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
/**
* Font Maker controller
*/
class FontMakerController extends ActionController
{
/**
* @var string Name of the extension this controller belongs to
*/
protected $extensionName = 'SrFreecap';
/**
* Display the font maker form
*
* @param Font $font
* @return string An HTML form for creating a new font
*/
public function newAction(Font $font = null)
{
if (!is_object($font)) {
$font = new Font();
}
$this->view->assign('font', $font);
}
/**
* Create the font file and display the result
*
* @param Font $font
* @return string HTML presenting the new font that was created
*/
public function createAction(Font $font)
{
// Create the font data
$font->createGdFontFile();
// Store the GD font file
$fontRepository = GeneralUtility::makeInstance(FontRepository::class);
$fontRepository->writeFontFile($font);
$this->view->assign('font', $font);
}
}

View File

@@ -0,0 +1,234 @@
<?php
namespace SJBR\SrFreecap\Controller;
/*
* Copyright notice
*
* (c) 2012-2022 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\Domain\Repository\WordRepository;
use SJBR\SrFreecap\View\ImageGenerator\ShowPng;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* Renders the CAPTCHA image
*/
class ImageGeneratorController extends ActionController
{
/**
* @var string Name of the extension this controller belongs to
*/
protected $extensionName = 'SrFreecap';
/**
* @var string Name of the extension this controller belongs to
*/
protected $extensionKey = 'sr_freecap';
/**
* @var WordRepository
*/
protected $wordRepository;
/**
* Dependency injection of the Word Repository
*
* @param WordRepository $wordRepository
*/
public function injectWordRepository(WordRepository $wordRepository)
{
$this->wordRepository = $wordRepository;
}
/**
* Show the CAPTCHA image
*/
public function showAction()
{
// Get session data
$word = $this->wordRepository->getWord();
// Which type of hash to use
// Store in session so can validate in form processor
$word->setHashFunction('md5');
$this->view = GeneralUtility::makeInstance(ShowPng::class);
$this->view->assign('word', $word);
// Adjust settings
$this->processSettings();
$this->view->assign('settings', $this->settings);
// Render the captcha image
$this->view->render();
// Store the session data
$this->wordRepository->setWord($word);
}
/**
* Reviews and adjusts plugin settings
*/
protected function processSettings()
{
// Image type:
// possible values: "jpg", "png", "gif"
// jpg doesn't support transparency (transparent bg option ends up white)
// png isn't supported by old browsers (see http://www.libpng.org/pub/png/pngstatus.html)
// gif may not be supported by your GD Lib.
$this->settings['imageFormat'] = (isset($this->settings['imageFormat']) && $this->settings['imageFormat']) ? $this->settings['imageFormat'] : 'png';
// true = generate pseudo-random string, false = use dictionary
// dictionary is easier to recognise
// - both for humans and computers, so use random string if you're paranoid.
$this->settings['useWordsList'] = (isset($this->settings['useWordsList']) && $this->settings['useWordsList']) ? true : false;
// if your server is NOT set up to deny web access to files beginning ".ht"
// then you should ensure the dictionary file is kept outside the web directory
// eg: if www.foo.com/index.html points to c:\website\www\index.html
// then the dictionary should be placed in c:\website\dict.txt
// test your server's config by trying to access the dictionary through a web browser
// you should NOT be able to view the contents.
// can leave this blank if not using dictionary
$this->settings['wordsListLocation'] = \SJBR\SrFreecap\Utility\LocalizationUtility::getWordsListLocation($this->settings['defaultWordsList'] ?? '');
// Used for non-dictionary word generation and to calculate image width
$this->settings['maxWordLength'] = (isset($this->settings['maxWordLength']) && $this->settings['maxWordLength']) ? $this->settings['maxWordLength'] : 6;
// Maximum times a user can refresh the image
// on a 6500 word dictionary, I think 15-50 is enough to not annoy users and make BF unfeasble.
// further notes re: BF attacks in "avoid brute force attacks" section, below
// on the other hand, those attempting OCR will find the ability to request new images
// very useful; if they can't crack one, just grab an easier target...
// for the ultra-paranoid, setting it to <5 will still work for most users
$this->settings['maxAttempts'] = (isset($this->settings['maxAttempts']) && $this->settings['maxAttempts']) ? $this->settings['maxAttempts'] : 50;
// List of fonts to use
// font size should be around 35 pixels wide for each character.
// you can use my GD fontmaker script at www.puremango.co.uk to create your own fonts
// There are other programs to can create GD fonts, but my script allows a greater
// degree of control over exactly how wide each character is, and is therefore
// recommended for 'special' uses. For normal use of GD fonts,
// the GDFontGenerator @ http://www.philiplb.de is excellent for convering ttf to GD
// the fonts included with freeCap *only* include lowercase alphabetic characters
// so are not suitable for most other uses
// to increase security, you really should add other fonts
if ($this->settings['generateNumbers'] ?? false) {
$this->settings['fontLocations'] = ['EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Fonts/anonymous.gdf'];
} else {
$this->settings['fontLocations'] = [
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Fonts/freecap_font1.gdf',
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Fonts/freecap_font2.gdf',
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Fonts/freecap_font3.gdf',
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Fonts/freecap_font4.gdf',
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Fonts/freecap_font5.gdf'
];
}
if ($this->settings['fontFiles'] ?? false) {
$this->settings['fontLocations'] = GeneralUtility::trimExplode(',', $this->settings['fontFiles'], 1);
}
for ($i = 0; $i < sizeof($this->settings['fontLocations']); $i++) {
if (substr($this->settings['fontLocations'][$i],0,4) == 'EXT:') {
$this->settings['fontLocations'][$i] = GeneralUtility::getFileAbsFileName($this->settings['fontLocations'][$i]);
} else {
$this->settings['fontLocations'][$i] = Environment::getPublicPath() . '/uploads/' . ExtensionManagementUtility::getCN($this->extensionKey) . '/' . $this->settings['fontLocations'][$i];
}
}
// Text color
// 0 = one random color for all letters
// 1 = different random color for each letter
if ($this->settings['textColor'] ?? false) {
$this->settings['textColor'] = 1;
} else {
$this->settings['textColor'] = 0;
}
// Text position
$this->settings['textPosition'] = [];
$this->settings['textPosition']['horizontal'] = isset($this->settings['textHorizontalPosition']) ? (int)$this->settings['textHorizontalPosition'] : 32;
$this->settings['textPosition']['vertical'] = isset($this->settings['textVerticalPosition']) ? (int)$this->settings['textVerticalPosition'] : 15;
// Text morphing factor
$this->settings['morphFactor'] = (isset($this->settings['morphFactor']) && $this->settings['morphFactor']) ? (int)$this->settings['morphFactor'] : 0;
// Limits for text color
$this->settings['colorMaximum'] = [];
if (isset($this->settings['colorMaximumDarkness'])) {
$this->settings['colorMaximum']['darkness'] = (int)$this->settings['colorMaximumDarkness'];
}
if (isset($this->settings['colorMaximumLightness'])) {
$this->settings['colorMaximum']['lightness'] = (int)$this->settings['colorMaximumLightness'];
}
// Background
// Many thanks to http://ocr-research.org.ua and http://sam.zoy.org/pwntcha/ for testing
// for jpgs, 'transparent' is white
if (!isset($this->settings['backgroundType']) || !in_array($this->settings['backgroundType'], ['Transparent', 'White with grid', 'White with squiggles', 'Morphed image blocks'])) {
$this->settings['backgroundType'] = 'White with grid';
}
// Should we blur the background? (looks nicer, makes text easier to read, takes longer)
$this->settings['backgroundBlur'] = ((isset($this->settings['backgroundBlur']) && $this->settings['backgroundBlur']) || !isset($this->settings['backgroundBlur'])) ? true : false;
// For background type 'Morphed image blocks', which images should we use?
// If you add your own, make sure they're fairly 'busy' images (ie a lot of shapes in them)
$this->settings['backgroundImages'] = [
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Images/freecap_im1.jpg',
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Images/freecap_im2.jpg',
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Images/freecap_im3.jpg',
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Images/freecap_im4.jpg',
'EXT:' . $this->extensionKey . '/Resources/Private/Captcha/Images/freecap_im5.jpg'
];
// For non-transparent backgrounds only:
// if 0, merges CAPTCHA with background
// if 1, write CAPTCHA over background
$this->settings['mergeWithBackground'] = (isset($this->settings['mergeWithBackground']) && $this->settings['mergeWithBackground']) ? 0 : 1;
// Should we morph the background? (recommend yes, but takes a little longer to compute)
$this->settings['backgroundMorph'] = (isset($this->settings['backgroundMorph']) && $this->settings['backgroundMorph']) ? true : false;
// Read each font and get font character widths
$this->settings['fontWidths'] = [];
for ($i=0 ; $i < sizeof($this->settings['fontLocations']); $i++) {
$handle = fopen($this->settings['fontLocations'][$i],"r");
// Read header of GD font, up to char width
$c_wid = fread($handle,12);
$this->settings['fontWidths'][$i] = ord($c_wid[8])+ord($c_wid[9])+ord($c_wid[10])+ord($c_wid[11]);
fclose($handle);
}
// Modify image width depending on maximum possible length of word
// you shouldn't need to use words > 6 chars in length really.
$this->settings['imageWidth'] = ($this->settings['maxWordLength'] * (array_sum($this->settings['fontWidths'])/sizeof($this->settings['fontWidths']))) + (isset($this->settings['imageAdditionalWidth']) ? (int)$this->settings['imageAdditionalWidth'] : 40);
$this->settings['imageHeight'] = (isset($this->settings['imageHeight']) && $this->settings['imageHeight']) ? (int)$this->settings['imageHeight'] : 90;
// Try to avoid the 'free p*rn' method of CAPTCHA circumvention
// see www.wikipedia.com/captcha for more info
// "To avoid spam, please do NOT enter the text if this site is not example.org";
// or more simply:
// "for use only on example.org";
// reword or add lines as you please
$this->settings['siteTag'] = (isset($this->settings['siteTag']) && $this->settings['siteTag']) ? explode('|', LocalizationUtility::translate('site_tag', $this->extensionName, [(isset($this->settings['siteTagDomain']) && $this->settings['siteTagDomain']) ? $this->settings['siteTagDomain'] : 'example.org'])) : [];
// where to write the above:
// 0=top
// 1=bottom
// 2=both
$this->settings['siteTagPosition'] = isset($this->settings['siteTagPosition']) ? (int)$this->settings['siteTagPosition'] : 1;
}
}

View File

@@ -0,0 +1,230 @@
<?php
namespace SJBR\SrFreecap\Domain\Model;
/*
* Copyright notice
*
* (c) 2012-2022 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\Utility\FontMakingUtility;
use SJBR\SrFreecap\Validation\Validator\TtfFileValidator;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Extbase\Annotation\Validate;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/**
* Font object
*
* This file must be iso-8859-2-encoded!
*
*/
class Font extends AbstractEntity
{
/**
* @var int
*/
protected $charactersIncludedInFont;
/**
* @var int
* @Validate("NumberRange", options={"minimum": 5, "maximum": 255})
*/
protected $characterWidth;
/**
* @var int
* @Validate("NumberRange", options={"minimum": 5, "maximum": 255})
*/
protected $characterHeight;
/**
* @var int
**/
protected $endianness;
/**
* @var string
* @Validate("NotEmpty")
* @Validate("StringLength", options={"minimum": 1, "maximum": 255})
* @Validate("\SJBR\SrFreecap\Validation\Validator\TtfFileValidator")
**/
protected $ttfFontFileName = '';
/**
* @var string
**/
protected $gdFontFilePrefix = 'font';
/**
* @var string
**/
protected $pngImageFileName = '';
/**
* @var string
**/
protected $gdFontData = '';
/**
* @var string
**/
protected $gdFontFileName = '';
public function __construct(
$charactersIncludedInFont = 0,
$characterWidth = 34,
$characterHeight = 50,
$endianness = 0,
$ttfFontFileName = '',
$gdFontFilePrefix = '',
$pngImageFileName = '',
$gdFontData = '',
$gdFontFileName = ''
) {
$this->setCharactersIncludedInFont($charactersIncludedInFont);
$this->setCharacterWidth($characterWidth);
$this->setCharacterHeight($characterHeight);
$this->setEndianness($endianness);
$this->setTtfFontFileName($ttfFontFileName);
$this->setGdFontFilePrefix($gdFontFilePrefix);
$this->setPngImageFileName($pngImageFileName);
$this->setGdFontData($gdFontData);
$this->setGdFontFileName($gdFontFileName);
}
public function setCharactersIncludedInFont($charactersIncludedInFont) {
$this->charactersIncludedInFont = (int)$charactersIncludedInFont;
}
public function getCharactersIncludedInFont() {
return $this->charactersIncludedInFont;
}
public function setCharacterWidth($characterWidth) {
$this->characterWidth = (int)$characterWidth;
}
public function getCharacterWidth() {
return $this->characterWidth;
}
public function setCharacterHeight($characterHeight) {
$this->characterHeight = (int)$characterHeight;
}
public function getCharacterHeight() {
return $this->characterHeight;
}
public function setEndianness($endianness) {
$this->endianness = (int)$endianness;
}
public function getEndianness() {
return $this->endianness;
}
public function setTtfFontFileName($ttfFontFileName) {
$this->ttfFontFileName = (string)$ttfFontFileName;
}
public function getTtfFontFileName() {
return $this->ttfFontFileName;
}
public function setGdFontFilePrefix($gdFontFilePrefix = 'font') {
$this->gdFontFilePrefix = (string)$gdFontFilePrefix;
}
public function getGdFontFilePrefix() {
return $this->gdFontFilePrefix;
}
public function setPngImageFileName($pngImageFileName) {
$this->pngImageFileName = (string)$pngImageFileName;
}
public function getPngImageFileName() {
return $this->pngImageFileName;
}
public function setGdFontData($gdFontData) {
$this->gdFontData = (string)$gdFontData;
}
public function getGdFontdata() {
return $this->gdFontData;
}
public function setGdFontFileName($gdFontFileName) {
$this->gdFontFileName = (string)$gdFontFileName;
}
public function getGdFontFileName() {
return $this->gdFontFileName;
}
/**
* Creates teh GD font file
*/
public function createGdFontFile()
{
switch ($this->charactersIncludedInFont) {
case 1:
$characters = 'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z';
$startCharacter = 'a';
break;
case 2:
$characters = 'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o'
.',p,q,r,s,t,u,v,w,x,y,z,-,-,-,-,-'
.',-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-'
.',-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-'
.',-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-'
.',-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-'
.',-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-'
.',-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,ß'
.',à,á,â,ă,ä,Í,ć,ç,č,é,ę,ë,ě,Í,î,ď'
.',đ,ń,ň,ó,ô,ő,ö,-,ř,ů,ú,ű,ü,ý,ţ,-';
$startCharacter = 'a';
break;
case 0:
default:
$characters = '0,1,2,3,4,5,6,7,8,9';
$startCharacter = '0';
break;
}
$numberOfCharacters = count(explode(',', $characters));
$this->setPngImageFileName(FontMakingUtility::makeFontImage($characters, $this->ttfFontFileName, $this->characterWidth, $this->characterHeight));
if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png'] ?? false) {
$image = @ImageCreateFromPNG(Environment::getPublicPath() . '/' . $this->pngImageFileName);
} else {
$image = @ImageCreateFromGIF(Environment::getPublicPath() . '/' . $this->pngImageFileName);
}
if ($image !== false) {
$this->setGdFontdata(FontMakingUtility::makeFont($image, $numberOfCharacters, $startCharacter, $this->characterWidth, $this->characterHeight, $this->endianness));
ImageDestroy($image);
}
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace SJBR\SrFreecap\Domain\Model;
/***************************************************************
* Copyright notice
*
* (c) 2012-2020 Stanislas Rolland <typo32020(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/**
* Word object
*/
class Word extends AbstractEntity
{
/**
* @var string
*/
protected $wordHash;
/**
* @var string
*/
protected $hashFunction;
/**
* @var array
*/
protected $wordCypher;
/**
* @var int
*/
protected $attempts;
public function __construct(
$wordHash = '',
$hashFunction = 'md5',
$wordCypher = array(),
$attempts = 0
)
{
$this->setWordHash($wordHash);
$this->setHashFunction($hashFunction);
$this->setWordCypher($wordCypher);
$this->setAttempts($attempts);
}
public function setWordHash($wordHash)
{
$this->wordHash = (string)$wordHash;
}
public function getWordHash()
{
return $this->wordHash;
}
public function setHashFunction($hashFunction)
{
$this->hashFunction = (string)$hashFunction;
}
public function getHashFunction()
{
return $this->hashFunction;
}
public function setWordCypher($wordCypher)
{
$this->wordCypher = (array)$wordCypher;
}
public function getWordCypher()
{
return $this->wordCypher;
}
public function setAttempts($attempts)
{
$this->attempts = (int)$attempts;
}
public function getAttempts()
{
return $this->attempts;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace SJBR\SrFreecap\Domain\Repository;
/*
* Copyright notice
*
* (c) 2012-2018 Stanislas Rolland <typo3@sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\Domain\Model\Font;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* Font repository
*/
class FontRepository extends Repository
{
/**
* @var string Name of the extension this controller belongs to
*/
protected $extensionKey = 'sr_freecap';
/**
* Writes the GD font file
*
* @param Font the object to be stored
* @return \SJBR\SrFreecap\Domain\Repository\FontRepository $this
*/
public function writeFontFile(Font $font)
{
$directory = Environment::getPublicPath() . '/' . 'uploads/' . ExtensionManagementUtility::getCN($this->extensionKey) . '/';
GeneralUtility::mkdir_deep($directory);
$fileName = $directory . $font->getGdFontFilePrefix() . '_' . md5($font->getGdFontData()) . '.gdf';
if (GeneralUtility::writeFile($fileName, $font->getGdFontData())) {
$font->setGdFontFileName($fileName);
}
return $this;
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace SJBR\SrFreecap\Domain\Repository;
/*
* Copyright notice
*
* (c) 2012-2021 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\Domain\Model\Word;
use SJBR\SrFreecap\Domain\Session\SessionStorage;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* Word repository in session storage
*/
class WordRepository extends Repository
{
/**
* The session sorage handler
* @var SessionStorage
*/
protected $sessionStorage;
/**
* Constructor
*/
public function __construct(SessionStorage $sessionStorage)
{
// Get an instance of the session storage handler
$this->sessionStorage = $sessionStorage;
}
/**
* Returns the object stored in the user's session
*
* @return Word the stored object
*/
public function getWord()
{
$word = $this->sessionStorage->restoreFromSession();
// If no Word object is found in session data, initialize a new one
if (!is_object($word)) {
$word = new Word();
}
return $word;
}
/**
* Writes the object into the user's session
*
* @param Word the object to be stored
* @return WordRepository
*/
public function setWord(Word $object)
{
$this->sessionStorage->writeToSession($object);
return $this;
}
/**
* Cleans up the session: removes the stored object from the user's session
*
* @return WordRepository
*/
public function cleanUpWord()
{
$this->sessionStorage->cleanUpSession();
return $this;
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace SJBR\SrFreecap\Domain\Session;
/*
* Copyright notice
*
* (c) 2012-2021 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use TYPO3\CMS\Core\Session\UserSession;
use TYPO3\CMS\Core\Session\UserSessionManager;
use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
/**
* Session storage
*/
class SessionStorage implements SingletonInterface
{
const SESSIONNAMESPACE = 'tx_srfreecap';
/**
* Returns the object stored in the user's PHP session
*
* @return Object the stored object
*/
public function restoreFromSession()
{
$sessionData = $this->getUser()->getSessionData(self::SESSIONNAMESPACE);
if ($sessionData === null) {
return null;
}
return unserialize($sessionData);
}
/**
* Writes an object into the PHP session
*
* @param $object any serializable object to store into the session
* @return SessionStorage
*/
public function writeToSession($object)
{
$sessionData = serialize($object);
$this->getUser()->setAndSaveSessionData(self::SESSIONNAMESPACE, $sessionData);
return $this;
}
/**
* Cleans up the session: removes the stored object from the PHP session
*
* @return SessionStorage
*/
public function cleanUpSession()
{
$this->getUser()->setAndSaveSessionData(self::SESSIONNAMESPACE, null);
return $this;
}
/**
* Gets a frontend user session
*
* @return User The current frontend user object
*/
protected function getUser() : FrontendUserAuthentication
{
if (!isset($GLOBALS['TSFE']) || !$GLOBALS['TSFE']->fe_user) {
throw new SessionNotFoundException('No frontend user found in session!');
}
return $GLOBALS['TSFE']->fe_user;
}
}

View File

@@ -0,0 +1,275 @@
<?php
namespace SJBR\SrFreecap\Http;
/*
* Copyright notice
*
* 2010 Daniel Lienert <daniel@lienert.cc>, Michael Knoll <mimi@kaktusteam.de>
* 2012-2022 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Error\Http\BadRequestException;
use TYPO3\CMS\Core\Http\NullResponse;
use TYPO3\CMS\Core\Localization\Locales;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\RootlineUtility;
use TYPO3\CMS\Extbase\Core\Bootstrap;
use TYPO3\CMS\Extbase\Mvc\Dispatcher;
use TYPO3\CMS\Extbase\Mvc\Request;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Dispatch the eid request
*/
class EidDispatcher
{
/**
* Array of all request Arguments
*
* @var array
*/
protected $requestArguments = [];
/**
* @var string
*/
protected $vendorName = 'SJBR';
/**
* @var string
*/
protected $extensionName = 'SrFreecap';
/**
* @var string
*/
protected $pluginName;
/**
* @var string
*/
protected $controllerName;
/**
* @var string
*/
protected $actionName;
/**
* @var string
*/
protected $formatName;
/**
* @var array
*/
protected $arguments = [];
/**
* Initializes and dispatches actions
* Call this function if you want to use this dispatcher "standalone"
* @param ServerRequestInterface $request
* @return Response
*/
public function initAndDispatch($request)
{
return $this->initTypoScriptConfiguration($request)
->initLanguage($request)
->initCallArguments()
->dispatch();
}
/**
* Builds an extbase context and returns the response
*
* @return ResponseInterface
*/
protected function dispatch()
{
$bootstrap = GeneralUtility::makeInstance(Bootstrap::class);
$configuration['vendorName'] = $this->vendorName;
$configuration['extensionName'] = $this->extensionName;
$configuration['pluginName'] = $this->pluginName;
$bootstrap->initialize($configuration);
$request = $this->buildRequest();
try {
$response = GeneralUtility::makeInstance(Dispatcher::class)->dispatch($request);
} catch (\Exception $e) {
throw new BadRequestException('An argument is missing or invalid', 1394587024);
}
return $response;
}
/**
* Get the TypoScript configuration
*
* @param ServerRequestInterface $request
* @return EidDispatcher
*/
protected function initTypoScriptConfiguration($request)
{
$controller = $request->getAttribute('frontend.controller');
$controller->type = 0;
$context = $controller->getContext();
$controller->rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $controller->id, $controller->MP, $context)->get();
$controller->getConfigArray();
return $this;
}
/**
* Set language and locale
*
* @param ServerRequestInterface $request
* @return EidDispatcher
*/
protected function initLanguage($request)
{
$controller = $request->getAttribute('frontend.controller');
$siteLanguage = $controller->getLanguage();
$locales = GeneralUtility::makeInstance(Locales::class);
$locales->setSystemLocaleFromSiteLanguage($siteLanguage);
return $this;
}
/**
* Build a request object
*
* @return Request $request
*/
protected function buildRequest()
{
$controllerClassName = $this->vendorName . '\\' . $this->extensionName . '\\' . 'Controller' . '\\' . $this->pluginName . 'Controller';
$request = GeneralUtility::makeInstance(Request::class, $controllerClassName);
$request->setControllerObjectName($controllerClassName);
$request->setPluginName($this->pluginName);
$request->setControllerActionName($this->actionName);
$request->setFormat($this->formatName);
$request->setArguments($this->arguments);
return $request;
}
/**
* Prepare the call arguments
*
* @return EidDispatcher
*/
public function initCallArguments() {
$request = GeneralUtility::_GP('request');
if ($request) {
$this->setRequestArgumentsFromJSON($request);
} else {
$this->setRequestArgumentsFromGetPost();
}
return $this->setPluginName($this->requestArguments['pluginName'])
->setControllerName()
->setActionName($this->requestArguments['actionName'])
->setFormatName($this->requestArguments['formatName'])
->setArguments($this->requestArguments['arguments'] ?? []);
}
/**
* Set the request array from JSON
*
* @param string $request
*/
protected function setRequestArgumentsFromJSON($request)
{
$requestArray = json_decode($request, true);
if (is_array($requestArray)) {
ArrayUtility::mergeRecursiveWithOverrule($this->requestArguments, $requestArray);
}
}
/**
* Set the request array from the getPost array
*/
protected function setRequestArgumentsFromGetPost()
{
$validArguments = ['pluginName', 'actionName', 'formatName', 'arguments'];
foreach ($validArguments as $argument) {
if (GeneralUtility::_GP($argument) ?? false) {
$this->requestArguments[$argument] = GeneralUtility::_GP($argument);
} else if (GeneralUtility::_GP('amp;' . $argument) ?? false) {
// Something went wrong...
$this->requestArguments[$argument] = GeneralUtility::_GP('amp;' . $argument);
} else if ($argument !== 'arguments') {
throw new BadRequestException('An argument is missing', 1394587023);
}
}
}
/**
* @param string $pluginName
* @return EidDispatcher
*/
protected function setPluginName($pluginName = 'ImageGenerator')
{
$this->pluginName = htmlspecialchars((string)$pluginName);
return $this;
}
/**
* @return EidDispatcher
*/
protected function setControllerName()
{
$this->controllerName = $this->pluginName;
return $this;
}
/**
* @param string $actionName
* @return EidDispatcher
*/
protected function setActionName($actionName = 'show')
{
$this->actionName = htmlspecialchars((string)$actionName);
return $this;
}
/**
* @param string $formatName
* @return EidDispatcher
*/
protected function setFormatName($formatName = 'txt')
{
$this->formatName = htmlspecialchars((string)$formatName);
return $this;
}
/**
* @param array $arguments
* @return EidDispatcher
*/
protected function setArguments($arguments)
{
if (!is_array($arguments)) {
$this->arguments = [];
} else {
$this->arguments = $arguments;
}
return $this;
}
}

View File

@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace SJBR\SrFreecap\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Http\DispatcherInterface;
use TYPO3\CMS\Core\Http\NullResponse;
use TYPO3\CMS\Core\Http\Response;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Lightweight alternative to regular frontend requests; used when $_GET[eID] is set.
* In the future, logic from the EidUtility will be moved to this class, however in most cases
* a custom PSR-15 middleware will be better suited for whatever job the eID functionality does currently.
*
* @internal
*/
class EidHandler implements MiddlewareInterface
{
/**
* @var DispatcherInterface
*/
protected $dispatcher;
/**
* @param DispatcherInterface $dispatcher
*/
public function __construct(DispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* Dispatches the request to the corresponding eID class or eID script
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$eID = $request->getParsedBody()['eIDSR'] ?? $request->getQueryParams()['eIDSR'] ?? null;
if ($eID === null) {
return $handler->handle($request);
}
// Remove any output produced until now
ob_clean();
/** @var Response $response */
$response = new Response();
if (empty($eID) || !isset($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sr_freecap']['eIDSR_include'][$eID])) {
$response = $response->withStatus(404, 'eIDSR not registered');
} else {
$configuration = $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sr_freecap']['eIDSR_include'][$eID];
$response = new NullResponse();
// Simple check to make sure that it's not an absolute file (to use the fallback)
if (strpos($configuration, '::') !== false || is_callable($configuration)) {
$frontendUser = $request->getAttribute('frontend.user');
$request = $request->withAttribute('target', $configuration);
$response = $this->dispatcher->dispatch($request);
}
}
return $response;
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace SJBR\SrFreecap;
/*
* Copyright notice
*
* (c) 2005-2021 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\Validation\Validator\CaptchaValidator;
use SJBR\SrFreecap\ViewHelpers\AudioViewHelper;
use SJBR\SrFreecap\ViewHelpers\ImageViewHelper;
use SJBR\SrFreecap\ViewHelpers\TranslateViewHelper;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
class PiBaseApi
{
/**
* @var string The extension key
*/
public $extKey = 'sr_freecap';
/**
* This function generates an array of markers used to render the captcha element
*
* @return array marker array containing the captcha markers to be sustituted in the html template
*/
public function makeCaptcha()
{
// Get the configuration manager
$configurationManager = GeneralUtility::makeInstance(ConfigurationManagerInterface::class);
// Get translation view helper
$translator = GeneralUtility::makeInstance(TranslateViewHelper::class);
$translator->injectConfigurationManager($configurationManager);
$markerArray = [];
$markerArray['###'. strtoupper($this->extKey) . '_NOTICE###'] = $translator->render('notice') . ' ' . $translator->render('explain');
// Get the captcha image view helper
$imageViewHelper = GeneralUtility::makeInstance(ImageViewHelper::class);
$imageViewHelper->injectConfigurationManager($configurationManager);
$markerArray['###'. strtoupper($this->extKey) . '_IMAGE###'] = $imageViewHelper->render('pi1');
$markerArray['###'. strtoupper($this->extKey) . '_CANT_READ###'] = '';
// Get the audio icon view helper
$audioViewHelper = GeneralUtility::makeInstance(AudioViewHelper::class);
$audioViewHelper->injectConfigurationManager($configurationManager);
$markerArray['###'. strtoupper($this->extKey) . '_ACCESSIBLE###'] = $audioViewHelper->render('pi1');
return $markerArray;
}
/**
* Check the word that was entered against the hashed value
*
* @param string $word: hte word that was entered
* @return bool true, if the word entered matches the hashes value
*/
public function checkWord($word)
{
// Get the validator
$validator = GeneralUtility::makeInstance(CaptchaValidator::class);
// Check word
return !$validator->validate($word)->hasErrors();
}
}
class_alias('SJBR\\SrFreecap\\PiBaseApi', 'tx_srfreecap_pi2');

View File

@@ -0,0 +1,128 @@
<?php
namespace SJBR\SrFreecap\Utility;
/*
* Copyright notice
*
* (c) 2012-2020 Stanislas Rolland <typo32020(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
/**
* Utility dealing with audio content
*/
class AudioContentUtility
{
/**
* Joins multiple audio files
*
* @param array $files: the array of audio files
* @param string $format: the audio format
* @return string the contents of joined audio file
*/
public static function joinAudioFiles($files, $format = 'wav') {
switch ($format) {
case 'mp3':
return self::joinMp3Files($files);
break;
case 'wav':
default:
return self::joinWavFiles($files);
break;
}
}
/**
* Joins multiple wav files
*
* All wave files need to have the same format and need to be uncompressed.
* The headers of the last file will be used (with recalculated datasize
* of course)
*
* @link http://ccrma.stanford.edu/CCRMA/Courses/422/projects/WaveFormat/
* @link http://www.thescripts.com/forum/thread3770.html
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <gohr@cosmocode.de>
*
* @param array $wavs: the array of wav files
* @return string the contents of joined wav file
*/
protected static function joinWavFiles($wavs)
{
$fields = join('/', array(
'H8Format',
'H8Subchunk1ID',
'VSubchunk1Size',
'vAudioFormat',
'vNumChannels',
'VSampleRate',
'VByteRate',
'vBlockAlign',
'vBitsPerSample'
));
$data = '';
foreach ($wavs as $wav){
$fp = fopen($wav, 'rb');
// Read ChunkID
$headerPart1 = fread($fp, 4);
// Read ChunkSize
$headerPart2 = fread($fp, 4);
// Read following fields
$headerPart3 = fread($fp, 28);
$info = unpack($fields, $headerPart3);
// Read optional extra stuff
// We will not use this since AudioFormat of all our sound files is PCM
if ($info['Subchunk1Size'] > 16) {
$headerPart3 .= fread($fp, ($info['Subchunk1Size']-16));
}
// Read SubChunk2ID
$headerPart3 .= fread($fp, 4);
// Read Subchunk2Size
$size = unpack('VSubChunk2Size', fread($fp, 4));
$size = $size['SubChunk2Size'];
// Read data
$data .= fread($fp, $size);
fclose($fp);
}
return $headerPart1 . pack('V', 36 + strlen($data)) . $headerPart3 . pack('V', strlen($data)) . $data;
}
/**
* Joins multiple mp3 files
*
* All mp3 files need to have the same format and need to be uncompressed.
* The headers of the last file will be used (with recalculated datasize
* of course)
*
* @link http://www.theblog.ca/merge-mp3s-php
* @param array $files: the array of mp3 files
* @return string the contents of joined mp3 file
*/
protected static function joinMp3Files($files)
{
$data = '';
foreach ($files as $file) {
$mp3 = new Mp3ContentUtility($file);
$data .= $mp3->striptags();
}
return $data;
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace SJBR\SrFreecap\Utility;
/*
* Copyright notice
*
* (c) 2012-2022 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
class EncryptionUtility
{
/**
* Salt
*/
const SALT = 'cH!swe!retReGu7W6bEDRup7usuDUh9THeD2CHeGE*ewr4n39=E@rAsp7c-Ph@pH';
/**
* Encrypts a string
*
* @param array $string: the string to be encrypted
* @return array an array with the string as the first element and the initialization vector as the second element
*/
public static function encrypt($string)
{
if (in_array('openssl', get_loaded_extensions())) {
$encryptionAlgorithm = strtolower($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sr_freecap']['encryptionAlgorithm'] ?? 'aes-256-cbc');
$availableAlgorithms = openssl_get_cipher_methods(true);
if (in_array($encryptionAlgorithm, $availableAlgorithms)) {
$key = md5($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] ?? 0, true);
$iv_size = openssl_cipher_iv_length($encryptionAlgorithm);
$salt = (isset($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sr_freecap']['salt']) && trim($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sr_freecap']['salt'])) ? trim($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sr_freecap']['salt']) : self::SALT;
$hash = hash('sha256', $salt . $key . $salt);
$iv = substr($hash, strlen($hash) - $iv_size);
$key = substr($hash, 0, 32);
$string = openssl_encrypt($string, $encryptionAlgorithm, $key, OPENSSL_RAW_DATA, $iv);
$cypher = [base64_encode($string), base64_encode($iv)];
} else {
$cypher = [base64_encode($string)];
}
} else {
$cypher = [base64_encode($string)];
}
return $cypher;
}
/**
* Decrypts a string
*
* @param array $cypher: an array as returned by encrypt()
* @return string the decrypted string
*/
public static function decrypt($cypher)
{
if (in_array('openssl', get_loaded_extensions())) {
$encryptionAlgorithm = strtolower($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sr_freecap']['encryptionAlgorithm'] ?? 'aes-256-cbc');
$availableAlgorithms = openssl_get_cipher_methods(true);
if (in_array($encryptionAlgorithm, $availableAlgorithms)) {
$key = md5($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] ?? 0, true);
$salt = isset($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sr_freecap']['salt']) ? trim($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sr_freecap']['salt']) : self::SALT;
$hash = hash('sha256', $salt . $key . $salt);
$key = substr($hash, 0, 32);
$string = trim(openssl_decrypt(base64_decode($cypher[0]), $encryptionAlgorithm, $key, OPENSSL_RAW_DATA, base64_decode($cypher[1])));
} else {
$string = base64_decode($cypher[0]);
}
} else {
$string = base64_decode($cypher[0]);
}
return $string;
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace SJBR\SrFreecap\Utility;
/*
* Copyright notice
*
* (c) 2012-2022 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Utility for making GD fonts
*/
class FontMakingUtility
{
/**
* Returns an image displaying a list of characters with specified font file and character size
*
* @param string $characters: text to display on the image
* @param string $font: specified True Type font file name
* @param integer $width: width of each character
* @param string $height: height of the image
* @return array image file info array
*/
public static function makeFontImage($characters, $font, $width = 34, $height = 50)
{
$size = intval($height * .8);
$vertOffset = intval($height * .7);
$color = '#000000';
$bgColor = 'white';
$align = 'left';
$charactersArray = explode(',', $characters);
$charactersCount = count($charactersArray);
$gifObjArray = array();
$gifObjArray['backColor'] = $bgColor;
$gifObjArray['transparentBackground'] = 0;
$gifObjArray['reduceColors'] = '';
$gifObjArray['maxWidth'] = ($charactersCount * $width) + 1;
$gifObjArray['XY'] = ($charactersCount * $width) . ',' . $height;
for ($ic = 1; $ic < $charactersCount+1; $ic++) {
$gifObjArray[$ic . '0'] = 'TEXT';
$gifObjArray[$ic . '0.']['text'] = $charactersArray[$ic-1];
$bbox = imagettfbbox($size, 0, GeneralUtility::getFileAbsFileName($font), $charactersArray[$ic-1]);
$hOffset = intval(($width - ($bbox[4] - $bbox[6]))/2);
$vOffset = intval(($width - ($bbox[7] - $bbox[1]))/2);
$gifObjArray[$ic . '0.']['niceText'] = 0;
$gifObjArray[$ic . '0.']['antiAlias'] = 1;
$gifObjArray[$ic . '0.']['align'] = $align;
$gifObjArray[$ic . '0.']['fontSize'] = $size;
$gifObjArray[$ic . '0.']['fontFile'] = '../' . $font;
$gifObjArray[$ic . '0.']['fontColor'] = $color;
$gifObjArray[$ic . '0.']['maxWidth'] = $width;
$gifObjArray[$ic . '0.']['offset'] = (($ic-1) * $width + $hOffset) . ',' . $vertOffset;
}
$gifCreator = GeneralUtility::makeInstance(GifBuilderUtility::class);
if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib'] ?? false) {
$gifCreator->start($gifObjArray, []);
return $gifCreator->gifBuild();
} else {
return false;
}
}
/************************************************************\
*
* GD Fontmaker Copyright 2005 Howard Yeend
* www.puremango.co.uk
*
* This file is part of GD Fontmaker.
*
* GD Fontmaker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GD Fontmaker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GD Fontmaker; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
\************************************************************/
public static function makeFont($image, $numchars, $startchar, $pixelwidth, $pixelheight, $endianness = 0)
{
$startchar = ord($startchar);
// encode this at start of font
if ($endianness) {
// big-endian
$fontdata = chr(0).chr(0).chr(0).chr($numchars).chr(0).chr(0).chr(0).chr($startchar).chr(0).chr(0).chr(0).chr($pixelwidth).chr(0).chr(0).chr(0).chr($pixelheight);
} else {
// little-endian
$fontdata = chr($numchars).chr(0).chr(0).chr(0).chr($startchar).chr(0).chr(0).chr(0).chr($pixelwidth).chr(0).chr(0).chr(0).chr($pixelheight).chr(0).chr(0).chr(0);
}
// loop through each pixel of each character of the PNG
// (we know the dimensions of the characters because the user told us what they were)
$y = 0;
$x = 0;
$start_x = 0;
for ($c = 0; $c < $numchars*$pixelwidth; $c += $pixelwidth) {
for ($y = 0; $y < $pixelheight; $y++) {
for ($x = $c; $x < $c+$pixelwidth; $x++) {
// get colour of this pixel
$rgb = ImageColorAt($image, $x, $y);
if ($rgb == 0) {
// it's black; font data
$fontdata .= chr(255);
} else {
// it's not black; background
$fontdata .= chr(0);
}
$i++;
}
}
}
return $fontdata;
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace SJBR\SrFreecap\Utility;
/*
* Copyright notice
*
* (c) 2012-2018 Stanislas Rolland <typo3(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Frontend\Imaging\GifBuilder;
/**
* Utility extending Gif builder
*/
class GifBuilderUtility extends GifBuilder
{
/**
* Returns the reference to a "resource" in TypoScript.
*
* @param string The resource value.
* @return string Returns the relative filepath
*/
public function checkFile($file)
{
$file = GeneralUtility::getFileAbsFileName(Environment::getPublicPath() . '/' . $file);
$file = PathUtility::stripPathSitePrefix($file);
return $file;
}
/**
* Writes the input GDlib image pointer to file
*
* @param resource The GDlib image resource pointer
* @param string The filename to write to
* @param int $quality The image quality (for JPEGs)
* @return mixed The output of either imageGif, imagePng or imageJpeg based on the filename to write
* @see maskImageOntoImage(), scale(), output()
*/
public function ImageWrite($destImg, $theImage, $quality = 0)
{
return parent::ImageWrite($destImg, Environment::getPublicPath() . '/' . $theImage, $quality);
}
}

View File

@@ -0,0 +1,446 @@
<?php
namespace SJBR\SrFreecap\Utility;
/*
* Copyright notice
*
* (c) 2012-2022 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
/************************************************************\
*
* freeCap v1.4.1 Copyright 2005 Howard Yeend
* www.puremango.co.uk
*
* This file is part of freeCap.
*
* freeCap is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* freeCap is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with freeCap; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
\************************************************************/
use SJBR\SrFreecap\Utility\RandomContentUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Utility dealing with image content
*/
class ImageContentUtility {
const BACKGROUND_TYPE_TRANSPARENT = 'Transparent';
const BACKGROUND_TYPE_WHITE_WITH_GRID = 'White with grid';
const BACKGROUND_TYPE_WHITE_WITH_SQUIGGLES = 'White with squiggles';
const BACKGROUND_TYPE_MORPHED_IMAGE_BLOCKS = 'Morphed image blocks';
const MERGE_BACKGROUND_OVER_CAPTCHA = 0;
const MERGE_CAPTCHA_OVER_BACKGROUND = 1;
const TEXT_COLOR_UNIFORM = 0;
const TEXT_COLOR_ONE_PER_CHARACTER = 1;
const SITE_TAG_POSITION_TOP = 0;
const SITE_TAG_POSITION_BOTTOM = 1;
const SITE_TAG_POSITION_BOTH = 2;
/**
* Write word on image
*
* @param int $width: width of the image in pixels
* @param int $height: width of the image in pixels
* @param string $word: the captcha word
* @param int $textColorType (see constants)
* @param array $textPosition: 'horizontal' => horizontal starting position of text, 'vertical' => vertical starting position of text
* @param int $colorMaximum: 'darkness' => maximum color darkness along any dimension, 'lightness' => maximum color lightness along any dimension
* @param string $backgroundType (see constants)
* @param array $fontLocations: array of font files locations
* @param array $fontWidths: array of font widths
* @param int $morphfactor: text morphing factor
* @return string GD image identifier of noisy background
*/
public static function writeWordOnImage($width, $height, $word, $textColorType, $textPosition, $colorMaximum, $backgroundType, $fontLocations, $fontWidths, $morphFactor = 1)
{
$image = ImageCreate($width, $height);
$image2 = ImageCreate($width, $height);
// Set background colour (can change to any colour not in possible $text_col range)
// it doesn't matter as it'll be transparent or coloured over.
// if you're using background type 'Morphed image blocks', you might want to try to ensure that the color chosen
// below doesn't appear too much in any of your background images.
$background = ImageColorAllocate($image, 254, 254, 254);
$background2 = ImageColorAllocate($image2, 254, 254, 254);
// Set transparencies
ImageColorTransparent($image, $background);
// $image2 transparent to allow characters to overlap slightly while morphing
ImageColorTransparent($image2, $background2);
// Fill backgrounds
ImageFill($image, 0, 0, $background);
ImageFill($image2, 0, 0, $background2);
// Write word in random starting X position
$word_start_x = RandomContentUtility::getRandomNumberInRange(5, $textPosition['horizontal']);
// Y positions jiggled about later
$word_start_y = $textPosition['vertical'];
// Get uniform color
if ($textColorType == self::TEXT_COLOR_UNIFORM) {
$textColor = RandomContentUtility::getRandomColor($colorMaximum['darkness'], $colorMaximum['lightness'], $backgroundType == self::BACKGROUND_TYPE_MORPHED_IMAGE_BLOCKS);
$textColor2 = ImageColorAllocate($image2, $textColor[0], $textColor[1], $textColor[2]);
}
// Write each character in different font
$textFontWidths = array();
$x_pos = $word_start_x;
for ($i = 0; $i < strlen($word); $i++) {
// Get changing color
if ($textColorType == self::TEXT_COLOR_ONE_PER_CHARACTER) {
$textColor = RandomContentUtility::getRandomColor($colorMaximum['darkness'], $colorMaximum['lightness'], $backgroundType == self::BACKGROUND_TYPE_MORPHED_IMAGE_BLOCKS);
$textColor2 = ImageColorAllocate($image2, $textColor[0], $textColor[1], $textColor[2]);
}
$fontIndex = RandomContentUtility::getRandomNumberInRange(0, sizeof($fontLocations)-1);
$font = ImageLoadFont($fontLocations[$fontIndex]);
ImageString($image2, $font, $x_pos, $word_start_y, $word[$i], $textColor2);
$textFontWidths[$i] = $fontWidths[$fontIndex];
$x_pos += $textFontWidths[$i];
}
// Morph Image
// Firstly move each character up or down a bit:
$x_pos = $word_start_x;
$y_pos = 0;
for ($i = 0; $i < strlen($word); $i++) {
// Move on Y axis
// Deviate at least 4 pixels between each letter
$prev_y = $y_pos;
do {
$y_pos = RandomContentUtility::getRandomNumberInRange(-5, 5);
} while ($y_pos < $prev_y + 2 && $y_pos > $prev_y - 2);
ImageCopy($image, $image2, $x_pos, $y_pos, $x_pos, 0, $textFontWidths[$i], $height);
$x_pos += $textFontWidths[$i];
}
ImageFilledRectangle($image2, 0, 0, $width, $height, $background2);
// Randomly morph each character individually on x-axis
// This is where the main distortion happens
$y_chunk = 1;
$morph_x = 0;
$orig_x = $word_start_x - $textFontWidths[0];
for ($j = 0; $j < strlen($word); $j++) {
$orig_x += $textFontWidths[$j];
$y_pos = 0;
for ($i = 0; $i <= $height; $i += $y_chunk) {
// morph x += so that instead of deviating from orig x each time, we deviate from where we last deviated to
// get it? instead of a zig zag, we get more of a sine wave.
// I wish we could deviate more but it looks crap if we do.
$morph_x += RandomContentUtility::getRandomNumberInRange(-$morphFactor, $morphFactor);
// had to change this to ImageCopyMerge when starting using ImageCreateTrueColor
// according to the manual; "when (pct is) 100 this function behaves identically to imagecopy()"
// but this is NOT true when dealing with transparencies...
ImageCopyMerge($image2, $image, $orig_x + $morph_x, $i + $y_pos, $orig_x, $i, $textFontWidths[$j], $y_chunk, 100);
}
}
ImageFilledRectangle($image, 0, 0, $width, $height, $background);
// Now do the same on the y-axis
// (much easier because we can just do it across the whole image, don't have to do it char-by-char)
$y_pos = 0;
$x_chunk = 1;
for ($i = 0; $i <= $width ; $i += $x_chunk) {
// Can result in image going too far off on Y-axis;
// not much I can do about that, apart from make image bigger
// again, I wish I could do 1.5 pixels
$y_pos += RandomContentUtility::getRandomNumberInRange(-1, 1);
ImageCopy($image, $image2, $i, $y_pos, $i, 0, $x_chunk, $height);
}
// Cleanup
ImageDestroy($image2);
return $image;
}
/**
* Generate noisy background
*
* @param int $width: width of the image in pixels
* @param int $height: width of the image in pixels
* @param string $word: the captcha word
* @param string $backgroundType (see constants)
* @param array $backgroundImages: array of background image file names
* @param boolean $morphBackground: if TRUE, the background will be morphed
* @param boolean $blurBackground: if TRUE, the background will be blurred
* @return string GD image identifier of noisy background
*/
public static function generateNoisyBackground($width, $height, $word, $backgroundType, $backgroundImages = array(), $morphBackground = true, $blurBackground = true)
{
$image = ImageCreateTrueColor($width, $height);
if ($backgroundType != self::BACKGROUND_TYPE_TRANSPARENT) {
// Generate noisy background, to be merged with CAPTCHA later
// any suggestions on how best to do this much appreciated
// sample code would be even better!
// I'm not an OCR expert (hell, I'm not even an image expert; puremango.co.uk was designed in MsPaint)
// so the noise models are based around my -guesswork- as to what would make it hard for an OCR prog
// ideally, the character obfuscation would be strong enough not to need additional background noise
// in any case, I hope at least one of the options given here provide some extra security!
$tempBackground = ImageCreateTrueColor($width*1.5, $height*1.5);
$background = ImageColorAllocate($image, 255, 255, 255);
ImageFill($image, 0, 0, $background);
$tempBackgroundColor = ImageColorAllocate($tempBackground, 255, 255, 255);
ImageFill($tempBackground, 0, 0, $tempBackgroundColor);
// We draw all noise onto tempBackground
// then if we're morphing, merge from tempBackground to image
// or if not, just copy a $width x $height portion of $tempBackground to $image
// tempBackground is much larger so that when morphing, the edges retain the noise.
switch ($backgroundType) {
case self::BACKGROUND_TYPE_WHITE_WITH_GRID:
// Draw grid on x
for ($i = RandomContentUtility::getRandomNumberInRange(6, 20); $i < $width*2; $i += RandomContentUtility::getRandomNumberInRange(10, 25)) {
ImageSetThickness($tempBackground, RandomContentUtility::getRandomNumberInRange(2, 6));
$textColor = RandomContentUtility::getRandomColor(100, 150);
$textColor2 = ImageColorAllocate($tempBackground, $textColor[0], $textColor[1], $textColor[2]);
ImageLine($tempBackground, $i, 0, $i, $height*2, $textColor2);
}
// Draw grid on y
for ($i = RandomContentUtility::getRandomNumberInRange(6, 20); $i < $height*2 ; $i += RandomContentUtility::getRandomNumberInRange(10, 25)) {
ImageSetThickness($tempBackground, RandomContentUtility::getRandomNumberInRange(2, 6));
$textColor = RandomContentUtility::getRandomColor(100, 150);
$textColor2 = ImageColorAllocate($tempBackground, $textColor[0], $textColor[1], $textColor[2]);
ImageLine($tempBackground, 0, $i, $width*2, $i , $textColor2);
}
break;
case self::BACKGROUND_TYPE_WHITE_WITH_SQUIGGLES:
ImageSetThickness($tempBackground, 4);
for ($i = 0; $i < strlen($word)+1; $i++) {
$textColor = RandomContentUtility::getRandomColor(100, 150);
$textColor2 = ImageColorAllocate($tempBackground, $textColor[0], $textColor[1], $textColor[2]);
$points = Array();
// Draw random squiggle for each character
// the longer the loop, the more complex the squiggle
// keep random so OCR can't say "if found shape has 10 points, ignore it"
// each squiggle will, however, be a closed shape, so OCR could try to find
// line terminations and start from there. (I don't think they're that advanced yet..)
for ($j = 1; $j < RandomContentUtility::getRandomNumberInRange(5, 10); $j++) {
$points[] = RandomContentUtility::getRandomNumberInRange(1*(20*($i+1)), 1*(50*($i+1)));
$points[] = RandomContentUtility::getRandomNumberInRange(30, $height+30);
}
ImagePolygon($tempBackground, $points, intval(sizeof($points)/2), $textColor2);
}
break;
case self::BACKGROUND_TYPE_MORPHED_IMAGE_BLOCKS:
// Take random chunks of $backgroundImages and paste them onto the background
for ($i = 0; $i < sizeof($backgroundImages); $i++) {
// Read each image and its size
$tempImages[$i] = ImageCreateFromJPEG(GeneralUtility::getFileAbsFileName($backgroundImages[$i]));
$tempWidths[$i] = imagesx($tempImages[$i]);
$tempHeights[$i] = imagesy($tempImages[$i]);
}
$blocksize = RandomContentUtility::getRandomNumberInRange(20, 60);
for ($i = 0; $i < $width*2; $i += $blocksize) {
// Could randomise blocksize here... hardly matters
for ($j = 0; $j < $height*2; $j += $blocksize) {
$imageIndex = RandomContentUtility::getRandomNumberInRange(0, sizeof($tempImages)-1);
$cut_x = RandomContentUtility::getRandomNumberInRange(0, $tempWidths[$imageIndex]-$blocksize);
$cut_y = RandomContentUtility::getRandomNumberInRange(0, $tempHeights[$imageIndex]-$blocksize);
ImageCopy($tempBackground, $tempImages[$imageIndex], $i, $j, $cut_x, $cut_y, $blocksize, $blocksize);
}
}
// Cleanup
for ($i = 0; $i < sizeof($tempImages); $i++) {
ImageDestroy($tempImages[$i]);
}
break;
}
if ($morphBackground) {
// Morph background
// We do this separately to the main text morph because:
// a) the main text morph is done char-by-char, this is done across whole image
// b) if an attacker could un-morph the background, it would un-morph the CAPTCHA
// hence background is morphed differently to text
// why do we morph it at all? it might make it harder for an attacker to remove the background
// morph_chunk 1 looks better but takes longer
// this is a different and less perfect morph than the one we do on the CAPTCHA
// occasonally you get some dark background showing through around the edges
// it doesn't need to be perfect as it's only the background.
$morph_chunk = RandomContentUtility::getRandomNumberInRange(1, 5);
$morph_y = 0;
for ($x = 0; $x < $width; $x += $morph_chunk) {
$morph_chunk = RandomContentUtility::getRandomNumberInRange(1, 5);
$morph_y += RandomContentUtility::getRandomNumberInRange(-1, 1);
ImageCopy($image, $tempBackground, $x, 0, $x+30, 30+$morph_y, $morph_chunk, $height*2);
}
ImageCopy($tempBackground, $image, 0, 0, 0, 0, $width, $height);
$morph_x = 0;
for ($y = 0; $y <= $height; $y += $morph_chunk) {
$morph_chunk = RandomContentUtility::getRandomNumberInRange(1, 5);
$morph_x += RandomContentUtility::getRandomNumberInRange(-1, 1);
ImageCopy($image, $tempBackground, $morph_x, $y, 0, $y, $width, $morph_chunk);
}
} else {
// Just copy tempBackground onto $image
ImageCopy($image, $tempBackground, 0, 0, 30, 30, $width, $height);
}
// Cleanup
ImageDestroy($tempBackground);
if ($blurBackground) {
$image = self::blurImage($image);
}
}
return $image;
}
/**
* Merge captcha image with background
*
* @param int $width: width of the image in pixels
* @param int $height: width of the image in pixels
* @param string $captchaImage: a GD image identifier
* @param string $backgroundImage: a GD image identifier
* @param string $backgroundType (see constants)
* @param int $mergeType: 0 - Background over captcha, 1 - Captcha over background (see constants)
* @param int $backgroundFadePercentage: fading factor for background
* @return string GD image identifier of merged image
*/
public static function mergeCaptchaWithBackground($width, $height, $captchaImage, $backgroundImage, $backgroundType, $mergeType)
{
if ($backgroundType != self::BACKGROUND_TYPE_TRANSPARENT) {
// How faded should the background be? (100=totally gone, 0=bright as the day)
// to test how much protection the bg noise gives, take a screenshot of the freeCap image
// and take it into a photo editor. play with contrast and brightness.
// If you can remove most of the background, then it's not a good enough percentage
switch ($backgroundType) {
case self::BACKGROUND_TYPE_WHITE_WITH_GRID:
case self::BACKGROUND_TYPE_WHITE_WITH_SQUIGGLES:
$backgroundFadePercentage = 65;
break;
case self::BACKGROUND_TYPE_MORPHED_IMAGE_BLOCKS:
$backgroundFadePercentage = 50;
break;
}
// Slightly randomize the background fade
$backgroundFadePercentage += RandomContentUtility::getRandomNumberInRange(-2, 2);
// Fade background
if ($backgroundType != self::BACKGROUND_TYPE_MORPHED_IMAGE_BLOCKS) {
$tempImage = ImageCreateTrueColor($width, $height);
$white = ImageColorAllocate($tempImage, 255, 255, 255);
ImageFill($tempImage, 0, 0, $white);
ImageCopyMerge($backgroundImage, $tempImage, 0, 0, 0, 0, $width, $height, $backgroundFadePercentage);
ImageDestroy($tempImage);
$colorFadePercentage = 50;
} else {
$colorFadePercentage = $backgroundFadePercentage;
}
// Merge background image with CAPTCHA image to create smooth background
if ($mergeType == self::MERGE_CAPTCHA_OVER_BACKGROUND) {
// Might want to not blur if using this method, otherwise leaves white-ish border around each letter
ImageCopyMerge($backgroundImage, $captchaImage, 0, 0, 0, 0, $width, $height, 100);
ImageCopy($captchaImage, $backgroundImage, 0, 0, 0, 0, $width, $height);
} else {
// Background over captcha
ImageCopyMerge($captchaImage, $backgroundImage, 0, 0, 0, 0, $width, $height, $colorFadePercentage);
}
}
return $captchaImage;
}
/**
* Blurs an image
*
* @param string $image: a GD image identifier
* @return string GD image identifier of blurred image
*/
public static function blurImage($image)
{
// w00t. my very own blur function
// in GD2, there's a gaussian blur function. bunch of bloody show-offs... :-)
$width = imagesx($image);
$height = imagesy($image);
$temp_im = ImageCreateTrueColor($width, $height);
$bg = ImageColorAllocate($temp_im, 150, 150, 150);
// preserves transparency if in orig image
ImageColorTransparent($temp_im, $bg);
// fill bg
ImageFill($temp_im, 0, 0, $bg);
// anything higher than 3 makes it totally unreadable
// might be useful in a 'real' blur function, though (ie blurring pictures not text)
$distance = 1;
// use $distance=30 to have multiple copies of the word. not sure if this is useful.
// blur by merging with itself at different x/y offsets:
ImageCopyMerge($temp_im, $image, 0, 0, 0, $distance, $width, $height-$distance, 70);
ImageCopyMerge($image, $temp_im, 0, 0, $distance, 0, $width-$distance, $height, 70);
ImageCopyMerge($temp_im, $image, 0, $distance, 0, 0, $width, $height, 70);
ImageCopyMerge($image, $temp_im, $distance, 0, 0, 0, $width, $height, 70);
// remove temp image
ImageDestroy($temp_im);
return $image;
}
/**
* Outputs the image with appropriate headers
*
* @param string $image: a GD image identifier
* @param string $imageType: type of image (jpg, gif or png)
* @return void
*/
public static function sendImage($image, $imageType)
{
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Pragma: no-cache');
header('Cache-Control: no-cache, no-store, must-revalidate');
// Setting privacy policy header for IE in popup window
header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
switch ($imageType) {
case 'jpg':
header('Content-Type: image/jpeg');
ImageJPEG($image);
break;
case 'gif':
header('Content-Type: image/gif');
ImageGIF($image);
break;
case 'png':
default:
header('Content-Type: image/png');
ImagePNG($image);
break;
}
}
}

View File

@@ -0,0 +1,159 @@
<?php
namespace SJBR\SrFreecap\Utility;
/*
* Copyright notice
*
* (c) 2009 Sebastian Kurfürst <sebastian@typo3.org>
* (c) 2013-2021 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Http\ApplicationType;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Localization\Locales;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Localization helper which should be used to fetch appropriate words list or voice rendering language
*
*/
class LocalizationUtility
{
/**
* Key of the extension to which this class belongs
*
* @var string
*/
protected static $extensionKey = 'sr_freecap';
/**
* Gets the location of the words list based on configured language
*
* @param string $defaultWordsList: location of the default words list
* @return string the location of the words list to be used
*/
public static function getWordsListLocation($defaultWordsList = '')
{
$languageKeys = static::getLanguageKeys();
$initialWordsList = $defaultWordsList;
if (!trim($initialWordsList)) {
$initialWordsList = 'EXT:' . self::$extensionKey . '/Resources/Private/Captcha/Words/default_freecap_words';
}
$path = dirname(GeneralUtility::getFileAbsFileName($initialWordsList)) . '/';
$wordsListLocation = $path . $languageKeys['languageKey'] . '_freecap_words';
if (!is_file($wordsListLocation)) {
foreach ($languageKeys['alternativeLanguageKeys'] as $language) {
$wordsListLocation = $path . $language . '_freecap_words';
if (is_file($wordsListLocation)) {
break;
}
}
}
if (!is_file($wordsListLocation)) {
$wordsListLocation = $path . 'default_freecap_words';
if (!is_file($wordsListLocation)) {
$wordsListLocation = '';
}
}
return $wordsListLocation;
}
/**
* Gets the directory of wav files based on configured language
*
* @return string name of the directory containing the wav files to be used
*/
public static function getVoicesDirectory()
{
$languageKeys = static::getLanguageKeys();
$path = ExtensionManagementUtility::extPath(self::$extensionKey) . 'Resources/Private/Captcha/Voices/';
$voicesDirectory = $path . $languageKeys['languageKey'] . '/';
if (!is_dir($voicesDirectory)) {
foreach ($languageKeys['alternativeLanguageKeys'] as $language) {
$voicesDirectory = $path . $language . '/';
if (is_dir($voicesDirectory)) {
break;
}
}
}
if (!is_dir($voicesDirectory)) {
$voicesDirectory = $path . 'default/';
}
return $voicesDirectory;
}
/**
* Sets the currently active language/language_alt keys.
* Default values are "default" for language key and an empty array for language_alt key.
*
* @return array
*/
protected static function getLanguageKeys(): array
{
$languageKeys = [
'languageKey' => 'default',
'alternativeLanguageKeys' => [],
];
if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
&& ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
) {
$tsfe = static::getTypoScriptFrontendController();
$pageId = $tsfe->id;
$siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
$currentSite = $siteFinder->getSiteByPageId($pageId);
$languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
// Current language
$currentSiteLanguageId = $languageAspect->getId();
$currentSiteLanguage = $currentSite->getLanguageById((int)$currentSiteLanguageId);
$languageKeys['languageKey'] = $currentSiteLanguage->getTypo3Language();
// Alternative languages
$alternativeLanguageIds = $languageAspect->getFallbackChain();
foreach ($alternativeLanguageIds as $alternativeLanguageId) {
$alternativeLanguage = $currentSite->getLanguageById((int)$alternativeLanguageId);
$languageKeys['alternativeLanguageKeys'][] = $alternativeLanguage->getTypo3Language();
}
if (empty($languageKeys['alternativeLanguageKeys'])) {
$locales = GeneralUtility::makeInstance(Locales::class);
if (in_array($languageKeys['languageKey'], $locales->getLocales())) {
foreach ($locales->getLocaleDependencies($languageKeys['languageKey']) as $language) {
$languageKeys['alternativeLanguageKeys'][] = $language;
}
}
}
} else {
$languageKeys['languageKey'] = $GLOBALS['BE_USER']->uc['lang'];
}
return $languageKeys;
}
/**
* @return TypoScriptFrontendController
*/
protected static function getTypoScriptFrontendController()
{
return $GLOBALS['TSFE'];
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace SJBR\SrFreecap\Utility;
/*
* Copyright notice
*
* (c) 2012-2020 Stanislas Rolland <typo32020(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
/**
* Utility dealing with mp3 audio content
* See http://www.theblog.ca/merge-mp3s-php
*/
class Mp3ContentUtility
{
var $mp3Content;
// Create a new mp3
public function __construct($file = '')
{
if ($file != '') {
$this->mp3Content = file_get_contents($file);
}
}
// Get the mp3 content
public function getContent()
{
return $this->mp3Content;
}
// Calculate where's the beginning of the sound file
protected function getStart()
{
$strlen = strlen($this->mp3Content);
for ($i=0; $i < $strlen; $i++) {
$v = substr($this->mp3Content, $i, 1);
$value = ord($v);
if ($value == 255) {
return $i;
}
}
}
// Calculate where's the end of the sound file
protected function getIdvEnd()
{
$strlen = strlen($this->mp3Content);
$str = substr($this->mp3Content, ($strlen - 128));
$str1 = substr($str, 0, 3);
if (strtolower($str1) == strtolower('TAG')) {
return $str;
} else {
return false;
}
}
// Remove the ID3 tags
public function striptags()
{
// Remove start stuff...
$newStr = '';
$s = $start = $this->getStart();
if ($s === false) {
return false;
} else {
$this->mp3Content = substr($this->mp3Content, $start);
}
//Remove end tag stuff
$end = $this->getIdvEnd();
if ($end !== false) {
$this->mp3Content = substr($this->mp3Content, 0, (strlen($this->mp3Content)-129));
}
return $this->mp3Content;
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace SJBR\SrFreecap\Utility;
/*
* Copyright notice
*
* (c) 2012-2020 Stanislas Rolland <typo32020(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
/**
* Random content utility
*/
class RandomContentUtility
{
/**
* Returns a random number within the given range
*
* @param int $min: the lower boundary of the range
* @param int $max: the upper boundary of the range
* @return int the generated number
*/
public static function getRandomNumberInRange($min, $max)
{
if ($min > $max) {
$newMin = $max;
$newMax = $min;
} else {
$newMin = $min;
$newMax = $max;
}
return mt_rand($newMin, $newMax);
}
/**
* Returns a random color in a the form of an array of 3 numbers
*
* @param int $colorMaximumDarkness: maximum darkness along any dimension
* @param int $colorMaximumLightness: maximum lightness along any dimension
* @param boolean $darker: if true, produce a possibly darker image by default
* @return array the random color
*/
public static function getRandomColor ($colorMaximumDarkness, $colorMaximumLightness, $darker = false)
{
$color = [];
if ($darker) {
// Needs darker colour..
$minimum = isset($colorMaximumDarkness) ? $colorMaximumDarkness : 10;
$maximum = isset($colorMaximumLightness) ? $colorMaximumLightness : 100;
} else {
$minimum = isset($colorMaximumDarkness) ? $colorMaximumDarkness : 30;
$maximum = isset($colorMaximumLightness) ? $colorMaximumLightness : 140;
}
for ($i = 0; $i < 3 ; $i++) {
$color[] = self::getRandomNumberInRange($minimum, $maximum);
}
return $color;
}
/**
* Returns a random word
*
* @param boolean $useWordsList: if true, a word dictionary is used
* @param string $wordsList: dictionary file location
* @param boolean $numbersOnly: if true, use only numbers
*
* @return string random word
*/
public static function getRandomWord($useWordsList = false, $wordsList = '', $numbersOnly = false, $maxWordLength = 6)
{
if ($useWordsList && is_file($wordsList)) {
// Load dictionary and choose random word
// Keep dictionary in non-web accessible folder, or htaccess it
// or modify so word comes from a database; SELECT word FROM words ORDER BY rand() LIMIT 1
$words = @file($wordsList);
$word = strtolower($words[self::getRandomNumberInRange(0, sizeof($words)-1)]);
// Cut off line breaks
$word = preg_replace('/['.preg_quote(chr(10).chr(13)).']+/', '', $word);
unset($words);
} else {
// Based on code originally by breakzero at hotmail dot com
// (http://uk.php.net/manual/en/function.rand.php)
// generate pseudo-random string
// doesn't use ijtf as are easily mistaken
// I'm not using numbers because the custom fonts I've created don't support anything other than
// lowercase or space (but you can download new fonts or create your own using my GD fontmaker script)
if ($numbersOnly) {
$consonants = '123456789';
$vowels = '123456789';
} else {
$consonants = 'bcdghklmnpqrsvwxyz';
$vowels = 'aeuo';
}
$word = '';
$wordlen = self::getRandomNumberInRange(5, $maxWordLength);
for ($i = 0; $i < $wordlen; $i++) {
// Don't allow to start with 'vowel'
if (self::getRandomNumberInRange(0, 20) >= 10 && $i != 0) {
$word .= $vowels[self::getRandomNumberInRange(0, strlen($vowels)-1)];
} else {
$word .= $consonants[self::getRandomNumberInRange(0, strlen($consonants)-1)];
}
}
}
return $word;
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace SJBR\SrFreecap\Validation\Validator;
/*
* Copyright notice
*
* (c) 2012-2021 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\Domain\Repository\WordRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator;
/**
* Captcha validator
*/
class CaptchaValidator extends AbstractValidator
{
/**
* Specifies whether this validator accepts empty values.
*
* If this is true, the validators isValid() method is not called in case of an empty value
* Note: A value is considered empty if it is null or an empty string!
* By default all validators except for NotEmpty and the Composite Validators accept empty values
*
* @var bool
*/
protected $acceptsEmptyValues = false;
/**
* @var string Name of the extension this controller belongs to
*/
protected $extensionName = 'SrFreecap';
/**
* Check the word that was entered against the hashed value
* Returns true, if the given property ($word) matches the session captcha value.
*
* @param string $word: the word that was entered and should be validated
* @return boolean true, if the word entered matches the hash value, false if an error occured
*/
public function isValid($word)
{
$isValid = false;
// This validator needs a frontend user session
if (is_object($GLOBALS ['TSFE']) && isset($GLOBALS ['TSFE']->fe_user)) {
// Get session data
$wordRepository = GeneralUtility::makeInstance(WordRepository::class);
$wordObject = $wordRepository->getWord();
$wordHash = $wordObject->getWordHash();
// Check the word hash against the stored hash value
if (!empty($wordHash) && !empty($word)) {
if ($wordObject->getHashFunction() == 'md5') {
// All freeCap words are lowercase.
// font #4 looks uppercase, but trust me, it's not...
if (md5(strtolower(utf8_decode($word))) == $wordHash) {
// Reset freeCap session vars
// Cannot stress enough how important it is to do this
// Defeats re-use of known image with spoofed session id
$wordRepository->cleanUpWord();
$isValid = true;
}
}
}
} else {
$isValid = empty($word);
}
if (!$isValid) {
// Please enter the word or number as it appears in the image. The entered value was incorrect.
$this->addError(
$this->translateErrorMessage(
'9221561048',
'SrFreecap'
),
9221561048
);
}
return $isValid;
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace SJBR\SrFreecap\Validation\Validator;
/***************************************************************
* Copyright notice
*
* (c) 2012-2018 Stanislas Rolland <typo3@sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator;
/**
* Validator for True Type Font file existence
*
*/
class TtfFileValidator extends AbstractValidator
{
/**
* Returns true, if the given property ($propertyValue) is a valid number in the given range.
*
* If at least one error occurred, the result is false.
*
* @param mixed $value The value that should be validated
* @return boolean true if the value is within the range, otherwise false
*/
protected function isValid($value)
{
$isValid = true;
$absoluteFileName = GeneralUtility::getFileAbsFileName($value);
// Check file existence
if (!is_file($absoluteFileName)) {
// A file with the given name could not be found.
$this->addError(
$this->translateErrorMessage(
'9221561046',
'SrFreecap'
),
9221561046
);
$isValid = false;
} else {
// Check file extension
$pathInfo = pathinfo($absoluteFileName);
if (strtolower($pathInfo['extension']) !== 'ttf') {
// The specified file is not a True Type Font file.
$this->addError(
$this->translateErrorMessage(
'9221561047',
'SrFreecap'
),
9221561046
);
$isValid = false;
}
}
return $isValid;
}
}

View File

@@ -0,0 +1,199 @@
<?php
namespace SJBR\SrFreecap\View\AudioPlayer;
/*
* Copyright notice
*
* (c) 2013-2022 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\Utility\AudioContentUtility;
use SJBR\SrFreecap\Utility\EncryptionUtility;
use SJBR\SrFreecap\Utility\LocalizationUtility;
use TYPO3Fluid\Fluid\View\ViewInterface;
/**
* Abstract class for rendering an audio version of the CAPTCHA
*/
class AbstractPlayFormat implements ViewInterface
{
/**
* @var string Name of the extension this view helper belongs to
*/
protected $extensionName = 'SrFreecap';
/**
* @var string Key of the extension this view helper belongs to
*/
protected $extensionKey = 'sr_freecap';
/**
* @var \TYPO3\CMS\Core\Domain\Model\Word
*/
protected $word;
/**
* Add a variable to the view data collection.
* Can be chained, so $this->view->assign(..., ...)->assign(..., ...); is possible
*
* @param string $key Key of variable
* @param mixed $value Value of object
* @return ViewInterface an instance of $this, to enable chaining
*/
public function assign($key, $value)
{
switch ($key) {
case 'word':
$this->word = $value;
break;
}
return $this;
}
/**
* Add multiple variables to the view data collection
*
* @param array $values array in the format array(key1 => value1, key2 => value2)
* @return ViewInterface an instance of $this, to enable chaining
*/
public function assignMultiple(array $values)
{
return $this;
}
/**
* Renders the audio version of captcha
*
* @return string The audio output to play
*/
public function render()
{
// Get the catcha word
$word = $this->getWord();
// Get the letter rensering files
$letterRenderingFiles = $this->getLetterRenderingFiles($word);
// Join the files
$audioContent = AudioContentUtility::joinAudioFiles($letterRenderingFiles);
// Output proper headers and echo audio content
$this->sendHeaders($audioContent);
// Send audio content
$this->sendAudioContent($audioContent);
// Return the audio content
return $audioContent;
}
/**
* Gets the word that was stored in session data
* @return string the retrieved and decoded word
*/
protected function getWord()
{
// Get cypher from session data
$cypher = $this->word->getWordCypher();
// Decrypt the word
$decryptedString = EncryptionUtility::decrypt($cypher);
return implode('-', preg_split('//', $decryptedString, -1, PREG_SPLIT_NO_EMPTY));
}
/**
* Builds an array of letter rendering files in the specified format
*
* @param string $word: the word to be spelled and played
* @param string $extension: the audio file extension being used
* @return array array of file names
*/
protected function getLetterRenderingFiles($word, $extension = 'wav')
{
$letterRenderingFiles = [];
// Split the word
$letters = preg_split('//', $word, -1, PREG_SPLIT_NO_EMPTY);
// Get the directory containing the wav files
$voicesDirectory = LocalizationUtility::getVoicesDirectory();
// Assemble the file names
foreach ($letters as $letter){
// Word lists are encoded in ISO-8859-1 (possibly in ISO-8859-2?)
$file = $voicesDirectory . ((isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) && $GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) ? utf8_encode($letter) : $letter) . '.' . $extension;
if (is_file($file)) {
$letterRenderingFiles[] = $file;
}
}
return $letterRenderingFiles;
}
/**
* Sends headers appropriate for audio content
*
* @param string $audioContent: the audio content that will be sent
* @param string $mimeType: the audio mime type being used
* @return void
*/
protected function sendHeaders($audioContent, $mimeType = 'x-wav')
{
header('Content-Type: audio/' . $mimeType);
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . strlen($audioContent));
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Pragma: no-cache');
header('Cache-Control: no-cache, no-store, must-revalidate');
// Setting privacy policy header for IE in popup window
header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
}
/**
* Sends audio content
*
* @param string $audioContent: the audio content that will be sent
* @return void
*/
protected function sendAudioContent($audioContent)
{
echo $audioContent;
}
/**
* Renders a given section.
*
* @param string $sectionName Name of section to render
* @param array $variables The variables to use
* @param boolean $ignoreUnknown Ignore an unknown section and just return an empty string
* @return string rendered template for the section
* @throws Exception\InvalidSectionException
*/
public function renderSection($sectionName, array $variables = [], $ignoreUnknown = false)
{
return '';
}
/**
* Renders a partial.
*
* @param string $partialName
* @param string $sectionName
* @param array $variables
* @param boolean $ignoreUnknown Ignore an unknown section and just return an empty string
* @return string
*/
public function renderPartial($partialName, $sectionName, array $variables, $ignoreUnknown = false)
{
return '';
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace SJBR\SrFreecap\View\AudioPlayer;
/*
* Copyright notice
*
* (c) 2013-2018 Stanislas Rolland <typo3(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\Utility\AudioContentUtility;
/**
* Renders a mp3 audio version of the CAPTCHA
*/
class PlayMp3 extends AbstractPlayFormat
{
/**
* Renders the audio version of captcha
*
* @return string The audio output to play
*/
public function render()
{
// Get the catcha word
$word = $this->getWord();
// Get the letter rensering files
$letterRenderingFiles = $this->getLetterRenderingFiles($word, 'mp3');
// Join the files
$audioContent = AudioContentUtility::joinAudioFiles($letterRenderingFiles, 'mp3');
// Output proper headers
$this->sendHeaders($audioContent, 'mpeg');
// Send audio content
$this->sendAudioContent($audioContent);
// Return the audio content
return $audioContent;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace SJBR\SrFreecap\View\AudioPlayer;
/***************************************************************
* Copyright notice
*
* (c) 2012-2013 Stanislas Rolland <typo3(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
* Renders a wav audio version of the CAPTCHA
*
* @author Stanislas Rolland <typo3(arobas)sjbr.ca>
*/
class PlayWav extends AbstractPlayFormat {
}

View File

@@ -0,0 +1,264 @@
<?php
namespace SJBR\SrFreecap\View\ImageGenerator;
/*
* Copyright notice
*
* (c) 2005-2022 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
/*
* Integrates freeCap v1.4.1 into TYPO3 and generates the freeCap CAPTCHA image.
*/
/*
*
* freeCap v1.4.1 Copyright 2005 Howard Yeend
* www.puremango.co.uk
*
* This file is part of freeCap.
*
* freeCap is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* freeCap is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with freeCap; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
use SJBR\SrFreecap\Domain\Model\Word;
use SJBR\SrFreecap\Domain\Repository\WordRepository;
use SJBR\SrFreecap\Utility\EncryptionUtility;
use SJBR\SrFreecap\Utility\ImageContentUtility;
use SJBR\SrFreecap\Utility\RandomContentUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3Fluid\Fluid\View\ViewInterface;
/**
* Renders a png image of the CAPTCHA
*/
class ShowPng implements ViewInterface
{
/**
* @var string Name of the extension this view helper belongs to
*/
protected $extensionName = 'SrFreecap';
/**
* @var string Name of the plugin this view helper belongs to
*/
protected $pluginName = 'ImageGenerator';
/**
* @var string Key of the extension this view helper belongs to
*/
protected $extensionKey = 'sr_freecap';
/**
* @var Word
*/
protected $word;
/**
* @var array Configuration of this view
*/
protected $settings;
/**
* Add a variable to the view data collection.
* Can be chained, so $this->view->assign(..., ...)->assign(..., ...); is possible
*
* @param string $key Key of variable
* @param mixed $value Value of object
* @return SJBR\SrFreecap\View\ImageGenerator\ShowPng an instance of $this, to enable chaining
* @api
*/
public function assign($key, $value)
{
switch ($key) {
case 'word':
$this->word = $value;
break;
case 'settings':
$this->settings = $value;
break;
}
return $this;
}
/**
* Add multiple variables to the view data collection
*
* @param array $values array in the format array(key1 => value1, key2 => value2)
* @return SJBR\SrFreecap\View\ImageGenerator\ShowPng an instance of $this, to enable chaining
* @api
*/
public function assignMultiple(array $values)
{
return $this;
}
/**
* Renders the captcha image
*
* @return string empty string (the image is sent here)
*/
public function render()
{
// Avoid Brute Force Attacks:
if (!$this->word->getAttempts()) {
$this->word->setAttempts(1);
} else {
$this->word->setAttempts($this->word->getAttempts() + 1);
// if more than ($this->settings['maxAttempts']) refreshes, block further refreshes
// can be negated by connecting with new session id
// could get round this by storing num attempts in database against IP
// could get round that by connecting with different IP (eg, using proxy servers)
// in short, there's little point trying to avoid brute forcing
// the best way to protect against BF attacks is to ensure the dictionary is not
// accessible via the web or use random string option
if ($this->word->getAttempts() > $this->settings['maxAttempts']) {
$this->word->setWordHash('');
$this->word->setWordCypher([]);
$this->word->setHashFunction('');
// Get an instance of the word repository
$wordRepository = GeneralUtility::makeInstance(WordRepository::class);
// Reset the word
$wordRepository->setWord($this->word);
$string = LocalizationUtility::translate('max_attempts', $this->extensionName);
$font = 5;
$width = imagefontwidth($font) * strlen($string);
$height = imagefontheight($font);
$image = ImageCreate($width+2, $height+20);
$background = ImageColorAllocate($image, 255,255,255);
ImageColorTransparent($image, $background);
$red = ImageColorAllocate($image, 255, 0, 0);
ImageString($image, $font, 1, 10, $string, $red);
ImageContentUtility::sendImage($image, $this->settings['imageFormat']);
ImageDestroy($image);
// Return an empty string
return '';
}
}
// Get random word
$word = RandomContentUtility::getRandomWord($this->settings['useWordsList'], $this->settings['wordsListLocation'], $this->settings['generateNumbers'] ?? 0, $this->settings['maxWordLength']);
// Save hash of word for comparison
// using hash so that if there's an insecurity elsewhere (eg on the form processor),
// an attacker could only get the hash
// also, shared servers usually give all users access to the session files
// echo `ls /tmp`; and echo `more /tmp/someone_elses_session_file`; usually work
// so even if your site is 100% secure, someone else's site on your server might not be
// hence, even if attackers can read the session file, they can't get the freeCap word
// (though most hashes are easy to brute force for simple strings)
$this->word->setWordHash(md5($word));
// We use a simple encrypt to prevent the session from being exposed
if ($this->settings['accessibleOutput'] ?? false) {
$this->word->setWordCypher(EncryptionUtility::encrypt($word));
}
// Build the image
$image = $this->buildImage($word, $this->settings['imageWidth'], $this->settings['imageHeight'], $this->settings['backgroundType']);
// Send the image
ImageContentUtility::sendImage($image, $this->settings['imageFormat']);
// Cleanup
ImageDestroy($image);
// Return an empty string
return '';
}
/**
* Builds the CAPTCHA image
*
* @param string $word
* @return string GD image identifier of image
*/
protected function buildImage($word, $width, $height, $backgroundType)
{
$image = ImageCreate($width, $height);
$background = ImageColorAllocate($image, 254, 254, 254);
// Write word on image
$image = ImageContentUtility::writeWordOnImage($width, $height, $word, $this->settings['textColor'], $this->settings['textPosition'], $this->settings['colorMaximum'], $backgroundType, $this->settings['fontLocations'], $this->settings['fontWidths'], $this->settings['morphFactor']);
// Blur edges
// Doesn't really add any security, but looks a lot nicer, and renders text a little easier to read
// for humans (hopefully not for OCRs, but if you know better, feel free to disable this function)
// (and if you do, let me know why)
$image = ImageContentUtility::blurImage($image);
if ($this->settings['imageFormat'] != 'jpg' && $backgroundType == ImageContentUtility::BACKGROUND_TYPE_TRANSPARENT) {
// Make background transparent
ImageColorTransparent($image, $background);
}
if ($backgroundType != ImageContentUtility::BACKGROUND_TYPE_TRANSPARENT) {
// Get noisy background
$image3 = ImageContentUtility::generateNoisyBackground($width, $height, $word, $backgroundType, $this->settings['backgroundImages'], $this->settings['backgroundMorph'], $this->settings['backgroundBlur']);
// Merge with obfuscated background
$image = ImageContentUtility::mergeCaptchaWithBackground($width, $height, $image, $image3, $backgroundType, $this->settings['mergeWithBackground']);
ImageDestroy($image3);
}
return $image;
}
/**
* Renders a given section.
*
* @param string $sectionName Name of section to render
* @param array $variables The variables to use
* @param boolean $ignoreUnknown Ignore an unknown section and just return an empty string
* @return string rendered template for the section
* @throws Exception\InvalidSectionException
*/
public function renderSection($sectionName, array $variables = [], $ignoreUnknown = false)
{
return '';
}
/**
* Renders a partial.
*
* @param string $partialName
* @param string $sectionName
* @param array $variables
* @param boolean $ignoreUnknown Ignore an unknown section and just return an empty string
* @return string
*/
public function renderPartial($partialName, $sectionName, array $variables, $ignoreUnknown = false)
{
return '';
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace SJBR\SrFreecap\ViewHelpers;
/*
* Copyright notice
*
* (c) 2013-2022 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\ViewHelpers\TranslateViewHelper;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
class AudioViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string Name of the extension this view helper belongs to
*/
protected $extensionName = 'SrFreecap';
/**
* @var string Name of the extension this view helper belongs to
*/
protected $pluginName = 'tx_srfreecap';
/**
* @var ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @param ConfigurationManagerInterface $configurationManager
* @return void
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* @var Context
*/
protected $context;
/**
* @param Context $context
*/
public function injectContext(Context $context)
{
$this->context = $context;
}
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('suffix', 'string', 'Suffix to be appended to the extenstion key when forming css class names', false, '');
}
/**
* Render the captcha audio rendering request icon
*
* @param string suffix to be appended to the extenstion key when forming css class names
* @return string The html used to render the captcha audio rendering request icon
*/
public function render($suffix = '')
{
// This viewhelper needs a frontend user session
if (!is_object($this->getTypoScriptFrontendController()) || !isset($this->getTypoScriptFrontendController()->fe_user)) {
throw new SessionNotFoundException('No frontend user found in session!');
}
$value = '';
// Get the plugin configuration
$settings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, $this->extensionName, $this->pluginName);
// Get the translation view helper
$translator = GeneralUtility::makeInstance(TranslateViewHelper::class);
// Generate the icon
if (($settings['accessibleOutput'] ?? false) && ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem'] ?? false)) {
$languageAspect = $this->context->getAspect('language');
$fakeId = substr(md5(uniqid(rand())), 0, 5);
$siteURL = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
$urlParams = [
'eIDSR' => 'sr_freecap_EidDispatcher',
'id' => $GLOBALS['TSFE']->id,
'pluginName' => 'AudioPlayer',
'actionName' => 'play',
'formatName' => 'wav',
'L' => $languageAspect->getId()
];
if ($this->getTypoScriptFrontendController()->MP) {
$urlParams['MP'] = $this->getTypoScriptFrontendController()->MP;
}
$audioURL = $siteURL . 'index.php?' . ltrim(GeneralUtility::implodeArrayForUrl('', $urlParams), '&');
if (isset($settings['accessibleOutputImage']) && $settings['accessibleOutputImage']) {
$value = '<input type="image" alt="' . $translator->render('click_here_accessible') . '"'
. ' title="' . $translator->render('click_here_accessible') . '"'
. ' src="' . $siteURL . PathUtility::stripPathSitePrefix(GeneralUtility::getFileAbsFileName($settings['accessibleOutputImage'])) . '"'
. ' onclick="' . $this->extensionName . '.playCaptcha(\'' . $fakeId . '\', \'' . $audioURL . '\', \'' . $translator->render('noPlayMessage') . '\');return false;" style="cursor: pointer;"'
. $this->getClassAttribute('image-accessible', $suffix) . ' />';
} else {
$value = '<span id="tx_srfreecap_captcha_playLink_' . $fakeId . '"'
. $this->getClassAttribute('accessible-link', $suffix) . '>' . $translator->render('click_here_accessible_before_link')
. '<a href="#" onClick="' . $this->extensionName . '.playCaptcha(\'' . $fakeId.'\', \'' . $audioURL . '\', \'' . $translator->render('noPlayMessage') . '\');return false;" style="cursor: pointer;" title="' . $translator->render('click_here_accessible') . '">'
. $translator->render('click_here_accessible_link') . '</a>'
. $translator->render('click_here_accessible_after_link') . '</span>';
}
$value .= '<span' . $this->getClassAttribute('accessible', $suffix) . ' id="tx_srfreecap_captcha_playAudio_' . $fakeId . '"></span>';
}
return $value;
}
/**
* Returns a class attribute with a class-name prefixed with $this->pluginName and with all underscores substituted to dashes (-)
*
* @param string $class The class name (or the END of it since it will be prefixed by $this->pluginName.'-')
* @param string suffix to be appended to the extenstion key when forming css class names
* @return string the class attribute with the combined class name (with the correct prefix)
*/
protected function getClassAttribute ($class, $suffix = '')
{
return ' class="' . trim(str_replace('_', '-', $this->pluginName) . ($suffix ? '-' . $suffix . '-' : '-') . $class) . '"';
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController()
{
return $GLOBALS['TSFE'];
}
}

View File

@@ -0,0 +1,174 @@
<?php
namespace SJBR\SrFreecap\ViewHelpers;
/*
* Copyright notice
*
* (c) 2013-2021 Stanislas Rolland <typo3AAAA(arobas)sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use SJBR\SrFreecap\ViewHelpers\TranslateViewHelper;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
class ImageViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string Name of the extension this view helper belongs to
*/
protected $extensionName = 'SrFreecap';
/**
* @var string Name of the extension this view helper belongs to
*/
protected $extensionKey = 'sr_freecap';
/**
* @var string Name of the plugin this view helper belongs to
*/
protected $pluginName = 'tx_srfreecap';
/**
* @var ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @param ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* @var PageRenderer
*/
protected $pageRenderer;
/**
* @param PageRenderer $pageRenderer
*/
public function injectPageRenderer(PageRenderer $pageRenderer)
{
$this->pageRenderer = $pageRenderer;
}
/**
* @var Context
*/
protected $context;
/**
* @param Context $context
*/
public function injectContext(Context $context)
{
$this->context = $context;
}
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('suffix', 'string', 'Suffix to be appended to the extenstion key when forming css class names', false, '');
}
/**
* Render the captcha image html
*
* @param string suffix to be appended to the extenstion key when forming css class names
* @return string The html used to render the captcha image
*/
public function render($suffix = '')
{
// This viewhelper needs a frontend user session
if (!is_object($this->getTypoScriptFrontendController()) || !isset($this->getTypoScriptFrontendController()->fe_user)) {
throw new SessionNotFoundException('No frontend user found in session!');
}
$value = '';
// Include the required JavaScript
$this->pageRenderer->addJsFooterFile(PathUtility::stripPathSitePrefix(ExtensionManagementUtility::extPath($this->extensionKey)) . 'Resources/Public/JavaScript/freeCap.js');
// Disable caching
$this->getTypoScriptFrontendController()->no_cache = true;
// Get the translation view helper
$translator = GeneralUtility::makeInstance(TranslateViewHelper::class);
// Generate the image url
$fakeId = substr(md5(uniqid(rand())), 0, 5);
$siteURL = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
$languageAspect = $this->context->getAspect('language');
$urlParams = [
'eIDSR' => 'sr_freecap_EidDispatcher',
'id' => $this->getTypoScriptFrontendController()->id,
'pluginName' => 'ImageGenerator',
'actionName' => 'show',
'formatName' => 'png',
'L' => $languageAspect->getId()
];
if ($this->getTypoScriptFrontendController()->MP) {
$urlParams['MP'] = $this->getTypoScriptFrontendController()->MP;
}
$urlParams['set'] = $fakeId;
$imageUrl = $siteURL . 'index.php?' . ltrim(GeneralUtility::implodeArrayForUrl('', $urlParams), '&');
// Generate the html text
$value = '<img' . $this->getClassAttribute('image', $suffix) . ' id="tx_srfreecap_captcha_image_' . $fakeId . '"'
. ' src="' . htmlspecialchars($imageUrl) . '"'
. ' alt="' . $translator->render('altText') . ' "/>'
. '<span' . $this->getClassAttribute('cant-read', $suffix) . '>' . $translator->render('cant_read1')
. ' <a href="#" onclick="this.blur();' . $this->extensionName . '.newImage(\'' . $fakeId . '\', \'' . $translator->render('noImageMessage').'\');return false;">'
. $translator->render('click_here') . '</a>'
. $translator->render('cant_read2') . '</span>';
return $value;
}
/**
* Returns a class attribute with a class-name prefixed with $this->pluginName and with all underscores substituted to dashes (-)
*
* @param string $class The class name (or the END of it since it will be prefixed by $this->pluginName.'-')
* @param string suffix to be appended to the extenstion key when forming css class names
* @return string the class attribute with the combined class name (with the correct prefix)
*/
protected function getClassAttribute($class, $suffix = '')
{
return ' class="' . trim(str_replace('_', '-', $this->pluginName) . ($suffix ? '-' . $suffix . '-' : '-') . $class) . '"';
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController()
{
return $GLOBALS['TSFE'];
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace SJBR\SrFreecap\ViewHelpers;
/*
* Copyright notice
*
* (c) 2013-2022 Stanislas Rolland <typo3AAAA@sjbr.ca>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Translate a key from locallang. The files are loaded from the folder
* "Resources/Private/Language/".
*
* Handle salutation suffixes
*
* == Examples ==
*
* <code title="Translate key">
* <f:translate key="key1" />
* </code>
* <output>
* // value of key "key1" in the current website language
* </output>
*
* <code title="Keep HTML tags">
* <f:translate key="htmlKey" htmlEscape="false" />
* </code>
* <output>
* // value of key "htmlKey" in the current website language, no htmlspecialchars applied
* </output>
*
* <code title="Translate key from custom locallang file">
* <f:translate key="LLL:EXT:myext/Resources/Private/Language/locallang.xml:key1" />
* </code>
* <output>
* // value of key "key1" in the current website language
* </output>
*
* <code title="Inline notation with arguments and default value">
* {f:translate(key: 'argumentsKey', arguments: {0: 'dog', 1: 'fox'}, default: 'default value')}
* </code>
* <output>
* // value of key "argumentsKey" in the current website language
* // with "%1" and "%2" are replaced by "dog" and "fox" (printf)
* // if the key is not found, the output is "default value"
* </output>
*/
class TranslateViewHelper extends AbstractViewHelper
{
/**
* @var string Name of the extension this view helper belongs to
*/
protected $extensionName = 'SrFreecap';
/**
* @var string Name of the extension this view helper belongs to
*/
protected $pluginName = 'tx_srfreecap';
/**
* @var array List of allowed suffixes
*/
protected $allowedSuffixes = ['formal', 'informal'];
/**
* @var ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @param ConfigurationManagerInterface $configurationManager
* @return void
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
public function initializeArguments()
{
$this->registerArgument('key', 'string', 'The language key to translate', true);
$this->registerArgument('default', 'string', 'Value to be used when the key is not found');
$this->registerArgument('htmlEscape', 'boolean', 'Whether to escape html', false, true);
$this->registerArgument('arguments', 'array', 'Arguments to be replaced in the string');
}
/**
* Translate a given key or use the tag body as default.
*
* @param string $key The locallang key
* @param string $default if the given locallang key could not be found, this value is used. . If this argument is not set, child nodes will be used to render the default
* @param boolean $htmlEscape true if the result should be htmlescaped. This won't have an effect for the default value
* @param array $arguments Arguments to be replaced in the resulting string
* @return string The translated key or tag body if key doesn't exist
* @author Christopher Hlubek <hlubek@networkteam.com>
* @author Bastian Waidelich <bastian@typo3.org>
*/
public function render($key = null)
{
if ($this->hasArgument('key')) {
$key = $this->arguments['key'];
}
$value = null;
$default = $this->hasArgument('default') ? $this->arguments['default'] : '';
$htmlEscape = $this->hasArgument('htmlEscape') ? $this->arguments['htmlEscape'] : false;
$arguments = $this->hasArgument('arguments') ? $this->arguments['arguments'] : [];
// If the suffix is allowed and we have a localized string for the desired salutation, we'll take that.
$settings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, $this->extensionName, $this->pluginName);
if (isset($settings['salutation']) && in_array($settings['salutation'], $this->allowedSuffixes, 1)) {
$expandedKey = $key . '_' . $settings['salutation'];
$value = LocalizationUtility::translate($expandedKey, $this->extensionName, $arguments);
}
if ($value === null) {
$value = LocalizationUtility::translate($key, $this->extensionName, $arguments);
}
if ($value === null) {
$value = $default !== null ? $default : $this->renderChildren();
} elseif ($htmlEscape) {
$value = htmlspecialchars($value);
}
return $value;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* An array consisting of implementations of middlewares for a middleware stack to be registered
*
* 'stackname' => [
* 'middleware-identifier' => [
* 'target' => classname or callable
* 'before/after' => array of dependencies
* ]
* ]
*/
return [
'frontend' => [
'sjbr/sr-freecap/eidsr' => [
'target' => \SJBR\SrFreecap\Middleware\EidHandler::class,
'after' => [
'typo3/cms-frontend/tsfe',
'typo3/cms-frontend/authentication',
'typo3/cms-frontend/backend-user-authentication',
'typo3/cms-frontend/site',
]
]
]
];

View File

@@ -0,0 +1,13 @@
# Configuration/Services.yaml
services:
_defaults:
autowire: true
autoconfigure: true
public: false
SJBR\SrFreecap\:
resource: '../Classes/*'
exclude: '../Classes/Domain/Model/*'
SJBR\SrFreecap\Configuration\ConfigurationHelper:
public: true

View File

@@ -0,0 +1,5 @@
<?php
defined('TYPO3') or die();
// Register SrFreecap static template
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile('sr_freecap', 'Configuration/TypoScript', 'freeCap CAPTCHA');

View File

@@ -0,0 +1,9 @@
## Configuring the Font Maker backend module
module.tx_srfreecap {
## Paths for backend templates
view {
templateRootPaths.0 = EXT:sr_freecap/Resources/Private/Backend/Templates/
partialRootPaths.0 = EXT:sr_freecap/Resources/Private/Backend/Partials/
layoutRootPaths.0 = EXT:sr_freecap/Resources/Private/Backend/Layouts/
}
}

View File

@@ -0,0 +1,73 @@
plugin.tx_srfreecap {
# cat=plugin.tx_srfreecap/dims/02; type=int+; label= CAPTCHA image height: Height of the generated image.
imageHeight = 90
# cat=plugin.tx_srfreecap/dims/03; type=int+; label= CAPTCHA image additional width: The width, in pixels, of the generated image is based on the maximum possible length of the word plus the value of this property.
imageAdditionalWidth = 30
# cat=plugin.tx_srfreecap/dims/04; type=int+; label= The horizontal (X) starting position of the word relative to the left of the image, in pixels.
textHorizontalPosition = 1
# cat=plugin.tx_srfreecap/dims/05; type=int+; label= The vertical (Y) starting position of the word relative to the top of the image, in pixels.
textVerticalPosition = 15
# cat=plugin.tx_srfreecap/dims/01; type=int[5-50]; label= Maximun word length: Maximun length of the randomly generated CAPTCHA string.
maxWordLength = 5
# cat=plugin.tx_srfreecap/color; type=boolean; label= Color of the text: 0=one random colour for all letters, 1=different random colour for each letter.
textColor = 0
# cat=plugin.tx_srfreecap/color; type=int[0-255]; label= Maximum darkness of the text color: The lower the number, the darker the color of the text may be.
colorMaximumDarkness = 5
# cat=plugin.tx_srfreecap/color; type=int[0-255]; label= Maximum lightness of the text color: The higher the number, the lighter the color of the text may be.
colorMaximumLightness = 50
# cat=plugin.tx_srfreecap//03; type=options[png,gif,jpg]; label= CAPTCHA image format: Format of the generated image.
imageFormat = png
# cat=plugin.tx_srfreecap/enable/01; type=boolean; label= Use list of words: If enabled, the CAPTCHA string will be selected from a list of words. If not, it will be randomly generated.
useWordsList = 0
# cat=plugin.tx_srfreecap/enable/02; type=boolean; label= Generate CAPTCHA with numbers only. This option is ignored if Use list of words is enabled.
generateNumbers = 0
# cat=plugin.tx_srfreecap//01; type=int+; label= Maximun attempts: Maximum times a user can refresh the image.
maxAttempts = 100
# cat=plugin.tx_srfreecap//04; type=options[,Transparent,White with grid,White with squiggles,Morphed image blocks]; label= Background type: If not set, will be random selection of possible types.
backgroundType = White with grid
# cat=plugin.tx_srfreecap/enable/04; type=boolean; label= Background blurring: If enabled, the background will be blurred. The string should be easier to read.
backgroundBlur = 1
# cat=plugin.tx_srfreecap/enable/05; type=boolean; label= Background morphing: If enabled, the background will be morphed (recommended).
backgroundMorph = 0
# cat=plugin.tx_srfreecap/enable/06; type=boolean; label= Merge string with background: 0=write CAPTCHA string over background, 1=merge CAPTCHA string with non-transparent background.
mergeWithBackground = 0
# cat=plugin.tx_srfreecap//05; type=string; label= Text morphing factor: Morphing factor applied to each character of the CAPTCHA string.
morphFactor = 0.1
# cat=plugin.tx_srfreecap/file/01; type=file[]; label= Default words list file: All words lists should be in the same directory.
defaultWordsList = EXT:sr_freecap/Resources/Private/Captcha/Words/default_freecap_words
# cat=plugin.tx_srfreecap//02; type=options[,formal,informal]; label= Type of salutation to use: Possible values are 'formal' or 'informal'.
salutation =
# cat=plugin.tx_srfreecap/file/02; type=string; label= List of GD font files: Coma-separated list of GD font files located in directory uploads/tx_srfreecap/ or in some extension directory using the usual syntax EXT:extensionKey/etc.
fontFiles =
# cat=plugin.tx_srfreecap/enable/03; type=boolean; label= Accessible output: If enabled, the CAPTCHA will include an audio version.
accessibleOutput = 0
# cat=plugin.tx_srfreecap/file/03; type=string; label= Accessible output image: File name of the accessible output image. If null, a textual message is used.
accessibleOutputImage = EXT:sr_freecap/Resources/Public/Images/audio.png
view {
# cat=plugin.tx_srfreecap/file; type=string; label=Path to template partials (FE)
partialRootPath = EXT:sr_freecap/Resources/Private/Partials/
}
}

View File

@@ -0,0 +1,66 @@
## Configuring the frontend rendering
plugin.tx_srfreecap {
settings {
imageHeight = {$plugin.tx_srfreecap.imageHeight}
imageAdditionalWidth = {$plugin.tx_srfreecap.imageAdditionalWidth}
imageFormat = {$plugin.tx_srfreecap.imageFormat}
textHorizontalPosition = {$plugin.tx_srfreecap.textHorizontalPosition}
textVerticalPosition = {$plugin.tx_srfreecap.textVerticalPosition}
backgroundType = {$plugin.tx_srfreecap.backgroundType}
backgroundBlur = {$plugin.tx_srfreecap.backgroundBlur}
backgroundMorph = {$plugin.tx_srfreecap.backgroundMorph}
maxWordLength = {$plugin.tx_srfreecap.maxWordLength}
useWordsList = {$plugin.tx_srfreecap.useWordsList}
generateNumbers = {$plugin.tx_srfreecap.generateNumbers}
colorMaximumDarkness = {$plugin.tx_srfreecap.colorMaximumDarkness}
colorMaximumLightness = {$plugin.tx_srfreecap.colorMaximumLightness}
textColor = {$plugin.tx_srfreecap.textColor}
mergeWithBackground = {$plugin.tx_srfreecap.mergeWithBackground}
morphFactor = {$plugin.tx_srfreecap.morphFactor}
defaultWordsList = {$plugin.tx_srfreecap.defaultWordsList}
fontFiles = {$plugin.tx_srfreecap.fontFiles}
maxAttempts = {$plugin.tx_srfreecap.maxAttempts}
accessibleOutput = {$plugin.tx_srfreecap.accessibleOutput}
accessibleOutputImage = {$plugin.tx_srfreecap.accessibleOutputImage}
salutation = {$plugin.tx_srfreecap.salutation}
}
view {
partialRootPaths {
0 = EXT:sr_freecap/Resources/Private/Partials/
1 = {$plugin.tx_srfreecap.view.partialRootPath}
}
}
_CSS_DEFAULT_STYLE (
.tx-srfreecap-captcha label { display: inline-block; width: 150px; }
img.tx-srfreecap-image { margin-left: 1em; margin-right: 1em; vertical-align: middle; }
.tx-srfreecap-image-accessible {
background-color: transparent;
vertical-align: middle;
}
.tx-srfreecap-accessible-link { width: 10em; float: right; vertical-align: middle; margin-right: 7em; }
.tx-srfreecap-cant-read { display: inline-block; width: 150px; }
.tx-srfreecap-cant-read a:link { background-color:transparent; font-weight:bold; text-decoration:none; }
)
}
## Backward pi_base compatibility
plugin.tx_srfreecap_pi2 {
_CSS_DEFAULT_STYLE (
.tx-srfreecap-pi1-captcha label { display: inline-block; width: 150px; }
img.tx-srfreecap-pi1-image { margin-left: 1em; margin-right: 1em; vertical-align: middle; }
.tx-srfreecap-pi1-image-accessible {
background-color: transparent;
vertical-align: middle;
}
.tx-srfreecap-pi1-accessible-link { width: 10em; float: right; vertical-align: middle; margin-right: 7em; }
.tx-srfreecap-pi1-cant-read { display: inline-block; width: 150px; }
.tx-srfreecap-pi1-cant-read a:link { background-color:transparent; font-weight:bold; text-decoration:none; }
)
}

View File

@@ -0,0 +1 @@
deny from all

View File

@@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@@ -0,0 +1,817 @@
<?
/************************************************************\
*
* freeCap v1.4.1 Copyright 2005 Howard Yeend
* www.puremango.co.uk
*
* This file is part of freeCap.
*
* freeCap is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* freeCap is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with freeCap; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
\************************************************************/
session_start();
//////////////////////////////////////////////////////
////// User Defined Vars:
//////////////////////////////////////////////////////
// try to avoid the 'free p*rn' method of CAPTCHA circumvention
// see www.wikipedia.com/captcha for more info
$site_tags[0] = "To avoid spam, please do NOT enter the text if";
$site_tags[1] = "this site is not puremango.co.uk";
// or more simply:
//$site_tags[0] = "for use only on puremango.co.uk";
// reword or add lines as you please
// or if you don't want any text:
$site_tags = null;
// where to write the above:
// 0=top
// 1=bottom
// 2=both
$tag_pos = 1;
// functions to call for random number generation
// mt_rand produces 'better' random numbers
// but if your server doesn't support it, it's fine to use rand instead
$rand_func = "mt_rand";
$seed_func = "mt_srand";
// which type of hash to use?
// possible values: "sha1", "md5", "crc32"
// sha1 supported by PHP4.3.0+
// md5 supported by PHP3+
// crc32 supported by PHP4.0.1+
$hash_func = "sha1";
// store in session so can validate in form processor
$_SESSION['hash_func'] = $hash_func;
// image type:
// possible values: "jpg", "png", "gif"
// jpg doesn't support transparency (transparent bg option ends up white)
// png isn't supported by old browsers (see http://www.libpng.org/pub/png/pngstatus.html)
// gif may not be supported by your GD Lib.
$output = "png";
// 0=generate pseudo-random string, 1=use dictionary
// dictionary is easier to recognise
// - both for humans and computers, so use random string if you're paranoid.
$use_dict = 1;
// if your server is NOT set up to deny web access to files beginning ".ht"
// then you should ensure the dictionary file is kept outside the web directory
// eg: if www.foo.com/index.html points to c:\website\www\index.html
// then the dictionary should be placed in c:\website\dict.txt
// test your server's config by trying to access the dictionary through a web browser
// you should NOT be able to view the contents.
// can leave this blank if not using dictionary
$dict_location = "./.ht_freecap_words";
// used to calculate image width, and for non-dictionary word generation
$max_word_length = 6;
// text colour
// 0=one random colour for all letters
// 1=different random colour for each letter
$col_type = 1;
// maximum times a user can refresh the image
// on a 6500 word dictionary, I think 15-50 is enough to not annoy users and make BF unfeasble.
// further notes re: BF attacks in "avoid brute force attacks" section, below
// on the other hand, those attempting OCR will find the ability to request new images
// very useful; if they can't crack one, just grab an easier target...
// for the ultra-paranoid, setting it to <5 will still work for most users
$max_attempts = 20;
// list of fonts to use
// font size should be around 35 pixels wide for each character.
// you can use my GD fontmaker script at www.puremango.co.uk to create your own fonts
// There are other programs to can create GD fonts, but my script allows a greater
// degree of control over exactly how wide each character is, and is therefore
// recommended for 'special' uses. For normal use of GD fonts,
// the GDFontGenerator @ http://www.philiplb.de is excellent for convering ttf to GD
// the fonts included with freeCap *only* include lowercase alphabetic characters
// so are not suitable for most other uses
// to increase security, you really should add other fonts
$font_locations = Array("./.ht_freecap_font1.gdf","./.ht_freecap_font2.gdf","./.ht_freecap_font3.gdf","./.ht_freecap_font4.gdf","./.ht_freecap_font5.gdf");
// background:
// 0=transparent (if jpg, white)
// 1=white bg with grid
// 2=white bg with squiggles
// 3=morphed image blocks
// 'random' background from v1.3 didn't provide any extra security (according to 2 independent experts)
// many thanks to http://ocr-research.org.ua and http://sam.zoy.org/pwntcha/ for testing
// for jpgs, 'transparent' is white
$bg_type = 2;
// should we blur the background? (looks nicer, makes text easier to read, takes longer)
$blur_bg = false;
// for bg_type 3, which images should we use?
// if you add your own, make sure they're fairly 'busy' images (ie a lot of shapes in them)
$bg_images = Array("./.ht_freecap_im1.jpg","./.ht_freecap_im2.jpg","./.ht_freecap_im3.jpg","./.ht_freecap_im4.jpg","./.ht_freecap_im5.jpg");
// for non-transparent backgrounds only:
// if 0, merges CAPTCHA with bg
// if 1, write CAPTCHA over bg
$merge_type = 0;
// should we morph the bg? (recommend yes, but takes a little longer to compute)
$morph_bg = true;
// you shouldn't need to edit anything below this, but it's extensively commented if you do want to play
// have fun, and email me with ideas, or improvements to the code (very interested in speed improvements)
// hope this script saves some spam :-)
//////////////////////////////////////////////////////
////// Create Images + initialise a few things
//////////////////////////////////////////////////////
// seed random number generator
// PHP 4.2.0+ doesn't need this, but lower versions will
$seed_func(make_seed());
// how faded should the bg be? (100=totally gone, 0=bright as the day)
// to test how much protection the bg noise gives, take a screenshot of the freeCap image
// and take it into a photo editor. play with contrast and brightness.
// If you can remove most of the bg, then it's not a good enough percentage
switch($bg_type)
{
case 0:
break;
case 1:
case 2:
$bg_fade_pct = 65;
break;
case 3:
$bg_fade_pct = 50;
break;
}
// slightly randomise the bg fade
$bg_fade_pct += $rand_func(-2,2);
// read each font and get font character widths
$font_widths = Array();
for($i=0 ; $i<sizeof($font_locations) ; $i++)
{
$handle = fopen($font_locations[$i],"r");
// read header of GD font, up to char width
$c_wid = fread($handle,11);
$font_widths[$i] = ord($c_wid{8})+ord($c_wid{9})+ord($c_wid{10})+ord($c_wid{11});
fclose($handle);
}
// modify image width depending on maximum possible length of word
// you shouldn't need to use words > 6 chars in length really.
$width = ($max_word_length*(array_sum($font_widths)/sizeof($font_widths))+75);
$height = 90;
$im = ImageCreate($width, $height);
$im2 = ImageCreate($width, $height);
//////////////////////////////////////////////////////
////// Avoid Brute Force Attacks:
//////////////////////////////////////////////////////
if(empty($_SESSION['freecap_attempts']))
{
$_SESSION['freecap_attempts'] = 1;
} else {
$_SESSION['freecap_attempts']++;
// if more than ($max_attempts) refreshes, block further refreshes
// can be negated by connecting with new session id
// could get round this by storing num attempts in database against IP
// could get round that by connecting with different IP (eg, using proxy servers)
// in short, there's little point trying to avoid brute forcing
// the best way to protect against BF attacks is to ensure the dictionary is not
// accessible via the web or use random string option
if($_SESSION['freecap_attempts']>$max_attempts)
{
$_SESSION['freecap_word_hash'] = false;
$bg = ImageColorAllocate($im,255,255,255);
ImageColorTransparent($im,$bg);
$red = ImageColorAllocate($im, 255, 0, 0);
// depending on how rude you want to be :-)
//ImageString($im,5,0,20,"bugger off you spamming bastards!",$red);
ImageString($im,5,15,20,"service no longer available",$red);
sendImage($im);
}
}
//////////////////////////////////////////////////////
////// Functions:
//////////////////////////////////////////////////////
function make_seed() {
// from http://php.net/srand
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}
function rand_color() {
global $bg_type,$rand_func;
if($bg_type==3)
{
// needs darker colour..
return $rand_func(10,100);
} else {
return $rand_func(60,170);
}
}
function myImageBlur($im)
{
// w00t. my very own blur function
// in GD2, there's a gaussian blur function. bunch of bloody show-offs... :-)
$width = imagesx($im);
$height = imagesy($im);
$temp_im = ImageCreateTrueColor($width,$height);
$bg = ImageColorAllocate($temp_im,150,150,150);
// preserves transparency if in orig image
ImageColorTransparent($temp_im,$bg);
// fill bg
ImageFill($temp_im,0,0,$bg);
// anything higher than 3 makes it totally unreadable
// might be useful in a 'real' blur function, though (ie blurring pictures not text)
$distance = 1;
// use $distance=30 to have multiple copies of the word. not sure if this is useful.
// blur by merging with itself at different x/y offsets:
ImageCopyMerge($temp_im, $im, 0, 0, 0, $distance, $width, $height-$distance, 70);
ImageCopyMerge($im, $temp_im, 0, 0, $distance, 0, $width-$distance, $height, 70);
ImageCopyMerge($temp_im, $im, 0, $distance, 0, 0, $width, $height, 70);
ImageCopyMerge($im, $temp_im, $distance, 0, 0, 0, $width, $height, 70);
// remove temp image
ImageDestroy($temp_im);
return $im;
}
function sendImage($pic)
{
// output image with appropriate headers
global $output,$im,$im2,$im3;
header(base64_decode("WC1DYXB0Y2hhOiBmcmVlQ2FwIDEuNCAtIHd3dy5wdXJlbWFuZ28uY28udWs="));
switch($output)
{
// add other cases as desired
case "jpg":
header("Content-Type: image/jpeg");
ImageJPEG($pic);
break;
case "gif":
header("Content-Type: image/gif");
ImageGIF($pic);
break;
case "png":
default:
header("Content-Type: image/png");
ImagePNG($pic);
break;
}
// kill GD images (removes from memory)
ImageDestroy($im);
ImageDestroy($im2);
ImageDestroy($pic);
if(!empty($im3))
{
ImageDestroy($im3);
}
exit();
}
//////////////////////////////////////////////////////
////// Choose Word:
//////////////////////////////////////////////////////
if($use_dict==1)
{
// load dictionary and choose random word
$words = @file($dict_location);
$word = strtolower($words[$rand_func(0,sizeof($words)-1)]);
// cut off line endings/other possible odd chars
$word = preg_replace('/[^a-z]/', '',$word);
// might be large file so forget it now (frees memory)
$words = "";
unset($words);
} else {
// based on code originally by breakzero at hotmail dot com
// (http://uk.php.net/manual/en/function.rand.php)
// generate pseudo-random string
// doesn't use ijtf as are easily mistaken
// I'm not using numbers because the custom fonts I've created don't support anything other than
// lowercase or space (but you can download new fonts or create your own using my GD fontmaker script)
$consonants = 'bcdghklmnpqrsvwxyz';
$vowels = 'aeuo';
$word = "";
$wordlen = $rand_func(5,$max_word_length);
for($i=0 ; $i<$wordlen ; $i++)
{
// don't allow to start with 'vowel'
if($rand_func(0,4)>=2 && $i!=0)
{
$word .= $vowels{$rand_func(0,strlen($vowels)-1)};
} else {
$word .= $consonants{$rand_func(0,strlen($consonants)-1)};
}
}
}
// save hash of word for comparison
// using hash so that if there's an insecurity elsewhere (eg on the form processor),
// an attacker could only get the hash
// also, shared servers usually give all users access to the session files
// echo `ls /tmp`; and echo `more /tmp/someone_elses_session_file`; usually work
// so even if your site is 100% secure, someone else's site on your server might not be
// hence, even if attackers can read the session file, they can't get the freeCap word
// (though most hashes are easy to brute force for simple strings)
$_SESSION['freecap_word_hash'] = $hash_func($word);
//////////////////////////////////////////////////////
////// Fill BGs and Allocate Colours:
//////////////////////////////////////////////////////
// set tag colour
// have to do this before any distortion
// (otherwise colour allocation fails when bg type is 1)
$tag_col = ImageColorAllocate($im,10,10,10);
$site_tag_col2 = ImageColorAllocate($im2,0,0,0);
// set debug colours (text colours are set later)
$debug = ImageColorAllocate($im, 255, 0, 0);
$debug2 = ImageColorAllocate($im2, 255, 0, 0);
// set background colour (can change to any colour not in possible $text_col range)
// it doesn't matter as it'll be transparent or coloured over.
// if you're using bg_type 3, you might want to try to ensure that the color chosen
// below doesn't appear too much in any of your background images.
$bg = ImageColorAllocate($im, 254, 254, 254);
$bg2 = ImageColorAllocate($im2, 254, 254, 254);
// set transparencies
ImageColorTransparent($im,$bg);
// im2 transparent to allow characters to overlap slightly while morphing
ImageColorTransparent($im2,$bg2);
// fill backgrounds
ImageFill($im,0,0,$bg);
ImageFill($im2,0,0,$bg2);
if($bg_type!=0)
{
// generate noisy background, to be merged with CAPTCHA later
// any suggestions on how best to do this much appreciated
// sample code would be even better!
// I'm not an OCR expert (hell, I'm not even an image expert; puremango.co.uk was designed in MsPaint)
// so the noise models are based around my -guesswork- as to what would make it hard for an OCR prog
// ideally, the character obfuscation would be strong enough not to need additional background noise
// in any case, I hope at least one of the options given here provide some extra security!
$im3 = ImageCreateTrueColor($width,$height);
$temp_bg = ImageCreateTrueColor($width*1.5,$height*1.5);
$bg3 = ImageColorAllocate($im3,255,255,255);
ImageFill($im3,0,0,$bg3);
$temp_bg_col = ImageColorAllocate($temp_bg,255,255,255);
ImageFill($temp_bg,0,0,$temp_bg_col);
// we draw all noise onto temp_bg
// then if we're morphing, merge from temp_bg to im3
// or if not, just copy a $widthx$height portion of $temp_bg to $im3
// temp_bg is much larger so that when morphing, the edges retain the noise.
if($bg_type==1)
{
// grid bg:
// draw grid on x
for($i=$rand_func(6,20) ; $i<$width*2 ; $i+=$rand_func(10,25))
{
ImageSetThickness($temp_bg,$rand_func(2,6));
$text_r = $rand_func(100,150);
$text_g = $rand_func(100,150);
$text_b = $rand_func(100,150);
$text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
ImageLine($temp_bg,$i,0,$i,$height*2,$text_colour3);
}
// draw grid on y
for($i=$rand_func(6,20) ; $i<$height*2 ; $i+=$rand_func(10,25))
{
ImageSetThickness($temp_bg,$rand_func(2,6));
$text_r = $rand_func(100,150);
$text_g = $rand_func(100,150);
$text_b = $rand_func(100,150);
$text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
ImageLine($temp_bg,0,$i,$width*2, $i ,$text_colour3);
}
} else if($bg_type==2) {
// draw squiggles!
$bg3 = ImageColorAllocate($im3,255,255,255);
ImageFill($im3,0,0,$bg3);
ImageSetThickness($temp_bg,4);
for($i=0 ; $i<strlen($word)+1 ; $i++)
{
$text_r = $rand_func(100,150);
$text_g = $rand_func(100,150);
$text_b = $rand_func(100,150);
$text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
$points = Array();
// draw random squiggle for each character
// the longer the loop, the more complex the squiggle
// keep random so OCR can't say "if found shape has 10 points, ignore it"
// each squiggle will, however, be a closed shape, so OCR could try to find
// line terminations and start from there. (I don't think they're that advanced yet..)
for($j=1 ; $j<$rand_func(5,10) ; $j++)
{
$points[] = $rand_func(1*(20*($i+1)),1*(50*($i+1)));
$points[] = $rand_func(30,$height+30);
}
ImagePolygon($temp_bg,$points,intval(sizeof($points)/2),$text_colour3);
}
} else if($bg_type==3) {
// take random chunks of $bg_images and paste them onto the background
for($i=0 ; $i<sizeof($bg_images) ; $i++)
{
// read each image and its size
$temp_im[$i] = ImageCreateFromJPEG($bg_images[$i]);
$temp_width[$i] = imagesx($temp_im[$i]);
$temp_height[$i] = imagesy($temp_im[$i]);
}
$blocksize = $rand_func(20,60);
for($i=0 ; $i<$width*2 ; $i+=$blocksize)
{
// could randomise blocksize here... hardly matters
for($j=0 ; $j<$height*2 ; $j+=$blocksize)
{
$image_index = $rand_func(0,sizeof($temp_im)-1);
$cut_x = $rand_func(0,$temp_width[$image_index]-$blocksize);
$cut_y = $rand_func(0,$temp_height[$image_index]-$blocksize);
ImageCopy($temp_bg, $temp_im[$image_index], $i, $j, $cut_x, $cut_y, $blocksize, $blocksize);
}
}
for($i=0 ; $i<sizeof($temp_im) ; $i++)
{
// remove bgs from memory
ImageDestroy($temp_im[$i]);
}
// for debug:
//sendImage($temp_bg);
}
// for debug:
//sendImage($im3);
if($morph_bg)
{
// morph background
// we do this separately to the main text morph because:
// a) the main text morph is done char-by-char, this is done across whole image
// b) if an attacker could un-morph the bg, it would un-morph the CAPTCHA
// hence bg is morphed differently to text
// why do we morph it at all? it might make it harder for an attacker to remove the background
// morph_chunk 1 looks better but takes longer
// this is a different and less perfect morph than the one we do on the CAPTCHA
// occasonally you get some dark background showing through around the edges
// it doesn't need to be perfect as it's only the bg.
$morph_chunk = $rand_func(1,5);
$morph_y = 0;
for($x=0 ; $x<$width ; $x+=$morph_chunk)
{
$morph_chunk = $rand_func(1,5);
$morph_y += $rand_func(-1,1);
ImageCopy($im3, $temp_bg, $x, 0, $x+30, 30+$morph_y, $morph_chunk, $height*2);
}
ImageCopy($temp_bg, $im3, 0, 0, 0, 0, $width, $height);
$morph_x = 0;
for($y=0 ; $y<=$height; $y+=$morph_chunk)
{
$morph_chunk = $rand_func(1,5);
$morph_x += $rand_func(-1,1);
ImageCopy($im3, $temp_bg, $morph_x, $y, 0, $y, $width, $morph_chunk);
}
} else {
// just copy temp_bg onto im3
ImageCopy($im3,$temp_bg,0,0,30,30,$width,$height);
}
ImageDestroy($temp_bg);
if($blur_bg)
{
myImageBlur($im3);
}
}
// for debug:
//sendImage($im3);
//////////////////////////////////////////////////////
////// Write Word
//////////////////////////////////////////////////////
// write word in random starting X position
$word_start_x = $rand_func(5,32);
// y positions jiggled about later
$word_start_y = 15;
if($col_type==0)
{
$text_r = rand_color();
$text_g = rand_color();
$text_b = rand_color();
$text_colour2 = ImageColorAllocate($im2, $text_r, $text_g, $text_b);
}
// write each char in different font
for($i=0 ; $i<strlen($word) ; $i++)
{
if($col_type==1)
{
$text_r = rand_color();
$text_g = rand_color();
$text_b = rand_color();
$text_colour2 = ImageColorAllocate($im2, $text_r, $text_g, $text_b);
}
$j = $rand_func(0,sizeof($font_locations)-1);
$font = ImageLoadFont($font_locations[$j]);
ImageString($im2, $font, $word_start_x+($font_widths[$j]*$i), $word_start_y, $word{$i}, $text_colour2);
}
// use last pixelwidth
$font_pixelwidth = $font_widths[$j];
// for debug:
//sendImage($im2);
//////////////////////////////////////////////////////
////// Morph Image:
//////////////////////////////////////////////////////
// calculate how big the text is in pixels
// (so we only morph what we need to)
$word_pix_size = $word_start_x+(strlen($word)*$font_pixelwidth);
// firstly move each character up or down a bit:
for($i=$word_start_x ; $i<$word_pix_size ; $i+=$font_pixelwidth)
{
// move on Y axis
// deviates at least 4 pixels between each letter
$prev_y = $y_pos;
do{
$y_pos = $rand_func(-5,5);
} while($y_pos<$prev_y+2 && $y_pos>$prev_y-2);
ImageCopy($im, $im2, $i, $y_pos, $i, 0, $font_pixelwidth, $height);
// for debug:
//ImageRectangle($im,$i,$y_pos+10,$i+$font_pixelwidth,$y_pos+70,$debug);
}
// for debug:
//sendImage($im);
ImageFilledRectangle($im2,0,0,$width,$height,$bg2);
// randomly morph each character individually on x-axis
// this is where the main distortion happens
// massively improved since v1.2
$y_chunk = 1;
$morph_factor = 1;
$morph_x = 0;
for($j=0 ; $j<strlen($word) ; $j++)
{
$y_pos = 0;
for($i=0 ; $i<=$height; $i+=$y_chunk)
{
$orig_x = $word_start_x+($j*$font_pixelwidth);
// morph x += so that instead of deviating from orig x each time, we deviate from where we last deviated to
// get it? instead of a zig zag, we get more of a sine wave.
// I wish we could deviate more but it looks crap if we do.
$morph_x += $rand_func(-$morph_factor,$morph_factor);
// had to change this to ImageCopyMerge when starting using ImageCreateTrueColor
// according to the manual; "when (pct is) 100 this function behaves identically to imagecopy()"
// but this is NOT true when dealing with transparencies...
ImageCopyMerge($im2, $im, $orig_x+$morph_x, $i+$y_pos, $orig_x, $i, $font_pixelwidth, $y_chunk, 100);
// for debug:
//ImageLine($im2, $orig_x+$morph_x, $i, $orig_x+$morph_x+1, $i+$y_chunk, $debug2);
//ImageLine($im2, $orig_x+$morph_x+$font_pixelwidth, $i, $orig_x+$morph_x+$font_pixelwidth+1, $i+$y_chunk, $debug2);
}
}
// for debug:
//sendImage($im2);
ImageFilledRectangle($im,0,0,$width,$height,$bg);
// now do the same on the y-axis
// (much easier because we can just do it across the whole image, don't have to do it char-by-char)
$y_pos = 0;
$x_chunk = 1;
for($i=0 ; $i<=$width ; $i+=$x_chunk)
{
// can result in image going too far off on Y-axis;
// not much I can do about that, apart from make image bigger
// again, I wish I could do 1.5 pixels
$y_pos += $rand_func(-1,1);
ImageCopy($im, $im2, $i, $y_pos, $i, 0, $x_chunk, $height);
// for debug:
//ImageLine($im,$i+$x_chunk,0,$i+$x_chunk,100,$debug);
//ImageLine($im,$i,$y_pos+25,$i+$x_chunk,$y_pos+25,$debug);
}
// for debug:
//sendImage($im);
// blur edges:
// doesn't really add any security, but looks a lot nicer, and renders text a little easier to read
// for humans (hopefully not for OCRs, but if you know better, feel free to disable this function)
// (and if you do, let me know why)
myImageBlur($im);
// for debug:
//sendImage($im);
if($output!="jpg" && $bg_type==0)
{
// make background transparent
ImageColorTransparent($im,$bg);
}
//////////////////////////////////////////////////////
////// Try to avoid 'free p*rn' style CAPTCHA re-use
//////////////////////////////////////////////////////
// ('*'ed to stop my site coming up for certain keyword searches on google)
// can obscure CAPTCHA word in some cases..
// write site tags 'shining through' the morphed image
ImageFilledRectangle($im2,0,0,$width,$height,$bg2);
if(is_array($site_tags))
{
for($i=0 ; $i<sizeof($site_tags) ; $i++)
{
// ensure tags are centered
$tag_width = strlen($site_tags[$i])*6;
// write tag is chosen position
if($tag_pos==0 || $tag_pos==2)
{
// write at top
ImageString($im2, 2, intval($width/2)-intval($tag_width/2), (10*$i), $site_tags[$i], $site_tag_col2);
}
if($tag_pos==1 || $tag_pos==2)
{
// write at bottom
ImageString($im2, 2, intval($width/2)-intval($tag_width/2), ($height-34+($i*10)), $site_tags[$i], $site_tag_col2);
}
}
}
ImageCopyMerge($im2,$im,0,0,0,0,$width,$height,80);
ImageCopy($im,$im2,0,0,0,0,$width,$height);
// for debug:
//sendImage($im);
//////////////////////////////////////////////////////
////// Merge with obfuscated background
//////////////////////////////////////////////////////
if($bg_type!=0)
{
// merge bg image with CAPTCHA image to create smooth background
// fade bg:
if($bg_type!=3)
{
$temp_im = ImageCreateTrueColor($width,$height);
$white = ImageColorAllocate($temp_im,255,255,255);
ImageFill($temp_im,0,0,$white);
ImageCopyMerge($im3,$temp_im,0,0,0,0,$width,$height,$bg_fade_pct);
// for debug:
//sendImage($im3);
ImageDestroy($temp_im);
$c_fade_pct = 50;
} else {
$c_fade_pct = $bg_fade_pct;
}
// captcha over bg:
// might want to not blur if using this method
// otherwise leaves white-ish border around each letter
if($merge_type==1)
{
ImageCopyMerge($im3,$im,0,0,0,0,$width,$height,100);
ImageCopy($im,$im3,0,0,0,0,$width,$height);
} else {
// bg over captcha:
ImageCopyMerge($im,$im3,0,0,0,0,$width,$height,$c_fade_pct);
}
}
// for debug:
//sendImage($im);
//////////////////////////////////////////////////////
////// Write tags, remove variables and output!
//////////////////////////////////////////////////////
// tag it
// feel free to remove/change
// but if it's not essential I'd appreciate you leaving it
// after all, I've put a lot of work into this and am giving it away for free
// the least you could do is give me credit (or buy me stuff from amazon!)
// but I understand that in professional environments, your boss might not like this tag
// so that's cool.
$tag_str = "freeCap v1.41 - puremango.co.uk";
// for debug:
//$tag_str = "[".$word."]";
// ensure tag is right-aligned
$tag_width = strlen($tag_str)*6;
// write tag
ImageString($im, 2, $width-$tag_width, $height-13, $tag_str, $tag_col);
// unset all sensetive vars
// in case someone include()s this file on a shared server
// you might think this unneccessary, as it exit()s
// but by using register_shutdown_function
// on a -very- insecure shared server, they -might- be able to get the word
unset($word);
// the below aren't really essential, but might aid an OCR attack if discovered.
// so we unset them
unset($use_dict);
unset($dict_location);
unset($max_word_length);
unset($bg_type);
unset($bg_images);
unset($merge_type);
unset($bg_fade_pct);
unset($morph_bg);
unset($col_type);
unset($max_attempts);
unset($font_locations);
// output final image :-)
sendImage($im);
// (sendImage also destroys all used images)
?>

View File

@@ -0,0 +1 @@
deny from all

View File

@@ -0,0 +1,11 @@
<f:be.container includeCssFiles="{0: '{f:uri.resource(path:\'StyleSheets/FontMaker.css\')}'}">
<div id="typo3-docheader">
<div class="typo3-docheader-functions">&nbsp;</div>
<div class="typo3-docheader-buttons">&nbsp;</div>
</div>
<div id="typo3-docbody">
<div id="typo3-inner-docbody">
<div id="typo3-mod-freecap-font-maker"><f:render section="Content" /></div>
</div>
</div>
</f:be.container>

View File

@@ -0,0 +1,10 @@
{namespace freeCap=SJBR\SrFreecap\ViewHelpers}
<f:form.validationResults for="{for}">
<f:if condition="{validationResults.flattenedErrors}">
<ul class="errors">
<f:for each="{validationResults.errors}" as="error">
<li><freeCap:translate key="{error.code}">{error.code}</freeCap:translate></li>
</f:for>
</ul>
</f:if>
</f:form.validationResults>

View File

@@ -0,0 +1,27 @@
<f:layout name="Default" />
<f:section name="Content">
<h2>{f:translate(key: 'title-created')}</h2>
<fieldset class="sjbr-freecap-font-maker-created">
<dl>
<dt>
<label>{f:translate(key: 'usingFontFile')}</label>
</dt>
<dd>
<strong>{font.ttfFontFileName}</strong>
</dd>
<dt>
<label for="sjbr-freecap-font-maker-characterWidth">{f:translate(key: 'pngImageCreated')}</label>
</dt>
<dd>
<img src="../{font.pngImageFileName}" alt="{f:translate(key: 'pngFontImage')}" />
</dd>
<dt>
<label for="sjbr-freecap-font-maker-characterHeight">{f:translate(key: 'gdFontFileCreated')}</label>
</dt>
<dd>
<strong>{font.gdFontFileName}</strong>
</dd>
</dl>
</fieldset>
</f:section>

View File

@@ -0,0 +1,61 @@
<f:layout name="Default" />
<f:section name="Content">
<h2>{f:translate(key: 'title')}</h2>
<f:form method="post" controller="FontMaker" action="create" name="font" object="{font}" class="sjbr-freecap-font-maker">
<fieldset>
<dl>
<dt>
<label>{f:translate(key: 'charactersToIncludeInFont')}</label>
</dt>
<dd>
<f:form.radio id="sjbr-freecap-font-maker-charactersIncludedInFont-0" property="charactersIncludedInFont" value="0" /><label for="sjbr-freecap-font-maker-charactersIncludedInFont-0">{f:translate(key: 'numbers-only')}</label>
<br />
<f:form.radio id="sjbr-freecap-font-maker-charactersIncludedInFont-1" property="charactersIncludedInFont" value="1" /><label for="sjbr-freecap-font-maker-charactersIncludedInFont-1">{f:translate(key: 'ASCII-lowercase-letters')}</label>
<br />
<f:form.radio id="sjbr-freecap-font-maker-charactersIncludedInFont-2" property="charactersIncludedInFont" value="2" /><label for="sjbr-freecap-font-maker-charactersIncludedInFont-2">{f:translate(key: 'ANSI-extended-ASCII-lowercase-letters')}</label>
</dd>
<dt>
<label for="sjbr-freecap-font-maker-characterWidth">{f:translate(key: 'characterWidth')}</label>
</dt>
<dd>
<f:form.textfield id="sjbr-freecap-font-maker-characterWidth" property="characterWidth" size="5" />
<f:render partial="FormErrors" arguments="{for: 'font.characterWidth'}" />
</dd>
<dt>
<label for="sjbr-freecap-font-maker-characterHeight">{f:translate(key: 'characterHeight')}</label>
</dt>
<dd>
<f:form.textfield id="sjbr-freecap-font-maker-characterHeight" property="characterHeight" size="5" />
<f:render partial="FormErrors" arguments="{for: 'font.characterHeight'}" />
</dd>
<dt>
<label>{f:translate(key: 'endianness')}</label>
</dt>
<dd>
<f:form.radio id="sjbr-freecap-font-maker-endianness-0" property="endianness" value="0" /><label for="sjbr-freecap-font-maker-endianness-0">{f:translate(key: 'littleEndian')}</label>
<br />
<f:form.radio id="sjbr-freecap-font-maker-endianness-1" property="endianness" value="1" /><label for="sjbr-freecap-font-maker-endianness-1">{f:translate(key: 'bigEndian')}</label>
</dd>
<dt>
<label for="sjbr-freecap-font-maker-ttfFontFileName">{f:translate(key: 'pathToTTFFile')}</label>
</dt>
<dd>
<f:form.textfield id="sjbr-freecap-font-maker-ttfFontFileName" property="ttfFontFileName" size="50" />
<f:render partial="FormErrors" arguments="{for: 'font.ttfFontFileName'}" />
</dd>
<dt>
<label for="sjbr-freecap-font-maker-gdFontFilePrefix">{f:translate(key: 'gdFontFilePrefix')}</label>
</dt>
<dd>
<f:form.textfield id="sjbr-freecap-font-maker-gdFontFilePrefix" property="gdFontFilePrefix" size="25" />
</dd>
<dt>
</dt>
<dd>
<f:form.submit class="submit" value="{f:translate(key: 'makeFont')}" />
</dd>
</dl>
</fieldset>
</f:form>
</f:section>

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Some files were not shown because too many files have changed in this diff Show More