<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/perl

use warnings;
use strict;

package IkiWiki;

sub page_locked ($$;$) { #{{{
	my $page=shift;
	my $session=shift;
	my $nonfatal=shift;
	
	my $user=$session-&gt;param("name");
	return if length $user &amp;&amp; is_admin($user);

	foreach my $admin (@{$config{adminuser}}) {
		my $locked_pages=userinfo_get($admin, "locked_pages");
		if (globlist_match($page, userinfo_get($admin, "locked_pages"))) {
			return 1 if $nonfatal;
			error(htmllink("", $page, 1)." is locked by ".
			      htmllink("", $admin, 1)." and cannot be edited.");
		}
	}

	return 0;
} #}}}

sub cgi_recentchanges ($) { #{{{
	my $q=shift;
	
	unlockwiki();

	my $template=HTML::Template-&gt;new(
		filename =&gt; "$config{templatedir}/recentchanges.tmpl"
	);
	$template-&gt;param(
		title =&gt; "RecentChanges",
		indexlink =&gt; indexlink(),
		wikiname =&gt; $config{wikiname},
		changelog =&gt; [rcs_recentchanges(100)],
	);
	print $q-&gt;header, $template-&gt;output;
} #}}}

sub cgi_signin ($$) { #{{{
	my $q=shift;
	my $session=shift;

	eval q{use CGI::FormBuilder};
	my $form = CGI::FormBuilder-&gt;new(
		title =&gt; "signin",
		fields =&gt; [qw(do title page subpage from name password confirm_password email)],
		header =&gt; 1,
		method =&gt; 'POST',
		validate =&gt; {
			confirm_password =&gt; {
				perl =&gt; q{eq $form-&gt;field("password")},
			},
			email =&gt; 'EMAIL',
		},
		required =&gt; 'NONE',
		javascript =&gt; 0,
		params =&gt; $q,
		action =&gt; $q-&gt;request_uri,
		header =&gt; 0,
		template =&gt; (-e "$config{templatedir}/signin.tmpl" ?
		              "$config{templatedir}/signin.tmpl" : "")
	);
	
	$form-&gt;field(name =&gt; "name", required =&gt; 0);
	$form-&gt;field(name =&gt; "do", type =&gt; "hidden");
	$form-&gt;field(name =&gt; "page", type =&gt; "hidden");
	$form-&gt;field(name =&gt; "title", type =&gt; "hidden");
	$form-&gt;field(name =&gt; "from", type =&gt; "hidden");
	$form-&gt;field(name =&gt; "subpage", type =&gt; "hidden");
	$form-&gt;field(name =&gt; "password", type =&gt; "password", required =&gt; 0);
	$form-&gt;field(name =&gt; "confirm_password", type =&gt; "password", required =&gt; 0);
	$form-&gt;field(name =&gt; "email", required =&gt; 0);
	if ($q-&gt;param("do") ne "signin") {
		$form-&gt;text("You need to log in first.");
	}
	
	if ($form-&gt;submitted) {
		# Set required fields based on how form was submitted.
		my %required=(
			"Login" =&gt; [qw(name password)],
			"Register" =&gt; [qw(name password confirm_password email)],
			"Mail Password" =&gt; [qw(name)],
		);
		foreach my $opt (@{$required{$form-&gt;submitted}}) {
			$form-&gt;field(name =&gt; $opt, required =&gt; 1);
		}
	
		# Validate password differently depending on how
		# form was submitted.
		if ($form-&gt;submitted eq 'Login') {
			$form-&gt;field(
				name =&gt; "password",
				validate =&gt; sub {
					length $form-&gt;field("name") &amp;&amp;
					shift eq userinfo_get($form-&gt;field("name"), 'password');
				},
			);
			$form-&gt;field(name =&gt; "name", validate =&gt; '/^\w+$/');
		}
		else {
			$form-&gt;field(name =&gt; "password", validate =&gt; 'VALUE');
		}
		# And make sure the entered name exists when logging
		# in or sending email, and does not when registering.
		if ($form-&gt;submitted eq 'Register') {
			$form-&gt;field(
				name =&gt; "name",
				validate =&gt; sub {
					my $name=shift;
					length $name &amp;&amp;
					! userinfo_get($name, "regdate");
				},
			);
		}
		else {
			$form-&gt;field(
				name =&gt; "name",
				validate =&gt; sub {
					my $name=shift;
					length $name &amp;&amp;
					userinfo_get($name, "regdate");
				},
			);
		}
	}
	else {
		# First time settings.
		$form-&gt;field(name =&gt; "name", comment =&gt; "use FirstnameLastName");
		$form-&gt;field(name =&gt; "confirm_password", comment =&gt; "(only needed");
		$form-&gt;field(name =&gt; "email",            comment =&gt; "for registration)");
		if ($session-&gt;param("name")) {
			$form-&gt;field(name =&gt; "name", value =&gt; $session-&gt;param("name"));
		}
	}

	if ($form-&gt;submitted &amp;&amp; $form-&gt;validate) {
		if ($form-&gt;submitted eq 'Login') {
			$session-&gt;param("name", $form-&gt;field("name"));
			if (defined $form-&gt;field("do") &amp;&amp; 
			    $form-&gt;field("do") ne 'signin') {
				print $q-&gt;redirect(cgiurl(
					do =&gt; $form-&gt;field("do"),
					page =&gt; $form-&gt;field("page"),
					title =&gt; $form-&gt;field("title"),
					subpage =&gt; $form-&gt;field("subpage"),
					from =&gt; $form-&gt;field("from"),
				));
			}
			else {
				print $q-&gt;redirect($config{url});
			}
		}
		elsif ($form-&gt;submitted eq 'Register') {
			my $user_name=$form-&gt;field('name');
			if (userinfo_setall($user_name, {
				           'email' =&gt; $form-&gt;field('email'),
				           'password' =&gt; $form-&gt;field('password'),
				           'regdate' =&gt; time
				         })) {
				$form-&gt;field(name =&gt; "confirm_password", type =&gt; "hidden");
				$form-&gt;field(name =&gt; "email", type =&gt; "hidden");
				$form-&gt;text("Registration successful. Now you can Login.");
				print $session-&gt;header();
				print misctemplate($form-&gt;title, $form-&gt;render(submit =&gt; ["Login"]));
			}
			else {
				error("Error saving registration.");
			}
		}
		elsif ($form-&gt;submitted eq 'Mail Password') {
			my $user_name=$form-&gt;field("name");
			my $template=HTML::Template-&gt;new(
				filename =&gt; "$config{templatedir}/passwordmail.tmpl"
			);
			$template-&gt;param(
				user_name =&gt; $user_name,
				user_password =&gt; userinfo_get($user_name, "password"),
				wikiurl =&gt; $config{url},
				wikiname =&gt; $config{wikiname},
				REMOTE_ADDR =&gt; $ENV{REMOTE_ADDR},
			);
			
			eval q{use Mail::Sendmail};
			my ($fromhost) = $config{cgiurl} =~ m!/([^/]+)!;
			sendmail(
				To =&gt; userinfo_get($user_name, "email"),
				From =&gt; "$config{wikiname} admin &lt;".(getpwuid($&gt;))[0]."@".$fromhost."&gt;",
				Subject =&gt; "$config{wikiname} information",
				Message =&gt; $template-&gt;output,
			) or error("Failed to send mail");
			
			$form-&gt;text("Your password has been emailed to you.");
			$form-&gt;field(name =&gt; "name", required =&gt; 0);
			print $session-&gt;header();
			print misctemplate($form-&gt;title, $form-&gt;render(submit =&gt; ["Login", "Register", "Mail Password"]));
		}
	}
	else {
		print $session-&gt;header();
		print misctemplate($form-&gt;title, $form-&gt;render(submit =&gt; ["Login", "Register", "Mail Password"]));
	}
} #}}}

