/class/captcha/xoopscaptcha.php

https://gitlab.com/VoyaTrax/vtCMS2 · PHP · 440 lines · 233 code · 45 blank · 162 comment · 36 complexity · f4b8710222712c46f5cdb30641d2f808 MD5 · raw file

  1. <?php
  2. /**
  3. * CAPTCHA configurations for Image mode
  4. *
  5. * Based on DuGris' SecurityImage
  6. *
  7. * You may not change or alter any portion of this comment or credits
  8. * of supporting developers from this source code or any supporting source code
  9. * which is considered copyrighted (c) material of the original comment or credit authors.
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. *
  14. * @copyright (c) 2000-2016 XOOPS Project (www.xoops.org)
  15. * @license GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
  16. * @package class
  17. * @subpackage CAPTCHA
  18. * @since 2.3.0
  19. * @author Taiwen Jiang <phppp@users.sourceforge.net>
  20. */
  21. defined('XOOPS_ROOT_PATH') || exit('Restricted access');
  22. /**
  23. * Class XoopsCaptcha
  24. */
  25. class XoopsCaptcha
  26. {
  27. // static $instance;
  28. public $active;
  29. public $handler;
  30. public $path_basic;
  31. public $path_plugin;
  32. public $name;
  33. public $config = array();
  34. public $message = array(); // Logging error messages
  35. /**
  36. * construct
  37. */
  38. public function __construct()
  39. {
  40. xoops_loadLanguage('captcha');
  41. // Load static configurations
  42. $this->path_basic = XOOPS_ROOT_PATH . '/class/captcha';
  43. $this->path_plugin = XOOPS_ROOT_PATH . '/Frameworks/captcha';
  44. $this->config = $this->loadConfig();
  45. $this->name = $this->config['name'];
  46. }
  47. /**
  48. * Get Instance
  49. *
  50. * @return Instance
  51. */
  52. public static function getInstance()
  53. {
  54. static $instance;
  55. if (!isset($instance)) {
  56. $class = __CLASS__;
  57. $instance = new $class();
  58. }
  59. return $instance;
  60. }
  61. /**
  62. * XoopsCaptcha::loadConfig()
  63. *
  64. * @param mixed $filename
  65. *
  66. * @return array
  67. */
  68. public function loadConfig($filename = null)
  69. {
  70. $basic_config = array();
  71. $plugin_config = array();
  72. $filename = empty($filename) ? 'config.php' : 'config.' . $filename . '.php';
  73. if (file_exists($file = $this->path_basic . '/' . $filename)) {
  74. $basic_config = include $file;
  75. }
  76. if (file_exists($file = $this->path_plugin . '/' . $filename)) {
  77. $plugin_config = include $file;
  78. }
  79. $config = array_merge($basic_config, $plugin_config);
  80. foreach ($config as $key => $val) {
  81. $config[$key] = $val;
  82. }
  83. return $config;
  84. }
  85. /**
  86. * XoopsCaptcha::isActive()
  87. *
  88. * @return bool
  89. */
  90. public function isActive()
  91. {
  92. if (isset($this->active)) {
  93. return $this->active;
  94. }
  95. if (!empty($this->config['disabled'])) {
  96. $this->active = false;
  97. return $this->active;
  98. }
  99. if (!empty($this->config['skipmember']) && is_object($GLOBALS['xoopsUser'])) {
  100. $this->active = false;
  101. return $this->active;
  102. }
  103. if (!isset($this->handler)) {
  104. $this->loadHandler();
  105. }
  106. $this->active = isset($this->handler);
  107. return $this->active;
  108. }
  109. /**
  110. * XoopsCaptcha::loadHandler()
  111. *
  112. * @param mixed $name
  113. * @return
  114. */
  115. public function loadHandler($name = null)
  116. {
  117. $name = !empty($name) ? $name : (empty($this->config['mode']) ? 'text' : $this->config['mode']);
  118. $class = 'XoopsCaptcha' . ucfirst($name);
  119. if (!empty($this->handler) && get_class($this->handler) == $class) {
  120. return $this->handler;
  121. }
  122. $this->handler = null;
  123. if (file_exists($file = $this->path_basic . '/' . $name . '.php')) {
  124. require_once $file;
  125. } else {
  126. if (file_exists($file = $this->path_plugin . '/' . $name . '.php')) {
  127. require_once $file;
  128. }
  129. }
  130. if (!class_exists($class)) {
  131. $class = 'XoopsCaptchaText';
  132. require_once $this->path_basic . '/text.php';
  133. }
  134. $handler = new $class($this);
  135. if ($handler->isActive()) {
  136. $this->handler = $handler;
  137. $this->handler->loadConfig($name);
  138. }
  139. return $this->handler;
  140. }
  141. /**
  142. * XoopsCaptcha::setConfigs()
  143. *
  144. * @param mixed $configs
  145. * @return bool
  146. */
  147. public function setConfigs($configs)
  148. {
  149. foreach ($configs as $key => $val) {
  150. $this->setConfig($key, $val);
  151. }
  152. return true;
  153. }
  154. /**
  155. * XoopsCaptcha::setConfig()
  156. *
  157. * @param mixed $name
  158. * @param mixed $val
  159. * @return bool
  160. */
  161. public function setConfig($name, $val)
  162. {
  163. if (isset($this->$name)) {
  164. $this->$name = $val;
  165. } else {
  166. $this->config[$name] = $val;
  167. }
  168. return true;
  169. }
  170. /**
  171. * Verify user submission
  172. */
  173. /**
  174. * XoopsCaptcha::verify()
  175. *
  176. * @param mixed $skipMember
  177. * @param mixed $name
  178. * @return bool
  179. */
  180. public function verify($skipMember = null, $name = null)
  181. {
  182. $sessionName = empty($name) ? $this->name : $name;
  183. $skipMember = ($skipMember === null) ? $_SESSION["{$sessionName}_skipmember"] : $skipMember;
  184. $maxAttempts = $_SESSION["{$sessionName}_maxattempts"];
  185. $attempt = $_SESSION["{$sessionName}_attempt"];
  186. $is_valid = false;
  187. // Skip CAPTCHA verification if disabled
  188. if (!$this->isActive()) {
  189. $is_valid = true;
  190. // Skip CAPTCHA for member if set
  191. } elseif (is_object($GLOBALS['xoopsUser']) && !empty($skipMember)) {
  192. $is_valid = true;
  193. // Kill too many attempts
  194. } elseif (!empty($maxAttempts) && $attempt > $maxAttempts) {
  195. $this->message[] = _CAPTCHA_TOOMANYATTEMPTS;
  196. // Verify the code
  197. } else {
  198. $is_valid = $this->handler->verify($sessionName);
  199. }
  200. if (!$is_valid) {
  201. // Increase the attempt records on failure
  202. $_SESSION["{$sessionName}_attempt"]++;
  203. // Log the error message
  204. $this->message[] = _CAPTCHA_INVALID_CODE;
  205. } else {
  206. // reset attempt records on success
  207. $_SESSION["{$sessionName}_attempt"] = null;
  208. }
  209. $this->destroyGarbage(true);
  210. return $is_valid;
  211. }
  212. /**
  213. * XoopsCaptcha::getCaption()
  214. *
  215. * @return mixed|string
  216. */
  217. public function getCaption()
  218. {
  219. return defined('_CAPTCHA_CAPTION') ? constant('_CAPTCHA_CAPTION') : '';
  220. }
  221. /**
  222. * XoopsCaptcha::getMessage()
  223. *
  224. * @return string
  225. */
  226. public function getMessage()
  227. {
  228. return implode('<br />', $this->message);
  229. }
  230. /**
  231. * Destroy historical stuff
  232. * @param bool $clearSession
  233. * @return bool
  234. */
  235. public function destroyGarbage($clearSession = false)
  236. {
  237. $this->loadHandler();
  238. if (is_callable($this->handler, 'destroyGarbage')) {
  239. $this->handler->destroyGarbage();
  240. }
  241. if ($clearSession) {
  242. $_SESSION[$this->name . '_name'] = null;
  243. $_SESSION[$this->name . '_skipmember'] = null;
  244. $_SESSION[$this->name . '_code'] = null;
  245. $_SESSION[$this->name . '_maxattempts'] = null;
  246. }
  247. return true;
  248. }
  249. /**
  250. * XoopsCaptcha::render()
  251. *
  252. * @return string
  253. */
  254. public function render()
  255. {
  256. $_SESSION[$this->name . '_name'] = $this->name;
  257. $_SESSION[$this->name . '_skipmember'] = $this->config['skipmember'];
  258. $form = '';
  259. if (!$this->active || empty($this->config['name'])) {
  260. return $form;
  261. }
  262. $maxAttempts = $this->config['maxattempts'];
  263. $_SESSION[$this->name . '_maxattempts'] = $maxAttempts;
  264. $attempt = isset($_SESSION[$this->name . '_attempt']) ? $_SESSION[$this->name . '_attempt'] : 0;
  265. $_SESSION[$this->name . '_attempt'] = $attempt;
  266. // Failure on too many attempts
  267. if (!empty($maxAttempts) && $attempt > $maxAttempts) {
  268. $form = _CAPTCHA_TOOMANYATTEMPTS;
  269. // Load the form element
  270. } else {
  271. $form = $this->loadForm();
  272. }
  273. return $form;
  274. }
  275. /**
  276. * XoopsCaptcha::renderValidationJS()
  277. *
  278. * @return string
  279. */
  280. public function renderValidationJS()
  281. {
  282. if (!$this->active || empty($this->config['name'])) {
  283. return '';
  284. }
  285. return $this->handler->renderValidationJS();
  286. }
  287. /**
  288. * XoopsCaptcha::setCode()
  289. *
  290. * @param mixed $code
  291. * @return bool
  292. */
  293. public function setCode($code = null)
  294. {
  295. $code = ($code === null) ? $this->handler->getCode() : $code;
  296. if (!empty($code)) {
  297. $_SESSION[$this->name . '_code'] = $code;
  298. return true;
  299. }
  300. return false;
  301. }
  302. /**
  303. * XoopsCaptcha::loadForm()
  304. *
  305. * @return
  306. */
  307. public function loadForm()
  308. {
  309. $form = $this->handler->render();
  310. $this->setCode();
  311. return $form;
  312. }
  313. }
  314. /**
  315. * Abstract class for CAPTCHA method
  316. *
  317. * Currently there are two types of CAPTCHA forms, text and image
  318. * The default mode is "text", it can be changed in the priority:
  319. * 1 If mode is set through XoopsFormCaptcha::setConfig("mode", $mode), take it
  320. * 2 Elseif mode is set though captcha/config.php, take it
  321. * 3 Else, take "text"
  322. */
  323. class XoopsCaptchaMethod
  324. {
  325. public $handler;
  326. public $config;
  327. public $code;
  328. /**
  329. * XoopsCaptchaMethod::__construct()
  330. *
  331. * @param mixed $handler
  332. */
  333. public function __construct($handler = null)
  334. {
  335. $this->handler = $handler;
  336. }
  337. /**
  338. * XoopsCaptchaMethod::isActive()
  339. *
  340. * @return bool
  341. */
  342. public function isActive()
  343. {
  344. return true;
  345. }
  346. /**
  347. * XoopsCaptchaMethod::loadConfig()
  348. *
  349. * @param string $name
  350. * @return void
  351. */
  352. public function loadConfig($name = '')
  353. {
  354. $this->config = empty($name) ? $this->handler->config : array_merge($this->handler->config, $this->handler->loadConfig($name));
  355. }
  356. /**
  357. * XoopsCaptchaMethod::getCode()
  358. *
  359. * @return string
  360. */
  361. public function getCode()
  362. {
  363. return (string)$this->code;
  364. }
  365. /**
  366. * XoopsCaptchaMethod::render()
  367. *
  368. * @return void
  369. */
  370. public function render()
  371. {
  372. }
  373. /**
  374. * @return string
  375. */
  376. public function renderValidationJS()
  377. {
  378. return '';
  379. }
  380. /**
  381. * XoopsCaptchaMethod::verify()
  382. *
  383. * @param mixed $sessionName
  384. * @return bool
  385. */
  386. public function verify($sessionName = null)
  387. {
  388. $is_valid = false;
  389. if (!empty($_SESSION["{$sessionName}_code"])) {
  390. $func = !empty($this->config['casesensitive']) ? 'strcmp' : 'strcasecmp';
  391. $is_valid = !$func(trim(@$_POST[$sessionName]), $_SESSION["{$sessionName}_code"]);
  392. }
  393. return $is_valid;
  394. }
  395. }