PluginProbe
Redirection / 4.3
Redirection v4.3
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.3, at models/htaccess.php

302 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 || 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 ) {
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 return array_merge( $existing, $flags );
167 }
168
169 private function action_random( $data, $code, $match_data ) {
170 // Pick a WP post at random
171 global $wpdb;
172
173 $post = $wpdb->get_var( "SELECT ID FROM {$wpdb->posts} ORDER BY RAND() LIMIT 0,1" );
174 $url = wp_parse_url( get_permalink( $post ) );
175
176 $flags = [ sprintf( 'R=%d', $code ) ];
177 $flags[] = 'L';
178 $flags = $this->get_source_flags( $flags, $match_data['source'] );
179
180 return $this->add_flags( $this->encode( $url['path'] ), $flags );
181 }
182
183 private function action_pass( $data, $code, $match_data ) {
184 $flags = $this->get_source_flags( [ 'L' ], $match_data['source'] );
185
186 return $this->add_flags( $this->encode2nd( $data ), $flags );
187 }
188
189 private function action_error( $data, $code, $match_data ) {
190 $flags = $this->get_source_flags( [ 'F' ], $match_data['source'] );
191
192 if ( $code === 410 ) {
193 $flags = $this->get_source_flags( [ 'G' ], $match_data['source'] );
194 }
195
196 return $this->add_flags( '/', $flags );
197 }
198
199 private function action_url( $data, $code, $match_data ) {
200 $flags = [ sprintf( 'R=%d', $code ) ];
201 $flags[] = 'L';
202 $flags = $this->get_source_flags( $flags, $match_data['source'] );
203
204 return $this->add_flags( $this->encode2nd( $data ), $flags );
205 }
206
207 private function target( $action, $data, $code, $match_data ) {
208 $target = 'action_' . $action;
209
210 if ( method_exists( $this, $target ) ) {
211 return $this->$target( $data, $code, $match_data );
212 }
213
214 return '';
215 }
216
217 private function generate() {
218 $version = red_get_plugin_data( dirname( dirname( __FILE__ ) ) . '/redirection.php' );
219
220 if ( count( $this->items ) === 0 ) {
221 return '';
222 }
223
224 $text[] = '# Created by Redirection';
225 $text[] = '# ' . date( 'r' );
226 $text[] = '# Redirection ' . trim( $version['Version'] ) . ' - https://redirection.me';
227 $text[] = '';
228
229 // mod_rewrite section
230 $text[] = '<IfModule mod_rewrite.c>';
231
232 // Add http => https option
233 $options = red_get_options();
234 if ( $options['https'] ) {
235 $text[] = 'RewriteCond %{HTTPS} off';
236 $text[] = 'RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}';
237 }
238
239 // Add redirects
240 $text = array_merge( $text, array_filter( array_map( [ $this, 'sanitize_redirect' ], $this->items ) ) );
241
242 // End of mod_rewrite
243 $text[] = '</IfModule>';
244 $text[] = '';
245
246 // End of redirection section
247 $text[] = '# End of Redirection';
248
249 $text = implode( "\n", $text );
250 return "\n" . $text . "\n";
251 }
252
253 public function add( $item ) {
254 $target = 'add_' . $item->get_match_type();
255
256 if ( method_exists( $this, $target ) && $item->is_enabled() ) {
257 $this->$target( $item, $item->match );
258 }
259 }
260
261 public function get( $existing = false ) {
262 $text = $this->generate();
263
264 if ( $existing ) {
265 if ( preg_match( self::INSERT_REGEX, $existing ) > 0 ) {
266 $text = preg_replace( self::INSERT_REGEX, str_replace( '$', '\\$', $text ), $existing );
267 } else {
268 $text = $text . "\n" . trim( $existing );
269 }
270 }
271
272 return trim( $text );
273 }
274
275 public function sanitize_redirect( $text ) {
276 return str_replace( [ '<?', '>' ], '', $text );
277 }
278
279 public function sanitize_filename( $filename ) {
280 return str_replace( '.php', '', $filename );
281 }
282
283 public function save( $filename, $content_to_save = false ) {
284 $existing = false;
285 $filename = $this->sanitize_filename( $filename );
286
287 if ( file_exists( $filename ) ) {
288 $existing = file_get_contents( $filename );
289 }
290
291 $file = @fopen( $filename, 'w' );
292 if ( $file ) {
293 $result = fwrite( $file, $this->get( $existing ) );
294 fclose( $file );
295
296 return $result !== false;
297 }
298
299 return false;
300 }
301 }
302