sub cgi_prefs ($$) { #{{{
	my $q=shift;
	my $session=shift;

	eval q{use CGI::FormBuilder};
	my $form = CGI::FormBuilder-&gt;new(
		title =&gt; "preferences",
		fields =&gt; [qw(do name password confirm_password email locked_pages)],
		header =&gt; 0,
		method =&gt; 'POST',
		validate =&gt; {
			confirm_password =&gt; {
				perl =&gt; q{eq $form-&gt;field("password")},
			},
			email =&gt; 'EMAIL',
		},
		required =&gt; 'NONE',
		javascript =&gt; 0,
		params =&gt; $q,
		action =&gt; $q-&gt;request_uri,
		template =&gt; (-e "$config{templatedir}/prefs.tmpl" ?
		              "$config{templatedir}/prefs.tmpl" : "")
	);
	my @buttons=("Save Preferences", "Logout", "Cancel");
	
	my $user_name=$session-&gt;param("name");
	$form-&gt;field(name =&gt; "do", type =&gt; "hidden");
	$form-&gt;field(name =&gt; "name", disabled =&gt; 1,
		value =&gt; $user_name, force =&gt; 1);
	$form-&gt;field(name =&gt; "password", type =&gt; "password");
	$form-&gt;field(name =&gt; "confirm_password", type =&gt; "password");
	$form-&gt;field(name =&gt; "locked_pages", size =&gt; 50,
		comment =&gt; "(".htmllink("", "GlobList", 1).")");
	
	if (! is_admin($user_name)) {
		$form-&gt;field(name =&gt; "locked_pages", type =&gt; "hidden");
	}
	
	if (! $form-&gt;submitted) {
		$form-&gt;field(name =&gt; "email", force =&gt; 1,
			value =&gt; userinfo_get($user_name, "email"));
		$form-&gt;field(name =&gt; "locked_pages", force =&gt; 1,
			value =&gt; userinfo_get($user_name, "locked_pages"));
	}
	
	if ($form-&gt;submitted eq 'Logout') {
		$session-&gt;delete();
		print $q-&gt;redirect($config{url});
		return;
	}
	elsif ($form-&gt;submitted eq 'Cancel') {
		print $q-&gt;redirect($config{url});
		return;
	}
	elsif ($form-&gt;submitted eq "Save Preferences" &amp;&amp; $form-&gt;validate) {
		foreach my $field (qw(password email locked_pages)) {
			if (length $form-&gt;field($field)) {
				userinfo_set($user_name, $field, $form-&gt;field($field)) || error("failed to set $field");
			}
		}
		$form-&gt;text("Preferences saved.");
	}
	
	print $session-&gt;header();
	print misctemplate($form-&gt;title, $form-&gt;render(submit =&gt; \@buttons));
} #}}}

