CaptchaHelper.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. /**
  3. * PHP5 / CakePHP1.3
  4. *
  5. * @author Mark Scherer
  6. * @link http://www.dereuromark.de
  7. * @package tools plugin
  8. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  9. */
  10. /**
  11. * works togehter with captcha behaviour/component
  12. * 2011-06-11 ms
  13. */
  14. class CaptchaHelper extends AppHelper {
  15. public $helpers = array('Form');
  16. protected $defaults = array(
  17. 'difficulty' => 1, # initial diff. level (@see operator: + = 0, +- = 1, +-* = 2)
  18. 'raiseDifficulty' => 2, # number of failed trails, after the x. one the following one it will be more difficult
  19. );
  20. protected $settings = null;
  21. protected $numberConvert = null;
  22. protected $operatorConvert = null;
  23. public function __construct($View = null, $settings = array()) {
  24. parent::__construct($View, $settings);
  25. # First of all we are going to set up an array with the text equivalents of all the numbers we will be using.
  26. $this->numberConvert = array(0=>'zero', 1=>'one', 2=>'two', 3=>'three', 4=>'four', 5=>'five', 6=>'six', 7=>'seven', 8=>'eight', 9=>'nine', 10=>'ten');
  27. # Set up an array with the operators that we want to use. With difficulty=1 it is only subtraction and addition.
  28. $this->operatorConvert = array(0=>array('+',__('calcPlus')), 1=>array('-',__('calcMinus')), 2=>'*',__('calcTimes'));
  29. App::import('Lib', 'Tools.CaptchaLib');
  30. $this->settings = array_merge(CaptchaLib::$defaults, $this->defaults);
  31. $settings = (array)Configure::read('Captcha');
  32. if (!empty($settings)) {
  33. $this->settings = array_merge($this->settings, $settings);
  34. }
  35. }
  36. /**
  37. * //TODO: move to Lib
  38. * shows the statusText of Relations
  39. * @param int $difficulty: not build in yet
  40. * 2008-12-12 ms
  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. * 2009-12-18 ms
  62. */
  63. public function captcha($modelName = null) {
  64. $captchaCode = $this->generate();
  65. # Session-Way (only one form at a time) - must be a component then
  66. //$this->Session->write('Captcha.result', $result);
  67. # DB-Way (several forms possible, high security via IP-Based max limits)
  68. // the following should be done in a component and passed to the view/helper
  69. // $Captcha = ClassRegistry::init('Captcha');
  70. // $this->Captcha->new(); $this->Captcha->update(); etc
  71. # Timestamp-SessionID-Hash-Way (several forms possible, not as secure)
  72. $hash = $this->buildHash($captchaCode);
  73. $return = '';
  74. if (in_array($this->settings['type'], array('active', 'both'))) {
  75. # //todo obscure html here?
  76. $fill = ''; //'<span></span>';
  77. $return .= '<span id="captchaCode">'.$fill.''.$captchaCode['code'].'</span>';
  78. }
  79. $field = $this->_fieldName($modelName);
  80. # add passive part on active forms as well
  81. $return .= '<div style="display:none">'.
  82. $this->Form->input($field.'_hash', array('value'=>$hash)).
  83. $this->Form->input($field.'_time', array('value'=>time())).
  84. $this->Form->input((!empty($modelName)?$modelName.'.':'').$this->settings['dummyField'], array('value'=>'')).
  85. '</div>';
  86. return $return;
  87. }
  88. /**
  89. * active math captcha
  90. * either combined with between=true (all in this one funtion)
  91. * or seperated by =false (needs input(false) and captcha() calls then)
  92. * @param bool between: [default: true]
  93. * 2010-01-08 ms
  94. */
  95. public function input($modelName = null, $options = array()) {
  96. $defaultOptions = array(
  97. 'type'=>'text',
  98. 'class'=>'captcha',
  99. 'value'=>'',
  100. 'maxlength'=>3,
  101. 'label'=>__('Captcha').BR.__('captchaExplained'),
  102. 'combined'=>true,
  103. 'autocomplete'=>'off'
  104. );
  105. $options = array_merge($defaultOptions, $options);
  106. if ($options['combined'] === true) {
  107. $options['between'] = $this->captcha($modelName);
  108. if (in_array($this->settings['type'], array('active', 'both'))) {
  109. $options['between'] .= ' = ';
  110. }
  111. }
  112. unset($options['combined']);
  113. $field = $this->_fieldName($modelName);
  114. return $this->Form->input($field.'', $options); // TODO: rename: _code
  115. }
  116. /**
  117. * passive captcha
  118. * 2010-01-08 ms
  119. */
  120. public function passive($modelName = null, $options = array()) {
  121. return $this->captcha($modelName);
  122. }
  123. /**
  124. * active captcha
  125. * (+ passive captcha right now)
  126. * 2010-01-08 ms
  127. */
  128. public function active($modelName = null, $options = array()) {
  129. return $this->input($modelName, $options);
  130. }
  131. /**
  132. * @param array $captchaCode
  133. */
  134. protected function buildHash($data) {
  135. return CaptchaLib::buildHash($data, $this->settings, true);
  136. }
  137. /** following not needed */
  138. /*
  139. public function validateCaptcha($modelName, $data = null) {
  140. if (!empty($data[$modelName]['homepage'])) {
  141. // trigger error - SPAM!!!
  142. } elseif (empty($data[$modelName]['captcha_hash']) || empty($this->data[$modelName]['captcha_time']) || $this->data[$modelName]['captcha_time'] > time()-CAPTCHA_MIN_TIME) {
  143. // trigger error - SPAM!!!
  144. } elseif (isset($this->data[$modelName]['captcha'])) {
  145. $timestamp = date(FORMAT_DB_DATE, $this->data[$modelName]['captcha_time']);
  146. $hash = Security::hash($timestamp.'_'.$captchaCode['result'].'_');
  147. if ($this->data[$modelName]['captcha_hash'] == $hash) {
  148. return true;
  149. }
  150. }
  151. return false;
  152. }
  153. */
  154. protected function _fieldName($modelName = null) {
  155. $fieldName = 'captcha';
  156. if (isSet($modelName)) {
  157. $fieldName = $modelName.'.'.$fieldName;
  158. }
  159. return $fieldName;
  160. }
  161. }