PluginProbe
Redirection / 4.6.2
Redirection v4.6.2
5.10.0 5.9.0 5.8.1 5.8.0 3.7.2 3.7.3 4.0 4.0.1 4.1 4.1.1 4.2 4.2.1 4.2.2 4.2.3 4.3 4.3.1 4.3.2 4.3.3 4.4 4.4.1 4.4.2 4.5 4.5.1 4.6.2 4.7.1 All 130 releases
redirection / models / htaccess.php

htaccess.php in Redirection 4.6.2, at models/htaccess.php

306 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Convert redirects to .htaccess format
5 *
6 * Ignores:
7 * - Trailing slash flag
8 * - Query flags
9 */
10 class Red_Htaccess {
11 private $items = array();
12 const INSERT_REGEX = '@\n?# Created by Redirection(?:.*?)# End of Redirection\n?@sm';
13
14 private function encode_from( $url ) {
15 $url = $this->encode( $url );
16
17 // Apache 2 does not need a leading slashing
18 $url = ltrim( $url, '/' );
19
20 // Exactly match the URL
21 return '^' . $url . '$';
22 }
23
24 // URL encode some things, but other things can be passed through
25 private function encode2nd( $url ) {
26 $allowed = [
27 '%2F' => '/',
28 '%3F' => '?',
29 '%3A' => ':',
30 '%3D' => '=',
31 '%26' => '&',
32 '%25' => '%',
33 '+' => '%20',
34 '%24' => '$',
35 '%23' => '#',
36 ];
37
38 $url = rawurlencode( $url );
39 return $this->replace_encoding( $url, $allowed );
40 }
41
42 private function replace_encoding( $str, $allowed ) {
43 foreach ( $allowed as $before => $after ) {
44 $str = str_replace( $before, $after, $str );
45 }
46
47 return $str;
48 }
49
50 private function encode( $url ) {
51 $allowed = [
52 '%2F' => '/',
53 '%3F' => '?',
54 '+' => '%20',
55 '.' => '\\.',
56 ];
57
58 return $this->replace_encoding( rawurlencode( $url ), $allowed );
59 }
60
61 private function encode_regex( $url ) {
62 // Remove any newlines
63 $url = preg_replace( "/[\r\n\t].*?$/s", '', $url );
64
65 // Remove invalid characters
66 $url = preg_replace( '/[^\PC\s]/u', '', $url );
67
68 // Make sure spaces are quoted
69 $url = str_replace( ' ', '%20', $url );
70 $url = str_replace( '%24', '$', $url );
71
72 // No leading slash
73 $url = ltrim( $url, '/' );
74
75 // If pattern has a ^ at the start then ensure we don't have a slash immediatley after
76 $url = preg_replace( '@^\^/@', '^', $url );
77
78 return $url;
79 }
80
81 private function add_referrer( $item, $match ) {
82 $from = $this->encode_from( ltrim( $item->get_url(), '/' ) );
83 if ( $item->is_regex() ) {
84 $from = $this->encode_regex( ltrim( $item->get_url(), '/' ) );
85 }
86
87 if ( ( $match->url_from || $match->url_notfrom ) && $match->referrer ) {
88 $this->items[] = sprintf( 'RewriteCond %%{HTTP_REFERER} %s [NC]', ( $match->regex ? $this->encode_regex( $match->referrer ) : $this->encode_from( $match->referrer ) ) );
89
90 if ( $match->url_from ) {
91 $to = $this->target( $item->get_action_type(), $match->url_from, $item->get_action_code(), $item->get_match_data() );
92 $this->items[] = sprintf( 'RewriteRule %s %s', $from, $to );
93 }
94
95 if ( $match->url_notfrom ) {
96 $to = $this->target( $item->get_action_type(), $match->url_notfrom, $item->get_action_code(), $item->get_match_data() );
97 $this->items[] = sprintf( 'RewriteRule %s %s', $from, $to );
98 }
99 }
100 }
101
102 private function add_agent( $item, $match ) {
103 $from = $this->encode( ltrim( $item->get_url(), '/' ) );
104 if ( $item->is_regex() ) {
105 $from = $this->encode_regex( ltrim( $item->get_url(), '/' ) );
106 }
107
108 if ( ( $match->url_from || $match->url_notfrom ) && $match->user_agent ) {
109 $this->items[] = sprintf( 'RewriteCond %%{HTTP_USER_AGENT} %s [NC]', ( $match->regex ? $this->encode_regex( $match->user_agent ) : $this->encode2nd( $match->user_agent ) ) );
110
111 if ( $match->url_from ) {
112 $to = $this->target( $item->get_action_type(), $match->url_from, $item->get_action_code(), $item->get_match_data() );
113 $this->items[] = sprintf( 'RewriteRule %s %s', $from, $to );
114 }
115
116 if ( $match->url_notfrom ) {
117 $to = $this->target( $item->get_action_type(), $match->url_notfrom, $item->get_action_code(), $item->get_match_data() );
118 $this->items[] = sprintf( 'RewriteRule %s %s', $from, $to );
119 }
120 }
121 }
122
123 private function add_server( $item, $match ) {
124 $match->url = $match->url_from;
125 $this->items[] = sprintf( 'RewriteCond %%{HTTP_HOST} ^%s$ [NC]', preg_quote( $match->server ) );
126 $this->add_url( $item, $match );
127 }
128
129 private function add_url( $item, $match ) {
130 $url = $item->get_url();
131
132 if ( $item->is_regex() === false && strpos( $url, '?' ) !== false ) {
133 $url_parts = wp_parse_url( $url );
134 $url = $url_parts['path'];
135 $query = isset( $url_parts['query'] ) ? $url_parts['query'] : '';
136 $this->items[] = sprintf( 'RewriteCond %%{QUERY_STRING} ^%s$', $query );
137 }
138
139 $to = $this->target( $item->get_action_type(), $match->url, $item->get_action_code(), $item->get_match_data() );
140 $from = $this->encode_from( $url );
141
142 if ( $item->is_regex() ) {
143 $from = $this->encode_regex( $item->get_url() );
144 }
145
146 if ( $to ) {
147 $this->items[] = sprintf( 'RewriteRule %s %s', $from, $to );
148 }
149 }
150
151 private function add_flags( $current, array $flags ) {
152 return $current . ' [' . implode( ',', $flags ) . ']';
153 }
154
155 private function get_source_flags( array $existing, array $source, $url ) {
156 $flags = [];
157
158 if ( isset( $source['flag_case'] ) && $source['flag_case'] ) {
159 $flags[] = 'NC';
160 }
161
162 if ( isset( $source['flag_query'] ) && $source['flag_query'] === 'pass' ) {
163 $flags[] = 'QSA';
164 }
165
166 if ( strpos( $url, '#' ) !== false ) {
167 $flags[] = 'NE';
168 }
169
170 return array_merge( $existing, $flags );
171 }
172
173 private function action_random( $data, $code, $match_data ) {
174 // Pick a WP post at random
175 global $wpdb;
176
177 $post = $wpdb->get_var( "SELECT ID FROM {$wpdb->posts} ORDER BY RAND() LIMIT 0,1" );
178 $url = wp_parse_url( get_permalink( $post ) );
179
180 $flags = [ sprintf( 'R=%d', $code ) ];
181 $flags[] = 'L';
182 $flags = $this->get_source_flags( $flags, $match_data['source'], $data );
183
184 return $this->add_flags( $this->encode( $url['path'] ), $flags );
185 }
186
187 private function action_pass( $data, $code, $match_data ) {
188 $flags = $this->get_source_flags( [ 'L' ], $match_data['source'], $data );
189
190 return $this->add_flags( $this->encode2nd( $data ), $flags );
191 }
192
193 private function action_error( $data, $code, $match_data ) {
194 $flags = $this->get_source_flags( [ 'F' ], $match_data['source'], $data );
195
196 if ( $code === 410 ) {
197 $flags = $this->get_source_flags( [ 'G' ], $match_data['source'], $data );
198 }
199
200 return $this->add_flags( '/', $flags );
201 }
202
203 private function action_url( $data, $code, $match_data ) {
204 $flags = [ sprintf( 'R=%d', $code ) ];
205 $flags[] = 'L';
206 $flags = $this->get_source_flags( $flags, $match_data['source'], $data );
207
208 return $this->add_flags( $this->encode2nd( $data ), $flags );
209 }
210
211 private function target( $action, $data, $code, $match_data ) {
212 $target = 'action_' . $action;
213
214 if ( method_exists( $this, $target ) ) {
215 return $this->$target( $data, $code, $match_data );
216 }
217
218 return '';
219 }
220
221 private function generate() {
222 $version = red_get_plugin_data( dirname( dirname( __FILE__ ) ) . '/redirection.php' );
223
224 if ( count( $this->items ) === 0 ) {
225 return '';
226 }
227
228 $text = [
229 '# Created by Redirection',
230 '# ' . date( 'r' ),
231 '# Redirection ' . trim( $version['Version'] ) . ' - https://redirection.me',
232 '',
233 '<IfModule mod_rewrite.c>',
234 ];
235
236 // Add http => https option
237 $options = red_get_options();
238 if ( $options['https'] ) {
239 $text[] = 'RewriteCond %{HTTPS} off';
240 $text[] = 'RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}';
241 }
242
243 // Add redirects
244 $text = array_merge( $text, array_filter( array_map( [ $this, 'sanitize_redirect' ], $this->items ) ) );
245
246 // End of mod_rewrite
247 $text[] = '</IfModule>';
248 $text[] = '';
249
250 // End of redirection section
251 $text[] = '# End of Redirection';
252
253 $text = implode( "\n", $text );
254 return "\n" . $text . "\n";
255 }
256
257 public function add( $item ) {
258 $target = 'add_' . $item->get_match_type();
259
260 if ( method_exists( $this, $target ) && $item->is_enabled() ) {
261 $this->$target( $item, $item->match );
262 }
263 }
264
265 public function get( $existing = false ) {
266 $text = $this->generate();
267
268 if ( $existing ) {
269 if ( preg_match( self::INSERT_REGEX, $existing ) > 0 ) {
270 $text = preg_replace( self::INSERT_REGEX, str_replace( '$', '\\$', $text ), $existing );
271 } else {
272 $text = $text . "\n" . trim( $existing );
273 }
274 }
275
276 return trim( $text );
277 }
278
279 public function sanitize_redirect( $text ) {
280 return str_replace( [ '<?', '>' ], '', $text );
281 }
282
283 public function sanitize_filename( $filename ) {
284 return str_replace( '.php', '', $filename );
285 }
286
287 public function save( $filename, $content_to_save = false ) {
288 $existing = false;
289 $filename = $this->sanitize_filename( $filename );
290
291 if ( file_exists( $filename ) ) {
292 $existing = file_get_contents( $filename );
293 }
294
295 $file = @fopen( $filename, 'w' );
296 if ( $file ) {
297 $result = fwrite( $file, $this->get( $existing ) );
298 fclose( $file );
299
300 return $result !== false;
301 }
302
303 return false;
304 }
305 }
306