sub cgi_editpage ($$) { #{{{
	my $q=shift;
	my $session=shift;

	eval q{use CGI::FormBuilder};
	my $form = CGI::FormBuilder-&gt;new(
		fields =&gt; [qw(do rcsinfo subpage from page content comments)],
		header =&gt; 1,
		method =&gt; 'POST',
		validate =&gt; {
			content =&gt; '/.+/',
		},
		required =&gt; [qw{content}],
		javascript =&gt; 0,
		params =&gt; $q,
		action =&gt; $q-&gt;request_uri,
		table =&gt; 0,
		template =&gt; "$config{templatedir}/editpage.tmpl"
	);
	my @buttons=("Save Page", "Preview", "Cancel");
	
	# This untaint is safe because titlepage removes any problimatic
	# characters.
	my ($page)=titlepage(possibly_foolish_untaint(lc($form-&gt;param('page'))));
	if (! defined $page || ! length $page ||
	    $page=~/$config{wiki_file_prune_regexp}/ || $page=~/^\//) {
		error("bad page name");
	}
	$page=lc($page);
	
	my $file=$page.$config{default_pageext};
	my $newfile=1;
	if (exists $pagesources{lc($page)}) {
		$file=$pagesources{lc($page)};
		$newfile=0;
	}

	$form-&gt;field(name =&gt; "do", type =&gt; 'hidden');
	$form-&gt;field(name =&gt; "from", type =&gt; 'hidden');
	$form-&gt;field(name =&gt; "rcsinfo", type =&gt; 'hidden');
	$form-&gt;field(name =&gt; "subpage", type =&gt; 'hidden');
	$form-&gt;field(name =&gt; "page", value =&gt; "$page", force =&gt; 1);
	$form-&gt;field(name =&gt; "comments", type =&gt; "text", size =&gt; 80);
	$form-&gt;field(name =&gt; "content", type =&gt; "textarea", rows =&gt; 20,
		cols =&gt; 80);
	$form-&gt;tmpl_param("can_commit", $config{rcs});
	$form-&gt;tmpl_param("indexlink", indexlink());
	$form-&gt;tmpl_param("helponformattinglink",
		htmllink("", "HelpOnFormatting", 1));
	if (! $form-&gt;submitted) {
		$form-&gt;field(name =&gt; "rcsinfo", value =&gt; rcs_prepedit($file),
			force =&gt; 1);
	}
	
	if ($form-&gt;submitted eq "Cancel") {
		print $q-&gt;redirect("$config{url}/".htmlpage($page));
		return;
	}
	elsif ($form-&gt;submitted eq "Preview") {
		require IkiWiki::Render;
		$form-&gt;tmpl_param("page_preview",
			htmlize($config{default_pageext},
				linkify($form-&gt;field('content'), $page)));
	}
	else {
		$form-&gt;tmpl_param("page_preview", "");
	}
	$form-&gt;tmpl_param("page_conflict", "");
	
	if (! $form-&gt;submitted || $form-&gt;submitted eq "Preview" || 
	    ! $form-&gt;validate) {
		if ($form-&gt;field("do") eq "create") {
			if (exists $pagesources{lc($page)}) {
				# hmm, someone else made the page in the
				# meantime?
				print $q-&gt;redirect("$config{url}/".htmlpage($page));
				return;
			}
			
			my @page_locs;
			my $best_loc;
			my ($from)=$form-&gt;param('from')=~/$config{wiki_file_regexp}/;
			if (! defined $from || ! length $from ||
			    $from ne $form-&gt;param('from') ||
			    $from=~/$config{wiki_file_prune_regexp}/ ||
			    $from=~/^\// ||
			    $form-&gt;submitted eq "Preview") {
				@page_locs=$best_loc=$page;
			}
			else {
				my $dir=$from."/";
				$dir=~s![^/]+/$!!;
				
				if ((defined $form-&gt;param('subpage') &amp;&amp; length $form-&gt;param('subpage')) ||
				    $page eq 'discussion') {
					$best_loc="$from/$page";
				}
				else {
					$best_loc=$dir.$page;
				}
				
				push @page_locs, $dir.$page;
				push @page_locs, "$from/$page";
				while (length $dir) {
					$dir=~s![^/]+/$!!;
					push @page_locs, $dir.$page;
				}

				@page_locs = grep {
					! exists $pagesources{lc($_)} &amp;&amp;
					! page_locked($_, $session, 1)
				} @page_locs;
			}

			$form-&gt;tmpl_param("page_select", 1);
			$form-&gt;field(name =&gt; "page", type =&gt; 'select',
				options =&gt; \@page_locs, value =&gt; $best_loc);
			$form-&gt;title("creating ".pagetitle($page));
		}
		elsif ($form-&gt;field("do") eq "edit") {
			page_locked($page, $session);
			if (! defined $form-&gt;field('content') || 
			    ! length $form-&gt;field('content')) {
				my $content="";
				if (exists $pagesources{lc($page)}) {
					$content=readfile("$config{srcdir}/$pagesources{lc($page)}");
					$content=~s/\n/\r\n/g;
				}
				$form-&gt;field(name =&gt; "content", value =&gt; $content,
					force =&gt; 1);
			}
			$form-&gt;tmpl_param("page_select", 0);
			$form-&gt;field(name =&gt; "page", type =&gt; 'hidden');
			$form-&gt;title("editing ".pagetitle($page));
		}
		
		print $form-&gt;render(submit =&gt; \@buttons);
	}
	else {
		# save page
		page_locked($page, $session);
		
		my $content=$form-&gt;field('content');
		$content=~s/\r\n/\n/g;
		$content=~s/\r/\n/g;
		writefile("$config{srcdir}/$file", $content);
		
		my $message="web commit ";
		if (length $session-&gt;param("name")) {
			$message.="by ".$session-&gt;param("name");
		}
		else {
			$message.="from $ENV{REMOTE_ADDR}";
		}
		if (defined $form-&gt;field('comments') &amp;&amp;
		    length $form-&gt;field('comments')) {
			$message.=": ".$form-&gt;field('comments');
		}
		
		if ($config{rcs}) {
			if ($newfile) {
				rcs_add($file);
			}
			# prevent deadlock with post-commit hook
			unlockwiki();
			# presumably the commit will trigger an update
			# of the wiki
			my $conflict=rcs_commit($file, $message,
				$form-&gt;field("rcsinfo"));
		
			if (defined $conflict) {
				$form-&gt;field(name =&gt; "rcsinfo", value =&gt; rcs_prepedit($file),
					force =&gt; 1);
				$form-&gt;tmpl_param("page_conflict", 1);
				$form-&gt;field("content", value =&gt; $conflict, force =&gt; 1);
				$form-&gt;field("do", "edit)");
				$form-&gt;tmpl_param("page_select", 0);
				$form-&gt;field(name =&gt; "page", type =&gt; 'hidden');
				$form-&gt;title("editing $page");
				print $form-&gt;render(submit =&gt; \@buttons);
				return;
			}
		}
		else {
			require IkiWiki::Render;
			refresh();
			saveindex();
		}
		
		# The trailing question mark tries to avoid broken
		# caches and get the most recent version of the page.
		print $q-&gt;redirect("$config{url}/".htmlpage($page)."?updated");
	}
} #}}}

