* Avoid locking the wiki at all when handling some basic cgi stuff
[ikiwiki] / IkiWiki / CGI.pm
1 #!/usr/bin/perl
2
3 use warnings;
4 use strict;
5 use IkiWiki;
6 use IkiWiki::UserInfo;
7 use open qw{:utf8 :std};
8 use Encode;
9
10 package IkiWiki;
11
12 sub printheader ($) { #{{{
13         my $session=shift;
14         
15         if ($config{sslcookie}) {
16                 print $session->header(-charset => 'utf-8',
17                         -cookie => $session->cookie(-secure => 1));
18         } else {
19                 print $session->header(-charset => 'utf-8');
20         }
21
22 } #}}}
23
24 sub redirect ($$) { #{{{
25         my $q=shift;
26         my $url=shift;
27         if (! $config{w3mmode}) {
28                 print $q->redirect($url);
29         }
30         else {
31                 print "Content-type: text/plain\n";
32                 print "W3m-control: GOTO $url\n\n";
33         }
34 } #}}}
35
36 sub page_locked ($$;$) { #{{{
37         my $page=shift;
38         my $session=shift;
39         my $nonfatal=shift;
40         
41         my $user=$session->param("name");
42         return if defined $user && is_admin($user);
43
44         foreach my $admin (@{$config{adminuser}}) {
45                 my $locked_pages=userinfo_get($admin, "locked_pages");
46                 if (pagespec_match($page, userinfo_get($admin, "locked_pages"))) {
47                         return 1 if $nonfatal;
48                         error(htmllink("", "", $page, 1)." is locked by ".
49                               htmllink("", "", $admin, 1)." and cannot be edited.");
50                 }
51         }
52
53         return 0;
54 } #}}}
55
56 sub decode_form_utf8 ($) { #{{{
57         my $form = shift;
58         foreach my $f ($form->field) {
59                 next if Encode::is_utf8(scalar $form->field($f));
60                 $form->field(name  => $f,
61                              value => decode_utf8($form->field($f)),
62                              force => 1,
63                             );
64         }
65 } #}}}
66
67 sub cgi_recentchanges ($) { #{{{
68         my $q=shift;
69         
70         # Optimisation: building recentchanges means calculating lots of
71         # links. Memoizing htmllink speeds it up a lot (can't be memoized
72         # during page builds as the return values may change, but they
73         # won't here.)
74         eval q{use Memoize};
75         error($@) if $@;
76         memoize("htmllink");
77
78         eval q{use Time::Duration};
79         error($@) if $@;
80         eval q{use CGI 'escapeHTML'};
81         error($@) if $@;
82
83         my $changelog=[rcs_recentchanges(100)];
84         foreach my $change (@$changelog) {
85                 $change->{when} = concise(ago($change->{when}));
86
87                 if ($change->{user} =~ m!^https?://! &&
88                     eval q{use Net::OpenID::VerifiedIdentity; 1} && !$@) {
89                         # Munge user-urls, as used by eg, OpenID.
90                         my $oid=Net::OpenID::VerifiedIdentity->new(identity => $change->{user});
91                         $change->{user} = "<a href=\"".$change->{user}."\">".escapeHTML($oid->display)."</a>";
92                 }
93                 else {
94                         $change->{user} = htmllink("", "", escapeHTML($change->{user}), 1);
95                 }
96
97                 my $is_excess = exists $change->{pages}[10]; # limit pages to first 10
98                 delete @{$change->{pages}}[10 .. @{$change->{pages}}] if $is_excess;
99                 $change->{pages} = [
100                         map {
101                                 $_->{link} = htmllink("", "", $_->{page}, 1);
102                                 $_;
103                         } @{$change->{pages}}
104                 ];
105                 push @{$change->{pages}}, { link => '...' } if $is_excess;
106         }
107
108         my $template=template("recentchanges.tmpl"); 
109         $template->param(
110                 title => "RecentChanges",
111                 indexlink => indexlink(),
112                 wikiname => $config{wikiname},
113                 changelog => $changelog,
114                 baseurl => baseurl(),
115         );
116         run_hooks(pagetemplate => sub {
117                 shift->(page => "", destpage => "", template => $template);
118         });
119         print $q->header(-charset => 'utf-8'), $template->output;
120 } #}}}
121
122 sub cgi_signin ($$) { #{{{
123         my $q=shift;
124         my $session=shift;
125
126         eval q{use CGI::FormBuilder};
127         error($@) if $@;
128         my $form = CGI::FormBuilder->new(
129                 title => "signin",
130                 fields => [qw(do name password openid_url)],
131                 header => 1,
132                 charset => "utf-8",
133                 method => 'POST',
134                 validate => {
135                         confirm_password => {
136                                 perl => q{eq $form->field("password")},
137                         },
138                         email => 'EMAIL',
139                 },
140                 required => 'NONE',
141                 javascript => 0,
142                 params => $q,
143                 action => $config{cgiurl},
144                 header => 0,
145                 template => (-e "$config{templatedir}/signin.tmpl" ?
146                              {template_params("signin.tmpl")} : ""),
147                 stylesheet => baseurl()."style.css",
148         );
149                 
150         decode_form_utf8($form);
151         
152         $form->field(name => "name", required => 0, size => 30);
153         $form->field(name => "do", type => "hidden");
154         $form->field(name => "password", type => "password", required => 0);
155         if ($config{openid}) {
156                 $form->field(name => "openid_url", label => "OpenID", size => 30,
157                         comment => '('.
158                                 htmllink("", "", "OpenID", 1, 0, "What's this?")
159                                 .($config{openidsignup} ? " | <a href=\"$config{openidsignup}\">Get an OpenID</a>" : "")
160                                 .')');
161         }
162         else {
163                 $form->field(name => "openid_url", type => "hidden");
164         }
165         if ($form->submitted eq "Register" || $form->submitted eq "Create Account") {
166                 $form->title("register");
167                 $form->text("");
168                 $form->fields(qw(do name password confirm_password email));
169                 $form->field(name => "confirm_password", type => "password");
170                 $form->field(name => "email", type => "text");
171                 $form->field(name => "openid_url", type => "hidden");
172         }
173         if ($q->param("do") ne "signin" && !$form->submitted) {
174                 $form->text("You need to log in first.");
175         }
176         
177         if ($form->submitted) {
178                 my $submittype=$form->submitted;
179                 # OpenID login uses the Login button, but validates
180                 # differently.
181                 if ($submittype eq "Login" && $config{openid} && 
182                     length $form->field("openid_url")) {
183                         $submittype="OpenID";
184                         
185                         $form->field(
186                                 name => "openid_url",
187                                 validate => sub {
188                                         # FIXME: ugh
189                                         IkiWiki::Plugin::openid::validate($q, $session, shift, $form);
190                                 },
191                         );
192                 }
193
194                 # Set required fields based on how form was submitted.
195                 my %required=(
196                         "Login" => [qw(name password)],
197                         "Register" => [],
198                         "Create Account" => [qw(name password confirm_password email)],
199                         "Mail Password" => [qw(name)],
200                         "OpenID" => [qw(openid_url)],
201                 );
202                 foreach my $opt (@{$required{$submittype}}) {
203                         $form->field(name => $opt, required => 1);
204                 }
205         
206                 # Validate password differently depending on how
207                 # form was submitted.
208                 if ($submittype eq 'Login') {
209                         $form->field(
210                                 name => "password",
211                                 validate => sub {
212                                         length $form->field("name") &&
213                                         shift eq userinfo_get($form->field("name"), 'password');
214                                 },
215                         );
216                         $form->field(name => "name", validate => '/^\w+$/');
217                 }
218                 elsif ($submittype ne 'OpenID') {
219                         $form->field(name => "password", validate => 'VALUE');
220                 }
221                 # And make sure the entered name exists when logging
222                 # in or sending email, and does not when registering.
223                 if ($submittype eq 'Create Account' ||
224                     $submittype eq 'Register') {
225                         $form->field(
226                                 name => "name",
227                                 validate => sub {
228                                         my $name=shift;
229                                         length $name &&
230                                         $name=~/$config{wiki_file_regexp}/ &&
231                                         ! userinfo_get($name, "regdate");
232                                 },
233                         );
234                 }
235                 elsif ($submittype ne 'OpenID') {
236                         $form->field(
237                                 name => "name",
238                                 validate => sub {
239                                         my $name=shift;
240                                         length $name &&
241                                         userinfo_get($name, "regdate");
242                                 },
243                         );
244                 }
245         }
246         else {
247                 # First time settings.
248                 $form->field(name => "name", comment => "use FirstnameLastName");
249                 if ($session->param("name")) {
250                         $form->field(name => "name", value => $session->param("name"));
251                 }
252         }
253
254         if ($form->submitted && $form->validate) {
255                 if ($form->submitted eq 'Login') {
256                         $session->param("name", $form->field("name"));
257                         cgi_postsignin($q, $session);
258                 }
259                 elsif ($form->submitted eq 'Create Account') {
260                         my $user_name=$form->field('name');
261                         if (userinfo_setall($user_name, {
262                                            'email' => $form->field('email'),
263                                            'password' => $form->field('password'),
264                                            'regdate' => time
265                                          })) {
266                                 $form->field(name => "confirm_password", type => "hidden");
267                                 $form->field(name => "email", type => "hidden");
268                                 $form->text("Account creation successful. Now you can Login.");
269                                 printheader($session);
270                                 print misctemplate($form->title, $form->render(submit => ["Login"]));
271                         }
272                         else {
273                                 error("Error creating account.");
274                         }
275                 }
276                 elsif ($form->submitted eq 'Mail Password') {
277                         my $user_name=$form->field("name");
278                         my $template=template("passwordmail.tmpl");
279                         $template->param(
280                                 user_name => $user_name,
281                                 user_password => userinfo_get($user_name, "password"),
282                                 wikiurl => $config{url},
283                                 wikiname => $config{wikiname},
284                                 REMOTE_ADDR => $ENV{REMOTE_ADDR},
285                         );
286                         
287                         eval q{use Mail::Sendmail};
288                         error($@) if $@;
289                         sendmail(
290                                 To => userinfo_get($user_name, "email"),
291                                 From => "$config{wikiname} admin <$config{adminemail}>",
292                                 Subject => "$config{wikiname} information",
293                                 Message => $template->output,
294                         ) or error("Failed to send mail");
295                         
296                         $form->text("Your password has been emailed to you.");
297                         $form->field(name => "name", required => 0);
298                         printheader($session);
299                         print misctemplate($form->title, $form->render(submit => ["Login", "Mail Password"]));
300                 }
301                 elsif ($form->submitted eq "Register") {
302                         printheader($session);
303                         print misctemplate($form->title, $form->render(submit => ["Create Account"]));
304                 }
305         }
306         elsif ($form->submitted eq "Create Account") {
307                 printheader($session);
308                 print misctemplate($form->title, $form->render(submit => ["Create Account"]));
309         }
310         else {
311                 printheader($session);
312                 print misctemplate($form->title, $form->render(submit => ["Login", "Register", "Mail Password"]));
313         }
314 } #}}}
315
316 sub cgi_postsignin ($$) { #{{{
317         my $q=shift;
318         my $session=shift;
319
320         # Continue with whatever was being done before the signin process.
321         if (defined $q->param("do") && $q->param("do") ne "signin" &&
322             defined $session->param("postsignin")) {
323                 my $postsignin=CGI->new($session->param("postsignin"));
324                 $session->clear("postsignin");
325                 cgi($postsignin, $session);
326                 exit;
327         }
328         else {
329                 redirect($q, $config{url});
330         }
331 } #}}}
332
333 sub cgi_prefs ($$) { #{{{
334         my $q=shift;
335         my $session=shift;
336
337         eval q{use CGI::FormBuilder};
338         error($@) if $@;
339         my $form = CGI::FormBuilder->new(
340                 title => "preferences",
341                 fields => [qw(do name password confirm_password email 
342                               subscriptions locked_pages)],
343                 header => 0,
344                 charset => "utf-8",
345                 method => 'POST',
346                 validate => {
347                         confirm_password => {
348                                 perl => q{eq $form->field("password")},
349                         },
350                         email => 'EMAIL',
351                 },
352                 required => 'NONE',
353                 javascript => 0,
354                 params => $q,
355                 action => $config{cgiurl},
356                 template => (-e "$config{templatedir}/prefs.tmpl" ?
357                              {template_params("prefs.tmpl")} : ""),
358                 stylesheet => baseurl()."style.css",
359         );
360         my @buttons=("Save Preferences", "Logout", "Cancel");
361         
362         my $user_name=$session->param("name");
363         $form->field(name => "do", type => "hidden");
364         $form->field(name => "name", disabled => 1,
365                 value => $user_name, force => 1, size => 30);
366         $form->field(name => "password", type => "password");
367         $form->field(name => "confirm_password", type => "password");
368         $form->field(name => "subscriptions", size => 50,
369                 comment => "(".htmllink("", "", "PageSpec", 1).")");
370         $form->field(name => "locked_pages", size => 50,
371                 comment => "(".htmllink("", "", "PageSpec", 1).")");
372         $form->field(name => "banned_users", size => 50);
373         
374         if (! is_admin($user_name)) {
375                 $form->field(name => "locked_pages", type => "hidden");
376                 $form->field(name => "banned_users", type => "hidden");
377         }
378
379         if ($config{httpauth}) {
380                 $form->field(name => "password", type => "hidden");
381                 $form->field(name => "confirm_password", type => "hidden");
382         }
383         
384         if (! $form->submitted) {
385                 $form->field(name => "email", force => 1,
386                         value => userinfo_get($user_name, "email"));
387                 $form->field(name => "subscriptions", force => 1,
388                         value => userinfo_get($user_name, "subscriptions"));
389                 $form->field(name => "locked_pages", force => 1,
390                         value => userinfo_get($user_name, "locked_pages"));
391                 if (is_admin($user_name)) {
392                         $form->field(name => "banned_users", force => 1,
393                                 value => join(" ", get_banned_users()));
394                 }
395         }
396         
397         decode_form_utf8($form);
398         
399         if ($form->submitted eq 'Logout') {
400                 $session->delete();
401                 redirect($q, $config{url});
402                 return;
403         }
404         elsif ($form->submitted eq 'Cancel') {
405                 redirect($q, $config{url});
406                 return;
407         }
408         elsif ($form->submitted eq "Save Preferences" && $form->validate) {
409                 foreach my $field (qw(password email subscriptions locked_pages)) {
410                         if (length $form->field($field)) {
411                                 userinfo_set($user_name, $field, $form->field($field)) || error("failed to set $field");
412                         }
413                 }
414                 if (is_admin($user_name)) {
415                         set_banned_users(grep { ! is_admin($_) }
416                                         split(' ', $form->field("banned_users")));
417                 }
418                 $form->text("Preferences saved.");
419         }
420         
421         printheader($session);
422         print misctemplate($form->title, $form->render(submit => \@buttons));
423 } #}}}
424
425 sub cgi_editpage ($$) { #{{{
426         my $q=shift;
427         my $session=shift;
428
429         my @fields=qw(do rcsinfo subpage from page type editcontent comments);
430         my @buttons=("Save Page", "Preview", "Cancel");
431         
432         eval q{use CGI::FormBuilder; use CGI::FormBuilder::Template::HTML};
433         error($@) if $@;
434         my $renderer=CGI::FormBuilder::Template::HTML->new(
435                 fields => \@fields,
436                 template_params("editpage.tmpl"),
437         );
438         run_hooks(pagetemplate => sub {
439                 shift->(page => "", destpage => "", template => $renderer->engine);
440         });
441         my $form = CGI::FormBuilder->new(
442                 fields => \@fields,
443                 header => 1,
444                 charset => "utf-8",
445                 method => 'POST',
446                 validate => {
447                         editcontent => '/.+/',
448                 },
449                 required => [qw{editcontent}],
450                 javascript => 0,
451                 params => $q,
452                 action => $config{cgiurl},
453                 table => 0,
454                 template => $renderer,
455         );
456         
457         decode_form_utf8($form);
458         
459         # This untaint is safe because titlepage removes any problematic
460         # characters.
461         my ($page)=$form->field('page');
462         $page=titlepage(possibly_foolish_untaint($page));
463         if (! defined $page || ! length $page ||
464             $page=~/$config{wiki_file_prune_regexp}/ || $page=~/^\//) {
465                 error("bad page name");
466         }
467         
468         my $from;
469         if (defined $form->field('from')) {
470                 ($from)=$form->field('from')=~/$config{wiki_file_regexp}/;
471         }
472         
473         my $file;
474         my $type;
475         if (exists $pagesources{$page}) {
476                 $file=$pagesources{$page};
477                 $type=pagetype($file);
478         }
479         else {
480                 $type=$form->param('type');
481                 if (defined $type && length $type && $hooks{htmlize}{$type}) {
482                         $type=possibly_foolish_untaint($type);
483                 }
484                 elsif (defined $from) {
485                         # favor the type of linking page
486                         $type=pagetype($pagesources{$from});
487                 }
488                 $type=$config{default_pageext} unless defined $type;
489                 $file=$page.".".$type;
490         }
491
492         my $newfile=0;
493         if (! -e "$config{srcdir}/$file") {
494                 $newfile=1;
495         }
496
497         $form->field(name => "do", type => 'hidden');
498         $form->field(name => "from", type => 'hidden');
499         $form->field(name => "rcsinfo", type => 'hidden');
500         $form->field(name => "subpage", type => 'hidden');
501         $form->field(name => "page", value => $page, force => 1);
502         $form->field(name => "type", value => $type, force => 1);
503         $form->field(name => "comments", type => "text", size => 80);
504         $form->field(name => "editcontent", type => "textarea", rows => 20,
505                 cols => 80);
506         $form->tmpl_param("can_commit", $config{rcs});
507         $form->tmpl_param("indexlink", indexlink());
508         $form->tmpl_param("helponformattinglink",
509                 htmllink("", "", "HelpOnFormatting", 1));
510         $form->tmpl_param("baseurl", baseurl());
511         if (! $form->submitted) {
512                 $form->field(name => "rcsinfo", value => rcs_prepedit($file),
513                         force => 1);
514         }
515         
516         if ($form->submitted eq "Cancel") {
517                 if ($newfile && defined $from) {
518                         redirect($q, "$config{url}/".htmlpage($from));
519                 }
520                 elsif ($newfile) {
521                         redirect($q, $config{url});
522                 }
523                 else {
524                         redirect($q, "$config{url}/".htmlpage($page));
525                 }
526                 return;
527         }
528         elsif ($form->submitted eq "Preview") {
529                 my $content=$form->field('editcontent');
530                 my $comments=$form->field('comments');
531                 $form->field(name => "editcontent",
532                                 value => $content, force => 1);
533                 $form->field(name => "comments",
534                                 value => $comments, force => 1);
535                 $config{rss}=$config{atom}=0; # avoid preview writing a feed!
536                 $form->tmpl_param("page_preview",
537                         htmlize($page, $type,
538                         linkify($page, "",
539                         preprocess($page, $page,
540                         filter($page, $content)))));
541         }
542         else {
543                 $form->tmpl_param("page_preview", "");
544         }
545         $form->tmpl_param("page_conflict", "");
546         
547         if (! $form->submitted || $form->submitted eq "Preview" || 
548             ! $form->validate) {
549                 if ($form->field("do") eq "create") {
550                         my @page_locs;
551                         my $best_loc;
552                         if (! defined $from || ! length $from ||
553                             $from ne $form->field('from') ||
554                             $from=~/$config{wiki_file_prune_regexp}/ ||
555                             $from=~/^\// ||
556                             $form->submitted eq "Preview") {
557                                 @page_locs=$best_loc=$page;
558                         }
559                         else {
560                                 my $dir=$from."/";
561                                 $dir=~s![^/]+/+$!!;
562                                 
563                                 if ((defined $form->field('subpage') && length $form->field('subpage')) ||
564                                     $page eq 'discussion') {
565                                         $best_loc="$from/$page";
566                                 }
567                                 else {
568                                         $best_loc=$dir.$page;
569                                 }
570                                 
571                                 push @page_locs, $dir.$page;
572                                 push @page_locs, "$from/$page";
573                                 while (length $dir) {
574                                         $dir=~s![^/]+/+$!!;
575                                         push @page_locs, $dir.$page;
576                                 }
577                         }
578
579                         @page_locs = grep {
580                                 ! exists $pagecase{lc $_} &&
581                                 ! page_locked($_, $session, 1)
582                         } @page_locs;
583                         
584                         if (! @page_locs) {
585                                 # hmm, someone else made the page in the
586                                 # meantime?
587                                 redirect($q, "$config{url}/".htmlpage($page));
588                                 return;
589                         }
590                         
591                         my @page_types;
592                         if (exists $hooks{htmlize}) {
593                                 @page_types=keys %{$hooks{htmlize}};
594                         }
595                         
596                         $form->tmpl_param("page_select", 1);
597                         $form->field(name => "page", type => 'select',
598                                 options => \@page_locs, value => $best_loc);
599                         $form->field(name => "type", type => 'select',
600                                 options => \@page_types);
601                         $form->title("creating ".pagetitle($page));
602                 }
603                 elsif ($form->field("do") eq "edit") {
604                         page_locked($page, $session);
605                         if (! defined $form->field('editcontent') || 
606                             ! length $form->field('editcontent')) {
607                                 my $content="";
608                                 if (exists $pagesources{$page}) {
609                                         $content=readfile(srcfile($pagesources{$page}));
610                                         $content=~s/\n/\r\n/g;
611                                 }
612                                 $form->field(name => "editcontent", value => $content,
613                                         force => 1);
614                         }
615                         $form->tmpl_param("page_select", 0);
616                         $form->field(name => "page", type => 'hidden');
617                         $form->field(name => "type", type => 'hidden');
618                         $form->title("editing ".pagetitle($page));
619                 }
620                 
621                 print $form->render(submit => \@buttons);
622         }
623         else {
624                 # save page
625                 page_locked($page, $session);
626                 
627                 my $content=$form->field('editcontent');
628
629                 $content=~s/\r\n/\n/g;
630                 $content=~s/\r/\n/g;
631                 writefile($file, $config{srcdir}, $content);
632                 
633                 my $message="web commit ";
634                 if (defined $session->param("name") && 
635                     length $session->param("name")) {
636                         $message.="by ".$session->param("name");
637                 }
638                 else {
639                         $message.="from $ENV{REMOTE_ADDR}";
640                 }
641                 if (defined $form->field('comments') &&
642                     length $form->field('comments')) {
643                         $message.=": ".$form->field('comments');
644                 }
645                 
646                 if ($config{rcs}) {
647                         if ($newfile) {
648                                 rcs_add($file);
649                         }
650                         # prevent deadlock with post-commit hook
651                         unlockwiki();
652                         # presumably the commit will trigger an update
653                         # of the wiki
654                         my $conflict=rcs_commit($file, $message,
655                                 $form->field("rcsinfo"));
656                 
657                         if (defined $conflict) {
658                                 $form->field(name => "rcsinfo", value => rcs_prepedit($file),
659                                         force => 1);
660                                 $form->tmpl_param("page_conflict", 1);
661                                 $form->field("editcontent", value => $conflict, force => 1);
662                                 $form->field(name => "comments", value => $form->field('comments'), force => 1);
663                                 $form->field("do", "edit)");
664                                 $form->tmpl_param("page_select", 0);
665                                 $form->field(name => "page", type => 'hidden');
666                                 $form->field(name => "type", type => 'hidden');
667                                 $form->title("editing $page");
668                                 print $form->render(submit => \@buttons);
669                                 return;
670                         }
671                 }
672                 else {
673                         require IkiWiki::Render;
674                         refresh();
675                         saveindex();
676                 }
677                 
678                 # The trailing question mark tries to avoid broken
679                 # caches and get the most recent version of the page.
680                 redirect($q, "$config{url}/".htmlpage($page)."?updated");
681         }
682 } #}}}
683
684 sub cgi (;$$) { #{{{
685         my $q=shift;
686         my $session=shift;
687
688         if (! $q) {
689                 eval q{use CGI; use CGI::Session};
690                 error($@) if $@;
691         
692                 $q=CGI->new;
693         
694                 run_hooks(cgi => sub { shift->($q) });
695         }
696
697         my $do=$q->param('do');
698         if (! defined $do || ! length $do) {
699                 my $error = $q->cgi_error;
700                 if ($error) {
701                         error("Request not processed: $error");
702                 }
703                 else {
704                         error("\"do\" parameter missing");
705                 }
706         }
707         
708         # Things that do not need a session.
709         if ($do eq 'recentchanges') {
710                 cgi_recentchanges($q);
711                 return;
712         }
713         elsif ($do eq 'hyperestraier') {
714                 cgi_hyperestraier();
715         }
716
717         # Need to lock the wiki before getting a session.
718         lockwiki();
719         
720         if (! $session) {
721                 CGI::Session->name("ikiwiki_session_".encode_utf8($config{wikiname}));
722         
723                 my $oldmask=umask(077);
724                 $session = CGI::Session->new("driver:DB_File", $q,
725                         { FileName => "$config{wikistatedir}/sessions.db" });
726                 umask($oldmask);
727         }
728         
729         # Auth hooks can sign a user in.
730         if ($do ne 'signin' && ! defined $session->param("name")) {
731                 run_hooks(auth => sub {
732                         shift->($q, $session)
733                 });
734                 if (defined $session->param("name")) {
735                         # Make sure whatever user was authed is in the
736                         # userinfo db.
737                         if (! userinfo_get($session->param("name"), "regdate")) {
738                                 userinfo_setall($session->param("name"), {
739                                         email => "",
740                                         password => "",
741                                         regdate => time,
742                                 });
743                         }
744                 }
745         }
746
747         # Everything below this point needs the user to be signed in.
748         if (((! $config{anonok} || $do eq 'prefs') &&
749              (! defined $session->param("name") ||
750              ! userinfo_get($session->param("name"), "regdate")))
751             || $do eq 'signin') {
752                 if ($do ne 'signin' && ! defined $session->param("postsignin")) {
753                         $session->param(postsignin => $ENV{QUERY_STRING});
754                 }
755                 cgi_signin($q, $session);
756         
757                 # Force session flush with safe umask.
758                 my $oldmask=umask(077);
759                 $session->flush;
760                 umask($oldmask);
761                 
762                 return;
763         }
764         elsif (defined $session->param("postsignin")) {
765                 cgi_postsignin($q, $session);
766         }
767
768         if (defined $session->param("name") && userinfo_get($session->param("name"), "banned")) {
769                 print $q->header(-status => "403 Forbidden");
770                 $session->delete();
771                 print "You are banned.";
772                 exit;
773         }
774         
775         if ($do eq 'create' || $do eq 'edit') {
776                 cgi_editpage($q, $session);
777         }
778         elsif ($do eq 'prefs') {
779                 cgi_prefs($q, $session);
780         }
781         elsif ($do eq 'blog') {
782                 my $page=titlepage(decode_utf8($q->param('title')));
783                 # if the page already exists, munge it to be unique
784                 my $from=$q->param('from');
785                 my $add="";
786                 while (exists $pagecase{lc "$from/$page$add"}) {
787                         $add=1 unless length $add;
788                         $add++;
789                 }
790                 $q->param('page', $page.$add);
791                 # now run same as create
792                 $q->param('do', 'create');
793                 cgi_editpage($q, $session);
794         }
795         else {
796                 error("unknown do parameter");
797         }
798 } #}}}
799
800 1