Add ForkBB\url() function
This function checks the url (partially) and normalizes it (also partially at the moment).
This commit is contained in:
parent
b1791cf33b
commit
75df01aa8e
4 changed files with 186 additions and 23 deletions
|
@ -24,7 +24,7 @@ class Generate extends Method
|
|||
$query = 'SELECT bb_structure
|
||||
FROM ::bbcode';
|
||||
|
||||
$content = "<?php\n\nuse function \\ForkBB\\__;\n\nreturn [\n";
|
||||
$content = "<?php\n\nuse function \\ForkBB\\__;\nuse function \\ForkBB\\url;\n\nreturn [\n";
|
||||
|
||||
$stmt = $this->c->DB->query($query);
|
||||
while ($row = $stmt->fetch()) {
|
||||
|
|
|
@ -203,16 +203,15 @@ HANDLER,
|
|||
],
|
||||
],
|
||||
'handler' => <<<'HANDLER'
|
||||
$def = $attrs['Def'] ?? $body;
|
||||
$def = \implode('@',
|
||||
\array_map('\\rawurlencode',
|
||||
\array_map('\\rawurldecode',
|
||||
\explode('@', $def)
|
||||
)
|
||||
)
|
||||
);
|
||||
$def = \htmlspecialchars_decode($attrs['Def'] ?? $body, \ENT_QUOTES | \ENT_HTML5);
|
||||
$def = url("mailto:{$def}");
|
||||
|
||||
return "<a href=\"mailto:{$def}\">{$body}</a>";
|
||||
if ('' == $def) {
|
||||
return '<span class="f-bb-badlink">BAD EMAIL</span>';
|
||||
} else {
|
||||
$def = $parser->e($def);
|
||||
return "<a href=\"{$def}\">{$body}</a>";
|
||||
}
|
||||
HANDLER,
|
||||
],
|
||||
[
|
||||
|
@ -371,25 +370,27 @@ if (isset($attrs['Def'])) {
|
|||
}
|
||||
}
|
||||
|
||||
$fUrl = \str_replace([' ', '\'', '`', '"'], ['%20', '', '', ''], $url);
|
||||
$fUrl = \htmlspecialchars_decode($url, \ENT_QUOTES | \ENT_HTML5);
|
||||
|
||||
if (0 === \strpos($url, 'ftp.')) {
|
||||
$fUrl = 'ftp://' . $fUrl;
|
||||
} elseif (! \preg_match('%^(?:\.?\.?/|#|[a-z](?:[a-z]|[a-z0-9]{1,6}):)%', $fUrl)) {
|
||||
if (\preg_match('%^[^/]+@[^/]+$%', $fUrl)) {
|
||||
$fUrl = 'mailto:' . $fUrl;
|
||||
} else {
|
||||
$fUrl = '//' . $fUrl;
|
||||
}
|
||||
|
||||
$fUrl = url($fUrl);
|
||||
|
||||
if ('' == $fUrl) {
|
||||
return '<span class="f-bb-badlink">BAD URL</span>';
|
||||
} else {
|
||||
$fUrl = $parser->e($fUrl);
|
||||
|
||||
if ($url === $body) {
|
||||
$url = \htmlspecialchars_decode($url, \ENT_QUOTES | \ENT_HTML5);
|
||||
$url = \mb_strlen($url, 'UTF-8') > 55 ? \mb_substr($url, 0, 39, 'UTF-8') . ' … ' . \mb_substr($url, -10, null, 'UTF-8') : $url;
|
||||
$body = $parser->e($url);
|
||||
}
|
||||
}
|
||||
|
||||
if ($url === $body) {
|
||||
$url = \htmlspecialchars_decode($url, \ENT_QUOTES | \ENT_HTML5);
|
||||
$url = \mb_strlen($url, 'UTF-8') > 55 ? \mb_substr($url, 0, 39, 'UTF-8') . ' … ' . \mb_substr($url, -10, null, 'UTF-8') : $url;
|
||||
$body = $parser->e($url);
|
||||
return "<a href=\"{$fUrl}\" rel=\"ugc\">{$body}</a>";
|
||||
}
|
||||
|
||||
return "<a href=\"{$fUrl}\" rel=\"ugc\">{$body}</a>";
|
||||
HANDLER,
|
||||
],
|
||||
[
|
||||
|
|
|
@ -148,3 +148,160 @@ function size(int $size): string
|
|||
|
||||
return __(['%s ' . $units[$i], num($size, $decimals)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает нормализованный (частично) url или пустую строку
|
||||
*/
|
||||
function url(string $url): string
|
||||
{
|
||||
if (
|
||||
! isset($url[1])
|
||||
|| \preg_match('%^\s%u', $url)
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch ($url[0]) {
|
||||
case '/':
|
||||
if ('/' === $url[1]) {
|
||||
$schemeOn = false;
|
||||
$hostOn = true;
|
||||
$url = 'http:' . $url;
|
||||
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
case '#':
|
||||
$schemeOn = false;
|
||||
$hostOn = false;
|
||||
$url = 'http://a.a' . $url;
|
||||
|
||||
break;
|
||||
default:
|
||||
$hostOn = true;
|
||||
|
||||
if (\preg_match('%^([a-z][a-z0-9+.-]*):(\S)?%i', $url, $m)) {
|
||||
if (
|
||||
! isset($m[2])
|
||||
|| isset($m[1][10])
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$schemeOn = true;
|
||||
} else {
|
||||
$schemeOn = false;
|
||||
$url = 'http://' . $url;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$p = \parse_url($url);
|
||||
|
||||
if (! \is_array($p)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$scheme = \strtolower($p['scheme'] ?? '');
|
||||
$result = $schemeOn && $scheme ? $scheme . ':' : '';
|
||||
|
||||
switch ($scheme) {
|
||||
case 'javascript':
|
||||
return '';
|
||||
case 'mailto':
|
||||
if (
|
||||
isset($p['host'])
|
||||
|| ! isset($p['path'])
|
||||
|| ! \preg_match('%^([^\x00-\x1F]+)@([^\x00-\x1F\s@]++)$%Du', $p['path'], $m)
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$result .= \rawurlencode(\rawurldecode($m[1])) . '@' . \rawurlencode(\rawurldecode($m[2]));
|
||||
|
||||
break;
|
||||
case 'tel':
|
||||
if (
|
||||
isset($p['host'])
|
||||
|| ! isset($p['path'])
|
||||
|| ! \preg_match('%^\+?[0-9.()-]+$%D', $p['path'])
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$result .= $p['path'];
|
||||
|
||||
break;
|
||||
default:
|
||||
if ($hostOn && isset($p['host'])) {
|
||||
$result .= '//';
|
||||
|
||||
if (isset($p['user'])) {
|
||||
$result .= \rawurlencode(\rawurldecode($p['user']));
|
||||
|
||||
if (isset($p['pass'])) {
|
||||
$result .= ':' . \rawurlencode(\rawurldecode($p['pass']));
|
||||
}
|
||||
|
||||
$result .= '@';
|
||||
}
|
||||
|
||||
if (\preg_match('%[\x80-\xFF]%', $p['host'])) {
|
||||
$p['host'] = \idn_to_ascii($p['host'], 0, \INTL_IDNA_VARIANT_UTS46);
|
||||
}
|
||||
|
||||
$host = \filter_var($p['host'], \FILTER_VALIDATE_DOMAIN, \FILTER_FLAG_HOSTNAME);
|
||||
|
||||
if (false !== $host) {
|
||||
$result .= $host;
|
||||
} elseif (
|
||||
'[' === $p['host'][0]
|
||||
&& ']' === $p['host'][-1]
|
||||
&& \filter_var(\substr($p['host'], 1, -1), \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)
|
||||
) {
|
||||
$result .= $p['host'];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (isset($p['port'])) {
|
||||
$result .= ':' . $p['port'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($p['path'])) {
|
||||
$result .= \preg_replace_callback(
|
||||
'%[^/\%\w.~-]|\%(?![0-9a-fA-F]{2})%',
|
||||
function($m) {
|
||||
return \rawurlencode($m[0]);
|
||||
},
|
||||
$p['path']
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($p['query'])) {
|
||||
$result .= '?' . \preg_replace_callback(
|
||||
'%[^=&\%\w.~-]|\%(?![0-9a-fA-F]{2})%',
|
||||
function($m) {
|
||||
return \rawurlencode($m[0]);
|
||||
},
|
||||
$p['query']
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($p['fragment'])) {
|
||||
$result .= '#' . \preg_replace_callback(
|
||||
'%[^\%\w.~-]|\%(?![0-9a-fA-F]{2})%',
|
||||
function($m) {
|
||||
return \rawurlencode($m[0]);
|
||||
},
|
||||
$p['fragment']
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
|
|
@ -1625,6 +1625,11 @@ body,
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
#fork .f-bb-badlink {
|
||||
color: #D8000C;
|
||||
background-color: #FFBABA;
|
||||
}
|
||||
|
||||
#fork .f-post-body p,
|
||||
#fork .f-post-body blockquote,
|
||||
#fork .f-post-body br,
|
||||
|
|
Loading…
Add table
Reference in a new issue