Skip to content
Snippets Groups Projects
wp-mat.php 5.73 KiB
<?php
/**
 * Plugin Name: wp-mat
 * Plugin URI: https://git.autistici.org/noblogs/wp-mat
 * Description: Process uploaded files through MAT
 * Version: 0.1.4
 * Author: Autistici/Inventati
 * Author URI: https://www.autistici.org/
 * License: MIT
 * License URI: http://opensource.org/licenses/MIT
 */

if (!defined("WP_MAT_ENDPOINT")) {
    define("WP_MAT_ENDPOINT", "http://localhost:7111/api/cleanup");
}

// This is generally useful for debugging.
if (!function_exists('wp_handle_upload_error')) {
    function wp_handle_upload_error(&$file, $message) {
        error_log("wp_handle_upload_error: ".$message);
        return array( "error" => $message );
    }
}

class WP_Mat {

    // Check if the mime type is one of those we want to clean up.
    // Use a blacklist, currently empty. Send everything by default
    // except video, and pdf.
    function is_supported_mime_type($mimetype) {
        if (substr($mimetype, 0, 6) === "video/") {
            return false;
        }
        if ($mimetype === "application/pdf") {
            return false;
        }
        return true;
    }

    // Make a POST request to the MAT service with the uploaded file and
    // store the results into '$output_path'. Returns true on success,
    // false on any error.
    function issue_cleanup_request($path, $mimetype, $output_path) {
        $upfile = new CURLFile($path, $mimetype, 'input');

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, WP_MAT_ENDPOINT);
        curl_setopt($curl, CURLOPT_USERAGENT, 'Wordpress');
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, array(
            "file" => $upfile,
            "mime_type" => $mimetype,
        ));

        if (($fp = fopen($output_path, "wb")) === false) {
            throw new Exception("fopen error for filename $output_path");
        }
        curl_setopt($curl, CURLOPT_FILE, $fp);
        curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);

        $ret = curl_exec($curl);

        if ($ret === false) {
            error_log("wp-mat: curl error: ".curl_error($curl));
        } else {
            $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
            if ($status != 200) {
                error_log("wp-mat: curl error: server returned HTTP status ".$status);
                $ret = false;
            }
        }

        fclose($fp);
        curl_close($curl);

        return $ret;
    }

    // Filter uploaded files before they are processed by Wordpress.
    function handle_upload_prefilter($file) {
        if (isset($file['error']) && $file['error']) {
            return $file;
        }
        if (get_option('wpmat_disabled')) {
            return $file;
        }

        // Autodetect MIME type if not passed by the browser.
        if (!$file['type']) {
            $file['type'] = mime_content_type($file['tmp_name']);
        }

        // Check if it's one of the MIME types we support.
        if (!$this->is_supported_mime_type($file['type'])) {
            error_log("wp-mat: unsupported mime type ".$file['type']);
            return $file;
        }

        // Issue a request to the MAT API, and if successful replace the
        // file with the sanitized one.
        $new_tmp_file = tempnam("", "wp_mat_");
        if ($this->issue_cleanup_request($file['tmp_name'], $file['type'], $new_tmp_file)) {
            // Rename the cleaned up file to the original file, or WP
            // will not pass the is_uploaded_file() check in wp_handle_upload().
            unlink($file['tmp_name']);
            rename($new_tmp_file, $file['tmp_name']);

            $new_file = array(
                "name" => $file['name'],
                "type" => $file['type'],
                "tmp_name" => $file['tmp_name'],
                "size" => filesize($file['tmp_name']),
                "error" => 0,
            );
            error_log("wp-mat: successfully cleaned file=".json_encode($new_file));
            $file = $new_file;
        } else {
            error_log("wp-mat: server error for file=".json_encode($file));
            @unlink($new_tmp_file);
        }

        return $file;
    }

    function settings_api_init() {
        // Add the section to reading settings so we can add our
        // fields to it.
        add_settings_section(
            'wpmat_settings_section',
            'Metadata anonymization for uploads',
            array($this, 'settings_section_callback'),
            'media'
        );

        // Add the field with the names and function to use for our new
        // settings, put it in our new section.
        add_settings_field(
            'wpmat_disabled',
            'Disable metadata anonymization',
            array($this, 'settings_field_callback'),
            'media',
            'wpmat_settings_section'
        );

        // Register our setting so that $_POST handling is done for us and
        // our callback function just has to echo the <input>.
        register_setting('media', 'wpmat_disabled');
    }

    function settings_section_callback() {
        echo('<p>Control whether uploaded files are run through metadata anonymization (using <a href="https://0xacab.org/jvoisin/mat2">MAT2</a>). ');
        echo('This feature is enabled by default but it can be disabled in case you are experiencing problems uploading files.</p>');
    }

    function settings_field_callback() {
        echo('<input name="wpmat_disabled" id="wpmat_disabled" type="checkbox" value="1" class="code" ' . checked(1, get_option('wpmat_disabled'), false) . ' /> <label for="wpmat_disabled">Disable metadata anonymization</label>');
    }

    function __construct() {
        add_filter('wp_handle_upload_prefilter', array($this, 'handle_upload_prefilter'));
        add_action('admin_init', array($this, 'settings_api_init'));
    }

}

new WP_Mat();