true ) ); add_action( 'frontglut_daily', array( __CLASS__, 'cleanup' ) ); add_filter( 'frontglut_default_settings', function ( $d ) { $d['uploads'] = array( 'allowed' => 'jpg,jpeg,png,gif,webp,pdf,doc,docx,xls,xlsx,ppt,pptx,zip,txt,csv,mp3,mp4', 'max_mb' => 8, 'per_hour' => 40, 'max_dim' => 2560, 'guest_ok' => 0, ); return $d; } ); } public static function allowed_exts() { $a = array_filter( array_map( 'trim', explode( ',', strtolower( (string) Settings::get( 'uploads.allowed' ) ) ) ) ); $deny = array( 'php', 'php3', 'php4', 'php5', 'phtml', 'phar', 'js', 'html', 'htm', 'exe', 'sh', 'cgi', 'pl', 'htaccess' ); return array_values( array_diff( $a, $deny ) ); } /** Identity used to own temp uploads: user id or guest token. */ public static function identity( $create = false ) { if ( is_user_logged_in() ) { return (string) get_current_user_id(); } $tok = isset( $_COOKIE['frontglut_gt'] ) ? preg_replace( '/[^A-Za-z0-9]/', '', sanitize_text_field( wp_unslash( $_COOKIE['frontglut_gt'] ) ) ) : ''; if ( ! $tok && $create && ! headers_sent() ) { $tok = wp_generate_password( 20, false ); setcookie( 'frontglut_gt', $tok, time() + DAY_IN_SECONDS, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN, is_ssl(), true ); $_COOKIE['frontglut_gt'] = $tok; } return $tok ? 'g:' . $tok : ''; } public static function handle( $req ) { $logged = is_user_logged_in(); if ( $logged && ! Caps::can( 'frontglut_upload_files' ) && ! Caps::can( 'frontglut_manage_posts' ) ) { Ajax::fail( __( 'You may not upload files.', 'frontglut' ), 403 ); } if ( ! $logged && ! Settings::get( 'uploads.guest_ok' ) && empty( $req['form'] ) ) { Ajax::fail( __( 'Please log in to upload.', 'frontglut' ), 401 ); } if ( ! $logged && ! empty( $req['form'] ) ) { $form = \FrontGlut\Modules\Forms\Forms::get( (int) $req['form'] ); if ( ! $form || empty( $form['settings']['guest'] ) ) { Ajax::fail( __( 'Uploads are not allowed for guests here.', 'frontglut' ), 403 ); } } if ( null === Ajax::file( 'file' ) ) { Ajax::fail( __( 'No file received.', 'frontglut' ) ); } if ( ! RateLimiter::hit( 'upload', (int) Settings::get( 'uploads.per_hour', 40 ), HOUR_IN_SECONDS, $logged ? 'u' . get_current_user_id() : '' ) ) { Ajax::fail( __( 'Upload limit reached. Try again later.', 'frontglut' ), 429 ); } $perm = ! empty( $req['perm'] ) && ( $logged || Settings::get( 'uploads.guest_ok' ) ); $id = self::store( 'file', ! $perm ); if ( is_wp_error( $id ) ) { Ajax::fail( $id->get_error_message() ); } $multi = ! empty( $req['multiple'] ); return array( 'id' => $id, 'html' => \FrontGlut\Support\Fields::uploader_item( $id, isset( $req['name'] ) ? sanitize_text_field( $req['name'] ) : 'file', $multi ), 'url' => wp_get_attachment_url( $id ) ); } /** Validate and store one uploaded file ($_FILES key). @return int|\WP_Error attachment id */ public static function store( $key, $temp = true ) { // Ajax::file() checks the AJAX nonce; the file itself is stored by media_handle_upload(). $raw = Ajax::file( $key ); if ( ! $raw || ! isset( $raw['error'], $raw['name'], $raw['tmp_name'] ) || is_array( $raw['error'] ) ) { return new \WP_Error( 'up', __( 'Upload failed.', 'frontglut' ) ); } $f = array( 'name' => sanitize_file_name( wp_unslash( (string) $raw['name'] ) ), 'tmp_name' => (string) $raw['tmp_name'], // Server-generated temp path. 'error' => (int) $raw['error'], 'size' => isset( $raw['size'] ) ? absint( $raw['size'] ) : 0, ); if ( UPLOAD_ERR_OK !== $f['error'] ) { return new \WP_Error( 'up', __( 'Upload failed.', 'frontglut' ) ); } $max = (float) Settings::get( 'uploads.max_mb', 8 ) * 1048576; if ( $f['size'] > $max ) { /* translators: %s: value. */ return new \WP_Error( 'up', sprintf( __( 'File is larger than %s MB.', 'frontglut' ), Settings::get( 'uploads.max_mb', 8 ) ) ); } $ext = strtolower( pathinfo( $f['name'], PATHINFO_EXTENSION ) ); if ( ! in_array( $ext, self::allowed_exts(), true ) ) { return new \WP_Error( 'up', __( 'This file type is not allowed.', 'frontglut' ) ); } require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; $restrict = function ( $mimes ) { $allowed = array(); foreach ( $mimes as $exts => $mime ) { foreach ( explode( '|', $exts ) as $e ) { if ( in_array( $e, self::allowed_exts(), true ) ) { $allowed[ $exts ] = $mime; break; } } } return $allowed; }; add_filter( 'upload_mimes', $restrict, 99 ); $chk = wp_check_filetype_and_ext( $f['tmp_name'], $f['name'] ); if ( empty( $chk['ext'] ) || empty( $chk['type'] ) ) { remove_filter( 'upload_mimes', $restrict, 99 ); return new \WP_Error( 'up', __( 'File content does not match its type.', 'frontglut' ) ); } $id = media_handle_upload( $key, 0, array(), array( 'test_form' => false ) ); remove_filter( 'upload_mimes', $restrict, 99 ); if ( is_wp_error( $id ) ) { return $id; } update_post_meta( $id, '_frontglut_uploader', self::identity( true ) ); if ( $temp ) { update_post_meta( $id, '_frontglut_temp', time() ); } return $id; } public static function owns( $attachment_id, $user_id = null ) { $attachment_id = absint( $attachment_id ); if ( ! $attachment_id || 'attachment' !== get_post_type( $attachment_id ) ) { return false; } if ( Caps::can( 'manage_options' ) ) { return true; } $owner = (string) get_post_meta( $attachment_id, '_frontglut_uploader', true ); if ( '' === $owner ) { // Media Library items: allow if user authored them. return is_user_logged_in() && (int) get_post_field( 'post_author', $attachment_id ) === get_current_user_id(); } return hash_equals( $owner, self::identity() ); } /** Keep only IDs the current identity owns. */ public static function filter_owned( array $ids ) { return array_values( array_filter( $ids, array( __CLASS__, 'owns' ) ) ); } /** Attach and un-temp. */ public static function claim( array $ids, $post_id ) { foreach ( $ids as $id ) { delete_post_meta( $id, '_frontglut_temp' ); if ( ! (int) get_post_field( 'post_parent', $id ) ) { wp_update_post( array( 'ID' => $id, 'post_parent' => (int) $post_id ) ); } } } public static function cleanup() { $q = get_posts( array( 'post_type' => 'attachment', 'post_status' => 'any', 'posts_per_page' => 100, 'fields' => 'ids', 'meta_query' => array( array( 'key' => '_frontglut_temp', 'value' => time() - DAY_IN_SECONDS, 'compare' => '<', 'type' => 'NUMERIC' ) ), ) ); foreach ( $q as $id ) { wp_delete_attachment( $id, true ); } } }