diff --git a/inc/config.php b/inc/config.php index 48a331cb..da97b496 100644 --- a/inc/config.php +++ b/inc/config.php @@ -267,7 +267,8 @@ 'recaptcha_response_field', 'spoiler', 'quick-reply', - 'page' + 'page', + 'file_url', ); // Enable reCaptcha to make spam even harder. Rarely necessary. @@ -349,6 +350,10 @@ // Optional URL prefix for links (eg. "http://anonym.to/?"). $config['link_prefix'] = ''; $config['url_ads'] = &$config['link_prefix']; // leave alias + + // Allow "uploading" images via URL as well. Users can enter the URL of the image and then Tinyboard will + // download it. Not usually recommended. + $config['allow_upload_by_url'] = false; // A wordfilter (sometimes referred to as just a "filter" or "censor") automatically scans users’ posts // as they are submitted and changes or censors particular words or phrases. @@ -870,7 +875,6 @@ $config['error']['unknownext'] = _('Unknown file extension.'); $config['error']['filesize'] = _('Maximum file size: %maxsz% bytes
Your file\'s size: %filesz% bytes'); $config['error']['maxsize'] = _('The file was too big.'); - $config['error']['invalidzip'] = _('Invalid archive!'); $config['error']['fileexists'] = _('That file already exists!'); $config['error']['fileexistsinthread'] = _('That file already exists in this thread!'); $config['error']['delete_too_soon'] = _('You\'ll have to wait another %s before deleting that.'); @@ -1130,7 +1134,7 @@ $config['mod']['bandelete'] = MOD; // Remove bans $config['mod']['unban'] = MOD; - // Spoiler file (and keep post) + // Spoiler image $config['mod']['spoilerimage'] = JANITOR; // Delete file (and keep post) $config['mod']['deletefile'] = JANITOR; @@ -1370,6 +1374,6 @@ // is the absolute maximum, because MySQL cannot handle table names greater than 64 characters. $config['board_regex'] = '[0-9a-zA-Z$_\x{0080}-\x{FFFF}]{1,58}'; - // Complex regular expression to catch URLs. - $config['url_regex'] = '/' . '(https?|ftp):\/\/' . '(([\w\-]+\.)+[a-zA-Z]{2,6}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' . '(:\d+)?' . '(\/([\w\-~.#\/?=&;:+%!*\[\]@$\'()+,|\^]+)?)?' . '/'; + // Regex for URLs. + $config['url_regex'] = '@^(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))$@'; diff --git a/inc/functions.php b/inc/functions.php index f8957ece..32d0b819 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -1444,7 +1444,10 @@ function wordfilters(&$body) { foreach ($config['wordfilters'] as $filter) { if (isset($filter[2]) && $filter[2]) { - $body = preg_replace($filter[0], $filter[1], $body); + if (is_callable($filter[1])) + $body = preg_replace_callback($filter[0], $filter[1], $body); + else + $body = preg_replace($filter[0], $filter[1], $body); } else { $body = str_ireplace($filter[0], $filter[1], $body); } @@ -1785,7 +1788,7 @@ function buildThread($id, $return = false, $mod = false) { 'mod' => $mod, 'hasnoko50' => $hasnoko50, 'isnoko50' => false, - 'antibot' => $mod ? false : create_antibot($board['uri'], $id), + 'antibot' => $mod || $return ? false : create_antibot($board['uri'], $id), 'boardlist' => createBoardlist($mod), 'return' => ($mod ? '?' . $board['url'] . $config['file_index'] : $config['root'] . $board['dir'] . $config['file_index']) )); diff --git a/inc/mod/config-editor.php b/inc/mod/config-editor.php index 27740378..3f15ebe5 100644 --- a/inc/mod/config-editor.php +++ b/inc/mod/config-editor.php @@ -115,8 +115,14 @@ function config_vars() { $already_exists = true; } - if (!$already_exists && permission_to_edit_config_var($var['name'])) + if (!$already_exists && permission_to_edit_config_var($var['name'])) { + foreach ($var['comment'] as &$comment) { + $comment = preg_replace_callback( + '/((?:https?:\/\/|ftp:\/\/|irc:\/\/)[^\s<>()"]+?(?:\([^\s<>()"]*?\)[^\s<>()"]*?)*)((?:\s|<|>|"|\.||\]|!|\?|,|,|")*(?:[\s<>()"]|$))/', + 'markup_url', $comment); + } $conf[] = $var; + } } } diff --git a/post.php b/post.php index 4e5114b4..6721e804 100644 --- a/post.php +++ b/post.php @@ -270,6 +270,50 @@ if (isset($_POST['delete'])) { $_POST['subject'] = ''; } + if ($config['allow_upload_by_url'] && isset($_POST['file_url']) && !empty($_POST['file_url'])) { + $post['file_url'] = $_POST['file_url']; + if (!preg_match($config['url_regex'], $post['file_url'])) + error($config['error']['invalidimg']); + + + $post['extension'] = strtolower(mb_substr($post['file_url'], mb_strrpos($post['file_url'], '.') + 1)); + if (!in_array($post['extension'], $config['allowed_ext']) && !in_array($post['extension'], $config['allowed_ext_files'])) + error($config['error']['unknownext']); + + $post['file_tmp'] = tempnam($config['tmp'], 'url'); + function unlink_tmp_file($file) { + @unlink($file); + } + register_shutdown_function('unlink_tmp_file', $post['file_tmp']); + + $fp = fopen($post['file_tmp'], 'w'); + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $post['file_url']); + curl_setopt($curl, CURLOPT_FAILONERROR, true); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($curl, CURLOPT_TIMEOUT, 15); + curl_setopt($curl, CURLOPT_USERAGENT, 'Tinyboard'); + curl_setopt($curl, CURLOPT_BINARYTRANSFER, true); + curl_setopt($curl, CURLOPT_FILE, $fp); + curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + + if (curl_exec($curl) === false) + error($config['error']['nomove']); + + curl_close($curl); + + fclose($fp); + + $_FILES['file'] = array( + 'name' => basename($post['file_url']), + 'tmp_name' => $post['file_tmp'], + 'error' => 0, + 'size' => filesize($post['file_tmp']) + ); + } + // Check for a file if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) { if (!isset($_FILES['file']['tmp_name']) || $_FILES['file']['tmp_name'] == '' && $config['force_image_op']) @@ -547,7 +591,11 @@ if (isset($_POST['delete'])) { } if (!isset($dont_copy_file) || !$dont_copy_file) { - if (!@move_uploaded_file($upload, $post['file'])) + if (isset($post['file_tmp'])) { + if (!@rename($upload, $post['file'])) + error($config['error']['nomove']); + chmod($post['file'], 0755); + } elseif (!@move_uploaded_file($upload, $post['file'])) error($config['error']['nomove']); } } diff --git a/templates/main.js b/templates/main.js index 71dcd92a..78054a3b 100644 --- a/templates/main.js +++ b/templates/main.js @@ -175,7 +175,7 @@ function dopost(form) { saved[document.location] = form.elements['body'].value; sessionStorage.body = JSON.stringify(saved); - return form.elements['body'].value != "" || form.elements['file'].value != ""; + return form.elements['body'].value != "" || form.elements['file'].value != "" || (form.elements.file_url && form.elements['file_url'].value != ""); } function citeReply(id) { diff --git a/templates/post_form.html b/templates/post_form.html index 92aa325a..93ca821e 100644 --- a/templates/post_form.html +++ b/templates/post_form.html @@ -85,6 +85,13 @@ + {% if config.allow_upload_by_url %} +
+
+ : + +
+ {% endif %} {{ antibot.html() }}