f = $f;
}
/**
* Whether the destination picker's id field selects "external URL".
*
* @param mixed $destinationFieldId The posted id field, or a stored dest404page value.
*/
public static function isExternalChoice($destinationFieldId): bool {
return is_scalar($destinationFieldId)
&& (string)$destinationFieldId === ABJ404_TYPE_EXTERNAL . '|' . ABJ404_TYPE_EXTERNAL;
}
/**
* Validate an absolute external destination for storage. The URL is stored
* and sent as typed, so it is normalized (invalid UTF-8 and control bytes
* removed, never percent-decoded) and sanitized as a URL (esc_url_raw, http
* and https only), never as text. Normalization happens here so every form
* that stores an external destination gets the same rule.
*
* @param string $destination The URL as the admin typed it.
* @return array{dest: string, message: string} message is '' when the URL may be stored.
*/
public function validate(string $destination): array {
$typed = trim(abj_service('sanitizer')->normalizeUrlString($destination, array('decode' => false)));
if ($typed === '') {
return array(
'dest' => '',
'message' => __('Error: You selected external URL but did not enter a URL.', '404-solution') . "
",
);
}
// esc_url_raw() returns '' for a URL it refuses outright, which is a URL
// with a scheme other than http or https (ftp:, mailto:, javascript:).
$destination = esc_url_raw($typed, array('http', 'https'));
if ($destination === '') {
return array('dest' => '', 'message' => self::schemeMessage());
}
if ($this->f->strlen($destination) < 8) {
return array(
'dest' => $destination,
'message' => __('Error: External URL is too short.', '404-solution') . "
",
);
}
if ($this->f->strpos($destination, "://") === false) {
return array(
'dest' => $destination,
'message' => __("Error: External URL doesn't contain ://", '404-solution') . "
",
);
}
$parsedUrl = parse_url($destination);
if (!is_array($parsedUrl) || !isset($parsedUrl['scheme'])
|| !in_array(strtolower($parsedUrl['scheme']), array('http', 'https'), true)) {
return array('dest' => $destination, 'message' => self::schemeMessage());
}
// A callback that returns false, a non-string, or a blank string (a
// forgotten "return" yields null) has not given a URL to store.
$validatedUrl = apply_filters('abj404_validate_external_redirect', $destination);
if (!is_string($validatedUrl) || trim($validatedUrl) === '') {
return array(
'dest' => $destination,
'message' => __('Error: Another plugin or your theme rejected this external URL.', '404-solution') . "
",
);
}
return array('dest' => $validatedUrl, 'message' => '');
}
private static function schemeMessage(): string {
return __('Error: External URL must use http:// or https:// protocol only.', '404-solution') . "
";
}
}