f5f2b4653a1fea2b3f7287612c792a7efab709a8
[speedfreak] / Server / system / libraries / Captcha.php
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3  * Captcha library.
4  *
5  * $Id: Captcha.php 4072 2009-03-13 17:20:38Z jheathco $
6  *
7  * @package    Captcha
8  * @author     Kohana Team
9  * @copyright  (c) 2007-2008 Kohana Team
10  * @license    http://kohanaphp.com/license.html
11  */
12 class Captcha_Core {
13
14         // Captcha singleton
15         protected static $instance;
16
17         // Style-dependent Captcha driver
18         protected $driver;
19
20         // Config values
21         public static $config = array
22         (
23                 'style'      => 'basic',
24                 'width'      => 150,
25                 'height'     => 50,
26                 'complexity' => 4,
27                 'background' => '',
28                 'fontpath'   => '',
29                 'fonts'      => array(),
30                 'promote'    => FALSE,
31         );
32
33         /**
34          * Singleton instance of Captcha.
35          *
36          * @return  object
37          */
38         public static function instance()
39         {
40                 // Create the instance if it does not exist
41                 empty(Captcha::$instance) and new Captcha;
42
43                 return Captcha::$instance;
44         }
45
46         /**
47          * Constructs and returns a new Captcha object.
48          *
49          * @param   string  config group name
50          * @return  object
51          */
52         public static function factory($group = NULL)
53         {
54                 return new Captcha($group);
55         }
56
57         /**
58          * Constructs a new Captcha object.
59          *
60          * @throws  Kohana_Exception
61          * @param   string  config group name
62          * @return  void
63          */
64         public function __construct($group = NULL)
65         {
66                 // Create a singleton instance once
67                 empty(Captcha::$instance) and Captcha::$instance = $this;
68
69                 // No config group name given
70                 if ( ! is_string($group))
71                 {
72                         $group = 'default';
73                 }
74
75                 // Load and validate config group
76                 if ( ! is_array($config = Kohana::config('captcha.'.$group)))
77                         throw new Kohana_Exception('captcha.undefined_group', $group);
78
79                 // All captcha config groups inherit default config group
80                 if ($group !== 'default')
81                 {
82                         // Load and validate default config group
83                         if ( ! is_array($default = Kohana::config('captcha.default')))
84                                 throw new Kohana_Exception('captcha.undefined_group', 'default');
85
86                         // Merge config group with default config group
87                         $config += $default;
88                 }
89
90                 // Assign config values to the object
91                 foreach ($config as $key => $value)
92                 {
93                         if (array_key_exists($key, Captcha::$config))
94                         {
95                                 Captcha::$config[$key] = $value;
96                         }
97                 }
98
99                 // Store the config group name as well, so the drivers can access it
100                 Captcha::$config['group'] = $group;
101
102                 // If using a background image, check if it exists
103                 if ( ! empty($config['background']))
104                 {
105                         Captcha::$config['background'] = str_replace('\\', '/', realpath($config['background']));
106
107                         if ( ! is_file(Captcha::$config['background']))
108                                 throw new Kohana_Exception('captcha.file_not_found', Captcha::$config['background']);
109                 }
110
111                 // If using any fonts, check if they exist
112                 if ( ! empty($config['fonts']))
113                 {
114                         Captcha::$config['fontpath'] = str_replace('\\', '/', realpath($config['fontpath'])).'/';
115
116                         foreach ($config['fonts'] as $font)
117                         {
118                                 if ( ! is_file(Captcha::$config['fontpath'].$font))
119                                         throw new Kohana_Exception('captcha.file_not_found', Captcha::$config['fontpath'].$font);
120                         }
121                 }
122
123                 // Set driver name
124                 $driver = 'Captcha_'.ucfirst($config['style']).'_Driver';
125
126                 // Load the driver
127                 if ( ! Kohana::auto_load($driver))
128                         throw new Kohana_Exception('core.driver_not_found', $config['style'], get_class($this));
129
130                 // Initialize the driver
131                 $this->driver = new $driver;
132
133                 // Validate the driver
134                 if ( ! ($this->driver instanceof Captcha_Driver))
135                         throw new Kohana_Exception('core.driver_implements', $config['style'], get_class($this), 'Captcha_Driver');
136
137                 Kohana::log('debug', 'Captcha Library initialized');
138         }
139
140         /**
141          * Validates a Captcha response and updates response counter.
142          *
143          * @param   string   captcha response
144          * @return  boolean
145          */
146         public static function valid($response)
147         {
148                 // Maximum one count per page load
149                 static $counted;
150
151                 // User has been promoted, always TRUE and don't count anymore
152                 if (Captcha::instance()->promoted())
153                         return TRUE;
154
155                 // Challenge result
156                 $result = (bool) Captcha::instance()->driver->valid($response);
157
158                 // Increment response counter
159                 if ($counted !== TRUE)
160                 {
161                         $counted = TRUE;
162
163                         // Valid response
164                         if ($result === TRUE)
165                         {
166                                 Captcha::instance()->valid_count(Session::instance()->get('captcha_valid_count') + 1);
167                         }
168                         // Invalid response
169                         else
170                         {
171                                 Captcha::instance()->invalid_count(Session::instance()->get('captcha_invalid_count') + 1);
172                         }
173                 }
174
175                 return $result;
176         }
177
178         /**
179          * Gets or sets the number of valid Captcha responses for this session.
180          *
181          * @param   integer  new counter value
182          * @param   boolean  trigger invalid counter (for internal use only)
183          * @return  integer  counter value
184          */
185         public function valid_count($new_count = NULL, $invalid = FALSE)
186         {
187                 // Pick the right session to use
188                 $session = ($invalid === TRUE) ? 'captcha_invalid_count' : 'captcha_valid_count';
189
190                 // Update counter
191                 if ($new_count !== NULL)
192                 {
193                         $new_count = (int) $new_count;
194
195                         // Reset counter = delete session
196                         if ($new_count < 1)
197                         {
198                                 Session::instance()->delete($session);
199                         }
200                         // Set counter to new value
201                         else
202                         {
203                                 Session::instance()->set($session, (int) $new_count);
204                         }
205
206                         // Return new count
207                         return (int) $new_count;
208                 }
209
210                 // Return current count
211                 return (int) Session::instance()->get($session);
212         }
213
214         /**
215          * Gets or sets the number of invalid Captcha responses for this session.
216          *
217          * @param   integer  new counter value
218          * @return  integer  counter value
219          */
220         public function invalid_count($new_count = NULL)
221         {
222                 return $this->valid_count($new_count, TRUE);
223         }
224
225         /**
226          * Resets the Captcha response counters and removes the count sessions.
227          *
228          * @return  void
229          */
230         public function reset_count()
231         {
232                 $this->valid_count(0);
233                 $this->valid_count(0, TRUE);
234         }
235
236         /**
237          * Checks whether user has been promoted after having given enough valid responses.
238          *
239          * @param   integer  valid response count threshold
240          * @return  boolean
241          */
242         public function promoted($threshold = NULL)
243         {
244                 // Promotion has been disabled
245                 if (Captcha::$config['promote'] === FALSE)
246                         return FALSE;
247
248                 // Use the config threshold
249                 if ($threshold === NULL)
250                 {
251                         $threshold = Captcha::$config['promote'];
252                 }
253
254                 // Compare the valid response count to the threshold
255                 return ($this->valid_count() >= $threshold);
256         }
257
258         /**
259          * Returns or outputs the Captcha challenge.
260          *
261          * @param   boolean  TRUE to output html, e.g. <img src="#" />
262          * @return  mixed    html string or void
263          */
264         public function render($html = TRUE)
265         {
266                 return $this->driver->render($html);
267         }
268
269         /**
270          * Magically outputs the Captcha challenge.
271          *
272          * @return  mixed
273          */
274         public function __toString()
275         {
276                 return $this->render();
277         }
278
279 } // End Captcha Class