Advertisement
Guest User

Untitled

a guest
Nov 24th, 2014
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.80 KB | None | 0 0
  1. abstract class DeathByCaptcha_Exception extends Exception
  2. {}
  3.  
  4.  
  5. /**
  6. * Exception to throw on environment or runtime related failures.
  7. *
  8. * @package DBCAPI
  9. * @subpackage PHP
  10. */
  11. class DeathByCaptcha_RuntimeException extends DeathByCaptcha_Exception
  12. {}
  13.  
  14.  
  15. /**
  16. * Exception to throw on network or disk IO failures.
  17. *
  18. * @package DBCAPI
  19. * @subpackage PHP
  20. */
  21. class DeathByCaptcha_IOException extends DeathByCaptcha_Exception
  22. {}
  23.  
  24.  
  25. /**
  26. * Generic exception to throw on API client errors.
  27. *
  28. * @package DBCAPI
  29. * @subpackage PHP
  30. */
  31. class DeathByCaptcha_ClientException extends DeathByCaptcha_Exception
  32. {}
  33.  
  34.  
  35. /**
  36. * Exception to throw on rejected login attemts due to invalid DBC credentials, low balance, or when account being banned.
  37. *
  38. * @package DBCAPI
  39. * @subpackage PHP
  40. */
  41. class DeathByCaptcha_AccessDeniedException extends DeathByCaptcha_ClientException
  42. {}
  43.  
  44.  
  45. /**
  46. * Exception to throw on invalid CAPTCHA image payload: on empty images, on images too big, on non-image payloads.
  47. *
  48. * @package DBCAPI
  49. * @subpackage PHP
  50. */
  51. class DeathByCaptcha_InvalidCaptchaException extends DeathByCaptcha_ClientException
  52. {}
  53.  
  54.  
  55. /**
  56. * Generic exception to throw on API server errors.
  57. *
  58. * @package DBCAPI
  59. * @subpackage PHP
  60. */
  61. class DeathByCaptcha_ServerException extends DeathByCaptcha_Exception
  62. {}
  63.  
  64.  
  65. /**
  66. * Exception to throw when service is overloaded.
  67. *
  68. * @package DBCAPI
  69. * @subpackage PHP
  70. */
  71. class DeathByCaptcha_ServiceOverloadException extends DeathByCaptcha_ServerException
  72. {}
  73.  
  74.  
  75. /**
  76. * Base Death by Captcha API client
  77. *
  78. * @property-read array|null $user User's details
  79. * @property-read float|null $balance User's balance (in US cents)
  80. *
  81. * @package DBCAPI
  82. * @subpackage PHP
  83. */
  84. abstract class DeathByCaptcha_Client
  85. {
  86. const API_VERSION = 'DBC/PHP v4.1.1';
  87.  
  88. const DEFAULT_TIMEOUT = 60;
  89. const POLLS_INTERVAL = 5;
  90.  
  91.  
  92. /**
  93. * DBC account credentials
  94. *
  95. * @var array
  96. */
  97. protected $_userpwd = array();
  98.  
  99.  
  100. /**
  101. * Verbosity flag.
  102. * When it's set to true, the client will produce debug output on every API call.
  103. *
  104. * @var bool
  105. */
  106. public $is_verbose = false;
  107.  
  108.  
  109. /**
  110. * Parses URL query encoded responses
  111. *
  112. * @param string $s
  113. * @return array
  114. */
  115. static public function parse_plain_response($s)
  116. {
  117. parse_str($s, $a);
  118. return $a;
  119. }
  120.  
  121. /**
  122. * Parses JSON encoded response
  123. *
  124. * @param string $s
  125. * @return array
  126. */
  127. static public function parse_json_response($s)
  128. {
  129. return json_decode(rtrim($s), true);
  130. }
  131.  
  132.  
  133. /**
  134. * Checks if CAPTCHA is valid (not empty)
  135. *
  136. * @param string $img Raw CAPTCHA image
  137. * @throws DeathByCaptcha_InvalidCaptchaException On invalid CAPTCHA images
  138. */
  139. protected function _is_valid_captcha($img)
  140. {
  141. if (0 == strlen($img)) {
  142. throw new DeathByCaptcha_InvalidCaptchaException(
  143. 'CAPTCHA image file is empty'
  144. );
  145. } else {
  146. return true;
  147. }
  148. }
  149.  
  150. protected function _load_captcha($captcha)
  151. {
  152. if (is_resource($captcha)) {
  153. $img = '';
  154. rewind($captcha);
  155. while ($s = fread($captcha, 8192)) {
  156. $img .= $s;
  157. }
  158. return $img;
  159. } else if (is_array($captcha)) {
  160. return implode('', array_map('chr', $captcha));
  161. } else if ('base64:' == substr($captcha, 0, 7)) {
  162. return base64_decode(substr($captcha, 7));
  163. } else {
  164. return file_get_contents($captcha);
  165. }
  166. }
  167.  
  168.  
  169. /**
  170. * Closes opened connection (if any), as gracefully as possible
  171. *
  172. * @return DeathByCaptcha_Client
  173. */
  174. abstract public function close();
  175.  
  176. /**
  177. * Returns user details
  178. *
  179. * @return array|null
  180. */
  181. abstract public function get_user();
  182.  
  183. /**
  184. * Returns user's balance (in US cents)
  185. *
  186. * @uses DeathByCaptcha_Client::get_user()
  187. * @return float|null
  188. */
  189. public function get_balance()
  190. {
  191. return ($user = $this->get_user()) ? $user['balance'] : null;
  192. }
  193.  
  194. /**
  195. * Returns CAPTCHA details
  196. *
  197. * @param int $cid CAPTCHA ID
  198. * @return array|null
  199. */
  200. abstract public function get_captcha($cid);
  201.  
  202. /**
  203. * Returns CAPTCHA text
  204. *
  205. * @uses DeathByCaptcha_Client::get_captcha()
  206. * @param int $cid CAPTCHA ID
  207. * @return string|null
  208. */
  209. public function get_text($cid)
  210. {
  211. return ($captcha = $this->get_captcha($cid)) ? $captcha['text'] : null;
  212. }
  213.  
  214. /**
  215. * Reports an incorrectly solved CAPTCHA
  216. *
  217. * @param int $cid CAPTCHA ID
  218. * @return bool
  219. */
  220. abstract public function report($cid);
  221.  
  222. /**
  223. * Uploads a CAPTCHA
  224. *
  225. * @param string|array|resource $captcha CAPTCHA image file name, vector of bytes, or file handle
  226. * @return array|null Uploaded CAPTCHA details on success
  227. * @throws DeathByCaptcha_InvalidCaptchaException On invalid CAPTCHA file
  228. */
  229. abstract public function upload($captcha);
  230.  
  231. /**
  232. * Tries to solve CAPTCHA by uploading it and polling for its status/text
  233. * with arbitrary timeout. See {@link DeathByCaptcha_Client::upload()} for
  234. * $captcha param details.
  235. *
  236. * @uses DeathByCaptcha_Client::upload()
  237. * @uses DeathByCaptcha_Client::get_captcha()
  238. * @param int $timeout Optional solving timeout (in seconds)
  239. * @return array|null CAPTCHA details hash on success
  240. */
  241. public function decode($captcha, $timeout=self::DEFAULT_TIMEOUT)
  242. {
  243. $deadline = time() + (0 < $timeout ? $timeout : self::DEFAULT_TIMEOUT);
  244. if ($c = $this->upload($captcha)) {
  245. while ($deadline > time() && $c && !$c['text']) {
  246. sleep(self::POLLS_INTERVAL);
  247. $c = $this->get_captcha($c['captcha']);
  248. }
  249. if ($c && $c['text'] && $c['is_correct']) {
  250. return $c;
  251. }
  252. }
  253. return null;
  254. }
  255.  
  256. /**
  257. * @param string $username DBC account username
  258. * @param string $password DBC account password
  259. * @throws DeathByCaptcha_RuntimeException On missing/empty DBC account credentials
  260. * @throws DeathByCaptcha_RuntimeException When required extensions/functions not found
  261. */
  262. public function __construct($username, $password)
  263. {
  264. foreach (array('username', 'password') as $k) {
  265. if (!$$k) {
  266. throw new DeathByCaptcha_RuntimeException(
  267. "Account {$k} is missing or empty"
  268. );
  269. }
  270. }
  271. $this->_userpwd = array($username, $password);
  272. }
  273.  
  274. /**
  275. * @ignore
  276. */
  277. public function __destruct()
  278. {
  279. $this->close();
  280. }
  281.  
  282. /**
  283. * @ignore
  284. */
  285. public function __get($key)
  286. {
  287. switch ($key) {
  288. case 'user':
  289. return $this->get_user();
  290. case 'balance':
  291. return $this->get_balance();
  292. }
  293. }
  294. }
  295.  
  296.  
  297. /**
  298. * Death by Captcha HTTP API Client
  299. *
  300. * @see DeathByCaptcha_Client
  301. * @package DBCAPI
  302. * @subpackage PHP
  303. */
  304. class DeathByCaptcha_HttpClient extends DeathByCaptcha_Client
  305. {
  306. const BASE_URL = 'http://api.dbcapi.me/api';
  307.  
  308.  
  309. protected $_conn = null;
  310. protected $_response_type = '';
  311. protected $_response_parser = null;
  312.  
  313.  
  314. /**
  315. * Sets up CURL connection
  316. */
  317. protected function _connect()
  318. {
  319. if (!is_resource($this->_conn)) {
  320. if ($this->is_verbose) {
  321. fputs(STDERR, time() . " CONN\n");
  322. }
  323.  
  324. if (!($this->_conn = curl_init())) {
  325. throw new DeathByCaptcha_RuntimeException(
  326. 'Failed initializing a CURL connection'
  327. );
  328. }
  329.  
  330. curl_setopt_array($this->_conn, array(
  331. CURLOPT_TIMEOUT => self::DEFAULT_TIMEOUT,
  332. CURLOPT_CONNECTTIMEOUT => (int)(self::DEFAULT_TIMEOUT / 4),
  333. CURLOPT_HEADER => false,
  334. CURLOPT_RETURNTRANSFER => true,
  335. CURLOPT_FOLLOWLOCATION => true,
  336. CURLOPT_AUTOREFERER => false,
  337. CURLOPT_HTTPHEADER => array(
  338. 'Accept: ' . $this->_response_type,
  339. 'Expect: ',
  340. 'User-Agent: ' . self::API_VERSION
  341. )
  342. ));
  343. }
  344.  
  345. return $this;
  346. }
  347.  
  348. /**
  349. * Makes an API call
  350. *
  351. * @param string $cmd API command
  352. * @param array $payload API call payload, essentially HTTP POST fields
  353. * @return array|null API response hash table on success
  354. * @throws DeathByCaptcha_IOException On network related errors
  355. * @throws DeathByCaptcha_AccessDeniedException On failed login attempt
  356. * @throws DeathByCaptcha_InvalidCaptchaException On invalid CAPTCHAs rejected by the service
  357. * @throws DeathByCaptcha_ServerException On API server errors
  358. */
  359. protected function _call($cmd, $payload=null)
  360. {
  361. if (null !== $payload) {
  362. $payload = array_merge($payload, array(
  363. 'username' => $this->_userpwd[0],
  364. 'password' => $this->_userpwd[1],
  365. ));
  366. }
  367.  
  368. $this->_connect();
  369.  
  370. $opts = array(CURLOPT_URL => self::BASE_URL . '/' . trim($cmd, '/'),
  371. CURLOPT_REFERER => '');
  372. if (null !== $payload) {
  373. $opts[CURLOPT_POST] = true;
  374. $opts[CURLOPT_POSTFIELDS] = array_key_exists('captchafile', $payload)
  375. ? $payload
  376. : http_build_query($payload);
  377. } else {
  378. $opts[CURLOPT_HTTPGET] = true;
  379. }
  380. curl_setopt_array($this->_conn, $opts);
  381.  
  382. if ($this->is_verbose) {
  383. fputs(STDERR, time() . " SEND: {$cmd} " . serialize($payload) . "\n");
  384. }
  385.  
  386. $response = curl_exec($this->_conn);
  387. if (0 < ($err = curl_errno($this->_conn))) {
  388. throw new DeathByCaptcha_IOException(
  389. "API connection failed: [{$err}] " . curl_error($this->_conn)
  390. );
  391. }
  392.  
  393. if ($this->is_verbose) {
  394. fputs(STDERR, time() . " RECV: {$response}\n");
  395. }
  396.  
  397. $status_code = curl_getinfo($this->_conn, CURLINFO_HTTP_CODE);
  398. if (403 == $status_code) {
  399. throw new DeathByCaptcha_AccessDeniedException(
  400. 'Access denied, check your credentials and/or balance'
  401. );
  402. } else if (400 == $status_code || 413 == $status_code) {
  403. throw new DeathByCaptcha_InvalidCaptchaException(
  404. "CAPTCHA was rejected by the service, check if it's a valid image"
  405. );
  406. } else if (503 == $status_code) {
  407. throw new DeathByCaptcha_ServiceOverloadException(
  408. "CAPTCHA was rejected due to service overload, try again later"
  409. );
  410. } else if (!($response = call_user_func($this->_response_parser, $response))) {
  411. throw new DeathByCaptcha_ServerException(
  412. 'Invalid API response'
  413. );
  414. } else {
  415. return $response;
  416. }
  417. }
  418.  
  419.  
  420. /**
  421. * @see DeathByCaptcha_Client::__construct()
  422. */
  423. public function __construct($username, $password)
  424. {
  425. if (!extension_loaded('curl')) {
  426. throw new DeathByCaptcha_RuntimeException(
  427. 'CURL extension not found'
  428. );
  429. }
  430. if (function_exists('json_decode')) {
  431. $this->_response_type = 'application/json';
  432. $this->_response_parser = array($this, 'parse_json_response');
  433. } else {
  434. $this->_response_type = 'text/plain';
  435. $this->_response_parser = array($this, 'parse_plain_response');
  436. }
  437. parent::__construct($username, $password);
  438. }
  439.  
  440. /**
  441. * @see DeathByCaptcha_Client::close()
  442. */
  443. public function close()
  444. {
  445. if (is_resource($this->_conn)) {
  446. if ($this->is_verbose) {
  447. fputs(STDERR, time() . " CLOSE\n");
  448. }
  449. curl_close($this->_conn);
  450. $this->_conn = null;
  451. }
  452. return $this;
  453. }
  454.  
  455. /**
  456. * @see DeathByCaptcha_Client::get_user()
  457. */
  458. public function get_user()
  459. {
  460. $user = $this->_call('user', array());
  461. return (0 < ($id = (int)@$user['user']))
  462. ? array('user' => $id,
  463. 'balance' => (float)@$user['balance'],
  464. 'is_banned' => (bool)@$user['is_banned'])
  465. : null;
  466. }
  467.  
  468. /**
  469. * @see DeathByCaptcha_Client::upload()
  470. * @throws DeathByCaptcha_RuntimeException When failed to save CAPTCHA image to a temporary file
  471. */
  472. public function upload($captcha)
  473. {
  474. $img = $this->_load_captcha($captcha);
  475. if ($this->_is_valid_captcha($img)) {
  476. $tmp_fn = tempnam(null, 'captcha');
  477. file_put_contents($tmp_fn, $img);
  478. try {
  479. $captcha = $this->_call('captcha', array(
  480. 'captchafile' => '@'. $tmp_fn,
  481. ));
  482. } catch (Exception $e) {
  483. @unlink($tmp_fn);
  484. throw $e;
  485. }
  486. @unlink($tmp_fn);
  487. if (0 < ($cid = (int)@$captcha['captcha'])) {
  488. return array(
  489. 'captcha' => $cid,
  490. 'text' => (!empty($captcha['text']) ? $captcha['text'] : null),
  491. 'is_correct' => (bool)@$captcha['is_correct'],
  492. );
  493. }
  494. }
  495. return null;
  496. }
  497.  
  498. /**
  499. * @see DeathByCaptcha_Client::get_captcha()
  500. */
  501. public function get_captcha($cid)
  502. {
  503. $captcha = $this->_call('captcha/' . (int)$cid);
  504. return (0 < ($cid = (int)@$captcha['captcha']))
  505. ? array('captcha' => $cid,
  506. 'text' => (!empty($captcha['text']) ? $captcha['text'] : null),
  507. 'is_correct' => (bool)$captcha['is_correct'])
  508. : null;
  509. }
  510.  
  511. /**
  512. * @see DeathByCaptcha_Client::report()
  513. */
  514. public function report($cid)
  515. {
  516. $captcha = $this->_call('captcha/' . (int)$cid . '/report', array());
  517. return !(bool)@$captcha['is_correct'];
  518. }
  519. }
  520.  
  521.  
  522. /**
  523. * Death by Captcha socket API Client
  524. *
  525. * @see DeathByCaptcha_Client
  526. * @package DBCAPI
  527. * @subpackage PHP
  528. */
  529. class DeathByCaptcha_SocketClient extends DeathByCaptcha_Client
  530. {
  531. const HOST = 'api.dbcapi.me';
  532. const FIRST_PORT = 8123;
  533. const LAST_PORT = 8130;
  534.  
  535. const TERMINATOR = "\r\n";
  536.  
  537.  
  538. protected $_sock = null;
  539.  
  540.  
  541. /**
  542. * Opens a socket connection to the API server
  543. *
  544. * @throws DeathByCaptcha_IOException When API connection fails
  545. * @throws DeathByCaptcha_RuntimeException When socket operations fail
  546. */
  547. protected function _connect()
  548. {
  549. if (null === $this->_sock) {
  550. if ($this->is_verbose) {
  551. fputs(STDERR, time() . " CONN\n");
  552. }
  553.  
  554. $errno = 0;
  555. $error = '';
  556. $port = rand(self::FIRST_PORT, self::LAST_PORT);
  557. $sock = null;
  558.  
  559. if (!($sock = @fsockopen(self::HOST, $port, $errno, $error, self::DEFAULT_TIMEOUT))) {
  560. throw new DeathByCaptcha_IOException(
  561. 'Failed connecting to ' . self::HOST . ":{$port}: fsockopen(): [{$errno}] {$error}"
  562. );
  563. } else if (!@stream_set_timeout($sock, self::DEFAULT_TIMEOUT / 4)) {
  564. fclose($sock);
  565. throw new DeathByCaptcha_IOException(
  566. 'Failed setting socket timeout'
  567. );
  568. } else {
  569. $this->_sock = $sock;
  570. }
  571. }
  572.  
  573. return $this;
  574. }
  575.  
  576. /**
  577. * Socket send()/recv() wrapper
  578. *
  579. * @param string $buf Raw API request to send
  580. * @return string Raw API response on success
  581. * @throws DeathByCaptcha_IOException On network failures
  582. */
  583. protected function _sendrecv($buf)
  584. {
  585. if ($this->is_verbose) {
  586. fputs(STDERR, time() . ' SEND: ' . strlen($buf) . ' ' . rtrim($buf) . "\n");
  587. }
  588.  
  589. $buf .= self::TERMINATOR;
  590. $response = '';
  591. while (true) {
  592. if ($buf) {
  593. if (!($n = fwrite($this->_sock, $buf))) {
  594. throw new DeathByCaptcha_IOException(
  595. 'Connection lost while sending API request'
  596. );
  597. } else {
  598. $buf = substr($buf, $n);
  599. }
  600. }
  601. if (!$buf) {
  602. if (!($s = fread($this->_sock, 4096))) {
  603. throw new DeathByCaptcha_IOException(
  604. 'Connection lost while receiving API response'
  605. );
  606. } else {
  607. $response .= $s;
  608. if (self::TERMINATOR == substr($s, strlen($s) - 2)) {
  609. $response = rtrim($response, self::TERMINATOR);
  610. if ($this->is_verbose) {
  611. fputs(STDERR, time() . ' RECV: ' . strlen($response) . " {$response}\n");
  612. }
  613. return $response;
  614. }
  615. }
  616. }
  617. }
  618.  
  619. throw new DeathByCaptcha_IOException('API request timed out');
  620. }
  621.  
  622. /**
  623. * Makes an API call
  624. *
  625. * @param string $cmd API command to call
  626. * @param array $payload API request payload
  627. * @return array|null API response hash map on success
  628. * @throws DeathByCaptcha_IOException On network errors
  629. * @throws DeathByCaptcha_AccessDeniedException On failed login attempt
  630. * @throws DeathByCaptcha_InvalidCaptchaException On invalid CAPTCHAs rejected by the service
  631. * @throws DeathByCaptcha_ServerException On API server errors
  632. */
  633. protected function _call($cmd, $payload=null)
  634. {
  635. if (null === $payload) {
  636. $payload = array();
  637. }
  638. $payload = array_merge($payload, array(
  639. 'cmd' => $cmd,
  640. 'version' => self::API_VERSION,
  641. ));
  642. $payload = json_encode($payload);
  643.  
  644. $response = null;
  645. for ($attempt = 2; 0 < $attempt && null === $response; $attempt--) {
  646. if (null === $this->_sock && 'login' != $cmd) {
  647. $this->_call('login', array(
  648. 'username' => $this->_userpwd[0],
  649. 'password' => $this->_userpwd[1],
  650. ));
  651. }
  652. $this->_connect();
  653. try {
  654. $response = $this->_sendrecv($payload);
  655. } catch (DeathByCaptcha_Exception $e) {
  656. $this->close();
  657. }
  658. }
  659.  
  660. try {
  661. if (null === $response) {
  662. throw new DeathByCaptcha_IOException(
  663. 'API connection lost or timed out'
  664. );
  665. } else if (!($response = $this->parse_json_response($response))) {
  666. throw new DeathByCaptcha_ServerException(
  667. 'Invalid API response'
  668. );
  669. }
  670.  
  671. if (!empty($response['error'])) {
  672. switch ($response['error']) {
  673. case 'not-logged-in':
  674. throw new DeathByCaptcha_AccessDeniedException(
  675. 'Access denied, check your credentials'
  676. );
  677. case 'banned':
  678. throw new DeathByCaptcha_AccessDeniedException(
  679. 'Access denied, account suspended'
  680. );
  681. case 'insufficient-funds':
  682. throw new DeathByCaptcha_AccessDeniedException(
  683. 'Access denied, balance is too low'
  684. );
  685. case 'invalid-captcha':
  686. throw new DeathByCaptcha_InvalidCaptchaException(
  687. "CAPTCHA was rejected by the service, check if it's a valid image"
  688. );
  689. case 'service-overload':
  690. throw new DeathByCaptcha_ServiceOverloadException(
  691. 'CAPTCHA was rejected due to service overload, try again later'
  692. );
  693. default:
  694. throw new DeathByCaptcha_ServerException(
  695. 'API server error occured: ' . $error
  696. );
  697. }
  698. } else {
  699. return $response;
  700. }
  701. } catch (Exception $e) {
  702. $this->close();
  703. throw $e;
  704. }
  705. }
  706.  
  707.  
  708. /**
  709. * @see DeathByCaptcha_Client::__construct()
  710. */
  711. public function __construct($username, $password)
  712. {
  713. // PHP for Windows lacks EAGAIN errno constant
  714. if (!defined('SOCKET_EAGAIN')) {
  715. define('SOCKET_EAGAIN', 11);
  716. }
  717.  
  718. foreach (array('json', ) as $k) {
  719. if (!extension_loaded($k)) {
  720. throw new DeathByCaptcha_RuntimeException(
  721. "Required {$k} extension not found, check your PHP configuration"
  722. );
  723. }
  724. }
  725. foreach (array('json_encode', 'json_decode', 'base64_encode') as $k) {
  726. if (!function_exists($k)) {
  727. throw new DeathByCaptcha_RuntimeException(
  728. "Required {$k}() function not found, check your PHP configuration"
  729. );
  730. }
  731. }
  732.  
  733. parent::__construct($username, $password);
  734. }
  735.  
  736. /**
  737. * @see DeathByCaptcha_Client::close()
  738. */
  739. public function close()
  740. {
  741. if (null !== $this->_sock) {
  742. if ($this->is_verbose) {
  743. fputs(STDERR, time() . " CLOSE\n");
  744. }
  745.  
  746. fclose($this->_sock);
  747. $this->_sock = null;
  748. }
  749.  
  750. return $this;
  751. }
  752.  
  753. /**
  754. * @see DeathByCaptcha_Client::get_user()
  755. */
  756. public function get_user()
  757. {
  758. $user = $this->_call('user');
  759. return (0 < ($id = (int)@$user['user']))
  760. ? array('user' => $id,
  761. 'balance' => (float)@$user['balance'],
  762. 'is_banned' => (bool)@$user['is_banned'])
  763. : null;
  764. }
  765.  
  766. /**
  767. * @see DeathByCaptcha_Client::get_user()
  768. */
  769. public function upload($captcha)
  770. {
  771. $img = $this->_load_captcha($captcha);
  772. if ($this->_is_valid_captcha($img)) {
  773. $captcha = $this->_call('upload', array(
  774. 'captcha' => base64_encode($img),
  775. ));
  776. if (0 < ($cid = (int)@$captcha['captcha'])) {
  777. return array(
  778. 'captcha' => $cid,
  779. 'text' => (!empty($captcha['text']) ? $captcha['text'] : null),
  780. 'is_correct' => (bool)@$captcha['is_correct'],
  781. );
  782. }
  783. }
  784. return null;
  785. }
  786.  
  787. /**
  788. * @see DeathByCaptcha_Client::get_captcha()
  789. */
  790. public function get_captcha($cid)
  791. {
  792. $captcha = $this->_call('captcha', array('captcha' => (int)$cid));
  793. return (0 < ($cid = (int)@$captcha['captcha']))
  794. ? array('captcha' => $cid,
  795. 'text' => (!empty($captcha['text']) ? $captcha['text'] : null),
  796. 'is_correct' => (bool)$captcha['is_correct'])
  797. : null;
  798. }
  799.  
  800. /**
  801. * @see DeathByCaptcha_Client::report()
  802. */
  803. public function report($cid)
  804. {
  805. $captcha = $this->_call('report', array('captcha' => (int)$cid));
  806. return !@$captcha['is_correct'];
  807. }
  808. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement