Extract write_setup_file(). No functional change.
[ikiwiki.git] / t / relativity.t
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4
5 use Test::More;
6 plan(skip_all => "IPC::Run not available")
7         unless eval q{
8                 use IPC::Run qw(run);
9                 1;
10         };
11
12 use IkiWiki;
13
14 use Cwd qw(getcwd);
15 use Errno qw(ENOENT);
16
17 my $pwd = getcwd();
18
19 # Black-box (ish) test for relative linking between CGI and static content
20
21 my $blob;
22 my ($content, $in, %bits);
23
24 sub parse_cgi_content {
25         my %bits;
26         if ($content =~ qr{<base href="([^"]+)" */>}) {
27                 $bits{basehref} = $1;
28         }
29         if ($content =~ qr{href="([^"]+/style.css)"}) {
30                 $bits{stylehref} = $1;
31         }
32         if ($content =~ qr{class="parentlinks">\s+<a href="([^"]+)">this is the name of my wiki</a>/}s) {
33                 $bits{tophref} = $1;
34         }
35         if ($content =~ qr{<a[^>]+href="([^"]+)\?do=prefs"}) {
36                 $bits{cgihref} = $1;
37         }
38         return %bits;
39 }
40
41 ok(! system("make -s ikiwiki.out"));
42 ok(! system("rm -rf t/tmp"));
43 ok(! system("mkdir t/tmp"));
44
45 sub write_old_file {
46         my $name = shift;
47         my $content = shift;
48
49         writefile($name, "t/tmp/in", $content);
50         ok(utime(333333333, 333333333, "t/tmp/in/$name"));
51 }
52
53 write_old_file("a.mdwn", "A");
54 write_old_file("a/b.mdwn", "B");
55 write_old_file("a/b/c.mdwn",
56 "* A: [[a]]\n".
57 "* B: [[b]]\n".
58 "* E: [[a/d/e]]\n");
59 write_old_file("a/d.mdwn", "D");
60 write_old_file("a/d/e.mdwn", "E");
61
62 sub write_setup_file {
63         my (%args) = @_;
64         my $urlline = defined $args{url} ? "url: $args{url}" : "";
65         my $w3mmodeline = defined $args{w3mmode} ? "w3mmode: $args{w3mmode}" : "";
66         my $reverseproxyline = defined $args{reverse_proxy} ? "reverse_proxy: $args{reverse_proxy}" : "";
67
68         writefile("test.setup", "t/tmp", <<EOF
69 # IkiWiki::Setup::Yaml - YAML formatted setup file
70 wikiname: this is the name of my wiki
71 srcdir: t/tmp/in
72 destdir: t/tmp/out
73 templatedir: templates
74 $urlline
75 cgiurl: $args{cgiurl}
76 $w3mmodeline
77 cgi_wrapper: t/tmp/ikiwiki.cgi
78 cgi_wrappermode: 0754
79 html5: $args{html5}
80 # make it easier to test previewing
81 add_plugins:
82 - anonok
83 anonok_pagespec: "*"
84 $reverseproxyline
85 ENV: { 'PERL5LIB': 'blib/lib:blib/arch' }
86 EOF
87         );
88 }
89
90 #######################################################################
91 # site 1: a perfectly ordinary ikiwiki
92
93 write_setup_file(
94         html5   => 0,
95         url     => "http://example.com/wiki/",
96         cgiurl  => "http://example.com/cgi-bin/ikiwiki.cgi",
97 );
98
99 ok(unlink("t/tmp/ikiwiki.cgi") || $!{ENOENT});
100 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
101
102 # CGI wrapper should be exactly the requested mode
103 my (undef, undef, $mode, undef, undef,
104         undef, undef, undef, undef, undef,
105         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
106 is($mode & 07777, 0754);
107
108 ok(-e "t/tmp/out/a/b/c/index.html");
109 $content = readfile("t/tmp/out/a/b/c/index.html");
110 # no <base> on static HTML
111 unlike($content, qr{<base\W});
112 # url and cgiurl are on the same host so the cgiurl is host-relative
113 like($content, qr{<a[^>]+href="/cgi-bin/ikiwiki.cgi\?do=prefs"});
114 # cross-links between static pages are relative
115 like($content, qr{<li>A: <a href="../../">a</a></li>});
116 like($content, qr{<li>B: <a href="../">b</a></li>});
117 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
118
119 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
120         $ENV{REQUEST_METHOD} = 'GET';
121         $ENV{SERVER_PORT} = '80';
122         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
123         $ENV{QUERY_STRING} = 'do=prefs';
124         $ENV{HTTP_HOST} = 'example.com';
125 });
126 %bits = parse_cgi_content($content);
127 is($bits{basehref}, "http://example.com/wiki/");
128 like($bits{stylehref}, qr{^(?:(?:http:)?//example.com)?/wiki/style.css$});
129 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
130 like($bits{cgihref}, qr{^(?:(?:http:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
131
132 # when accessed via HTTPS, links are secure
133 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
134         $ENV{REQUEST_METHOD} = 'GET';
135         $ENV{SERVER_PORT} = '443';
136         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
137         $ENV{QUERY_STRING} = 'do=prefs';
138         $ENV{HTTP_HOST} = 'example.com';
139         $ENV{HTTPS} = 'on';
140 });
141 %bits = parse_cgi_content($content);
142 is($bits{basehref}, "https://example.com/wiki/");
143 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
144 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
145 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
146
147 # when accessed via a different hostname, links stay on that host
148 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
149         $ENV{REQUEST_METHOD} = 'GET';
150         $ENV{SERVER_PORT} = '80';
151         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
152         $ENV{QUERY_STRING} = 'do=prefs';
153         $ENV{HTTP_HOST} = 'staging.example.net';
154 });
155 %bits = parse_cgi_content($content);
156 is($bits{basehref}, "http://staging.example.net/wiki/");
157 like($bits{stylehref}, qr{^(?:(?:http:)?//staging.example.net)?/wiki/style.css$});
158 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
159 like($bits{cgihref}, qr{^(?:(?:http:)?//staging.example.net)?/cgi-bin/ikiwiki.cgi$});
160
161 # previewing a page
162 $in = 'do=edit&page=a/b/c&Preview';
163 run(["./t/tmp/ikiwiki.cgi"], \$in, \$content, init => sub {
164         $ENV{REQUEST_METHOD} = 'POST';
165         $ENV{SERVER_PORT} = '80';
166         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
167         $ENV{HTTP_HOST} = 'example.com';
168         $ENV{CONTENT_LENGTH} = length $in;
169 });
170 %bits = parse_cgi_content($content);
171 is($bits{basehref}, "http://example.com/wiki/a/b/c/");
172 like($bits{stylehref}, qr{^(?:(?:http:)?//example.com)?/wiki/style.css$});
173 like($bits{tophref}, qr{^(?:/wiki|\.\./\.\./\.\.)/$});
174 like($bits{cgihref}, qr{^(?:(?:http:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
175
176 # in html5, the <base> is allowed to be relative, and we take full
177 # advantage of that
178 write_setup_file(
179         html5   => 1,
180         url     => "http://example.com/wiki/",
181         cgiurl  => "http://example.com/cgi-bin/ikiwiki.cgi",
182 );
183
184 ok(unlink("t/tmp/ikiwiki.cgi") || $!{ENOENT});
185 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
186
187 # CGI wrapper should be exactly the requested mode
188 (undef, undef, $mode, undef, undef,
189         undef, undef, undef, undef, undef,
190         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
191 is($mode & 07777, 0754);
192
193 ok(-e "t/tmp/out/a/b/c/index.html");
194 $content = readfile("t/tmp/out/a/b/c/index.html");
195 # no <base> on static HTML
196 unlike($content, qr{<base\W});
197 # url and cgiurl are on the same host so the cgiurl is host-relative
198 like($content, qr{<a[^>]+href="/cgi-bin/ikiwiki.cgi\?do=prefs"});
199 # cross-links between static pages are relative
200 like($content, qr{<li>A: <a href="../../">a</a></li>});
201 like($content, qr{<li>B: <a href="../">b</a></li>});
202 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
203
204 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
205         $ENV{REQUEST_METHOD} = 'GET';
206         $ENV{SERVER_PORT} = '80';
207         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
208         $ENV{QUERY_STRING} = 'do=prefs';
209         $ENV{HTTP_HOST} = 'example.com';
210 });
211 %bits = parse_cgi_content($content);
212 is($bits{basehref}, "/wiki/");
213 is($bits{stylehref}, "/wiki/style.css");
214 is($bits{tophref}, "/wiki/");
215 is($bits{cgihref}, "/cgi-bin/ikiwiki.cgi");
216
217 # when accessed via HTTPS, links are secure - this is easy because under
218 # html5 they're independent of the URL at which the CGI was accessed
219 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
220         $ENV{REQUEST_METHOD} = 'GET';
221         $ENV{SERVER_PORT} = '443';
222         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
223         $ENV{QUERY_STRING} = 'do=prefs';
224         $ENV{HTTP_HOST} = 'example.com';
225         $ENV{HTTPS} = 'on';
226 });
227 %bits = parse_cgi_content($content);
228 is($bits{basehref}, "/wiki/");
229 is($bits{stylehref}, "/wiki/style.css");
230 is($bits{tophref}, "/wiki/");
231 is($bits{cgihref}, "/cgi-bin/ikiwiki.cgi");
232
233 # when accessed via a different hostname, links stay on that host -
234 # this is really easy in html5 because we can use relative URLs
235 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
236         $ENV{REQUEST_METHOD} = 'GET';
237         $ENV{SERVER_PORT} = '80';
238         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
239         $ENV{QUERY_STRING} = 'do=prefs';
240         $ENV{HTTP_HOST} = 'staging.example.net';
241 });
242 %bits = parse_cgi_content($content);
243 is($bits{basehref}, "/wiki/");
244 is($bits{stylehref}, "/wiki/style.css");
245 is($bits{tophref}, "/wiki/");
246 is($bits{cgihref}, "/cgi-bin/ikiwiki.cgi");
247
248 # previewing a page
249 $in = 'do=edit&page=a/b/c&Preview';
250 run(["./t/tmp/ikiwiki.cgi"], \$in, \$content, init => sub {
251         $ENV{REQUEST_METHOD} = 'POST';
252         $ENV{SERVER_PORT} = '80';
253         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
254         $ENV{HTTP_HOST} = 'example.com';
255         $ENV{CONTENT_LENGTH} = length $in;
256 });
257 %bits = parse_cgi_content($content);
258 is($bits{basehref}, "/wiki/a/b/c/");
259 is($bits{stylehref}, "/wiki/style.css");
260 like($bits{tophref}, qr{^(?:/wiki|\.\./\.\./\.\.)/$});
261 is($bits{cgihref}, "/cgi-bin/ikiwiki.cgi");
262
263 #######################################################################
264 # site 2: static content and CGI are on different servers
265
266 write_setup_file(
267         html5   => 0,
268         url     => "http://static.example.com/",
269         cgiurl  => "http://cgi.example.com/ikiwiki.cgi",
270 );
271
272 ok(unlink("t/tmp/ikiwiki.cgi"));
273 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
274
275 # CGI wrapper should be exactly the requested mode
276 (undef, undef, $mode, undef, undef,
277         undef, undef, undef, undef, undef,
278         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
279 is($mode & 07777, 0754);
280
281 ok(-e "t/tmp/out/a/b/c/index.html");
282 $content = readfile("t/tmp/out/a/b/c/index.html");
283 # no <base> on static HTML
284 unlike($content, qr{<base\W});
285 # url and cgiurl are not on the same host so the cgiurl has to be
286 # protocol-relative or absolute
287 like($content, qr{<a[^>]+href="(?:http:)?//cgi.example.com/ikiwiki.cgi\?do=prefs"});
288 # cross-links between static pages are still relative
289 like($content, qr{<li>A: <a href="../../">a</a></li>});
290 like($content, qr{<li>B: <a href="../">b</a></li>});
291 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
292
293 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
294         $ENV{REQUEST_METHOD} = 'GET';
295         $ENV{SERVER_PORT} = '80';
296         $ENV{SCRIPT_NAME} = '/ikiwiki.cgi';
297         $ENV{QUERY_STRING} = 'do=prefs';
298         $ENV{HTTP_HOST} = 'cgi.example.com';
299 });
300 %bits = parse_cgi_content($content);
301 like($bits{basehref}, qr{^http://static.example.com/$});
302 like($bits{stylehref}, qr{^(?:(?:http:)?//static.example.com)?/style.css$});
303 like($bits{tophref}, qr{^(?:http:)?//static.example.com/$});
304 like($bits{cgihref}, qr{^(?:(?:http:)?//cgi.example.com)?/ikiwiki.cgi$});
305
306 # when accessed via HTTPS, links are secure
307 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
308         $ENV{REQUEST_METHOD} = 'GET';
309         $ENV{SERVER_PORT} = '443';
310         $ENV{SCRIPT_NAME} = '/ikiwiki.cgi';
311         $ENV{QUERY_STRING} = 'do=prefs';
312         $ENV{HTTP_HOST} = 'cgi.example.com';
313         $ENV{HTTPS} = 'on';
314 });
315 %bits = parse_cgi_content($content);
316 like($bits{basehref}, qr{^https://static.example.com/$});
317 like($bits{stylehref}, qr{^(?:(?:https:)?//static.example.com)?/style.css$});
318 like($bits{tophref}, qr{^(?:https:)?//static.example.com/$});
319 like($bits{cgihref}, qr{^(?:(?:https:)?//cgi.example.com)?/ikiwiki.cgi$});
320
321 # when accessed via a different hostname, links to the CGI (only) should
322 # stay on that host?
323 $in = 'do=edit&page=a/b/c&Preview';
324 run(["./t/tmp/ikiwiki.cgi"], \$in, \$content, init => sub {
325         $ENV{REQUEST_METHOD} = 'POST';
326         $ENV{SERVER_PORT} = '80';
327         $ENV{SCRIPT_NAME} = '/ikiwiki.cgi';
328         $ENV{HTTP_HOST} = 'staging.example.net';
329         $ENV{CONTENT_LENGTH} = length $in;
330 });
331 %bits = parse_cgi_content($content);
332 like($bits{basehref}, qr{^http://static.example.com/a/b/c/$});
333 like($bits{stylehref}, qr{^(?:(?:http:)?//static.example.com|\.\./\.\./\.\.)/style.css$});
334 like($bits{tophref}, qr{^(?:(?:http:)?//static.example.com|\.\./\.\./\.\.)/$});
335 like($bits{cgihref}, qr{^(?:(?:http:)?//(?:staging\.example\.net|cgi\.example\.com))?/ikiwiki.cgi$});
336 TODO: {
337 local $TODO = "use self-referential CGI URL?";
338 like($bits{cgihref}, qr{^(?:(?:http:)?//staging.example.net)?/ikiwiki.cgi$});
339 }
340
341 write_setup_file(
342         html5   => 1,
343         url     => "http://static.example.com/",
344         cgiurl  => "http://cgi.example.com/ikiwiki.cgi",
345 );
346
347 ok(unlink("t/tmp/ikiwiki.cgi"));
348 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
349
350 # CGI wrapper should be exactly the requested mode
351 (undef, undef, $mode, undef, undef,
352         undef, undef, undef, undef, undef,
353         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
354 is($mode & 07777, 0754);
355
356 ok(-e "t/tmp/out/a/b/c/index.html");
357 $content = readfile("t/tmp/out/a/b/c/index.html");
358 # no <base> on static HTML
359 unlike($content, qr{<base\W});
360 # url and cgiurl are not on the same host so the cgiurl has to be
361 # protocol-relative or absolute
362 like($content, qr{<a[^>]+href="(?:http:)?//cgi.example.com/ikiwiki.cgi\?do=prefs"});
363 # cross-links between static pages are still relative
364 like($content, qr{<li>A: <a href="../../">a</a></li>});
365 like($content, qr{<li>B: <a href="../">b</a></li>});
366 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
367
368 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
369         $ENV{REQUEST_METHOD} = 'GET';
370         $ENV{SERVER_PORT} = '80';
371         $ENV{SCRIPT_NAME} = '/ikiwiki.cgi';
372         $ENV{QUERY_STRING} = 'do=prefs';
373         $ENV{HTTP_HOST} = 'cgi.example.com';
374 });
375 %bits = parse_cgi_content($content);
376 is($bits{basehref}, "//static.example.com/");
377 is($bits{stylehref}, "//static.example.com/style.css");
378 is($bits{tophref}, "//static.example.com/");
379 is($bits{cgihref}, "//cgi.example.com/ikiwiki.cgi");
380
381 # when accessed via HTTPS, links are secure - in fact they're exactly the
382 # same as when accessed via HTTP
383 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
384         $ENV{REQUEST_METHOD} = 'GET';
385         $ENV{SERVER_PORT} = '443';
386         $ENV{SCRIPT_NAME} = '/ikiwiki.cgi';
387         $ENV{QUERY_STRING} = 'do=prefs';
388         $ENV{HTTP_HOST} = 'cgi.example.com';
389         $ENV{HTTPS} = 'on';
390 });
391 %bits = parse_cgi_content($content);
392 is($bits{basehref}, "//static.example.com/");
393 is($bits{stylehref}, "//static.example.com/style.css");
394 is($bits{tophref}, "//static.example.com/");
395 is($bits{cgihref}, "//cgi.example.com/ikiwiki.cgi");
396
397 # when accessed via a different hostname, links to the CGI (only) should
398 # stay on that host?
399 $in = 'do=edit&page=a/b/c&Preview';
400 run(["./t/tmp/ikiwiki.cgi"], \$in, \$content, init => sub {
401         $ENV{REQUEST_METHOD} = 'POST';
402         $ENV{SERVER_PORT} = '80';
403         $ENV{SCRIPT_NAME} = '/ikiwiki.cgi';
404         $ENV{HTTP_HOST} = 'staging.example.net';
405         $ENV{CONTENT_LENGTH} = length $in;
406 });
407 %bits = parse_cgi_content($content);
408 is($bits{basehref}, "//static.example.com/a/b/c/");
409 is($bits{stylehref}, "//static.example.com/style.css");
410 is($bits{tophref}, "../../../");
411 like($bits{cgihref}, qr{//(?:staging\.example\.net|cgi\.example\.com)/ikiwiki\.cgi});
412 TODO: {
413 local $TODO = "use self-referential CGI URL maybe?";
414 is($bits{cgihref}, "//staging.example.net/ikiwiki.cgi");
415 }
416
417 #######################################################################
418 # site 3: we specifically want everything to be secure
419
420 write_setup_file(
421         html5   => 0,
422         url     => "https://example.com/wiki/",
423         cgiurl  => "https://example.com/cgi-bin/ikiwiki.cgi",
424 );
425
426 ok(unlink("t/tmp/ikiwiki.cgi"));
427 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
428
429 # CGI wrapper should be exactly the requested mode
430 (undef, undef, $mode, undef, undef,
431         undef, undef, undef, undef, undef,
432         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
433 is($mode & 07777, 0754);
434
435 ok(-e "t/tmp/out/a/b/c/index.html");
436 $content = readfile("t/tmp/out/a/b/c/index.html");
437 # no <base> on static HTML
438 unlike($content, qr{<base\W});
439 # url and cgiurl are on the same host so the cgiurl is host-relative
440 like($content, qr{<a[^>]+href="/cgi-bin/ikiwiki.cgi\?do=prefs"});
441 # cross-links between static pages are relative
442 like($content, qr{<li>A: <a href="../../">a</a></li>});
443 like($content, qr{<li>B: <a href="../">b</a></li>});
444 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
445
446 # when accessed via HTTPS, links are secure
447 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
448         $ENV{REQUEST_METHOD} = 'GET';
449         $ENV{SERVER_PORT} = '443';
450         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
451         $ENV{QUERY_STRING} = 'do=prefs';
452         $ENV{HTTP_HOST} = 'example.com';
453         $ENV{HTTPS} = 'on';
454 });
455 %bits = parse_cgi_content($content);
456 is($bits{basehref}, "https://example.com/wiki/");
457 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
458 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
459 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
460
461 # when not accessed via HTTPS, links should still be secure
462 # (but if this happens, that's a sign of web server misconfiguration)
463 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
464         $ENV{REQUEST_METHOD} = 'GET';
465         $ENV{SERVER_PORT} = '80';
466         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
467         $ENV{QUERY_STRING} = 'do=prefs';
468         $ENV{HTTP_HOST} = 'example.com';
469 });
470 %bits = parse_cgi_content($content);
471 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
472 TODO: {
473 local $TODO = "treat https in configured url, cgiurl as required?";
474 is($bits{basehref}, "https://example.com/wiki/");
475 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
476 }
477 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
478
479 # when accessed via a different hostname, links stay on that host
480 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
481         $ENV{REQUEST_METHOD} = 'GET';
482         $ENV{SERVER_PORT} = '443';
483         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
484         $ENV{QUERY_STRING} = 'do=prefs';
485         $ENV{HTTP_HOST} = 'staging.example.net';
486         $ENV{HTTPS} = 'on';
487 });
488 %bits = parse_cgi_content($content);
489 is($bits{basehref}, "https://staging.example.net/wiki/");
490 like($bits{stylehref}, qr{^(?:(?:https:)?//staging.example.net)?/wiki/style.css$});
491 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
492 like($bits{cgihref}, qr{^(?:(?:https:)?//staging.example.net)?/cgi-bin/ikiwiki.cgi$});
493
494 # previewing a page
495 $in = 'do=edit&page=a/b/c&Preview';
496 run(["./t/tmp/ikiwiki.cgi"], \$in, \$content, init => sub {
497         $ENV{REQUEST_METHOD} = 'POST';
498         $ENV{SERVER_PORT} = '443';
499         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
500         $ENV{HTTP_HOST} = 'example.com';
501         $ENV{CONTENT_LENGTH} = length $in;
502         $ENV{HTTPS} = 'on';
503 });
504 %bits = parse_cgi_content($content);
505 is($bits{basehref}, "https://example.com/wiki/a/b/c/");
506 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
507 like($bits{tophref}, qr{^(?:/wiki|\.\./\.\./\.\.)/$});
508 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
509
510 # not testing html5: 0 here because that ends up identical to site 1
511
512 #######################################################################
513 # site 4 (NetBSD wiki): CGI is secure, static content doesn't have to be
514
515 write_setup_file(
516         html5   => 0,
517         url     => "http://example.com/wiki/",
518         cgiurl  => "https://example.com/cgi-bin/ikiwiki.cgi",
519 );
520
521 ok(unlink("t/tmp/ikiwiki.cgi"));
522 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
523
524 # CGI wrapper should be exactly the requested mode
525 (undef, undef, $mode, undef, undef,
526         undef, undef, undef, undef, undef,
527         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
528 is($mode & 07777, 0754);
529
530 ok(-e "t/tmp/out/a/b/c/index.html");
531 $content = readfile("t/tmp/out/a/b/c/index.html");
532 # no <base> on static HTML
533 unlike($content, qr{<base\W});
534 # url and cgiurl are on the same host but different schemes
535 like($content, qr{<a[^>]+href="https://example.com/cgi-bin/ikiwiki.cgi\?do=prefs"});
536 # cross-links between static pages are relative
537 like($content, qr{<li>A: <a href="../../">a</a></li>});
538 like($content, qr{<li>B: <a href="../">b</a></li>});
539 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
540
541 # when accessed via HTTPS, links are secure (to avoid mixed-content)
542 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
543         $ENV{REQUEST_METHOD} = 'GET';
544         $ENV{SERVER_PORT} = '443';
545         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
546         $ENV{QUERY_STRING} = 'do=prefs';
547         $ENV{HTTP_HOST} = 'example.com';
548         $ENV{HTTPS} = 'on';
549 });
550 %bits = parse_cgi_content($content);
551 is($bits{basehref}, "https://example.com/wiki/");
552 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
553 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
554 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
555
556 # when not accessed via HTTPS, ???
557 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
558         $ENV{REQUEST_METHOD} = 'GET';
559         $ENV{SERVER_PORT} = '80';
560         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
561         $ENV{QUERY_STRING} = 'do=prefs';
562         $ENV{HTTP_HOST} = 'example.com';
563 });
564 %bits = parse_cgi_content($content);
565 like($bits{basehref}, qr{^https?://example.com/wiki/$});
566 like($bits{stylehref}, qr{^(?:(?:https?:)?//example.com)?/wiki/style.css$});
567 like($bits{tophref}, qr{^(?:(?:https?://example.com)?/wiki|\.)/$});
568 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
569
570 # when accessed via a different hostname, links stay on that host
571 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
572         $ENV{REQUEST_METHOD} = 'GET';
573         $ENV{SERVER_PORT} = '443';
574         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
575         $ENV{QUERY_STRING} = 'do=prefs';
576         $ENV{HTTP_HOST} = 'staging.example.net';
577         $ENV{HTTPS} = 'on';
578 });
579 %bits = parse_cgi_content($content);
580 # because the static and dynamic stuff is on the same server, we assume that
581 # both are also on the staging server
582 like($bits{basehref}, qr{^https://staging.example.net/wiki/$});
583 like($bits{stylehref}, qr{^(?:(?:https:)?//staging.example.net)?/wiki/style.css$});
584 like($bits{tophref}, qr{^(?:(?:(?:https:)?//staging.example.net)?/wiki|\.)/$});
585 like($bits{cgihref}, qr{^(?:(?:https:)?//(?:staging\.example\.net|example\.com))?/cgi-bin/ikiwiki.cgi$});
586 TODO: {
587 local $TODO = "this should really point back to itself but currently points to example.com";
588 like($bits{cgihref}, qr{^(?:(?:https:)?//staging.example.net)?/cgi-bin/ikiwiki.cgi$});
589 }
590
591 # previewing a page
592 $in = 'do=edit&page=a/b/c&Preview';
593 run(["./t/tmp/ikiwiki.cgi"], \$in, \$content, init => sub {
594         $ENV{REQUEST_METHOD} = 'POST';
595         $ENV{SERVER_PORT} = '443';
596         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
597         $ENV{HTTP_HOST} = 'example.com';
598         $ENV{CONTENT_LENGTH} = length $in;
599         $ENV{HTTPS} = 'on';
600 });
601 %bits = parse_cgi_content($content);
602 is($bits{basehref}, "https://example.com/wiki/a/b/c/");
603 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
604 like($bits{tophref}, qr{^(?:/wiki|\.\./\.\./\.\.)/$});
605 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
606
607 write_setup_file(
608         html5   => 1,
609         url     => "http://example.com/wiki/",
610         cgiurl  => "https://example.com/cgi-bin/ikiwiki.cgi",
611 );
612
613 ok(unlink("t/tmp/ikiwiki.cgi"));
614 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
615
616 # CGI wrapper should be exactly the requested mode
617 (undef, undef, $mode, undef, undef,
618         undef, undef, undef, undef, undef,
619         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
620 is($mode & 07777, 0754);
621
622 ok(-e "t/tmp/out/a/b/c/index.html");
623 $content = readfile("t/tmp/out/a/b/c/index.html");
624 # no <base> on static HTML
625 unlike($content, qr{<base\W});
626 # url and cgiurl are on the same host but different schemes
627 like($content, qr{<a[^>]+href="https://example.com/cgi-bin/ikiwiki.cgi\?do=prefs"});
628 # cross-links between static pages are relative
629 like($content, qr{<li>A: <a href="../../">a</a></li>});
630 like($content, qr{<li>B: <a href="../">b</a></li>});
631 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
632
633 # when accessed via HTTPS, links are secure (to avoid mixed-content)
634 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
635         $ENV{REQUEST_METHOD} = 'GET';
636         $ENV{SERVER_PORT} = '443';
637         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
638         $ENV{QUERY_STRING} = 'do=prefs';
639         $ENV{HTTP_HOST} = 'example.com';
640         $ENV{HTTPS} = 'on';
641 });
642 %bits = parse_cgi_content($content);
643 is($bits{basehref}, "/wiki/");
644 is($bits{stylehref}, "/wiki/style.css");
645 is($bits{tophref}, "/wiki/");
646 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
647
648 # when not accessed via HTTPS, ???
649 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
650         $ENV{REQUEST_METHOD} = 'GET';
651         $ENV{SERVER_PORT} = '80';
652         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
653         $ENV{QUERY_STRING} = 'do=prefs';
654         $ENV{HTTP_HOST} = 'example.com';
655 });
656 %bits = parse_cgi_content($content);
657 like($bits{basehref}, qr{^(?:https?://example.com)?/wiki/$});
658 like($bits{stylehref}, qr{^(?:(?:https?:)?//example.com)?/wiki/style.css$});
659 like($bits{tophref}, qr{^(?:(?:https?://example.com)?/wiki|\.)/$});
660 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
661
662 # when accessed via a different hostname, links stay on that host
663 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
664         $ENV{REQUEST_METHOD} = 'GET';
665         $ENV{SERVER_PORT} = '443';
666         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
667         $ENV{QUERY_STRING} = 'do=prefs';
668         $ENV{HTTP_HOST} = 'staging.example.net';
669         $ENV{HTTPS} = 'on';
670 });
671 %bits = parse_cgi_content($content);
672 # because the static and dynamic stuff is on the same server, we assume that
673 # both are also on the staging server
674 is($bits{basehref}, "/wiki/");
675 is($bits{stylehref}, "/wiki/style.css");
676 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
677 like($bits{cgihref}, qr{^(?:(?:https:)?//(?:example\.com|staging\.example\.net))?/cgi-bin/ikiwiki.cgi$});
678 TODO: {
679 local $TODO = "this should really point back to itself but currently points to example.com";
680 like($bits{cgihref}, qr{^(?:(?:https:)?//staging.example.net)?/cgi-bin/ikiwiki.cgi$});
681 }
682
683 # previewing a page
684 $in = 'do=edit&page=a/b/c&Preview';
685 run(["./t/tmp/ikiwiki.cgi"], \$in, \$content, init => sub {
686         $ENV{REQUEST_METHOD} = 'POST';
687         $ENV{SERVER_PORT} = '443';
688         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
689         $ENV{HTTP_HOST} = 'example.com';
690         $ENV{CONTENT_LENGTH} = length $in;
691         $ENV{HTTPS} = 'on';
692 });
693 %bits = parse_cgi_content($content);
694 is($bits{basehref}, "/wiki/a/b/c/");
695 is($bits{stylehref}, "/wiki/style.css");
696 like($bits{tophref}, qr{^(?:/wiki|\.\./\.\./\.\.)/$});
697 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
698
699 # Deliberately not testing https static content with http cgiurl,
700 # because that makes remarkably little sense.
701
702 #######################################################################
703 # site 5: w3mmode, as documented in [[w3mmode]]
704
705 write_setup_file(
706         html5   => 0, 
707         url     => undef,
708         cgiurl  => "ikiwiki.cgi",
709         w3mmode => 1,
710 );
711
712 ok(unlink("t/tmp/ikiwiki.cgi"));
713 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
714
715 # CGI wrapper should be exactly the requested mode
716 (undef, undef, $mode, undef, undef,
717         undef, undef, undef, undef, undef,
718         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
719 is($mode & 07777, 0754);
720
721 ok(-e "t/tmp/out/a/b/c/index.html");
722 $content = readfile("t/tmp/out/a/b/c/index.html");
723 # no <base> on static HTML
724 unlike($content, qr{<base\W});
725 # FIXME: does /$LIB/ikiwiki-w3m.cgi work under w3m?
726 like($content, qr{<a[^>]+href="(?:file://)?/\$LIB/ikiwiki-w3m.cgi/ikiwiki.cgi\?do=prefs"});
727 # cross-links between static pages are still relative
728 like($content, qr{<li>A: <a href="../../">a</a></li>});
729 like($content, qr{<li>B: <a href="../">b</a></li>});
730 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
731
732 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
733         $ENV{REQUEST_METHOD} = 'GET';
734         $ENV{PATH_INFO} = '/ikiwiki.cgi';
735         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki-w3m.cgi';
736         $ENV{QUERY_STRING} = 'do=prefs';
737 });
738 %bits = parse_cgi_content($content);
739 like($bits{tophref}, qr{^(?:\Q$pwd\E/t/tmp/out|\.)/$});
740 like($bits{cgihref}, qr{^(?:file://)?/\$LIB/ikiwiki-w3m.cgi/ikiwiki.cgi$});
741 like($bits{basehref}, qr{^(?:(?:file:)?//)?\Q$pwd\E/t/tmp/out/$});
742 like($bits{stylehref}, qr{^(?:(?:(?:file:)?//)?\Q$pwd\E/t/tmp/out|\.)/style.css$});
743
744 write_setup_file(
745         html5   => 1,
746         url     => undef,
747         cgiurl  => "ikiwiki.cgi",
748         w3mmode => 1,
749 );
750
751 ok(unlink("t/tmp/ikiwiki.cgi"));
752 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
753
754 # CGI wrapper should be exactly the requested mode
755 (undef, undef, $mode, undef, undef,
756         undef, undef, undef, undef, undef,
757         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
758 is($mode & 07777, 0754);
759
760 ok(-e "t/tmp/out/a/b/c/index.html");
761 $content = readfile("t/tmp/out/a/b/c/index.html");
762 # no <base> on static HTML
763 unlike($content, qr{<base\W});
764 # FIXME: does /$LIB/ikiwiki-w3m.cgi work under w3m?
765 like($content, qr{<a[^>]+href="(?:file://)?/\$LIB/ikiwiki-w3m.cgi/ikiwiki.cgi\?do=prefs"});
766 # cross-links between static pages are still relative
767 like($content, qr{<li>A: <a href="../../">a</a></li>});
768 like($content, qr{<li>B: <a href="../">b</a></li>});
769 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
770
771 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
772         $ENV{REQUEST_METHOD} = 'GET';
773         $ENV{PATH_INFO} = '/ikiwiki.cgi';
774         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki-w3m.cgi';
775         $ENV{QUERY_STRING} = 'do=prefs';
776 });
777 %bits = parse_cgi_content($content);
778 like($bits{tophref}, qr{^(?:\Q$pwd\E/t/tmp/out|\.)/$});
779 like($bits{cgihref}, qr{^(?:file://)?/\$LIB/ikiwiki-w3m.cgi/ikiwiki.cgi$});
780 like($bits{basehref}, qr{^(?:(?:file:)?//)?\Q$pwd\E/t/tmp/out/$});
781 like($bits{stylehref}, qr{^(?:(?:(?:file:)?//)?\Q$pwd\E/t/tmp/out|\.)/style.css$});
782
783 #######################################################################
784 # site 6: we're behind a reverse-proxy
785
786 write_setup_file(
787         html5   => 0,
788         url     => "https://example.com/wiki/",
789         cgiurl  => "https://example.com/cgi-bin/ikiwiki.cgi",
790         reverse_proxy => 1,
791 );
792
793 ok(unlink("t/tmp/ikiwiki.cgi"));
794 ok(! system("./ikiwiki.out --setup t/tmp/test.setup --rebuild --wrappers"));
795
796 # CGI wrapper should be exactly the requested mode
797 (undef, undef, $mode, undef, undef,
798         undef, undef, undef, undef, undef,
799         undef, undef, undef) = stat("t/tmp/ikiwiki.cgi");
800 is($mode & 07777, 0754);
801
802 ok(-e "t/tmp/out/a/b/c/index.html");
803 $content = readfile("t/tmp/out/a/b/c/index.html");
804 # no <base> on static HTML
805 unlike($content, qr{<base\W});
806 # url and cgiurl are on the same host so the cgiurl is host-relative
807 like($content, qr{<a[^>]+href="/cgi-bin/ikiwiki.cgi\?do=prefs"});
808 # cross-links between static pages are relative
809 like($content, qr{<li>A: <a href="../../">a</a></li>});
810 like($content, qr{<li>B: <a href="../">b</a></li>});
811 like($content, qr{<li>E: <a href="../../d/e/">e</a></li>});
812
813 # because we are behind a reverse-proxy we must assume that
814 # we're being accessed by the configured cgiurl
815 run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
816         $ENV{REQUEST_METHOD} = 'GET';
817         $ENV{SERVER_PORT} = '80';
818         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
819         $ENV{QUERY_STRING} = 'do=prefs';
820         $ENV{HTTP_HOST} = 'localhost';
821 });
822 %bits = parse_cgi_content($content);
823 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
824 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
825 is($bits{basehref}, "https://example.com/wiki/");
826 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
827
828 # previewing a page
829 $in = 'do=edit&page=a/b/c&Preview';
830 run(["./t/tmp/ikiwiki.cgi"], \$in, \$content, init => sub {
831         $ENV{REQUEST_METHOD} = 'POST';
832         $ENV{SERVER_PORT} = '80';
833         $ENV{SCRIPT_NAME} = '/cgi-bin/ikiwiki.cgi';
834         $ENV{HTTP_HOST} = 'localhost';
835         $ENV{CONTENT_LENGTH} = length $in;
836 });
837 %bits = parse_cgi_content($content);
838 like($bits{tophref}, qr{^(?:/wiki|\.\./\.\./\.\.)/$});
839 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
840 is($bits{basehref}, "https://example.com/wiki/a/b/c/");
841 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
842
843 # not testing html5: 1 because it would be the same as site 1 -
844 # the reverse_proxy config option is unnecessary under html5
845
846 done_testing;