sub cgi () { #{{{
	eval q{use CGI};
	eval q{use CGI::Session};
	
	my $q=CGI-&gt;new;
	
	my $do=$q-&gt;param('do');
	if (! defined $do || ! length $do) {
		error("\"do\" parameter missing");
	}
	
	# Things that do not need a session.
	if ($do eq 'recentchanges') {
		cgi_recentchanges($q);
		return;
	}
	
	CGI::Session-&gt;name("ikiwiki_session_$config{wikiname}");
	
	my $oldmask=umask(077);
	my $session = CGI::Session-&gt;new("driver:db_file", $q,
		{ FileName =&gt; "$config{wikistatedir}/sessions.db" });
	umask($oldmask);
	
	# Everything below this point needs the user to be signed in.
	if ((! $config{anonok} &amp;&amp; ! defined $session-&gt;param("name") ||
	     ! defined $session-&gt;param("name") ||
	     ! userinfo_get($session-&gt;param("name"), "regdate")) || $do eq 'signin') {
		cgi_signin($q, $session);
	
		# Force session flush with safe umask.
		my $oldmask=umask(077);
		$session-&gt;flush;
		umask($oldmask);
		
		return;
	}
	
	if ($do eq 'create' || $do eq 'edit') {
		cgi_editpage($q, $session);
	}
	elsif ($do eq 'prefs') {
		cgi_prefs($q, $session);
	}
	elsif ($do eq 'blog') {
		my $page=titlepage(lc($q-&gt;param('title')));
		# if the page already exists, munge it to be unique
		my $from=$q-&gt;param('from');
		my $add="";
		while (exists $oldpagemtime{"$from/$page$add"}) {
			$add=1 unless length $add;
			$add++;
		}
		$q-&gt;param('page', $page.$add);
		# now run same as create
		$q-&gt;param('do', 'create');
		cgi_editpage($q, $session);
	}
	else {
		error("unknown do parameter");
	}
} #}}}

1
</pre></body></html>