Fix CDATA sections with literal elements.
[xhtml-compiler.git] / htaccess.php
blobb8ce18447b96153b6291b08e69609de7e7b7502b
1 <?php
3 /**
4 * Generates the appropriate mod_rewrite rules in the htaccess file.
5 * @note If a .htaccess.in prototype file is present in the directory,
6 * it will be used as the "base" htaccess to determine the new
7 * one, and local changes to .htaccess will ALWAYS be overwritten
8 * @todo Create an HTAccess object to edit
9 */
11 require 'common.php';
13 $xc = XHTMLCompiler::getInstance();
15 $identifier_begin = '# BEGIN xhtml-compiler/main.php mod_rewrite';
16 $identifier_end = '# END xhtml-compiler/main.php mod_rewrite';
17 $identifier_here = '# HERE xhtml-compiler/main.php mod_rewrite';
19 if (file_exists('.htaccess')) {
21 // do time check
22 $files_to_check = array(
23 '.htaccess.in',
24 'xhtml-compiler/config.php',
25 'xhtml-compiler/config.default.php',
26 'xhtml-compiler/config.smoketest.php',
27 'xhtml-compiler/htaccess.php',
28 'redirects.txt',
31 $mtime_htaccess = filemtime('.htaccess');
33 $no_changes_needed = true;
34 foreach ($files_to_check as $file) {
35 if (file_exists($file) && filemtime($file) > $mtime_htaccess) {
36 $no_changes_needed = false;
37 break;
41 if ($no_changes_needed) {
42 throw new XHTMLCompiler_Exception(503, false,
43 'No changes detected in <tt>xhtml-compiler/config.php</tt>,
44 <tt>xhtml-compiler/config.default.php</tt> or
45 <tt>xhtml-compiler/htaccess.php</tt>.');
48 if (!file_exists('.htaccess.in')) {
49 $contents = file_get_contents('.htaccess');
50 } else {
51 $contents = file_get_contents('.htaccess.in');
54 // do writeability check
55 if (
57 strpos($contents, $identifier_begin) === false ||
58 strpos($contents, $identifier_end) === false
59 ) &&
60 strpos($contents, $identifier_here) === false
61 ) {
62 throw new XHTMLCompiler_Exception(503, false,
63 'Pre-existing htaccess not configured to accept new rules');
66 // replace old rules with new set
67 $regex =
68 '/' .
69 preg_quote($identifier_begin, '/') .
70 '.+?' .
71 preg_quote($identifier_end, '/') .
72 '/s';
74 $contents = preg_replace($regex, $identifier_here, $contents);
76 } else {
77 $contents = $identifier_here;
80 // build the new htaccess
81 $new_contents = array();
82 $new_contents[] = $identifier_begin;
83 $new_contents[] = 'RewriteEngine on';
84 $new_contents[] = 'RewriteBase ' . $xc->getConf('web_path') . '/';
86 // create permanent redirects
87 if (file_exists('redirects.txt')) {
88 $redirects = explode("\n", file_get_contents('redirects.txt'));
89 foreach ($redirects as $redirect) {
90 $redirect = trim($redirect) . ' ';
91 if ($redirect === ' ') continue;
92 if ($redirect[0] === '#') continue;
93 list($src, $dest, $p) = explode(' ', $redirect);
94 if ($p !== '[P]') $src = '^' . preg_quote($src) . '$';
95 // We use rewrite to prevent the appending of ?f= querystring
96 $new_contents[] = 'RewriteRule ' . $src . ' ' . $dest . ' [R=permanent,L]';
100 $big_exp = array();
101 $directory_index = $xc->getConf('directory_index');
102 $indexed_dirs = $xc->getConf('indexed_dirs');
103 $allowed_dirs = $xc->getConf('allowed_dirs');
104 foreach ($allowed_dirs as $dir => $recursive) {
105 $r = '';
106 if ($recursive) {
107 $r = "([^/]+/)*"; // escaped slashes not necessary
109 $len = strlen($dir);
110 $slash = (!$len || $dir[$len-1] === '/') ? '' : '/';
111 $dir_exp = preg_quote($dir) . $slash . $r;
113 if (is_array($indexed_dirs)) {
114 $intercept = isset($indexed_dirs[$dir]) ? $indexed_dirs[$dir] : true;
115 } else {
116 $intercept = $indexed_dirs;
118 if (is_string($directory_index) && $intercept) {
119 // setup index rewrite
120 $new_contents[] = "RewriteRule ^($dir_exp)$ \$1$directory_index";
122 $big_exp[] = $dir_exp;
125 $full_dir_exp = implode('|', $big_exp);
126 $new_contents[] = 'RewriteCond %{REQUEST_FILENAME} !-f [OR]';
127 $new_contents[] = 'RewriteCond %{QUERY_STRING} purge=1 [OR]';
128 $new_contents[] = 'RewriteCond %{HTTP_COOKIE} purgatory=1';
129 $new_contents[] = "RewriteRule ^(($full_dir_exp)[^/]+\.html)$ xhtml-compiler/main.php?f=\$1 [L,QSA]";
131 // if purge is set, also handle directories
132 $new_contents[] = 'RewriteCond %{QUERY_STRING} purge=1';
133 $new_contents[] = "RewriteRule ^($full_dir_exp)$ xhtml-compiler/main.php?f=\$1 [L,QSA]";
135 // add application/xhtml+xml if the browser supports it
136 $new_contents[] = 'RewriteCond %{HTTP_ACCEPT} application/xhtml\\+xml';
137 $new_contents[] = "RewriteRule ^(($full_dir_exp)[^/]+\.html)$ - \"[T=application/xhtml+xml,L]\"";
139 // xc-deps are forbidden to outside world
140 $new_contents[] = '<Files ~ "\.xc-deps$">';
141 $new_contents[] = ' Order allow,deny';
142 $new_contents[] = ' Deny from all';
143 $new_contents[] = '</Files>';
145 // errors.log is forbidden to outside world. In theory, this will occur only
146 // in xhtml-compiler/, but it won't hurt to deny it everywhere.
147 $new_contents[] = '<Files errors.log>';
148 $new_contents[] = ' Order allow,deny';
149 $new_contents[] = ' Deny from all';
150 $new_contents[] = '</Files>';
152 // setup RSS
153 $new_contents[] = 'AddType application/rss+xml rss';
154 $new_contents[] = 'AddCharset UTF-8 .rss';
155 $new_contents[] = '<IfModule mod_headers.c>';
156 $new_contents[] = ' <Files ~ "\.rss$">';
157 $new_contents[] = ' Header append Cache-Control "no-cache, must-revalidate"';
158 $new_contents[] = ' </Files>';
159 $new_contents[] = '</IfModule>';
161 // set UTF-8 for HTML pages
162 $new_contents[] = 'AddCharset UTF-8 .html';
164 $new_contents[] = $identifier_end;
166 $contents = str_replace($identifier_here, implode($new_contents, PHP_EOL), $contents);
168 file_put_contents('.htaccess', $contents);
169 chmod('.htaccess', 0644);
171 ?><h1>200: Okay</h1>New <tt>.htaccess</tt> file successfully written