diff --git a/composer.json b/composer.json
index e1951679..ec4a090d 100644
--- a/composer.json
+++ b/composer.json
@@ -32,9 +32,7 @@
"inc/mod/auth.php",
"inc/lock.php",
"inc/queue.php",
- "inc/functions.php",
- "inc/driver/http-driver.php",
- "inc/service/captcha-queries.php"
+ "inc/functions.php"
]
},
"license": "Tinyboard + vichan",
diff --git a/inc/config.php b/inc/config.php
index 23c31990..9140201c 100644
--- a/inc/config.php
+++ b/inc/config.php
@@ -1231,8 +1231,6 @@
$config['error']['captcha'] = _('You seem to have mistyped the verification.');
$config['error']['flag_undefined'] = _('The flag %s is undefined, your PHP version is too old!');
$config['error']['flag_wrongtype'] = _('defined_flags_accumulate(): The flag %s is of the wrong type!');
- $config['error']['remote_io_error'] = _('IO error while interacting with a remote service.');
- $config['error']['local_io_error'] = _('IO error while interacting with a local resource or service.');
// Moderator errors
diff --git a/inc/context.php b/inc/context.php
deleted file mode 100644
index 5e540bab..00000000
--- a/inc/context.php
+++ /dev/null
@@ -1,28 +0,0 @@
-config = $config;
- }
-
- public function getHttpDriver(): HttpDriver {
- if (is_null($this->http)) {
- $this->http = HttpDrivers::getHttpDriver($this->config['upload_by_url_timeout'], $this->config['max_filesize']);
- }
- return $this->http;
- }
-}
diff --git a/inc/driver/http-driver.php b/inc/driver/http-driver.php
deleted file mode 100644
index cfbedfad..00000000
--- a/inc/driver/http-driver.php
+++ /dev/null
@@ -1,151 +0,0 @@
-inner);
- curl_setopt_array($this->inner, array(
- CURLOPT_URL => $url,
- CURLOPT_TIMEOUT => $this->timeout,
- CURLOPT_USERAGENT => $this->user_agent,
- CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
- ));
- }
-
- private function setSizeLimit(): void {
- // Adapted from: https://stackoverflow.com/a/17642638
- curl_setopt($this->inner, CURLOPT_NOPROGRESS, false);
-
- if (PHP_MAJOR_VERSION >= 8 && PHP_MINOR_VERSION >= 2) {
- curl_setopt($this->inner, CURLOPT_XFERINFOFUNCTION, function($res, $next_dl, $dl, $next_up, $up) {
- return (int)($dl <= $this->max_file_size);
- });
- } else {
- curl_setopt($this->inner, CURLOPT_PROGRESSFUNCTION, function($res, $next_dl, $dl, $next_up, $up) {
- return (int)($dl <= $this->max_file_size);
- });
- }
- }
-
- function __construct($timeout, $user_agent, $max_file_size) {
- $this->inner = curl_init();
- $this->timeout = $timeout;
- $this->user_agent = $user_agent;
- $this->max_file_size = $max_file_size;
- }
-
- function __destruct() {
- curl_close($this->inner);
- }
-
- /**
- * Execute a GET request.
- *
- * @param string $endpoint Uri endpoint.
- * @param ?array $data Optional GET parameters.
- * @param int $timeout Optional request timeout in seconds. Use the default timeout if 0.
- * @return string Returns the body of the response.
- * @throws RuntimeException Throws on IO error.
- */
- public function requestGet(string $endpoint, ?array $data, int $timeout = 0): string {
- if (!empty($data)) {
- $endpoint .= '?' . http_build_query($data);
- }
- if ($timeout == 0) {
- $timeout = $this->timeout;
- }
-
- $this->resetTowards($endpoint, $timeout);
- curl_setopt($this->inner, CURLOPT_RETURNTRANSFER, true);
- $ret = curl_exec($this->inner);
-
- if ($ret === false) {
- throw new \RuntimeException(curl_error($this->inner));
- }
- return $ret;
- }
-
- /**
- * Execute a POST request.
- *
- * @param string $endpoint Uri endpoint.
- * @param ?array $data Optional POST parameters.
- * @param int $timeout Optional request timeout in seconds. Use the default timeout if 0.
- * @return string Returns the body of the response.
- * @throws RuntimeException Throws on IO error.
- */
- public function requestPost(string $endpoint, ?array $data, int $timeout = 0): string {
- if ($timeout == 0) {
- $timeout = $this->timeout;
- }
-
- $this->resetTowards($endpoint, $timeout);
- curl_setopt($this->inner, CURLOPT_POST, true);
- if (!empty($data)) {
- curl_setopt($this->inner, CURLOPT_POSTFIELDS, http_build_query($data));
- }
- curl_setopt($this->inner, CURLOPT_RETURNTRANSFER, true);
- $ret = curl_exec($this->inner);
-
- if ($ret === false) {
- throw new \RuntimeException(curl_error($this->inner));
- }
- return $ret;
- }
-
- /**
- * Download the url's target with curl.
- *
- * @param string $url Url to the file to download.
- * @param ?array $data Optional GET parameters.
- * @param resource $fd File descriptor to save the content to.
- * @param int $timeout Optional request timeout in seconds. Use the default timeout if 0.
- * @return bool Returns true on success, false if the file was too large.
- * @throws RuntimeException Throws on IO error.
- */
- public function requestGetInto(string $endpoint, ?array $data, mixed $fd, int $timeout = 0): bool {
- if (!empty($data)) {
- $endpoint .= '?' . http_build_query($data);
- }
- if ($timeout == 0) {
- $timeout = $this->timeout;
- }
-
- $this->resetTowards($endpoint, $timeout);
- curl_setopt($this->inner, CURLOPT_FAILONERROR, true);
- curl_setopt($this->inner, CURLOPT_FOLLOWLOCATION, false);
- curl_setopt($this->inner, CURLOPT_FILE, $fd);
- curl_setopt($this->inner, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
- $this->setSizeLimit();
- $ret = curl_exec($this->inner);
-
- if ($ret === false) {
- if (curl_errno($this->inner) === CURLE_ABORTED_BY_CALLBACK) {
- return false;
- }
-
- throw new \RuntimeException(curl_error($this->inner));
- }
- return true;
- }
-}
diff --git a/inc/service/captcha-queries.php b/inc/service/captcha-queries.php
deleted file mode 100644
index d7966501..00000000
--- a/inc/service/captcha-queries.php
+++ /dev/null
@@ -1,102 +0,0 @@
-http = $http;
- $this->secret = $secret;
- $this->endpoint = $endpoint;
- }
-
- /**
- * Checks if the user at the remote ip passed the captcha.
- *
- * @param string $response User provided response.
- * @param string $remote_ip User ip.
- * @return bool Returns true if the user passed the captcha.
- * @throws RuntimeException|JsonException Throws on IO errors or if it fails to decode the answer.
- */
- public function verify(string $response, string $remote_ip): bool {
- $data = array(
- 'secret' => $this->secret,
- 'response' => $response,
- 'remoteip' => $remote_ip
- );
-
- $ret = $this->http->requestGet($this->endpoint, $data);
- $resp = json_decode($ret, true, 16, JSON_THROW_ON_ERROR);
-
- return isset($resp['success']) && $resp['success'];
- }
-}
-
-class NativeCaptchaQuery {
- private HttpDriver $http;
- private string $domain;
- private string $provider_check;
-
-
- /**
- * @param HttpDriver $http The http client.
- * @param string $domain The server's domain.
- * @param string $provider_check Path to the endpoint.
- */
- function __construct(HttpDriver $http, string $domain, string $provider_check) {
- $this->http = $http;
- $this->domain = $domain;
- $this->provider_check = $provider_check;
- }
-
- /**
- * Checks if the user at the remote ip passed the native vichan captcha.
- *
- * @param string $extra Extra http parameters.
- * @param string $user_text Remote user's text input.
- * @param string $user_cookie Remote user cookie.
- * @return bool Returns true if the user passed the check.
- * @throws RuntimeException Throws on IO errors.
- */
- public function verify(string $extra, string $user_text, string $user_cookie): bool {
- $data = array(
- 'mode' => 'check',
- 'text' => $user_text,
- 'extra' => $extra,
- 'cookie' => $user_cookie
- );
-
- $ret = $this->http->requestGet($this->domain . '/' . $this->provider_check, $data);
- return $ret === '1';
- }
-}
diff --git a/post.php b/post.php
index d0e30bdc..e22171ea 100644
--- a/post.php
+++ b/post.php
@@ -5,10 +5,6 @@
require_once 'inc/bootstrap.php';
-use Vichan\AppContext;
-use Vichan\Driver\HttpDriver;
-use Vichan\Service\{RemoteCaptchaQuery, NativeCaptchaQuery};
-
/**
* Utility functions
*/
@@ -65,27 +61,54 @@ function strip_symbols($input) {
}
}
+/**
+ * Download the url's target with curl.
+ *
+ * @param string $url Url to the file to download.
+ * @param int $timeout Request timeout in seconds.
+ * @param File $fd File descriptor to save the content to.
+ * @return null|string Returns a string on error.
+ */
+function download_file_into($url, $timeout, $fd) {
+ $err = null;
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $url);
+ curl_setopt($curl, CURLOPT_FAILONERROR, true);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
+ curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
+ curl_setopt($curl, CURLOPT_USERAGENT, 'Tinyboard');
+ curl_setopt($curl, CURLOPT_FILE, $fd);
+ curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+
+ if (curl_exec($curl) === false) {
+ $err = curl_error($curl);
+ }
+
+ curl_close($curl);
+ return $err;
+}
+
/**
* Download a remote file from the given url.
* The file is deleted at shutdown.
*
- * @param HttpDriver $http The http client.
* @param string $file_url The url to download the file from.
* @param int $request_timeout Timeout to retrieve the file.
* @param array $extra_extensions Allowed file extensions.
* @param string $tmp_dir Temporary directory to save the file into.
* @param array $error_array An array with error codes, used to create exceptions on failure.
- * @return array|false Returns an array describing the file on success, or false if the file was too large
- * @throws InvalidArgumentException|RuntimeException Throws on invalid arguments and IO errors.
+ * @return array Returns an array describing the file on success.
+ * @throws Exception on error.
*/
-function download_file_from_url(HttpDriver $http, $file_url, $request_timeout, $allowed_extensions, $tmp_dir, &$error_array) {
+function download_file_from_url($file_url, $request_timeout, $allowed_extensions, $tmp_dir, &$error_array) {
if (!preg_match('@^https?://@', $file_url)) {
throw new InvalidArgumentException($error_array['invalidimg']);
}
- $param_idx = mb_strpos($file_url, '?');
- if ($param_idx !== false) {
- $url_without_params = mb_substr($file_url, 0, $param_idx);
+ if (mb_strpos($file_url, '?') !== false) {
+ $url_without_params = mb_substr($file_url, 0, mb_strpos($file_url, '?'));
} else {
$url_without_params = $file_url;
}
@@ -105,13 +128,10 @@ function download_file_from_url(HttpDriver $http, $file_url, $request_timeout, $
$fd = fopen($tmp_file, 'w');
- try {
- $success = $http->requestGetInto($url_without_params, null, $fd, $request_timeout);
- if (!$success) {
- return false;
- }
- } finally {
- fclose($fd);
+ $dl_err = download_file_into($fd, $request_timeout, $fd);
+ fclose($fd);
+ if ($dl_err !== null) {
+ throw new Exception($error_array['nomove'] . '
Curl says: ' . $dl_err);
}
return array(
@@ -145,7 +165,6 @@ function ocr_image(array $config, string $img_path): string {
return trim($ret);
}
-
/**
* Trim an image's EXIF metadata
*
@@ -171,7 +190,6 @@ function strip_image_metadata(string $img_path): int {
*/
$dropped_post = false;
-$context = new AppContext($config);
// Is it a post coming from NNTP? Let's extract it and pretend it's a normal post.
if (isset($_GET['Newsgroups']) && $config['nntpchan']['enabled']) {
@@ -469,32 +487,22 @@ if (isset($_POST['delete'])) {
if (count($report) > $config['report_limit'])
error($config['error']['toomanyreports']);
+ if ($config['report_captcha'] && !isset($_POST['captcha_text'], $_POST['captcha_cookie'])) {
+ error($config['error']['bot']);
+ }
if ($config['report_captcha']) {
- if (!isset($_POST['captcha_text'], $_POST['captcha_cookie'])) {
- error($config['error']['bot']);
- }
+ $ch = curl_init($config['domain'].'/'.$config['captcha']['provider_check'] . "?" . http_build_query([
+ 'mode' => 'check',
+ 'text' => $_POST['captcha_text'],
+ 'extra' => $config['captcha']['extra'],
+ 'cookie' => $_POST['captcha_cookie']
+ ]));
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ $resp = curl_exec($ch);
- try {
- $query = new NativeCaptchaQuery(
- $context->getHttpDriver(),
- $config['domain'],
- $config['captcha']['provider_check']
- );
- $success = $query->verify(
- $config['captcha']['extra'],
- $_POST['captcha_text'],
- $_POST['captcha_cookie']
- );
-
- if (!$success) {
- error($config['error']['captcha']);
- }
- } catch (RuntimeException $e) {
- if ($config['syslog']) {
- _syslog(LOG_ERR, "Native captcha IO exception: {$e->getMessage()}");
- }
- error($config['error']['local_io_error']);
+ if ($resp !== '1') {
+ error($config['error']['captcha']);
}
}
@@ -584,60 +592,62 @@ if (isset($_POST['delete'])) {
// Check if banned
checkBan($board['uri']);
- // Check for CAPTCHA right after opening the board so the "return" link is in there.
- try {
- // With our custom captcha provider
- if ($config['captcha']['enabled'] || ($post['op'] && $config['new_thread_capt'])) {
- $query = new NativeCaptchaQuery($context->getHttpDriver(), $config['domain'], $config['captcha']['provider_check']);
- $success = $query->verify($config['captcha']['extra'], $_POST['captcha_text'], $_POST['captcha_cookie']);
+ // Check for CAPTCHA right after opening the board so the "return" link is in there
+ if ($config['recaptcha']) {
+ if (!isset($_POST['g-recaptcha-response']))
+ error($config['error']['bot']);
- if (!$success) {
- error(
- $config['error']['captcha']
- . ''
- );
- }
- }
- // Remote 3rd party captchas.
- else {
- // recaptcha
- if ($config['recaptcha']) {
- if (!isset($_POST['g-recaptcha-response'])) {
- error($config['error']['bot']);
- }
- $response = $_POST['g-recaptcha-response'];
- $query = RemoteCaptchaQuery::withRecaptcha($context->getHttpDriver(), $config['recaptcha_private']);
- }
- // hCaptcha
- elseif ($config['hcaptcha']) {
- if (!isset($_POST['h-captcha-response'])) {
- error($config['error']['bot']);
- }
- $response = $_POST['g-recaptcha-response'];
- $query = RemoteCaptchaQuery::withHCaptcha($context->getHttpDriver(), $config['hcaptcha_private']);
- }
+ // Check what reCAPTCHA has to say...
+ $resp = json_decode(file_get_contents(sprintf('https://www.recaptcha.net/recaptcha/api/siteverify?secret=%s&response=%s&remoteip=%s',
+ $config['recaptcha_private'],
+ urlencode($_POST['g-recaptcha-response']),
+ $_SERVER['REMOTE_ADDR'])), true);
- $success = $query->verify($response, $_SERVER['REMOTE_ADDR']);
- if (!$success) {
- error($config['error']['captcha']);
- }
+ if (!$resp['success']) {
+ error($config['error']['captcha']);
}
- } catch (RuntimeException $e) {
- if ($config['syslog']) {
- _syslog(LOG_ERR, "Captcha IO exception: {$e->getMessage()}");
- }
- error($config['error']['remote_io_error']);
- } catch (JsonException $e) {
- if ($config['syslog']) {
- _syslog(LOG_ERR, "Bad JSON reply to captcha: {$e->getMessage()}");
- }
- error($config['error']['remote_io_error']);
}
+ // hCaptcha
+ if ($config['hcaptcha']) {
+ if (!isset($_POST['h-captcha-response'])) {
+ error($config['error']['bot']);
+ }
+ $data = array(
+ 'secret' => $config['hcaptcha_private'],
+ 'response' => $_POST['h-captcha-response'],
+ 'remoteip' => $_SERVER['REMOTE_ADDR']
+ );
+
+ $hcaptchaverify = curl_init();
+ curl_setopt($hcaptchaverify, CURLOPT_URL, "https://hcaptcha.com/siteverify");
+ curl_setopt($hcaptchaverify, CURLOPT_POST, true);
+ curl_setopt($hcaptchaverify, CURLOPT_POSTFIELDS, http_build_query($data));
+ curl_setopt($hcaptchaverify, CURLOPT_RETURNTRANSFER, true);
+ $hcaptcharesponse = curl_exec($hcaptchaverify);
+
+ $resp = json_decode($hcaptcharesponse, true); // Decoding $hcaptcharesponse instead of $response
+
+ if (!$resp['success']) {
+ error($config['error']['captcha']);
+ }
+ }
+ // Same, but now with our custom captcha provider
+ if (($config['captcha']['enabled']) || (($post['op']) && ($config['new_thread_capt'])) ) {
+ $ch = curl_init($config['domain'].'/'.$config['captcha']['provider_check'] . "?" . http_build_query([
+ 'mode' => 'check',
+ 'text' => $_POST['captcha_text'],
+ 'extra' => $config['captcha']['extra'],
+ 'cookie' => $_POST['captcha_cookie']
+ ]));
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ $resp = curl_exec($ch);
+
+ if ($resp !== '1') {
+ error($config['error']['captcha'] .
+ '');
+ }
+ }
if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) ||
(!$post['op'] && $_POST['post'] == $config['button_reply'])))
@@ -741,21 +751,7 @@ if (isset($_POST['delete'])) {
}
try {
- $ret = download_file_from_url(
- $context->getHttpDriver(),
- $_POST['file_url'],
- $config['upload_by_url_timeout'],
- $allowed_extensions,
- $config['tmp'],
- $config['error']
- );
- if ($ret === false) {
- error(sprintf3($config['error']['filesize'], array(
- 'filesz' => 'more than that',
- 'maxsz' => number_format($config['max_filesize'])
- )));
- }
- $_FILES['file'] = $ret;
+ $_FILES['file'] = download_file_from_url($_POST['file_url'], $config['upload_by_url_timeout'], $allowed_extensions, $config['tmp'], $config['error']);
} catch (Exception $e) {
error($e->getMessage());
}