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