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