XpressEngine Core  1.11.2
 All Classes Namespaces Files Functions Variables Pages
captcha.addon.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) NAVER <http://www.navercorp.com> */
3 
4 if(!defined("__XE__")) exit();
5 
12 if(!class_exists('AddonCaptcha', false))
13 {
14  class AddonCaptcha
15  {
16 
17  var $addon_info;
18  var $target_acts = NULL;
19 
20  function setInfo(&$addon_info)
21  {
22  $this->addon_info = $addon_info;
23  }
24 
25  function before_module_proc()
26  {
27  if($this->addon_info->act_type == 'everytime' && $_SESSION['captcha_authed'])
28  {
29  unset($_SESSION['captcha_authed']);
30  }
31  }
32 
33  function before_module_init(&$ModuleHandler)
34  {
35  $logged_info = Context::get('logged_info');
36  if($logged_info->is_admin == 'Y' || $logged_info->is_site_admin)
37  {
38  return false;
39  }
40  if($this->addon_info->target != 'all' && Context::get('is_logged'))
41  {
42  return false;
43  }
44  if($_SESSION['XE_VALIDATOR_ERROR'] == -1)
45  {
46  $_SESSION['captcha_authed'] = false;
47  }
48  if($_SESSION['captcha_authed'])
49  {
50  return false;
51  }
52 
53  $type = Context::get('captchaType');
54 
55  $this->target_acts = array('procBoardInsertDocument', 'procBoardInsertComment', 'procIssuetrackerInsertIssue', 'procIssuetrackerInsertHistory', 'procTextyleInsertComment');
56 
57  if(Context::getRequestMethod() != 'XMLRPC' && Context::getRequestMethod() !== 'JSON')
58  {
59  if($type == 'inline')
60  {
61  if(!$this->compareCaptcha())
62  {
63  Context::loadLang(_XE_PATH_ . 'addons/captcha/lang');
64  $_SESSION['XE_VALIDATOR_ERROR'] = -1;
65  $_SESSION['XE_VALIDATOR_MESSAGE'] = Context::getLang('captcha_denied');
66  $_SESSION['XE_VALIDATOR_MESSAGE_TYPE'] = 'error';
67  $_SESSION['XE_VALIDATOR_RETURN_URL'] = Context::get('error_return_url');
68  $ModuleHandler->_setInputValueToSession();
69  }
70  }
71  else
72  {
73  Context::addHtmlHeader('<script>
74  if(!captchaTargetAct) {var captchaTargetAct = [];}
75  captchaTargetAct.push("' . implode('","', $this->target_acts) . '");
76  </script>');
77  Context::loadFile(array('./addons/captcha/captcha.min.js', 'body', '', null), true);
78  }
79  }
80 
81  // compare session when calling actions such as writing a post or a comment on the board/issue tracker module
82  if(!$_SESSION['captcha_authed'] && in_array(Context::get('act'), $this->target_acts))
83  {
84  Context::loadLang(_XE_PATH_ . 'addons/captcha/lang');
85  $ModuleHandler->error = "captcha_denied";
86  }
87 
88  return true;
89  }
90 
91  function createKeyword()
92  {
93  $type = Context::get('captchaType');
94  if($type == 'inline' && $_SESSION['captcha_keyword'])
95  {
96  return;
97  }
98 
99  $arr = range('A', 'Y');
100  shuffle($arr);
101  $arr = array_slice($arr, 0, 6);
102  $_SESSION['captcha_keyword'] = join('', $arr);
103  }
104 
105  function before_module_init_setCaptchaSession()
106  {
107  if($_SESSION['captcha_authed'])
108  {
109  return false;
110  }
111  // Load language files
112  Context::loadLang(_XE_PATH_ . 'addons/captcha/lang');
113  // Generate keywords
114  $this->createKeyword();
115 
116  $target = Context::getLang('target_captcha');
117  header("Content-Type: text/xml; charset=UTF-8");
118  header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
119  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
120  header("Cache-Control: no-store, no-cache, must-revalidate");
121  header("Cache-Control: post-check=0, pre-check=0", false);
122  header("Pragma: no-cache");
123  printf("<response>\r\n <error>0</error>\r\n <message>success</message>\r\n <about_captcha><![CDATA[%s]]></about_captcha>\r\n <captcha_reload><![CDATA[%s]]></captcha_reload>\r\n <captcha_play><![CDATA[%s]]></captcha_play>\r\n <cmd_input><![CDATA[%s]]></cmd_input>\r\n <cmd_cancel><![CDATA[%s]]></cmd_cancel>\r\n </response>"
124  , Context::getLang('about_captcha')
125  , Context::getLang('captcha_reload')
126  , Context::getLang('captcha_play')
127  , Context::getLang('cmd_input')
128  , Context::getLang('cmd_cancel')
129  );
130  Context::close();
131  exit();
132  }
133 
134  function before_module_init_captchaImage()
135  {
136  if($_SESSION['captcha_authed'])
137  {
138  return false;
139  }
140  if(Context::get('renew'))
141  {
142  $this->createKeyword();
143  }
144 
145  $keyword = $_SESSION['captcha_keyword'];
146  $im = $this->createCaptchaImage($keyword);
147 
148  header("Cache-Control: ");
149  header("Pragma: ");
150  header("Content-Type: image/png");
151 
152  imagepng($im);
153  imagedestroy($im);
154 
155  Context::close();
156  exit();
157  }
158 
159  function createCaptchaImage($string)
160  {
161  $arr = array();
162  for($i = 0, $c = strlen($string); $i < $c; $i++)
163  {
164  $arr[] = $string{$i};
165  }
166 
167  // Font site
168  $w = 18;
169  $h = 25;
170 
171  // Character length
172  $c = count($arr);
173 
174  // Character image
175  $im = array();
176 
177  // Create an image by total size
178  $im[] = imagecreate(($w + 2) * count($arr), $h);
179 
180  $deg = range(-30, 30);
181  shuffle($deg);
182 
183  // Create an image for each letter
184  foreach($arr as $i => $str)
185  {
186  $im[$i + 1] = @imagecreate($w, $h);
187  $background_color = imagecolorallocate($im[$i + 1], 255, 255, 255);
188  $text_color = imagecolorallocate($im[$i + 1], 0, 0, 0);
189 
190  // Control font size
191  $ran = range(1, 20);
192  shuffle($ran);
193 
194  if(function_exists('imagerotate'))
195  {
196  imagestring($im[$i + 1], (array_pop($ran) % 3) + 3, 2, (array_pop($ran) % 8), $str, $text_color);
197  $im[$i + 1] = imagerotate($im[$i + 1], array_pop($deg), 0);
198 
199  $background_color = imagecolorallocate($im[$i + 1], 255, 255, 255);
200  imagecolortransparent($im[$i + 1], $background_color);
201  }
202  else
203  {
204  imagestring($im[$i + 1], (array_pop($ran) % 3) + 3, 2, (array_pop($ran) % 4), $str, $text_color);
205  }
206  }
207 
208  // Combine images of each character
209  for($i = 1, $c = count($im); $i<$c; $i++)
210  {
211  imagecopy($im[0], $im[$i], (($w + 2) * ($i - 1)), 0, 0, 0, $w, $h);
212  imagedestroy($im[$i]);
213  }
214 
215  // Larger image
216  $big_count = 2;
217  $big = imagecreatetruecolor(($w + 2) * $big_count * $c, $h * $big_count);
218  imagecopyresized($big, $im[0], 0, 0, 0, 0, ($w + 2) * $big_count * $c, $h * $big_count, ($w + 2) * $c, $h);
219  imagedestroy($im[0]);
220 
221  if(function_exists('imageantialias'))
222  {
223  imageantialias($big, true);
224  }
225 
226  // Background line
227  $line_color = imagecolorallocate($big, 0, 0, 0);
228 
229  $w = ($w + 2) * $big_count * $c;
230  $h = $h * $big_count;
231  $d = array_pop($deg);
232 
233  for($i = -abs($d); $i < $h + abs($d); $i = $i + 7)
234  {
235  imageline($big, 0, $i + $d, $w, $i, $line_color);
236  }
237 
238  $x = range(0, ($w - 10));
239  shuffle($x);
240 
241  for($i = 0; $i < 200; $i++)
242  {
243  imagesetpixel($big, $x[$i] % $w, $x[$i + 1] % $h, $line_color);
244  }
245 
246  return $big;
247  }
248 
249  function before_module_init_captchaAudio()
250  {
251  if($_SESSION['captcha_authed'])
252  {
253  return false;
254  }
255 
256  $keyword = strtoupper($_SESSION['captcha_keyword']);
257  $data = $this->createCaptchaAudio($keyword);
258 
259  header('Content-type: audio/mpeg');
260  header("Content-Disposition: attachment; filename=\"captcha_audio.mp3\"");
261  header('Cache-Control: no-store, no-cache, must-revalidate');
262  header('Expires: Sun, 1 Jan 2000 12:00:00 GMT');
263  header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
264  header('Content-Length: ' . strlen($data));
265 
266  echo $data;
267  Context::close();
268  exit();
269  }
270 
271  function createCaptchaAudio($string)
272  {
273  $data = '';
274  $_audio = './addons/captcha/audio/F_%s.mp3';
275  for($i = 0, $c = strlen($string); $i < $c; $i++)
276  {
277  $_data = FileHandler::readFile(sprintf($_audio, $string{$i}));
278 
279  $start = rand(5, 68); // Random start in 4-byte header and 64 byte data
280  $datalen = strlen($_data) - $start - 256; // Last unchanged 256 bytes
281 
282  for($j = $start; $j < $datalen; $j+=64)
283  {
284  $ch = ord($_data{$j});
285  if($ch < 9 || $ch > 119)
286  {
287  continue;
288  }
289  $_data{$j} = chr($ch + rand(-8, 8));
290  }
291 
292  $data .= $_data;
293  }
294 
295  return $data;
296  }
297 
298  function compareCaptcha()
299  {
300  if(!in_array(Context::get('act'), $this->target_acts)) return true;
301 
302  if($_SESSION['captcha_authed'])
303  {
304  return true;
305  }
306 
307  if(strtoupper($_SESSION['captcha_keyword']) == strtoupper(Context::get('secret_text')))
308  {
309  $_SESSION['captcha_authed'] = true;
310  return true;
311  }
312 
313  unset($_SESSION['captcha_authed']);
314 
315  return false;
316  }
317 
318  function before_module_init_captchaCompare()
319  {
320  if(!$this->compareCaptcha())
321  {
322  return false;
323  }
324 
325  header("Content-Type: text/xml; charset=UTF-8");
326  header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
327  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
328  header("Cache-Control: no-store, no-cache, must-revalidate");
329  header("Cache-Control: post-check=0, pre-check=0", false);
330  header("Pragma: no-cache");
331  print("<response>\r\n<error>0</error>\r\n<message>success</message>\r\n</response>");
332 
333  Context::close();
334  exit();
335  }
336 
337  function inlineDisplay()
338  {
339  unset($_SESSION['captcha_authed']);
340  $this->createKeyword();
341 
342  $swfURL = getUrl() . 'addons/captcha/swf/play.swf';
343  Context::unloadFile('./addons/captcha/captcha.min.js');
344  Context::loadFile(array('./addons/captcha/inline_captcha.js', 'body'));
345 
346  global $lang;
347 
348  $tags = <<<EOD
349 <img src="%s" id="captcha_image" alt="CAPTCHA" width="240" height="50" style="width:240px; height:50px; border:1px solid #b0b0b0" />
350 <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="0" height="0" id="captcha_audio" align="middle">
351 <param name="allowScriptAccess" value="always" />
352 <param name="quality" value="high" />
353 <param name="movie" value="%s" />
354 <param name="wmode" value="window" />
355 <param name="allowFullScreen" value="false">
356 <param name="bgcolor" value="#fffff" />
357 <embed src="%s" quality="high" wmode="window" allowFullScreen="false" bgcolor="#ffffff" width="0" height="0" name="captcha_audio" align="middle" allowScriptAccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
358 </object>
359 <button type="button" class="captchaReload text">%s</button>
360 <button type="button" class="captchaPlay text">%s</button><br />
361 <input type="hidden" name="captchaType" value="inline" />
362 <input name="secret_text" type="text" id="secret_text" />
363 EOD;
364  $tags = sprintf($tags, getUrl('captcha_action', 'captchaImage', 'rand', mt_rand(10000, 99999))
365  , $swfURL
366  , $swfURL
367  , $lang->reload
368  , $lang->play);
369  return $tags;
370  }
371 
372  }
373  $GLOBALS['__AddonCaptcha__'] = new AddonCaptcha;
374  $GLOBALS['__AddonCaptcha__']->setInfo($addon_info);
375  Context::set('oCaptcha', $GLOBALS['__AddonCaptcha__']);
376 }
377 
378 $oAddonCaptcha = &$GLOBALS['__AddonCaptcha__'];
379 
380 if(method_exists($oAddonCaptcha, $called_position))
381 {
382  if(!call_user_func_array(array(&$oAddonCaptcha, $called_position), array(&$this)))
383  {
384  return false;
385  }
386 }
387 
388 $addon_act = Context::get('captcha_action');
389 if($addon_act && method_exists($oAddonCaptcha, $called_position . '_' . $addon_act))
390 {
391  if(!call_user_func_array(array(&$oAddonCaptcha, $called_position . '_' . $addon_act), array(&$this)))
392  {
393  return false;
394  }
395 }
396 /* End of file captcha.addon.php */
397 /* Location: ./addons/captcha/captcha.addon.php */
unloadFile($file, $targetIe= '', $media= 'all')
loadFile($args)
loadLang($path)
if(file_exists(_XE_PATH_. 'config/config.user.inc.php')) if(!defined('__DEBUG__')) if(!defined('__DEBUG_OUTPUT__')) if(!defined('__DEBUG_PROTECT__')) if(!defined('__DEBUG_PROTECT_IP__')) if(!defined('__DEBUG_DB_OUTPUT__')) if(!defined('__LOG_SLOW_QUERY__')) if(!defined('__LOG_SLOW_TRIGGER__')) if(!defined('__LOG_SLOW_ADDON__')) if(!defined('__LOG_SLOW_WIDGET__')) if(!defined('__DEBUG_QUERY__')) if(!defined('__OB_GZHANDLER_ENABLE__')) if(!defined('__ENABLE_PHPUNIT_TEST__')) if(!defined('__PROXY_SERVER__')) if(!defined('__ERROR_LOG__')) if(!defined('__DISABLE_DEFAULT_CSS__')) if(!defined('__AUTO_OPCACHE_INVALIDATE__')) if((__DEBUG_OUTPUT__==2)&&version_compare(PHP_VERSION, '6.0.0')===-1) if(version_compare(PHP_VERSION, '5.3.0') >=0) $GLOBALS['__xe_autoload_file_map']
Definition: config.inc.php:324
addHtmlHeader($header)
set($key, $val, $set_to_get_vars=0)
if(method_exists($oAddonCaptcha, $called_position)) $addon_act
if(!class_exists('AddonCaptcha', false)) $oAddonCaptcha
getLang($code)
const _XE_PATH_
Definition: config.inc.php:49
readFile($filename)
getUrl()
Definition: func.inc.php:297
if(isset($_REQUEST['encode'])) if(isset($_REQUEST['decode'])) $lang
Definition: example.php:23