agp_time_gmt() - 5 ) {
return;
}
}
update_site_option( 'scoper_main_htaccess_date', agp_time_gmt() ); // stores to site_meta table for network installs. Note: scoper_update_site_option is NOT equivalent
$include_rs_rules = $include_rs_rules && scoper_get_option( 'file_filtering' );
// sleep() time is necessary to avoid .htaccess file i/o race conditions since other plugins (W3 Total Cache) may also perform or trigger .htaccess update, and those file operations don't all use flock
// This update only occurs on plugin activation, the first time a MS site has an attachment to a private/restricted page, and on various plugin option changes.
if ( IS_MU_RS ) {
add_action( 'shutdown', create_function( '', "sleep(2); require_once( dirname(__FILE__).'/rewrite-mu_rs.php' ); ScoperRewriteMU::update_mu_htaccess( '$include_rs_rules' );" ) );
} else {
if ( file_exists( ABSPATH . '/wp-admin/includes/misc.php' ) )
include_once( ABSPATH . '/wp-admin/includes/misc.php' );
if ( file_exists( ABSPATH . '/wp-admin/includes/file.php' ) )
include_once( ABSPATH . '/wp-admin/includes/file.php' );
add_action( 'shutdown', create_function( '', 'global $wp_rewrite; if ( ! did_action("delete_option_rewrite_rules") && ! empty($wp_rewrite) ) { sleep(2); $wp_rewrite->flush_rules(true); }' ), 999 );
}
}
function build_site_rules( $ifmodule_wrapper = true ) {
$http_auth = scoper_get_option( 'feed_link_http_auth' );
$filtering = IS_MU_RS && get_site_option( 'scoper_file_filtering' ); // scoper_get_option is not reliable for initial execution following plugin activation
$new_rules = '';
if ( $http_auth || $filtering ) {
require_once( dirname(__FILE__).'/uploads_rs.php' );
$new_rules = "\n# BEGIN Role Scoper\n";
if ( $ifmodule_wrapper )
$new_rules .= "\n";
$new_rules .= "RewriteEngine On\n\n";
if ( $http_auth ) {
// workaround for HTTP Authentication with PHP running as CGI
$new_rules .= "RewriteCond %{HTTP:Authorization} ^(.*)\n";
$new_rules .= "RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]\n";
}
if ( $filtering ) {
$new_rules .= ScoperRewriteMU::build_blog_file_redirects();
}
if ( $ifmodule_wrapper )
$new_rules .= "\n";
$new_rules .= "\n# END Role Scoper\n\n";
}
return $new_rules;
}
function site_config_supports_rewrite() {
require_once( dirname(__FILE__).'/uploads_rs.php' );
$uploads = scoper_get_upload_info();
if ( false === strpos( $uploads['baseurl'], untrailingslashit( get_option('siteurl') ) ) )
return false;
// don't risk leaving custom .htaccess files in content folder at deactivation due to difficulty of reconstructing custom path for each blog
if ( IS_MU_RS ) {
global $blog_id;
if ( 'site-new.php' == $GLOBALS['pagenow'] )
return true;
if ( UPLOADS != UPLOADBLOGSDIR . "/$blog_id/files/" )
return false;
if ( BLOGUPLOADDIR != WP_CONTENT_DIR . "/blogs.dir/$blog_id/files/" )
return false;
}
return true;
}
function update_blog_file_rules( $include_rs_rules = true ) {
global $blog_id;
// avoid file collision by skipping if another flush was initiated < 10 seconds ago
if ( $last_regen = scoper_get_option( 'file_htaccess_date' ) ) {
if ( intval($last_regen) > agp_time_gmt() - 10 ) {
return;
}
}
scoper_update_option( 'file_htaccess_date', agp_time_gmt() );
$include_rs_rules = $include_rs_rules && scoper_get_option( 'file_filtering' );
if ( ! ScoperRewrite::site_config_supports_rewrite() )
return;
elseif ( ! $include_rs_rules )
$rules = '';
else
$rules = ScoperRewrite::build_blog_file_rules();
require_once( dirname(__FILE__).'/uploads_rs.php' );
$uploads = scoper_get_upload_info();
// If a filter has changed MU basedir, don't filter file attachments for this blog because we might not be able to regenerate the basedir for rule removal at RS deactivation
if ( ! IS_MU_RS || strpos( $uploads['basedir'], "/blogs.dir/$blog_id/files" ) || ( false !== strpos( $uploads['basedir'], trailingslashit(WP_CONTENT_DIR) . 'uploads' ) ) ) {
$htaccess_path = trailingslashit($uploads['basedir']) . '.htaccess';
ScoperRewrite::insert_with_markers( $htaccess_path, 'Role Scoper', $rules );
}
}
function &build_blog_file_rules() {
$new_rules = '';
require_once( dirname(__FILE__).'/analyst_rs.php' );
if ( ! $attachment_results = ScoperAnalyst::identify_protected_attachments() )
return $new_rules;
global $wpdb;
require_once( dirname(__FILE__).'/uploads_rs.php' );
$home_root = parse_url(get_option('home'));
$home_root = trailingslashit( $home_root['path'] );
$uploads = scoper_get_upload_info();
$baseurl = trailingslashit( $uploads['baseurl'] );
$arr_url = parse_url( $baseurl );
$rewrite_base = $arr_url['path'];
$file_keys = array();
$has_postmeta = array();
if ( $key_results = scoper_get_results( "SELECT pm.meta_value, p.guid, p.ID FROM $wpdb->postmeta AS pm INNER JOIN $wpdb->posts AS p ON p.ID = pm.post_id WHERE pm.meta_key = '_rs_file_key'" ) ) {
foreach ( $key_results as $row ) {
$file_keys[$row->guid] = $row->meta_value;
$has_postmeta[$row->ID] = $row->meta_value;
}
}
$new_rules = "\n";
$new_rules .= "RewriteEngine On\n";
$new_rules .= "RewriteBase $rewrite_base\n\n";
$main_rewrite_rule = "RewriteRule ^(.*) {$home_root}index.php?attachment=$1&rs_rewrite=1 [NC,L]\n";
$htaccess_urls = array();
foreach ( $attachment_results as $row ) {
if ( false !== strpos( $row->guid, $baseurl ) ) { // no need to include any attachments which are not in the uploads folder
if ( ! empty($file_keys[ $row->guid ] ) ) {
$key = $file_keys[ $row->guid ];
} else {
$key = urlencode( str_replace( '.', '', uniqid( strval( rand() ), true ) ) );
$file_keys[ $row->guid ] = $key;
}
if ( ! isset( $has_postmeta[$row->ID] ) || ( $key != $has_postmeta[$row->ID] ) )
update_post_meta( $row->ID, "_rs_file_key", $key );
if ( isset( $htaccess_urls[$row->guid] ) ) // if a file is attached to multiple protected posts, use a single rewrite rule for it
continue;
$htaccess_urls[$row->guid] = true;
$rel_path = str_replace( $baseurl, '', $row->guid );
// escape spaces
$file_path = str_replace( ' ', '\s', $rel_path );
// escape horiz tabs (yes, at least one user has them in filenames)
$file_path = str_replace( chr(9), '\t', $file_path );
// strip out all other nonprintable characters. Affected files will not be filtered, but we avoid 500 error. Possible TODO: advisory in file attachment utility
$file_path = preg_replace( '/[\x00-\x1f\x7f]/', '', $file_path );
// escape all other regular expression operator characters
$file_path = preg_replace( '/[\^\$\.\+\[\]\(\)\{\}]/', '\\\$0', $file_path );
$new_rules .= "RewriteCond %{REQUEST_URI} ^(.*)/$file_path" . "$ [NC]\n";
$new_rules .= "RewriteCond %{QUERY_STRING} !^(.*)rs_file_key=$key(.*)\n";
$new_rules .= $main_rewrite_rule;
if ( $pos_ext = strrpos( $file_path, '\.' ) ) {
$thumb_path = substr( $file_path, 0, $pos_ext );
$ext = substr( $file_path, $pos_ext + 2 );
$new_rules .= "RewriteCond %{REQUEST_URI} ^(.*)/$thumb_path" . '-[0-9]{2,4}x[0-9]{2,4}\.' . $ext . "$ [NC]\n";
$new_rules .= "RewriteCond %{QUERY_STRING} !^(.*)rs_file_key=$key(.*)\n";
$new_rules .= $main_rewrite_rule;
// if resized image file(s) exist, include rules for them
$guid_pos_ext = strrpos( $rel_path, '.' );
$pattern = $uploads['path'] . '/' . substr( $rel_path, 0, $guid_pos_ext ) . '-??????????????' . substr( $rel_path, $guid_pos_ext );
if ( glob( $pattern ) ) {
$new_rules .= "RewriteCond %{REQUEST_URI} ^(.*)/$thumb_path" . '-[0-9,a-f]{14}\.' . $ext . "$ [NC]\n";
$new_rules .= "RewriteCond %{QUERY_STRING} !^(.*)rs_file_key=$key(.*)\n";
$new_rules .= $main_rewrite_rule;
}
}
}
} // end foreach protected attachment
if ( IS_MU_RS ) {
global $blog_id;
$file_filtered_sites = (array) get_site_option( 'scoper_file_filtered_sites' );
if ( ! in_array( $blog_id, $file_filtered_sites ) ) {
// this site needs a file redirect rule in root .htaccess
scoper_flush_site_rules();
}
if ( defined('SCOPER_MU_FILE_PROCESSING') ) { // unless SCOPER_MU_FILE_PROCESSING is defined (indicating blogs.php has been modified for compatibility), blogs.php processing will be bypassed for all files
$content_path = trailingslashit( str_replace( $strip_path, '', str_replace( '\\', '/', WP_CONTENT_DIR ) ) );
$new_rules .= "\n# Default WordPress cache handling\n";
$new_rules .= "RewriteRule ^(.*) {$content_path}blogs.php?file=$1 [L]\n";
}
}
$new_rules .= "\n";
return $new_rules;
}
// called by agp_return_file() in abnormal cases where file access is approved, but key for protected file is lost/corrupted in postmeta record or .htaccess file
function resync_file_rules() {
// Don't allow this to execute too frequently, to prevent abuse or accidental recursion
if ( agp_time_gmt() - get_option( 'last_htaccess_resync_rs' ) > 30 ) {
update_option( 'last_htaccess_resync_rs', agp_time_gmt() );
// Only the files / uploads .htaccess for current blog is regenerated
scoper_flush_file_rules();
usleep(10000); // Allow 10 milliseconds for server to regather itself following .htaccess update
}
}
} // end class ScoperRewrite
?>