CaptchaHelper.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <?php
  2. App::uses('AppHelper', 'View/Helper');
  3. App::uses('CaptchaLib', 'Tools.Lib');
  4. if (!defined('BR')) {
  5. define('BR', '<br />');
  6. }
  7. /**
  8. * PHP5 / CakePHP2
  9. *
  10. * @author Mark Scherer
  11. * @link http://www.dereuromark.de
  12. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  13. */
  14. /**
  15. * Works togehter with captcha behaviour/component
  16. */
  17. class CaptchaHelper extends AppHelper {
  18. public $helpers = array('Form');
  19. protected $_defaultConfig = array(
  20. 'difficulty' => 1, # initial diff. level (@see operator: + = 0, +- = 1, +-* = 2)
  21. 'raiseDifficulty' => 2, # number of failed trails, after the x. one the following one it will be more difficult
  22. );
  23. protected $numberConvert = null;
  24. protected $operatorConvert = null;
  25. public function __construct($View = null, $config = array()) {
  26. $defaults = CaptchaLib::$defaults + $this->_defaultConfig;
  27. $defaults = (array)Configure::read('Captcha') + $defaults;
  28. $config += $defaults;
  29. parent::__construct($View, $config);
  30. // First of all we are going to set up an array with the text equivalents of all the numbers we will be using.
  31. $this->numberConvert = array(0 => __d('tools', 'zero'), 1 => __d('tools', 'one'), 2 => __d('tools', 'two'), 3 => __d('tools', 'three'), 4 => __d('tools', 'four'), 5 => __d('tools', 'five'), 6 => __d('tools', 'six'), 7 => __d('tools', 'seven'), 8 => __d('tools', 'eight'), 9 => __d('tools', 'nine'), 10 => __d('tools', 'ten'));
  32. // Set up an array with the operators that we want to use. With difficulty=1 it is only subtraction and addition.
  33. $this->operatorConvert = array(0 => array('+', __d('tools', 'calcPlus')), 1 => array('-', __d('tools', 'calcMinus')), 2 => '*', __d('tools', 'calcTimes'));
  34. }
  35. /**
  36. * //TODO: move to Lib
  37. * shows the statusText of Relations
  38. *
  39. * @param int $difficulty: not build in yet
  40. * @return array
  41. */
  42. protected function _generate($difficulty = null) {
  43. // Choose the first number randomly between 6 and 10. This is to stop the answer being negative.
  44. $numberOne = mt_rand(6, 10);
  45. // Choose the second number randomly between 0 and 5.
  46. $numberTwo = mt_rand(0, 5);
  47. // Choose the operator randomly from the array.
  48. $captchaOperatorSelection = $this->operatorConvert[mt_rand(0, 1)];
  49. $captchaOperator = $captchaOperatorSelection[mt_rand(0, 1)];
  50. // Get the equation in textual form to show to the user.
  51. $code = (mt_rand(0, 1) == 1 ? $this->numberConvert[$numberOne] : $numberOne) . ' ' . $captchaOperator . ' ' . (mt_rand(0, 1) == 1 ? $this->numberConvert[$numberTwo] : $numberTwo);
  52. // Evaluate the equation and get the result.
  53. eval('$result = ' . $numberOne . ' ' . $captchaOperatorSelection[0] . ' ' . $numberTwo . ';');
  54. return array('code' => $code, 'result' => $result);
  55. }
  56. /**
  57. * Main captcha output (usually called from $this->input() automatically)
  58. * - hash-based
  59. * - session-based (not impl.)
  60. * - db-based (not impl.)
  61. *
  62. * @return string HTML
  63. */
  64. public function captcha($modelName = null) {
  65. $captchaCode = $this->_generate();
  66. // Session-Way (only one form at a time) - must be a component then
  67. //$this->Session->write('Captcha.result', $result);
  68. // DB-Way (several forms possible, high security via IP-Based max limits)
  69. // the following should be done in a component and passed to the view/helper
  70. // $Captcha = ClassRegistry::init('Captcha');
  71. // $this->Captcha->new(); $this->Captcha->update(); etc
  72. // Timestamp-SessionID-Hash-Way (several forms possible, not as secure)
  73. $hash = $this->_buildHash($captchaCode);
  74. $return = '';
  75. if (in_array($this->settings['type'], array('active', 'both'))) {
  76. // //todo obscure html here?
  77. $fill = ''; //'<span></span>';
  78. $return .= '<span id="captchaCode">' . $fill . '' . $captchaCode['code'] . '</span>';
  79. }
  80. $field = $this->_fieldName($modelName);
  81. // add passive part on active forms as well
  82. $return .= '<div style="display:none">' .
  83. $this->Form->input($field . '_hash', array('value' => $hash)) .
  84. $this->Form->input($field . '_time', array('value' => time())) .
  85. $this->Form->input((!empty($modelName) ? $modelName . '.' : '') . $this->settings['dummyField'], array('value' => '')) .
  86. '</div>';
  87. return $return;
  88. }
  89. /**
  90. * Active math captcha
  91. * either combined with between=true (all in this one funtion)
  92. * or seperated by =false (needs input(false) and captcha() calls then)
  93. *
  94. * @param bool between: [default: true]
  95. * @return string HTML
  96. */
  97. public function input($modelName = null, $options = array()) {
  98. $defaults = array(
  99. 'type' => 'text',
  100. 'class' => 'captcha',
  101. 'value' => '',
  102. 'maxlength' => 3,
  103. 'label' => __d('tools', 'Captcha') . BR . __d('tools', 'captchaExplained'),
  104. 'combined' => true,
  105. 'autocomplete' => 'off',
  106. 'after' => __d('tools', 'captchaTip'),
  107. );
  108. $options += $defaults;
  109. if ($options['combined'] === true) {
  110. $options['between'] = $this->captcha($modelName);
  111. if (in_array($this->settings['type'], array('active', 'both'))) {
  112. $options['between'] .= ' = ';
  113. }
  114. }
  115. unset($options['combined']);
  116. $field = $this->_fieldName($modelName);
  117. return $this->Form->input($field . '', $options); // TODO: rename: _code
  118. }
  119. /**
  120. * Passive captcha
  121. *
  122. * @return string HTML
  123. */
  124. public function passive($modelName = null, $options = array()) {
  125. $tmp = $this->settings['type'];
  126. $this->settings['type'] = 'passive';
  127. $res = $this->captcha($modelName);
  128. $this->settings['type'] = $tmp;
  129. return $res;
  130. }
  131. /**
  132. * Active captcha
  133. * (+ passive captcha right now)
  134. *
  135. * @return string Form input
  136. */
  137. public function active($modelName = null, $options = array()) {
  138. return $this->input($modelName, $options);
  139. }
  140. /**
  141. * @param array $captchaCode
  142. * @return string Hash
  143. */
  144. protected function _buildHash($data) {
  145. return CaptchaLib::buildHash($data, $this->settings, true);
  146. }
  147. /**
  148. * @return string Field name
  149. */
  150. protected function _fieldName($modelName = null) {
  151. $fieldName = 'captcha';
  152. if (isSet($modelName)) {
  153. $fieldName = $modelName . '.' . $fieldName;
  154. }
  155. return $fieldName;
  156. }
  157. }