/
attachment-filters_rs.php
226 lines (174 loc) · 9.29 KB
/
attachment-filters_rs.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
<?php
if( basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']) )
die();
function agp_return_file( $file_path, $attachment_id = 0 ) {
require_once( dirname(__FILE__).'/uploads_rs.php' );
$uploads = scoper_get_upload_info();
if ( false === strpos( $file_path, $uploads['basedir'] ) )
$file_path = untrailingslashit($uploads['basedir']) . "/$file_path";
$file_url = str_replace( untrailingslashit($uploads['basedir']), untrailingslashit($uploads['baseurl']), $file_path );
//rs_errlog( "agp_return_file: $file_path" );
if ( ! $attachment_id ) {
global $wpdb; // we've already confirmed that this user can read the file; if it is attached to more than one post any corresponding file key will do
// 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 );
//rs_errlog( "orig_file_url: $orig_file_url" );
if ( ! $attachment_id = scoper_get_var( "SELECT ID FROM $wpdb->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
?>