160 lines
4.6 KiB
PHP
160 lines
4.6 KiB
PHP
<?php
|
|
/**
|
|
* PicDefense copyright risk analysis provider.
|
|
*
|
|
* @package MediaRightsAudit\External
|
|
*/
|
|
|
|
namespace MediaRightsAudit\External;
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
use MediaRightsAudit\Admin\Settings;
|
|
|
|
/**
|
|
* Submits images to the PicDefense API and normalises the response
|
|
* into a ScanResult.
|
|
*
|
|
* Uses POST-based image URL submission with X-API-TOKEN header authentication.
|
|
* Reads credentials (picdefense_user_id and picdefense_api_key) from plugin settings.
|
|
* Match count reflects backlink_count from the API response; top domains are
|
|
* extracted from the found_at array.
|
|
*/
|
|
class PicDefenseProvider extends AbstractProvider {
|
|
|
|
/**
|
|
* PicDefense API endpoint.
|
|
*/
|
|
const ENDPOINT = 'https://app.picdefense.io/apiRouterv2/checkImageRisk';
|
|
|
|
/**
|
|
* Returns the machine-readable provider identifier.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function provider_slug(): string {
|
|
return 'picdefense';
|
|
}
|
|
|
|
/**
|
|
* Returns the human-readable provider display name.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function provider_name(): string {
|
|
return 'PicDefense';
|
|
}
|
|
|
|
/**
|
|
* Submits the image URL to PicDefense and returns a ScanResult.
|
|
*
|
|
* @param int $attachment_id WordPress attachment ID.
|
|
* @param string $file_url Public URL of the image.
|
|
*
|
|
* @return ScanResult
|
|
*
|
|
* @throws \RuntimeException On configuration, HTTP, or parse errors.
|
|
*/
|
|
protected function do_scan( int $attachment_id, string $file_url ): ScanResult {
|
|
list( $user_id, $api_key ) = $this->get_credentials();
|
|
|
|
if ( '' === $user_id || '' === $api_key ) {
|
|
throw new \RuntimeException( 'PicDefense credentials are not configured.' );
|
|
}
|
|
|
|
$body = wp_json_encode( array( 'url' => $file_url ) );
|
|
if ( false === $body ) {
|
|
throw new \RuntimeException( 'Failed to encode PicDefense request body.' );
|
|
}
|
|
|
|
$response = wp_remote_post(
|
|
self::ENDPOINT,
|
|
array(
|
|
'headers' => array(
|
|
'Content-Type' => 'application/json; charset=utf-8',
|
|
'X-API-TOKEN' => $user_id . ':' . $api_key,
|
|
),
|
|
'body' => $body,
|
|
'timeout' => 60,
|
|
)
|
|
);
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
throw new \RuntimeException( $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
|
}
|
|
|
|
$http_code = (int) wp_remote_retrieve_response_code( $response );
|
|
if ( 200 !== $http_code ) {
|
|
throw new \RuntimeException(
|
|
sprintf( 'PicDefense API returned HTTP %d.', $http_code ) // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
|
);
|
|
}
|
|
|
|
$raw_body = wp_remote_retrieve_body( $response );
|
|
$decoded = json_decode( $raw_body, true );
|
|
|
|
if ( ! is_array( $decoded ) ) {
|
|
throw new \RuntimeException( 'Failed to parse PicDefense API response.' );
|
|
}
|
|
|
|
return $this->parse_response( $decoded );
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Private helpers
|
|
// -------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Reads PicDefense credentials from plugin settings.
|
|
*
|
|
* @return array{string, string} [user_id, api_key] — empty strings if not configured.
|
|
*/
|
|
private function get_credentials(): array {
|
|
$opts = Settings::get_effective_settings();
|
|
|
|
$uid_val = $opts['picdefense_user_id'] ?? null;
|
|
$key_val = $opts['picdefense_api_key'] ?? null;
|
|
$user_id = is_string( $uid_val ) ? trim( $uid_val ) : '';
|
|
$api_key = is_string( $key_val ) ? trim( $key_val ) : '';
|
|
return array( $user_id, $api_key );
|
|
}
|
|
|
|
/**
|
|
* Parses a decoded PicDefense API response into a ScanResult.
|
|
*
|
|
* @param array<mixed, mixed> $decoded json_decode()'d API response.
|
|
*
|
|
* @return ScanResult
|
|
*
|
|
* @throws \RuntimeException When the API response indicates failure.
|
|
*/
|
|
private function parse_response( array $decoded ): ScanResult {
|
|
$status_val = $decoded['status'] ?? null;
|
|
$status = is_numeric( $status_val ) ? (int) $status_val : 0;
|
|
if ( 1 !== $status ) {
|
|
throw new \RuntimeException( 'PicDefense API returned non-success status.' );
|
|
}
|
|
|
|
$data_raw = $decoded['data'] ?? null;
|
|
$data_arr = is_array( $data_raw ) ? $data_raw : array();
|
|
$data = isset( $data_arr[0] ) && is_array( $data_arr[0] ) ? $data_arr[0] : array();
|
|
|
|
$bc_val = $data['backlink_count'] ?? null;
|
|
$match_count = is_numeric( $bc_val ) ? (int) $bc_val : 0;
|
|
|
|
$found_at_raw = $data['found_at'] ?? null;
|
|
$found_at = is_array( $found_at_raw ) ? $found_at_raw : array();
|
|
$found_at = array_slice( $found_at, 0, 10 );
|
|
|
|
$top_domains = array();
|
|
foreach ( $found_at as $domain ) {
|
|
if ( is_string( $domain ) && '' !== $domain ) {
|
|
$top_domains[ $domain ] = 1;
|
|
}
|
|
}
|
|
|
|
return new ScanResult( $match_count, $top_domains, $decoded );
|
|
}
|
|
}
|