posts WHERE post_type = 'attachment' AND guid = '$orig_file_url' AND post_parent > 0 LIMIT 1" ) ) return; } if ( ! $key = get_post_meta( $attachment_id, '_rs_file_key' ) ) { // The key was lost from DB, so regenerate it (and files / uploads .htaccess) require_once( dirname(__FILE__).'/rewrite-rules_rs.php' ); ScoperRewrite::resync_file_rules(); // If the key is still not available, fail out to avoid recursion if ( ! $key = get_post_meta( $attachment_id, '_rs_file_key' ) ) { exit(0); } } elseif ( strpos( $_SERVER['REQUEST_URI'], 'rs_file_key' ) ) { // Apparantly, the .htaccess rules contain an entry for this file, but with invalid file key. URL with this valid key already passed through RewriteRules. // Regenerate .htaccess file in uploads folder, but don't risk recursion by redirecting again. Note that Firefox browser cache may need to be cleared following this error. $last_resync = get_option( 'scoper_last_htaccess_resync' ); if ( ( ! $last_resync ) || ( time() - $last_resync > 3600 ) ) { // prevent abuse (mismatched .htaccess keys should not be a frequent occurance) update_option( 'scoper_last_htaccess_resync', time() ); require_once( dirname(__FILE__).'/rewrite-rules_rs.php' ); ScoperRewrite::resync_file_rules(); } exit(0); // If htaccess rewrite was instantaneous, we could just continue without this exit. But settle for the one-time image access failure to avoid a redirect loop on delayed file update. } if ( is_array($key) ) $key = reset($key); if ( IS_MU_RS ) { $basedir = parse_url( $uploads['basedir'] ); $baseurl = parse_url( $uploads['baseurl'] ); global $base; $file_url = str_replace( ABSPATH, $baseurl['scheme'] . '://'. $baseurl['host'] . $base , $file_path ); $file_url = str_replace( '\\', '/', $file_url ); } $redirect = $file_url . "?rs_file_key=$key"; //rs_errlog( "redirect: $redirect" ); usleep( 10 ); wp_redirect( $redirect ); exit(0); } class AttachmentFilters_RS { function user_can_read_file( $file ) { $return_attachment_id = 0; $matched_public_post = array(); return AttachmentFilters_RS::_user_can_read_file( $file, $return_attachment_id, $matched_public_post ); } function _user_can_read_file( $file, &$return_attachment_id, &$matched_published_post, $uploads = '' ) { // don't filter the direct file URL request if filtering is disabled, or if the request is from wp-admin if ( defined('DISABLE_QUERYFILTERS_RS') || is_content_administrator_rs() || ! scoper_get_option( 'file_filtering' ) || ( ! empty($_SERVER['HTTP_REFERER']) && ( false !== strpos($_SERVER['HTTP_REFERER'], '/wp-admin' ) ) && ( false !== strpos($_SERVER['HTTP_REFERER'], get_option('siteurl') . '/wp-admin' ) ) ) ) { // note: image links from wp-admin should now never get here due to http_referer RewriteRule, but leave above check just in case - inexpensive since we're checking for wp-admin before calling get_option //rs_errlog("skipping filtering for $file"); return true; } if ( ! is_array( $uploads ) || empty($uploads['basedir']) ) { require_once( dirname(__FILE__).'/uploads_rs.php' ); $uploads = scoper_get_upload_info(); } //rs_errlog('_user_can_read_file'); $file_path = $uploads['basedir'] . "/$file"; //rs_errlog("$file_path exists."); global $wpdb, $wp_query; $file_url = $uploads['baseurl'] . "/$file"; // auto-resized copies have -NNNxNNN suffix, but the base filename is stored as attachment. Strip the suffix out for db query. $orig_file_url = preg_replace( "/-[0-9]{2,4}x[0-9]{2,4}./", '.', $file_url ); // manually resized copies have -?????????????? suffix, but the base filename is stored as attachment. Strip the suffix out for db query. $orig_file_url = preg_replace( "/-[0-9,a-z]{14}./", '.', $orig_file_url ); $qry = "SELECT * FROM $wpdb->posts WHERE post_type = 'attachment' AND post_parent > 0 AND guid = '$orig_file_url'"; $results = scoper_get_results( $qry ); $matched_published_post = array(); $return_attachment_id = 0; if ( empty($results) ) { $args = array( 'skip_any_object_check' => true, 'skip_any_term_check' => true ); return cr_user_can( 'edit_others_posts', 0, 0, $args ) || cr_user_can( 'edit_others_pages', 0, 0, $args ); } else { // set global flag (checked by flt_user_has_cap, which filters current_user_Can) global $scoper_checking_attachment_access; $scoper_checking_attachment_access = true; foreach ( $results as $attachment ) { //rs_errlog( "found attachment: " . serialize($attachment) ); if ( is_content_administrator_rs() ) { $return_attachment_id = $attachment->ID; break; } if ( $attachment->post_parent ) { if ( $parent_post = scoper_get_row( "SELECT post_type, post_status FROM $wpdb->posts WHERE ID = '$attachment->post_parent' LIMIT 1" ) ) { $object_type = $parent_post->post_type; $containing_post_status = $parent_post->post_status; // Only return content that is attached to published (potentially including private) posts/pages // If some other statuses for published posts are introduced in later WP versions, // the failure mode here will be to overly suppress attachments if ( ( 'publish' == $containing_post_status ) || ( 'private' == $containing_post_status ) ) { if ( current_user_can( "read_$object_type", $attachment->post_parent ) ) { $return_attachment_id = $attachment->ID; break; } else { global $current_user; $matched_published_post[$object_type] = $attachment->post_name; } } } } } // clear global flag $scoper_checking_attachment_access = false; } if ( $return_attachment_id ) return true; } // handle access to uploaded file where request was a direct file URL, which was rewritten according to our .htaccess addition function parse_query_for_direct_access ( &$query ) { if ( empty($query->query_vars['attachment']) || ( false === strpos($_SERVER['QUERY_STRING'], 'rs_rewrite') ) ) return; $file = $query->query_vars['attachment']; require_once( dirname(__FILE__).'/uploads_rs.php' ); $uploads = scoper_get_upload_info(); $return_attachment_id = 0; $matched_published_post = array(); if ( AttachmentFilters_RS::_user_can_read_file( $file, $return_attachment_id, $matched_published_post, $uploads ) ) { agp_return_file($file, $return_attachment_id); return; } // File access was not granted. Since a 404 page will now be displayed, add filters which (for performance) were suppressed on the direct file access request global $scoper; $scoper->direct_file_access = false; $scoper->add_main_filters(); $scoper->add_hardway_filters(); //Determine if teaser message should be triggered if ( file_exists( $uploads['basedir'] . "/$file" ) ) { if ( $matched_published_post && scoper_get_otype_option('do_teaser', 'post') ) { foreach ( array_keys($matched_published_post) as $object_type ) { if ( $use_teaser_type = scoper_get_otype_option('use_teaser', 'post', $object_type) ) { if ( $matched_published_post[$object_type] ) { if ( ! defined('SCOPER_QUIET_FILE_404') ) { // note: subsequent act_attachment_access will call impose_post_teaser() $will_tease = true; // will_tease flag only used within this function $wp_query->query_vars['attachment'] = $matched_published_post[$object_type]; break; } } } } } status_header(401); // Unauthorized if ( empty($will_tease) ) { // User is not qualified to access the requested attachment, and no teaser will apply // Normally, allow the function to return for WordPress 404 handling // But end script execution here if requested attachment is a media type (or if definition set) // Linking pages won't want WP html returned in place of inaccessable image / video if ( defined('SCOPER_QUIET_FILE_404') ) { exit; } // this may not be necessary $wp_query->is_404 = true; $wp_query->is_single = true; $wp_query->is_singular = true; $wp_query->query_vars['is_single'] = true; } } } } // end class ?>