1: <?php
2: /**
3: * CAPTCHA class For XOOPS
4: *
5: * You may not change or alter any portion of this comment or credits
6: * of supporting developers from this source code or any supporting source code
7: * which is considered copyrighted (c) material of the original comment or credit authors.
8: * This program is distributed in the hope that it will be useful,
9: * but WITHOUT ANY WARRANTY; without even the implied warranty of
10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11: *
12: * @copyright (c) 2000-2021 XOOPS Project (https://xoops.org)
13: * @license GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
14: * @since 2.3.0
15: * @author Taiwen Jiang <phppp@users.sourceforge.net>
16: * @package class
17: * @subpackage CAPTCHA
18: */
19:
20: include __DIR__ . '/../../../../mainfile.php';
21:
22: error_reporting(0);
23: $xoopsLogger->activated = false;
24:
25: /**
26: * Class XoopsCaptchaImageHandler
27: */
28: class XoopsCaptchaImageHandler
29: {
30: public $config = array();
31: public $code;
32: public $mode = 'gd';
33: public $invalid = false;
34:
35: public $oImage;
36: public $font;
37: public $spacing;
38: public $width;
39: public $height;
40:
41: public $captchaHandler;
42:
43: /**
44: *
45: */
46: public function __construct()
47: {
48: xoops_load('XoopsCaptcha');
49: $this->captchaHandler = XoopsCaptcha::getInstance();
50: $this->config = $this->captchaHandler->loadConfig('image');
51: }
52:
53: public function loadImage()
54: {
55: $this->generateCode();
56: $this->createImage();
57: }
58:
59: /**
60: * Create Code
61: */
62: public function generateCode()
63: {
64: if ($this->invalid) {
65: return false;
66: }
67:
68: if ($this->mode === 'bmp') {
69: $this->config['num_chars'] = 4;
70: $this->code = mt_rand(pow(10, $this->config['num_chars'] - 1), (int)str_pad('9', $this->config['num_chars'], '9'));
71: } else {
72: $raw_code = md5(uniqid(mt_rand(), 1));
73: if (!empty($this->config['skip_characters'])) {
74: $valid_code = str_replace($this->config['skip_characters'], '', $raw_code);
75: $this->code = substr($valid_code, 0, $this->config['num_chars']);
76: } else {
77: $this->code = substr($raw_code, 0, $this->config['num_chars']);
78: }
79: if (!$this->config['casesensitive']) {
80: $this->code = strtoupper($this->code);
81: }
82: }
83: $this->captchaHandler->setCode($this->code);
84:
85: return true;
86: }
87:
88: /**
89: * @return string|bool
90: */
91: public function createImage()
92: {
93: if ($this->invalid) {
94: header('Content-type: image/gif');
95: readfile(XOOPS_ROOT_PATH . '/images/subject/icon2.gif');
96:
97: return null;
98: }
99:
100: if ($this->mode === 'bmp') {
101: return $this->createImageBmp();
102: } else {
103: return $this->createImageGd();
104: }
105: }
106:
107: /**
108: * @param string $name
109: * @param string $extension
110: *
111: * @return array|mixed
112: */
113: public function getList($name, $extension = '')
114: {
115: xoops_load('XoopsCache');
116: if ($items = XoopsCache::read("captcha_captcha_{$name}")) {
117: return $items;
118: }
119:
120: require_once XOOPS_ROOT_PATH . '/class/xoopslists.php';
121: $file_path = XOOPS_ROOT_PATH . "/class/captcha/image/{$name}";
122: $files = XoopsLists::getFileListAsArray($file_path);
123: $items = array();
124: foreach ($files as $item) {
125: if (empty($extension) || preg_match("/(\.{$extension})$/i", $item)) {
126: $items[] = $item;
127: }
128: }
129: XoopsCache::write("captcha_captcha_{$name}", $items);
130:
131: return $items;
132: }
133:
134: /**
135: * Create CAPTCHA iamge with GD
136: * Originated by DuGris' SecurityImage
137: * --------------------------------------------------------------------------- //
138: * Class : SecurityImage 1.5 //
139: * Author: DuGris aka L. Jen <http://www.dugris.info> //
140: * Email : DuGris@wanadoo.fr //
141: * Licence: GNU //
142: * Project: The XOOPS Project //
143: * --------------------------------------------------------------------------- //
144: */
145: public function createImageGd()
146: {
147: $this->loadFont();
148: $this->setImageSize();
149:
150: $this->oImage = imagecreatetruecolor((int)$this->width, (int)$this->height);
151: $background = imagecolorallocate($this->oImage, 255, 255, 255);
152: imagefilledrectangle($this->oImage, 0, 0, (int)$this->width, (int)$this->height, $background);
153:
154: switch ($this->config['background_type']) {
155: default:
156: case 0:
157: $this->drawBars();
158: break;
159:
160: case 1:
161: $this->drawCircles();
162: break;
163:
164: case 2:
165: $this->drawLines();
166: break;
167:
168: case 3:
169: $this->drawRectangles();
170: break;
171:
172: case 4:
173: $this->drawEllipses();
174: break;
175:
176: case 5:
177: $this->drawPolygons();
178: break;
179:
180: case 100:
181: $this->createFromFile();
182: break;
183: }
184: $this->drawBorder();
185: $this->drawCode();
186:
187: header('Content-type: image/jpeg');
188: if (!imagejpeg($this->oImage)) {
189: // Log or handle the error as you see fit
190: return false;
191: }
192:
193: if (!imagedestroy($this->oImage)) {
194: // Log or handle the error as you see fit
195: return false;
196: }
197:
198: return true;
199: }
200:
201: public function loadFont()
202: {
203: $fonts = $this->getList('fonts', 'ttf');
204: $this->font = XOOPS_ROOT_PATH . '/class/captcha/image/fonts/' . $fonts[array_rand($fonts)];
205: }
206:
207: public function setImageSize()
208: {
209: $MaxCharWidth = 0;
210: $MaxCharHeight = 0;
211: $oImage = imagecreatetruecolor(100, 100);
212: $text_color = imagecolorallocate($oImage, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100));
213: $FontSize = $this->config['fontsize_max'];
214: for ($Angle = -30; $Angle <= 30; ++$Angle) {
215: for ($i = 65; $i <= 90; ++$i) {
216: $CharDetails = imageftbbox($FontSize, $Angle, $this->font, chr($i), array());
217: $_MaxCharWidth = abs($CharDetails[0] + $CharDetails[2]);
218: if ($_MaxCharWidth > $MaxCharWidth) {
219: $MaxCharWidth = $_MaxCharWidth;
220: }
221: $_MaxCharHeight = abs($CharDetails[1] + $CharDetails[5]);
222: if ($_MaxCharHeight > $MaxCharHeight) {
223: $MaxCharHeight = $_MaxCharHeight;
224: }
225: }
226: }
227: imagedestroy($oImage);
228:
229: $this->height = $MaxCharHeight + 2;
230: $this->spacing = (int)(($this->config['num_chars'] * $MaxCharWidth) / $this->config['num_chars']);
231: $this->width = ($this->config['num_chars'] * $MaxCharWidth) + ($this->spacing / 2);
232: }
233:
234: /**
235: * Return random background
236: *
237: * @return string|null
238: */
239: public function loadBackground()
240: {
241: $RandBackground = null;
242: if ($backgrounds = $this->getList('backgrounds', '(gif|jpg|png)')) {
243: $RandBackground = XOOPS_ROOT_PATH . '/class/captcha/image/backgrounds/' . $backgrounds[array_rand($backgrounds)];
244: }
245:
246: return $RandBackground;
247: }
248:
249: /**
250: * Draw Image background
251: */
252: public function createFromFile()
253: {
254: if ($RandImage = $this->loadBackground()) {
255: $ImageType = @getimagesize($RandImage);
256: if (isset($ImageType[2])) {
257: switch ($ImageType[2]) {
258: case 1:
259: $BackgroundImage = imagecreatefromgif($RandImage);
260: break;
261:
262: case 2:
263: $BackgroundImage = imagecreatefromjpeg($RandImage);
264: break;
265:
266: case 3:
267: $BackgroundImage = imagecreatefrompng($RandImage);
268: break;
269: }
270: }
271: }
272: if (!empty($BackgroundImage)) {
273: imagecopyresized($this->oImage, $BackgroundImage, 0, 0, 0, 0, imagesx($this->oImage), imagesy($this->oImage), imagesx($BackgroundImage), imagesy($BackgroundImage));
274: imagedestroy($BackgroundImage);
275: } else {
276: $this->drawBars();
277: }
278: }
279:
280: /**
281: * Draw Code
282: */
283: public function drawCode()
284: {
285: for ($i = 0; $i < $this->config['num_chars']; ++$i) {
286: // select random greyscale colour
287: $text_color = imagecolorallocate($this->oImage, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100));
288:
289: // write text to image
290: $Angle = mt_rand(10, 30);
291: if ($i % 2) {
292: $Angle = mt_rand(-30, -10);
293: }
294:
295: // select random font size
296: $FontSize = mt_rand($this->config['fontsize_min'], $this->config['fontsize_max']);
297:
298: $CharDetails = imageftbbox($FontSize, $Angle, $this->font, $this->code[$i], array());
299: $CharHeight = abs($CharDetails[1] + $CharDetails[5]);
300:
301: // calculate character starting coordinates
302: $posX = ($this->spacing / 2) + ($i * $this->spacing);
303: $posY = 2 + ($this->height / 2) + ($CharHeight / 4);
304:
305: imagefttext($this->oImage, $FontSize, $Angle, (int)$posX, (int)$posY, $text_color, $this->font, $this->code[$i], array());
306: }
307: }
308:
309: /**
310: * Draw Border
311: */
312: public function drawBorder()
313: {
314: $rgb = mt_rand(50, 150);
315: $border_color = imagecolorallocate($this->oImage, $rgb, $rgb, $rgb);
316: imagerectangle($this->oImage, 0, 0, $this->width - 1, $this->height - 1, $border_color);
317: }
318:
319: /**
320: * Draw Circles background
321: */
322: public function drawCircles()
323: {
324: for ($i = 1; $i <= $this->config['background_num']; ++$i) {
325: $randomcolor = imagecolorallocate($this->oImage, mt_rand(190, 255), mt_rand(190, 255), mt_rand(190, 255));
326: imagefilledellipse($this->oImage, mt_rand(0, $this->width - 10), mt_rand(0, $this->height - 3), mt_rand(10, 20), mt_rand(20, 30), $randomcolor);
327: }
328: }
329:
330: /**
331: * Draw Lines background
332: */
333: public function drawLines()
334: {
335: for ($i = 0; $i < $this->config['background_num']; ++$i) {
336: $randomcolor = imagecolorallocate($this->oImage, mt_rand(190, 255), mt_rand(190, 255), mt_rand(190, 255));
337: imageline($this->oImage, mt_rand(0, $this->width), mt_rand(0, $this->height), mt_rand(0, $this->width), mt_rand(0, $this->height), $randomcolor);
338: }
339: }
340:
341: /**
342: * Draw Rectangles background
343: */
344: public function drawRectangles()
345: {
346: for ($i = 1; $i <= $this->config['background_num']; ++$i) {
347: $randomcolor = imagecolorallocate($this->oImage, mt_rand(190, 255), mt_rand(190, 255), mt_rand(190, 255));
348: imagefilledrectangle($this->oImage, mt_rand(0, $this->width), mt_rand(0, $this->height), mt_rand(0, $this->width), mt_rand(0, $this->height), $randomcolor);
349: }
350: }
351:
352: /**
353: * Draw Bars background
354: */
355: public function drawBars()
356: {
357: for ($i = 0; $i <= $this->height;) {
358: $randomcolor = imagecolorallocate($this->oImage, mt_rand(190, 255), mt_rand(190, 255), mt_rand(190, 255));
359: imageline($this->oImage, 0, (int)$i, (int)$this->width, (int)$i, (int)$randomcolor);
360: $i += 2.5;
361: }
362: for ($i = 0; $i <= $this->width;) {
363: $randomcolor = imagecolorallocate($this->oImage, mt_rand(190, 255), mt_rand(190, 255), mt_rand(190, 255));
364: imageline($this->oImage, (int)$i, 0, (int)$i, (int)$this->height, (int)$randomcolor);
365: $i += 2.5;
366: }
367: }
368:
369: /**
370: * Draw Ellipses background
371: */
372: public function drawEllipses()
373: {
374: for ($i = 1; $i <= $this->config['background_num']; ++$i) {
375: $randomcolor = imagecolorallocate($this->oImage, mt_rand(190, 255), mt_rand(190, 255), mt_rand(190, 255));
376: imageellipse($this->oImage, mt_rand(0, $this->width), mt_rand(0, $this->height), mt_rand(0, $this->width), mt_rand(0, $this->height), $randomcolor);
377: }
378: }
379:
380: /**
381: * Draw polygons background
382: */
383: public function drawPolygons()
384: {
385: for ($i = 1; $i <= $this->config['background_num']; ++$i) {
386: $randomcolor = imagecolorallocate($this->oImage, mt_rand(190, 255), mt_rand(190, 255), mt_rand(190, 255));
387: $coords = array();
388: for ($j = 1; $j <= $this->config['polygon_point']; ++$j) {
389: $coords[] = mt_rand(0, $this->width);
390: $coords[] = mt_rand(0, $this->height);
391: }
392: imagefilledpolygon($this->oImage, $coords, $this->config['polygon_point'], $randomcolor);
393: }
394: }
395: /**#@-*/
396:
397: /**
398: * Create CAPTCHA image with BMP
399: *
400: * TODO
401: * @param string $file
402: * @return string
403: */
404: public function createImageBmp($file = '')
405: {
406: $image = '';
407:
408: if (empty($file)) {
409: header('Content-type: image/bmp');
410: echo $image;
411: } else {
412: return $image;
413: }
414: return null;
415: }
416: }
417:
418: $imageHandler = new XoopsCaptchaImageHandler();
419: $imageHandler->loadImage();
420: