aboutsummaryrefslogtreecommitdiff
path: root/IkiWiki/Receive.pm
blob: 14936f8cdca6bb7e9cdad935253be51d4f339b1f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/perl
package IkiWiki::Receive;

use warnings;
use strict;
use IkiWiki;

sub getuser () {
	my $user=(getpwuid(exists $ENV{CALLER_UID} ? $ENV{CALLER_UID} : $<))[0];
	if (! defined $user) {
		error("cannot determine username for $<");
	}
	return $user;
}

sub trusted () {
	my $user=getuser();
	return ! ref $config{untrusted_committers} ||
		! grep { $_ eq $user } @{$config{untrusted_committers}};
}

sub genwrapper () {
	# Test for commits from untrusted committers in the wrapper, to
	# avoid starting ikiwiki proper at all for trusted commits.

	my $ret=<<"EOF";
	{
		int u=getuid();
EOF
	$ret.="\t\tif ( ".
		join("&&", map {
			my $uid=getpwnam($_);
			if (! defined $uid) {
				error(sprintf(gettext("cannot determine id of untrusted committer %s"), $_));
			}
			"u != $uid";
		} @{$config{untrusted_committers}}).
		") exit(0);\n";

	
	$ret.=<<"EOF";
		asprintf(&s, "CALLER_UID=%i", u);
		newenviron[i++]=s;
	}
EOF
	return $ret;
}

sub test () {
	exit 0 if trusted();
	
	IkiWiki::lockwiki();
	IkiWiki::loadindex();
	
	# Dummy up a cgi environment to use when calling check_canedit
	# and friends.
	eval q{use CGI};
	error($@) if $@;
	my $cgi=CGI->new;

	# And dummy up a session object.
	require IkiWiki::CGI;
	my $session=IkiWiki::cgi_getsession($cgi);
	$session->param("name", getuser());
	# Make sure whatever user was authed is in the
	# userinfo db.
	require IkiWiki::UserInfo;
	if (! IkiWiki::userinfo_get($session->param("name"), "regdate")) {
		IkiWiki::userinfo_setall($session->param("name"), {
			email => "",
			password => "",
			regdate => time,
		}) || error("failed adding user");
	}
	
	my %newfiles;

	foreach my $change (IkiWiki::rcs_receive()) {
		# This untaint is safe because we check file_pruned and
		# wiki_file_regexp.
		my ($file)=$change->{file}=~/$config{wiki_file_regexp}/;
		$file=IkiWiki::possibly_foolish_untaint($file);
		if (! defined $file || ! length $file ||
		    IkiWiki::file_pruned($file)) {
			error(gettext("bad file name %s"), $file);
		}

		my $type=pagetype($file);
		my $page=pagename($file) if defined $type;
		
		if ($change->{action} eq 'add') {
			$newfiles{$file}=1;
		}

		if ($change->{action} eq 'change' ||
		    $change->{action} eq 'add') {
			if (defined $page) {
				next if IkiWiki::check_canedit($page, $cgi, $session, 1);
			}
			else {
				if (IkiWiki::Plugin::attachment->can("check_canattach")) {
					IkiWiki::Plugin::attachment::check_canattach($session, $file, $change->{path});
					next;
				}
			}
		}
		elsif ($change->{action} eq 'remove') {
			# check_canremove tests to see if the file is present
			# on disk. This will fail is a single commit adds a
			# file and then removes it again. Avoid the problem
			# by not testing the removal in such pairs of changes.
			# (The add is still tested, just to make sure that
			# no data is added to the repo that a web edit
			# could not add.)
			next if $newfiles{$file};

			if (IkiWiki::Plugin::remove->can("check_canremove")) {
				IkiWiki::Plugin::remove::check_canremove(defined $page ? $page : $file, $cgi, $session);
				next;
			}
		}
		else {
			error "unknown action ".$change->{action};
		}
		
		error sprintf(gettext("you are not allowed to change %s"), $file);
	}

	exit 0;
}

1