PageRenderTime 33ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/htdocs/wp-content/plugins/redirection/models/htaccess.php

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