PluginProbe
Redirection / 3.7
Redirection v3.7
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 / redirect.php

redirect.php in Redirection 3.7, at models/redirect.php

601 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class Red_Item {
4 private $id = null;
5 private $url = null;
6 private $regex = false;
7 private $action_data = null;
8 private $action_code = 0;
9 private $action_type;
10 private $match_type;
11 private $title;
12 private $last_access = null;
13 private $last_count = 0;
14 private $status = 'enabled';
15 private $position;
16 private $group_id;
17
18 function __construct( $values, $type = '', $match = '' ) {
19 if ( is_object( $values ) ) {
20 $this->load_from_data( $values );
21
22 if ( $this->last_access === '0000-00-00 00:00:00' ) {
23 $this->last_access = 0;
24 } else {
25 $this->last_access = mysql2date( 'U', $this->last_access );
26 }
27 }
28 }
29
30 private function load_from_data( stdClass $values ) {
31 foreach ( $values as $key => $value ) {
32 $this->$key = $value;
33 }
34
35 if ( $this->match_type === '' ) {
36 $this->match_type = 'url';
37 }
38
39 $this->regex = (bool) $this->regex;
40 $this->match = Red_Match::create( $this->match_type, $this->action_data );
41 $this->match->id = $this->id;
42 $this->match->action_code = $this->action_code;
43
44 $action = false;
45
46 if ( $this->action_type ) {
47 $action = Red_Action::create( $this->action_type, $this->action_code );
48 }
49
50 if ( $action ) {
51 $this->action = $action;
52 $this->match->action = $this->action;
53 } else {
54 $this->action = Red_Action::create( 'nothing', 0 );
55 }
56 }
57
58 static function get_all_for_module( $module ) {
59 global $wpdb;
60
61 $rows = $wpdb->get_results(
62 $wpdb->prepare(
63 "SELECT {$wpdb->prefix}redirection_items.* FROM {$wpdb->prefix}redirection_items
64 INNER JOIN {$wpdb->prefix}redirection_groups ON {$wpdb->prefix}redirection_groups.id={$wpdb->prefix}redirection_items.group_id
65 AND {$wpdb->prefix}redirection_groups.status='enabled' AND {$wpdb->prefix}redirection_groups.module_id=%d
66 WHERE {$wpdb->prefix}redirection_items.status='enabled'
67 ORDER BY {$wpdb->prefix}redirection_groups.position,{$wpdb->prefix}redirection_items.position",
68 $module
69 )
70 );
71 $items = array();
72
73 foreach ( (array) $rows as $row ) {
74 $items[] = new Red_Item( $row );
75 }
76
77 return $items;
78 }
79
80 static function get_for_url( $url ) {
81 global $wpdb;
82
83 $rows = $wpdb->get_results(
84 $wpdb->prepare(
85 "SELECT {$wpdb->prefix}redirection_items.*,{$wpdb->prefix}redirection_groups.position AS group_pos
86 FROM {$wpdb->prefix}redirection_items INNER JOIN {$wpdb->prefix}redirection_groups ON
87 {$wpdb->prefix}redirection_groups.id={$wpdb->prefix}redirection_items.group_id AND {$wpdb->prefix}redirection_groups.status='enabled'
88 AND {$wpdb->prefix}redirection_groups.module_id=%d WHERE ({$wpdb->prefix}redirection_items.regex=1
89 OR {$wpdb->prefix}redirection_items.url=%s)",
90 WordPress_Module::MODULE_ID,
91 $url
92 )
93 );
94
95 $items = array();
96 if ( count( $rows ) > 0 ) {
97 foreach ( $rows as $row ) {
98 $items[] = array(
99 'position' => ( $row->group_pos * 1000 ) + $row->position,
100 'item' => new Red_Item( $row ),
101 );
102 }
103 }
104
105 usort( $items, array( 'Red_Item', 'sort_urls' ) );
106 $items = array_map( array( 'Red_Item', 'reduce_sorted_items' ), $items );
107
108 // Sort it in PHP
109 ksort( $items );
110 $items = array_values( $items );
111 return $items;
112 }
113
114 static function get_all() {
115 global $wpdb;
116
117 $rows = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}redirection_items" );
118 $items = array();
119
120 foreach ( (array) $rows as $row ) {
121 $items[] = new Red_Item( $row );
122 }
123
124 return $items;
125 }
126
127 static function sort_urls( $first, $second ) {
128 if ( $first['position'] === $second['position'] ) {
129 return 0;
130 }
131
132 return ( $first['position'] < $second['position'] ) ? -1 : 1;
133 }
134
135 static function reduce_sorted_items( $item ) {
136 return $item['item'];
137 }
138
139 static function get_by_id( $id ) {
140 global $wpdb;
141
142 $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}redirection_items WHERE id=%d", $id ) );
143 if ( $row ) {
144 return new Red_Item( $row );
145 }
146
147 return false;
148 }
149
150 public static function disable_where_matches( $url ) {
151 global $wpdb;
152
153 $wpdb->update( $wpdb->prefix . 'redirection_items', array( 'status' => 'disabled' ), array( 'url' => $url ) );
154 }
155
156 public function delete() {
157 global $wpdb;
158
159 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}redirection_items WHERE id=%d", $this->id ) );
160 do_action( 'redirection_redirect_deleted', $this );
161
162 Red_Module::flush( $this->group_id );
163 }
164
165 static function create( array $details ) {
166 global $wpdb;
167
168 $sanitizer = new Red_Item_Sanitize();
169 $data = $sanitizer->get( $details );
170 if ( is_wp_error( $data ) ) {
171 return $data;
172 }
173
174 $data['status'] = 'enabled';
175 if ( ( isset( $details['enabled'] ) && ( $details['enabled'] === 'disabled' || $details['enabled'] === false ) ) || ( isset( $details['status'] ) && $details['status'] === 'disabled' ) ) {
176 $data['status'] = 'disabled';
177 }
178
179 if ( ! isset( $details['position'] ) || $details['position'] === 0 ) {
180 $data['position'] = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_items WHERE group_id=%d", $data['group_id'] ) );
181 }
182
183 $data = apply_filters( 'redirection_create_redirect', $data );
184
185 // Create
186 if ( $wpdb->insert( $wpdb->prefix . 'redirection_items', $data ) !== false ) {
187 Red_Module::flush( $data['group_id'] );
188
189 $redirect = self::get_by_id( $wpdb->insert_id );
190 do_action( 'redirection_redirect_updated', $wpdb->insert_id, $redirect );
191
192 return $redirect;
193 }
194
195 return new WP_Error( 'redirect', __( 'Unable to add new redirect' ) );
196 }
197
198 public function update( $details ) {
199 global $wpdb;
200
201 $sanitizer = new Red_Item_Sanitize();
202 $data = $sanitizer->get( $details );
203
204 if ( is_wp_error( $data ) ) {
205 return $data;
206 }
207
208 $old_group = false;
209 if ( $data['group_id'] !== $this->group_id ) {
210 $old_group = $this->group_id;
211 }
212
213 // Save this
214 $data = apply_filters( 'redirection_update_redirect', $data );
215
216 $wpdb->update( $wpdb->prefix . 'redirection_items', $data, array( 'id' => $this->id ) );
217 do_action( 'redirection_redirect_updated', $this, self::get_by_id( $this->id ) );
218
219 $this->load_from_data( (object) $data );
220
221 Red_Module::flush( $this->group_id );
222
223 if ( $old_group !== $this->group_id ) {
224 Red_Module::flush( $old_group );
225 }
226
227 return true;
228 }
229
230 public function matches( $url ) {
231 if ( ! $this->is_enabled() ) {
232 return false;
233 }
234
235 $this->url = str_replace( ' ', '%20', $this->url );
236 $matches = false;
237
238 // Check if we match the URL
239 if ( ( $this->regex === false && ( $this->url === $url || $this->url === rtrim( $url, '/' ) || $this->url === urldecode( $url ) ) ) || ( $this->regex === true && @preg_match( '@' . str_replace( '@', '\\@', $this->url ) . '@', $url, $matches ) > 0 ) || ( $this->regex === true && @preg_match( '@' . str_replace( '@', '\\@', $this->url ) . '@', urldecode( $url ), $matches ) > 0 ) ) {
240 // Check if our match wants this URL
241 $target = $this->match->get_target( $url, $this->url, $this->regex );
242 $target = apply_filters( 'redirection_url_target', $target, $this->url );
243 $target = $this->action->process_before( $this->action_code, $target );
244
245 if ( $target ) {
246 do_action( 'redirection_visit', $this, $url, $target );
247 return $this->action->process_after( $this->action_code, $target );
248 }
249 }
250
251 return false;
252 }
253
254 public function visit( $url, $target ) {
255 global $wpdb;
256
257 $options = red_get_options();
258
259 // Update the counters
260 $this->last_count++;
261 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}redirection_items SET last_count=last_count+1, last_access=NOW() WHERE id=%d", $this->id ) );
262
263 if ( isset( $options['expire_redirect'] ) && $options['expire_redirect'] !== -1 && $target ) {
264 $details = array(
265 'redirect_id' => $this->id,
266 'group_id' => $this->group_id,
267 );
268
269 RE_Log::create( $url, $target, Redirection_Request::get_user_agent(), Redirection_Request::get_ip(), Redirection_Request::get_referrer(), $details );
270 }
271 }
272
273 public function is_enabled() {
274 return $this->status === 'enabled';
275 }
276
277 public function reset() {
278 global $wpdb;
279
280 $this->last_count = 0;
281 $this->last_access = '0000-00-00 00:00:00';
282
283 $update = array(
284 'last_count' => 0,
285 'last_access' => $this->last_access,
286 );
287 $where = array(
288 'id' => $this->id,
289 );
290
291 $wpdb->update( $wpdb->prefix . 'redirection_items', $update, $where );
292 }
293
294 public function enable() {
295 global $wpdb;
296
297 $this->status = 'enabled';
298 $wpdb->update( $wpdb->prefix . 'redirection_items', array( 'status' => $this->status ), array( 'id' => $this->id ) );
299 }
300
301 public function disable() {
302 global $wpdb;
303
304 $this->status = 'disabled';
305 $wpdb->update( $wpdb->prefix . 'redirection_items', array( 'status' => $this->status ), array( 'id' => $this->id ) );
306 }
307
308 public function get_id() {
309 return intval( $this->id, 10 );
310 }
311
312 public function get_position() {
313 return intval( $this->position, 10 );
314 }
315
316 public function get_group_id() {
317 return intval( $this->group_id, 10 );
318 }
319
320 public function get_url() {
321 return $this->url;
322 }
323
324 public function get_title() {
325 return $this->title ? $this->title : '';
326 }
327
328 public function get_hits() {
329 return intval( $this->last_count, 10 );
330 }
331
332 public function get_last_hit() {
333 return intval( $this->last_access, 10 );
334 }
335
336 public function is_regex() {
337 return $this->regex ? true : false;
338 }
339
340 public function get_match_type() {
341 return $this->match_type;
342 }
343
344 public function get_action_type() {
345 return $this->action_type;
346 }
347
348 public function get_action_code() {
349 return intval( $this->action_code, 10 );
350 }
351
352 public function get_action_data() {
353 return $this->action_data ? $this->action_data : '';
354 }
355
356 public static function get_filtered( array $params ) {
357 global $wpdb;
358
359 $orderby = 'id';
360 $direction = 'DESC';
361 $limit = RED_DEFAULT_PER_PAGE;
362 $offset = 0;
363 $where = '';
364
365 if ( isset( $params['orderby'] ) && in_array( $params['orderby'], array( 'url', 'last_count', 'last_access', 'position' ), true ) ) {
366 $orderby = $params['orderby'];
367 }
368
369 if ( isset( $params['direction'] ) && in_array( $params['direction'], array( 'asc', 'desc' ), true ) ) {
370 $direction = strtoupper( $params['direction'] );
371 }
372
373 if ( isset( $params['filter'] ) && strlen( $params['filter'] ) > 0 && $params['filter'] !== '0' ) {
374 if ( isset( $params['filterBy'] ) && $params['filterBy'] === 'group' ) {
375 $where = $wpdb->prepare( 'WHERE group_id=%d', intval( $params['filter'], 10 ) );
376 } else {
377 $where = $wpdb->prepare( 'WHERE url LIKE %s', '%' . $wpdb->esc_like( trim( $params['filter'] ) ) . '%' );
378 }
379 }
380
381 if ( isset( $params['per_page'] ) ) {
382 $limit = intval( $params['per_page'], 10 );
383 $limit = min( RED_MAX_PER_PAGE, $limit );
384 $limit = max( 5, $limit );
385 }
386
387 if ( isset( $params['page'] ) ) {
388 $offset = intval( $params['page'], 10 );
389 $offset = max( 0, $offset );
390 $offset *= $limit;
391 }
392
393 // $orderby and $direction is whitelisted
394 $rows = $wpdb->get_results(
395 "SELECT * FROM {$wpdb->prefix}redirection_items $where ORDER BY $orderby $direction " . $wpdb->prepare( 'LIMIT %d,%d', $offset, $limit )
396 );
397 $total_items = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_items " . $where ) );
398 $items = array();
399
400 foreach ( $rows as $row ) {
401 $group = new Red_Item( $row );
402 $items[] = $group->to_json();
403 }
404
405 return array(
406 'items' => $items,
407 'total' => intval( $total_items, 10 ),
408 );
409 }
410
411 public function to_json() {
412 return array(
413 'id' => $this->get_id(),
414 'url' => $this->get_url(),
415 'action_code' => $this->get_action_code(),
416 'action_type' => $this->get_action_type(),
417 'action_data' => $this->match->get_data(),
418 'match_type' => $this->get_match_type(),
419 'title' => $this->get_title(),
420 'hits' => $this->get_hits(),
421 'regex' => $this->is_regex(),
422 'group_id' => $this->get_group_id(),
423 'position' => $this->get_position(),
424 'last_access' => $this->get_last_hit() > 0 ? date_i18n( get_option( 'date_format' ), $this->get_last_hit() ) : '-',
425 'enabled' => $this->is_enabled(),
426 );
427 }
428 }
429
430 class Red_Item_Sanitize {
431 private function clean_array( $array ) {
432 foreach ( $array as $name => $value ) {
433 if ( is_array( $value ) ) {
434 $array[ $name ] = $this->clean_array( $value );
435 } else {
436 $value = trim( $value );
437 $array[ $name ] = $value;
438 }
439 };
440
441 return $array;
442 }
443
444 public function get( array $details ) {
445 $data = array();
446 $details = $this->clean_array( $details );
447
448 $data['regex'] = isset( $details['regex'] ) && intval( $details['regex'], 10 ) === 1 ? 1 : 0;
449
450 $url = empty( $details['url'] ) ? $this->auto_generate() : $details['url'];
451 if ( strpos( $url, 'http:' ) !== false || strpos( $url, 'https:' ) !== false ) {
452 $domain = wp_parse_url( $url, PHP_URL_HOST );
453
454 // Auto-convert an absolute URL to relative + server match
455 if ( $domain && $domain !== Redirection_Request::get_server_name() ) {
456 $details['match_type'] = 'server';
457
458 if ( isset( $details['action_data']['url'] ) ) {
459 $details['action_data'] = array(
460 'server' => $domain,
461 'url_from' => $details['action_data']['url'],
462 );
463 } else {
464 $details['action_data'] = array( 'server' => $domain );
465 }
466
467 $url = wp_parse_url( $url, PHP_URL_PATH );
468 if ( is_wp_error( $url ) || $url === null ) {
469 $url = '/';
470 }
471 }
472 }
473
474 $data['url'] = $this->get_url( $url, $data['regex'] );
475 $data['title'] = isset( $details['title'] ) ? $details['title'] : null;
476 $data['group_id'] = $this->get_group( isset( $details['group_id'] ) ? $details['group_id'] : 0 );
477 $data['position'] = $this->get_position( $details );
478
479 if ( $data['title'] ) {
480 $data['title'] = substr( $data['title'], 0, 500 );
481 }
482
483 $matcher = Red_Match::create( isset( $details['match_type'] ) ? $details['match_type'] : false );
484 if ( ! $matcher ) {
485 return new WP_Error( 'redirect', __( 'Invalid redirect matcher', 'redirection' ) );
486 }
487
488 $action_code = isset( $details['action_code'] ) ? intval( $details['action_code'], 10 ) : 0;
489 $action = Red_Action::create( isset( $details['action_type'] ) ? $details['action_type'] : false, $action_code );
490 if ( ! $action ) {
491 return new WP_Error( 'redirect', __( 'Invalid redirect action', 'redirection' ) );
492 }
493
494 $data['action_type'] = $details['action_type'];
495 $data['action_code'] = $this->get_code( $details['action_type'], $action_code );
496 $data['match_type'] = $details['match_type'];
497
498 if ( isset( $details['action_data'] ) ) {
499 $match_data = $matcher->save( $details['action_data'] ? $details['action_data'] : array(), ! $this->is_url_type( $data['action_type'] ) );
500 $data['action_data'] = is_array( $match_data ) ? serialize( $match_data ) : $match_data;
501 }
502
503 // Any errors?
504 foreach ( $data as $value ) {
505 if ( is_wp_error( $value ) ) {
506 return $value;
507 }
508 }
509
510 return apply_filters( 'redirection_validate_redirect', $data );
511 }
512
513 protected function get_position( $details ) {
514 if ( isset( $details['position'] ) ) {
515 return max( 0, intval( $details['position'], 10 ) );
516 }
517
518 return 0;
519 }
520
521 protected function is_url_type( $type ) {
522 if ( $type === 'url' || $type === 'pass' ) {
523 return true;
524 }
525
526 return false;
527 }
528
529 protected function get_code( $action_type, $code ) {
530 if ( $action_type === 'url' || $action_type === 'random' ) {
531 if ( in_array( $code, array( 301, 302, 303, 304, 307, 308 ), true ) ) {
532 return $code;
533 }
534
535 return 301;
536 }
537
538 if ( $action_type === 'error' ) {
539 if ( in_array( $code, array( 400, 401, 403, 404, 410, 418 ), true ) ) {
540 return $code;
541 }
542
543 return 404;
544 }
545
546 return 0;
547 }
548
549 protected function get_group( $group_id ) {
550 $group_id = intval( $group_id, 10 );
551
552 if ( ! Red_Group::get( $group_id ) ) {
553 return new WP_Error( 'redirect', __( 'Invalid group when creating redirect', 'redirection' ) );
554 }
555
556 return $group_id;
557 }
558
559 protected function get_url( $url, $regex ) {
560 $url = self::sanitize_url( $url, $regex );
561
562 if ( $url === '' ) {
563 return new WP_Error( 'redirect', __( 'Invalid source URL', 'redirection' ) );
564 }
565
566 return $url;
567 }
568
569 protected function auto_generate() {
570 $options = red_get_options();
571 $url = '';
572
573 if ( isset( $options['auto_target'] ) && $options['auto_target'] ) {
574 $id = time();
575 $url = str_replace( '$dec$', $id, $options['auto_target'] );
576 $url = str_replace( '$hex$', sprintf( '%x', $id ), $url );
577 }
578
579 return $url;
580 }
581
582 public function sanitize_url( $url, $regex = false ) {
583 // Make sure that the old URL is relative
584 $url = preg_replace( '@^https?://(.*?)/@', '/', $url );
585 $url = preg_replace( '@^https?://(.*?)$@', '/', $url );
586
587 // No new lines
588 $url = preg_replace( "/[\r\n\t].*?$/s", '', $url );
589
590 // Clean control codes
591 $url = preg_replace( '/[^\PC\s]/u', '', $url );
592
593 // Ensure a slash at start
594 if ( substr( $url, 0, 1 ) !== '/' && $regex === false ) {
595 $url = '/' . $url;
596 }
597
598 return $url;
599 }
600 }
601