/[pub]/suikawiki/script/lib/suikawiki.pl
Suika

Contents of /suikawiki/script/lib/suikawiki.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.4 - (hide annotations) (download)
Tue Apr 29 10:41:07 2003 UTC (21 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.3: +24 -271 lines
File MIME type: text/plain
bug fix.

1 wakaba 1.1 # -*- perl -*-
2     use strict;
3    
4     package main;
5 wakaba 1.4 our $VERSION = '2.'.do{my @r=(q$Revision: 1.3 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
6 wakaba 1.1 binmode STDOUT; binmode STDIN;
7 wakaba 1.3 use Fcntl;
8 wakaba 1.1 require Yuki::YukiWikiCache;
9 wakaba 1.3 require SuikaWiki::Name::Space;
10     require SuikaWiki::View;
11 wakaba 1.1 require SuikaWiki::Plugin;
12     our %fmt; ## formatter objects
13     our %embed_command = (
14     form => qr/\[\[\#form(?:\(([A-Za-z0-9-]+)\))?:'((?:[^'\\]|\\.)*)':'((?:[^'\\]|\\.)*)'(?::'((?:[^'\\]|\\.)*)')?\]\]/,
15     );
16     our ($modifier_dbtype,$url_cgi,%uri,%PathTo);
17 wakaba 1.3 our (%PageName,$kanjicode,$lang);
18 wakaba 1.1
19     my %form;
20     our %database;
21     our $database = bless {}, 'wiki::dummy';
22     my %interwiki;
23     my %command_do = (
24     default => \&do_view,
25     adminchangepassword => \&do_adminchangepassword,
26     write => \&do_write,
27     searchform => \&do_searchform,
28     comment => \&do_comment,
29     RandomJump => \&do_random_jump,
30     wikiform => \&do_wikiform,
31     );
32     our $UA = ''; ## User agent name
33     $| = 1;
34    
35     sub main {
36     $UA = $main::ENV{HTTP_USER_AGENT};
37     &open_db;
38     &init_form;
39     if ($command_do{$form{mycmd}}) {
40     &{$command_do{$form{mycmd}}};
41     } else {
42     &{$command_do{default}};
43     }
44     &close_db;
45     }
46    
47     sub do_view {
48     my $content = $database{$form{mypage}};
49     my $lm = $database->mtime ($form{mypage});
50     wiki::referer::add ($form{mypage}, $ENV{HTTP_REFERER});
51     wiki::useragent::add ($ENV{HTTP_USER_AGENT});
52     &load_formatter ('view');
53     my $view = $form{mycmd};
54     if ($view eq 'edit') {
55     $view = 'adminedit' if $form{admin};
56     } elsif ($view =~ /[^0-9A-Za-z_]/) {
57     $view = 'default'
58     }
59     if ($view eq 'default' || !$view) {
60     ## BUG: this code is not strict
61     if ($main::ENV{HTTP_COOKIE} =~ /SelectedMode=([0-9A-Za-z]+)/) {
62     $view = $1;
63     } else {
64     $view = 'read';
65     }
66     }
67     my ($magic, $content) = &SuikaWiki::Plugin::magic_and_content (undef, $content);
68     $magic ||= '#?SuikaWiki/0.9';
69     my $o = bless {param => \%form, page => $form{mypage}, toc => [],
70     magic => $magic, content => $content,
71     formatter => $fmt{view}, &_compatible_options ()}, 'SuikaWiki::Plugin';
72 wakaba 1.3 my $view_def = SuikaWiki::View->definition ($view);
73     if (!$view_def->check ($o)) {
74 wakaba 1.1 print "Status: 406 Unsupported Media Type\n";
75     $view = '-UnsupportedMediaType';
76 wakaba 1.3 $view_def = SuikaWiki::View->definition ($view);
77 wakaba 1.1 }
78 wakaba 1.3 my $media = $view_def->properties->{media};
79     if ($view_def->properties->{xmedia} && $UA =~ /Gecko/) {
80     $media = $view_def->properties->{xmedia};
81 wakaba 1.1 $o->{media} = $media;
82     } elsif ($UA =~ m#Mozilla/0\..+Windows#) {
83     $kanjicode = 'shift_jis';
84     }
85     if ($magic =~ m!^\#\?SuikaWiki/0.9!) {
86     &print_header ($form{mypage}, -last_modified => ($magic =~ /interactive="yes"/ ? time : $lm),
87     -expires => ($magic =~ /interactive="yes"/ ? 1 : undef), o => $o,
88     -media => $media, -magic => $magic, content => $content);
89     } else {
90     &print_header($form{mypage}, -media => $media,
91     -magic => $magic, -last_modified => $lm, o => $o);
92     }
93     if ($kanjicode ne 'euc') {
94 wakaba 1.3 my $s = $fmt{view}->replace ($view_def->as_string => $o, {formatter => $fmt{view}});
95 wakaba 1.1 print &code_convert (\$s => $kanjicode);
96     } else {
97 wakaba 1.3 print $fmt{view}->replace ($view_def->as_string => $o, {formatter => $fmt{view}});
98 wakaba 1.1 }
99     }
100    
101     sub _do_view_msg (%) {
102     my %option = @_;
103     &load_formatter ('view');
104     my $o = bless {param => \%form, page => $option{-page}, toc => [], condition => \%option,
105     formatter => $fmt{view}, &_compatible_options ()}, 'SuikaWiki::Plugin';
106 wakaba 1.3 my $view_def = SuikaWiki::View->definition ($option{-view});
107     unless ($view_def->check ($o)) {
108 wakaba 1.1 print "Status: 406 Unsupported Media Type\n";
109     $option{-view} = '-UnsupportedMediaType';
110 wakaba 1.3 $view_def = SuikaWiki::View->definition ($option{-view});
111 wakaba 1.1 }
112 wakaba 1.3 my $media = $view_def->properties->{media};
113     if ($view_def->properties->{xmedia} && $UA =~ /Gecko/) {
114     $media = $view_def->properties->{xmedia};
115 wakaba 1.1 $o->{media} = $media;
116     }
117     &print_header($option{-page}, -media => $media, o => $o, -goto => $option{-goto});
118 wakaba 1.3 print $fmt{view}->replace ($view_def->as_string => $o, {formatter => $fmt{view}});
119 wakaba 1.1 }
120    
121     sub do_adminchangepassword {
122     if ($form{mynewpassword} ne $form{mynewpassword2}) {
123     &_do_view_msg (-view => '-error', -page => $form{mypage},
124     error_message => &Resource ('Error:PasswordMismatch'));
125     return;
126     }
127     my ($validpassword_crypt) = $database->meta (AdminPassword => $PageName{AdminSpecialPage});
128     if ($validpassword_crypt) {
129     if (not &valid_password($form{myoldpassword})) {
130     &_do_view_msg (-view => '-error', -page => $form{mypage},
131     error_message => &Resource ('Error:PasswordIsIncorrect'));
132     return;
133     }
134     }
135     my ($sec, $min, $hour, $day, $mon, $year, $weekday) = localtime(time);
136     my (@token) = ('0'..'9', 'A'..'Z', 'a'..'z');
137     my $salt1 = $token[(time | $$) % scalar(@token)];
138     my $salt2 = $token[($sec + $min*60 + $hour*60*60) % scalar(@token)];
139     my $crypted = crypt($form{mynewpassword}, "$salt1$salt2");
140     $database->meta (AdminPassword => $PageName{AdminSpecialPage} => $crypted);
141    
142     &_do_view_msg (-view => '-wrote', -page => $form{mypage});
143     }
144    
145     sub valid_password ($) {
146     my ($validpassword_crypt) = $database->meta (AdminPassword => $PageName{AdminSpecialPage});
147     return crypt (shift, $validpassword_crypt) eq $validpassword_crypt ? 1 : 0;
148     }
149    
150     sub do_write {
151     if (&frozen_reject()) {
152     return;
153     }
154    
155     if (not &is_editable($form{mypage})) {
156     &_do_view_msg (-view => '-error', -page => $form{mypage},
157     error_message => &Resource ('Error:ThisPageIsUneditable'));
158     return;
159     }
160    
161     ## Check confliction
162     if ($form{myLastModified} ne $database->mtime ($form{mypage})) {
163     &_do_view_msg (-view => '-conflict', -page => $form{mypage});
164     return;
165     }
166    
167     if ($form{mymsg}) {
168     if ($form{mytouch} || !ref $database) {
169     $database{$form{mypage}} = $form{mymsg};
170     } else {
171     $database->STORE ($form{mypage} => $form{mymsg}, -touch => 0);
172     }
173     $database->meta (IsFrozen => $form{mypage} => 0 + $form{myfrozen});
174     my $fragment = '';
175     $fragment .= qq(;after_edit_cmd=@{[&encode($form{after_edit_cmd})]}) if $form{after_edit_cmd};
176     if ($form{__comment_anchor_index}) {
177     $fragment .= qq(#anchor-$form{__comment_anchor_index});
178     } elsif ($form{__wikiform_anchor_index}) {
179     $fragment .= qq(#wikiform-$form{__wikiform_anchor_index});
180     }
181     &_do_view_msg (-view => '-wrote', -page => $form{mypage}, -goto => $url_cgi.'?mycmd='.&encode($form{after_edit_cmd}||'default').';mypage='.&encode($form{mypage}).qq(;x-param=@{[time.[0..9]->[rand 10]]}$fragment));
182     } else {
183     delete $database{$form{mypage}};
184     &_do_view_msg (-view => '-deleted', -page => $form{mypage});
185     }
186     }
187    
188     sub _compatible_options () {
189     (use_anchor_name => ($UA =~ m#Mozilla/[12]\.|Microsoft Internet Explorer# ? 1 : 0));
190     }
191    
192     sub get_search_result ($;%) {
193     my $word = lc shift;
194     my $SearchResult = SuikaWiki::Plugin->cache ('search');
195     my %option = @_;
196     my @r;
197     unless (defined $SearchResult->{$word}) {
198     for my $page (keys %database) {
199     next if !$option{-match_myself} && ($page eq $word);
200     my $content = lc $database{$page};
201     $content =~ s/^[^\x0A\x0D]+[\x0D\x0A]+//s;
202     if (index (lc $page, $word) > -1) {
203     my $c = $content =~ s/\Q$word\E//g;
204     push @r, [$page, $c+20];
205     } elsif (index ($word, lc $page) > -1) {
206     my $c = $content =~ s/\Q$word\E//g;
207     push @r, [$page, $c+10];
208     } elsif (my $c = $content =~ s/\Q$word\E//g) {
209     push @r, [$page, $c];
210     }
211     }
212     @r = sort {$b->[1] <=> $a->[1] || $a->[0] cmp $b->[0]} @r;
213     $SearchResult->{$word} = join "\x1E", map {$_->[0]."\x1F".$_->[1]} @r;
214     } else {
215     @r = map {[split /\x1F/, $_, 2]} split /\x1E/, $SearchResult->{$word};
216     }
217     #my $em = sub { my $s = shift; $s =~ s#(\Q$word\E)#<em>$1</em>#gi; $s };
218     my $r = join "\n", map {qq(<li>[$_->[1]] <a href ="$url_cgi?@{[&encode($_->[0])]}" class="wiki">@{[&escape($_->[0])]}</a> <span class="wikipage-summary">@{[&escape(&get_subjectline($_->[0]))]}</span></li>)} @r;
219     $r = qq|<ul class="search-result">$r</ul>| if $r;
220     wantarray? ($r, scalar @r): $r;
221     }
222    
223     sub do_random_jump {
224     my @list = keys %database;
225     my $name = &encode ($list[rand @list]);
226     print "Location: $uri{wiki}?$name\n";
227     print "\n";
228     }
229    
230     sub print_header ($;%) {
231     my ($page, %option) = @_;
232     my @head;
233     $option{o}->{-header}->{class} = &is_frozen($page) ? 'frozen' : '';
234     $option{o}->{-header}->{class} .= " wiki-page-obsoleted" if $option{-magic} =~ /obsoleted="yes"/;
235     if ($option{-goto}) {
236     if ($UA =~ m#Opera|MSIE 2\.#) {
237     ## WARNING: This code may output unsafe HTML document if
238     ## $option{-goto} is not clean.
239     $option{-goto} =~ tr/;/&/ if $UA =~ m#Opera#;
240     print qq{Refresh: 0; url=$option{-goto}\n};
241     push @head, qq(<meta http-equiv="refresh" content="0; url=$option{-goto}">);
242     } elsif ($UA =~ /Gecko/) {
243     print qq{Refresh: 0; url="$option{-goto}"\n};
244     push @head, qq(<meta http-equiv="refresh" content="0; url=&quot;@{[&escape($option{-goto})]}&quot;" />);
245     } else {
246     $option{-goto} =~ tr/;/&/ if $UA =~ m#Mozilla/[1-4]\.#;
247     print qq{Refresh: 0; url="$option{-goto}"\n};
248     push @head, qq(<meta http-equiv="refresh" content="0; url=&quot;@{[&escape($option{-goto})]}&quot;">);
249     }
250     }
251     print qq{Last-Modified: @{[scalar gmtime $option{-last_modified}]}\n} if $option{-last_modified};
252     if ($option{-expires} != -1) {
253     if (defined $option{-expires}) { ## TODO: Don't use asctime
254     print qq{Expires: @{[scalar gmtime (time + $option{-expires})]}\n};
255     } elsif ($option{-media}->{expires} != -1) {
256     print qq{Expires: @{[scalar gmtime (time + $option{-media}->{expires})]}\n};
257     }
258     }
259     if ($option{-media}->{charset} && $UA =~ m#Mozilla/[12]\.#) {
260     my $ct = qq{$option{-media}->{type}; charset=@{[&get_charset_name($kanjicode,compatible=>1)]}};
261     print qq{Content-Type: $ct\n};
262     $option{o}->{-header}->{meta_ct} = qq{<meta http-equiv="content-type" content="$ct">\n};
263     } elsif (!$option{-media}->{charset} || $UA =~ m#Infomosaic|Mozilla/0\.#) {
264     print qq{Content-Type: $option{-media}->{type}\n};
265     $option{o}->{-header}->{meta_ct} = qq{<meta http-equiv="content-type" content="$option{-media}->{type}; charset=@{[&get_charset_name($kanjicode,compatible=>1)]}">\n};
266     } else {
267     my $type = $option{-media}->{type};
268 wakaba 1.4 $type = 'application/xml' if ($type =~ m!^application/(?:rdf|rss)\+xml$!) && ($UA =~ m#Gecko#);
269 wakaba 1.1 print qq{Content-Type: $type; charset=@{[&get_charset_name($kanjicode)]}\n};
270     }
271 wakaba 1.4 ## TODO: more Vary: support
272     print <<"EOD";
273     Vary: User-Agent,Accept-Language
274 wakaba 1.1 Content-Style-Type: text/css
275    
276     EOD
277     $option{o}->{-header}->{links} = join "\n", (@head);
278     }
279    
280     sub get_charset_name ($;%) {
281     my ($charset, %option) = (lc shift, @_);
282     if ($charset =~ 'euc') {
283     $charset = $option{compatible} ? 'x-euc-jp' : 'euc-jp';
284     } elsif ($charset =~ 'sjis' || $charset =~ 'shift') {
285     $charset = $option{compatible} ? 'x-sjis' : 'shift_jis';
286     } elsif ($charset =~ 'jis') {
287     $charset = 'iso-2022-jp';
288     }
289     $charset;
290     }
291    
292     sub escape {
293     my $s = shift;
294     $s =~ s|&|&amp;|g;
295     $s =~ s|<|&lt;|g;
296     $s =~ s|>|&gt;|g;
297     $s =~ s|"|&quot;|g;
298     return $s;
299     }
300    
301     sub unescape {
302     my $s = shift;
303     $s =~ s|&lt;|<|g;
304     $s =~ s|&gt;|>|g;
305     $s =~ s|&quot;|"|g;
306     $s =~ s|&amp;|&|g;
307     return $s;
308     }
309    
310     sub convert_format ($$$;%) {
311     my ($content, $d => $t, %option) = @_;
312     my $f = SuikaWiki::Plugin->format_converter ($d => $t);
313     if (ref $f) {
314     $option{content} = $content;
315     $option{from} = $d;
316     $option{to} = $t;
317     &$f ({}, bless (\%option, 'SuikaWiki::Plugin'));
318     } elsif ($option{-error_no_return}) {
319     return undef;
320     } elsif ($t =~ /HTML|xml/) {
321     length $content ? '<pre>'.&escape($content).'</pre>' : '';
322     } else {
323     $content;
324     }
325     }
326    
327     sub make_wikilink ($%) {
328     my ($name, %option) = @_;
329     my $ename = &escape (length $option{label} ? $option{label} : $name);
330     $option{latest} = $option{latest} ? qq(mycmd=default;x-param=@{[time.[0..9]->[rand 10]]};mypage=) : '';
331 wakaba 1.2
332     ## Namespace
333 wakaba 1.3 #if ($SuikaWiki::Name::Space::VERSION) {
334     $name = SuikaWiki::Name::Space::normalize_name ( ## Foo// + .//Bar -> Foo////Bar
335     SuikaWiki::Name::Space::resolve_relative_name (
336     SuikaWiki::Name::Space::normalize_name ($option{base}, -might_be_ns_path => 1)
337     =>
338     SuikaWiki::Name::Space::normalize_name ($name)));
339     #}
340 wakaba 1.2 $name ||= $PageName{FrontPage};
341    
342 wakaba 1.1 if ($database{$name}) {
343 wakaba 1.2 my $subject = &escape ($name.&get_subjectline ($name));
344 wakaba 1.1 if ($option{anchor}) {
345     return qq(<a title="$subject" href="$uri{wiki}?$option{latest}@{[&encode($name)]}#anchor-$option{anchor}" class="wiki">$ename&gt;&gt;$option{anchor}</a>);
346     } else {
347     return qq(<a title="$subject" href="$uri{wiki}?$option{latest}@{[&encode($name)]}" class="wiki">$ename</a>);
348     }
349     } else {
350 wakaba 1.2 return qq(<a title="@{[&escape($name).&Resource('Title-Summary Delimiter',escape=>1).&Resource('JumpAndEditWikiPage',escape=>1)]}" href="$uri{wiki}?$option{latest}@{[&encode($name)]}" class="wiki not-exist">$ename<span class="mark">@{[&Resource('JumpAndEditWikiPageMark',escape=>1)]}</span></a>);
351 wakaba 1.1 }
352     }
353    
354     sub make_urilink ($;%) {
355     require URI;
356     my $uri = shift;
357     if ($uri =~ s/^IW://) { ## InterWiki (not URI)
358     $uri = &unescape ($uri);
359     if ($uri =~ /^([^\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+|"(?:\\.|[^"\\])+"):([^\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+|"(?:\\.|[^"\\])+")$/) {
360     my ($site, $name) = ($1, $2);
361     for ($site, $name) {
362     if (s/^"//) { s/"$//; s/\\(.)/$1/g }
363     }
364     &init_InterWikiName () unless $interwiki{'[[]]'};
365     if ($interwiki{$site}) {
366     &load_formatter ('interwiki');
367     my $uri = &escape ($fmt{interwiki}->replace ($interwiki{$site} => {site => $site, name => $name}));
368     $site = &escape ($site); $name = &escape ($name);
369     qq(&lt;<a href="$uri" class="out-of-wiki interwiki" title="$name ($site); URI: &lt;$uri&gt;"><span class="interwiki-site">$site:</span><span class="interwiki-name">$name</span></a>&gt;);
370     } else {
371     qq(&lt;@{[&Resource('Error:UnknownInterWikiName=',escape=>1)]}@{[&escape ($site)]}&gt;);
372     }
373     } else {
374     qq(&lt;@{[&Resource('Error:InvalidInterWiki=',escape=>1)]}@{[&escape($uri)]}&gt;);
375     }
376     } elsif ($uri =~ /^urn:/) { ## URN
377     my $uri2 = &escape (URI->new ('/uri-res/N2L?'.&unescape ($uri), 'http')->canonical);
378     qq(&lt;<a href="$uri2" title="URI: &lt;$uri&gt; (via &lt;$uri2&gt;)" class="out-of-wiki urn">$uri</a>&gt;);
379     } elsif ($uri =~ s/^MAIL://) { ## mail address (not URI)
380     my $uri2 = &escape (URI->new ('mailto:'.&unescape ($uri))->canonical);
381     qq(&lt;<a href="$uri2" class="out-of-wiki mail">$uri</a>&gt;);
382     } elsif ($uri =~ s/^IMG(?:\([^)]+\))?://) { ## image (not URI itself)
383     my $uri2 = &escape (URI->new (&unescape ($uri))->canonical);
384     qq(<img src="$uri2" alt="" title="URI: &lt;$uri2&gt;" class="out-of-wiki">);
385     } else { ## misc. URI
386     CGI::Carp::warningsToBrowser (0);
387     my $uri2 = &escape (URI->new (&unescape ($uri))->canonical);
388     CGI::Carp::warningsToBrowser (1);
389     qq(&lt;<a href="$uri2" title="URI: &lt;$uri2&gt;" class="out-of-wiki">$uri</a>&gt;);
390     }
391     }
392    
393     {my %FormIndex;
394     sub make_custom_form ($$$$%) {
395     my ($wfname, $definition, $template, $foption, $option) = @_;
396     ## $template is currently not used in this procedure.
397     #unless ($main::_EMBEDED) {
398     $FormIndex{$option->{page}}++;
399     if (length $definition) {
400     my $param = bless {depth=>10}, 'SuikaWiki::Plugin';
401     my $lastmodified = $database->mtime ($option->{page});
402     &load_formatter (qw/form_input form_option/);
403     $definition = &unescape ($definition);
404     $definition =~ s/\\(['\\])/$1/g;
405     $foption = &unescape ($foption);
406     $foption =~ s/\\(['\\])/$1/g;
407     $fmt{form_option}->replace ($foption, $param);
408     $param->{output}->{form} = 1 unless defined $param->{output}->{form};
409     $param->{output}->{form} = 0 if $main::_EMBEDED;
410     $definition .= ' %submit;' if $definition !~ /%submit/ && !$param->{output}->{nosubmit} && $param->{output}->{form};
411     $param->{output}->{page} ||= $option->{page};
412     $param->{form_disabled} = 1 if $database->meta (IsFrozen => $option->{page});
413     my $target_form = $param->{output}->{id};
414     my $r = '';
415     $r = <<EOH if $param->{output}->{form};
416     <form method="post" action="$url_cgi" id="wikiform-$FormIndex{$option->{page}}" class="wikiform">
417     <input type="hidden" name="mycmd" value="@{[$param->{form_disabled}?'default':'wikiform']}" />
418     <input type="hidden" name="mypage" value="@{[&escape($param->{output}->{page})]}" />
419     <input type="hidden" name="myLastModified" value="$lastmodified" />
420     <input type="hidden" name="mytouch" value="on" />
421     <input type="hidden" name="@{[$target_form? qq(wikiform_targetform" value="@{[&escape($target_form)]}) : qq(wikiform_index" value="$FormIndex{$option->{page}})]}" />
422     EOH
423     $r .= qq(<a name="wikiform-$FormIndex{$option->{page}}"></a>) if $UA =~ m#Mozilla/[12]\.#;
424     $r .= $fmt{form_input}->replace ($definition, $param);
425     $r .= "</form>\n" if $param->{output}->{form};
426     $r;
427     } else { ## No input-interface WikiForm
428     qq(<a id="wikiform-$FormIndex{$option->{page}}" name="wikiform-$FormIndex{$option->{page}}"><!-- #form --></a>);
429     }
430     #} else {
431     # qq(<ins class="wiki-error">@{[&Resource('Error:WikiForm:EmbedIsNotSupported',escape=>1)]}</ins>);
432     #}
433     }}
434    
435     sub init_form {
436     ## TODO: Support multipart/form-data
437     my $query = '';
438     if (uc $main::ENV{REQUEST_METHOD} eq 'POST') {
439     read STDIN, $query, $main::ENV{CONTENT_LENGTH};
440     }
441     $query .= ($query ? ';' : '') . $main::ENV{QUERY_STRING};
442     if ($main::ENV{REQUEST_METHOD} ne 'POST' && $main::ENV{QUERY_STRING} && $main::ENV{QUERY_STRING} !~ /[&;=]/) {
443     my $query = &decode($main::ENV{QUERY_STRING});
444     $query = &code_convert(\$query, $kanjicode);
445     $form{mypage} = $query;
446     $form{mycmd} = 'default';
447     } else {
448     for (split /[;&]/, $query) {
449     if (my ($n, $v) = split /=/, $_, 2) {
450     for ($n, $v) {tr/+/ /; s/%([0-9A-Fa-f][0-9A-Fa-f])/pack 'C', hex $1/ge};
451     $form{$n} = $v;
452     }
453     }
454     unless (defined $form{mypage}) {
455     $form{mypage} = $form{epage};
456     $form{mypage} =~ s/([0-9A-F]{2})/ord hex $1/g;
457     }
458     $form{mypage} = &code_convert (\$form{mypage}, $kanjicode);
459     }
460     $form{mypage} ||= $PageName{FrontPage};
461     $form{mypage} =~ tr/\x00-\x1F\x7F//d;
462 wakaba 1.3 $form{mypage} = SuikaWiki::Name::Space::normalize_name ($form{mypage});
463 wakaba 1.1 $form{mycmd} ||= 'default';
464 wakaba 1.4 $form{mycmd} =~ tr/-/_/;
465 wakaba 1.1
466     # mypreview_edit -> do_edit, with preview.
467     # mypreview_adminedit -> do_adminedit, with preview.
468     # mypreview_write -> do_write, without preview.
469     foreach (keys %form) {
470     if (/^mypreview_(.*)$/) {
471     $form{mycmd} = $1;
472     $form{mypreview} = 1;
473     }
474     }
475    
476     #
477     # $form{mycmd} is frozen here.
478     #
479    
480 wakaba 1.3 for (grep /^(?:wikiform__|pi_)/, keys %form) {
481 wakaba 1.1 $form{$_} = &code_convert (\$form{$_}, $kanjicode);
482     }
483     $form{mymsg} = &code_convert(\$form{mymsg}, $kanjicode);
484     $form{myname} = &code_convert(\$form{myname}, $kanjicode);
485     }
486    
487     sub get_subjectline {
488     my ($page, %option) = @_;
489     my $SubjectLine = SuikaWiki::Plugin->cache ('headline');
490     unless (defined $SubjectLine->{$page}) {
491     if (not &is_editable($page)) {
492     $SubjectLine->{$page} = "";
493     } else {
494     $SubjectLine->{$page} = do {
495     my $s=$database{$page};
496     $s =~ s!^\#\?[^\x0A\x0D]+[\x0A\x0D]*!!s;
497     $s =~ s/\x0D?\x0A.*//s;
498     $s =~ s/^[-=]*\s*\[\d+\]\s*//;
499     $s =~ s/'''?//g;
500     $s =~ s/\[[A-Z]+(?:\([^)]+\))?\[([^]]+)\](?:\s\[([^]]+)\])?\]/$1$2/g;
501     $s =~ s/\[\[([^]]+)\]\]/$1/g;
502     $s};
503     }
504     }
505     if (length $SubjectLine->{$page}) {
506     $option{delimiter} = defined $option{delimiter} ? $option{delimiter} : &Resource('Title-Summary Delimiter');
507     $option{delimiter}.$SubjectLine->{$page}.$option{tail};
508     } else {
509     '';
510     }
511     }
512    
513     sub open_db {
514     if ($modifier_dbtype eq 'dbmopen') {
515     dbmopen(%database, $PathTo{WikiDataBase}, 0666) or die "(dbmopen) $PathTo{WikiDataBase}";
516     } elsif ($modifier_dbtype eq 'AnyDBM_File') {
517     eval q{use AnyDBM_File};
518     tie(%database, "AnyDBM_File", $PathTo{WikiDataBase}, O_RDWR|O_CREAT, 0666) or die ("(tie AnyDBM_File) $PathTo{WikiDataBase}");
519     } elsif ($modifier_dbtype eq 'Yuki::YukiWikiDB') {
520     eval q{use Yuki::YukiWikiDB};
521     tie(%database, "Yuki::YukiWikiDB", $PathTo{WikiDataBase}) or die ("(tie Yuki::YukiWikiDB) $PathTo{WikiDataBase}");
522     } else { ## Yuki::YukiWikiDB || Yuki::YukiWikiDBMeta
523     eval qq{use $modifier_dbtype};
524     $database = tie(%database, $modifier_dbtype => $PathTo{WikiDataBase}, -lock => 2, -backup => $wiki::diff::UseDiff) or die ("(tie $modifier_dbtype) $PathTo{WikiDataBase}");
525     }
526     }
527    
528     sub close_db {
529     if ($modifier_dbtype eq 'dbmopen') {
530     dbmclose(%database);
531     } else {
532     untie(%database);
533     }
534     }
535    
536     sub editform (@) {
537     my %option = @_;
538     my $frozen = &is_frozen ($option{page});
539     $option{content} = $database{$option{page}} unless defined $option{content};
540     $option{content} = $database{NewPageTemplate} unless length $option{content};
541     $option{last_modified} = $database->mtime ($option{page}) unless defined $option{last_modified};
542     my $f = '';
543     my $magic = '';
544     $magic = $1 if $option{content} =~ m/^([^\x0A\x0D]+)/s;
545    
546     my $selected = 'default';
547     if ($form{after_edit_cmd}) {
548     $selected = $form{after_edit_cmd};
549     } elsif ($magic =~ /Const|Config|CSS/) {
550     $selected = 'edit';
551     }
552     my $afteredit = <<EOH;
553     <select name="after_edit_cmd">
554     <option value="default" label="@{[&Resource('Edit:SaveAndDefault',escape=>1)]}"@{[$selected eq 'default' ? ' selected="selected"':'']}>@{[&Resource('Edit:SaveAndDefault',escape=>1)]}</option>
555     <option value="read" label="@{[&Resource('Edit:SaveAndView',escape=>1)]}"@{[$selected eq 'read' ? ' selected="selected"':'']}>@{[&Resource('Edit:SaveAndView',escape=>1)]}</option>
556     <option value="edit" label="@{[&Resource('Edit:SaveAndEdit',escape=>1)]}"@{[$selected eq 'edit' ? ' selected="selected"':'']}>@{[&Resource('Edit:SaveAndEdit',escape=>1)]}</option>
557     </select>
558     EOH
559     $f .= <<"EOD";
560     <form action="$uri{wiki}" method="post">
561     @{[ $option{conflict} ? '' : qq(<label><input type="submit" name="mypreview_write" value="@{[&Resource('Edit:Save',escape=>1)]}" /><kbd>S</kbd></label>) ]}
562     @{[ $option{admin} ? qq(<label>@{[&Resource('Edit:Password=',escape=>1)]}<input type="password" name="mypassword" value="" size="10" /></label>) : "" ]} [@{[&get_new_anchor_index($option{content})]}]<br />
563     <input type="hidden" name="myLastModified" value="$option{last_modified}" />
564     <input type="hidden" name="mypage" value="@{[&escape($form{mypage})]}" />
565     <textarea cols="@{[&Resource('Edit:Form:Cols')+0||80]}" rows="@{[&Resource('Edit:Form:Rows')+0||20]}" name="mymsg" tabindex="1">@{[&escape($option{content})]}</textarea><br />
566     @{[
567     $option{admin} ?
568     qq(
569     <label><input type="radio" name="myfrozen" value="1" @{[$frozen ? qq(checked="checked") : ""]} />@{[&Resource('Edit:Freeze',escape=>1)]}</label>
570     <label><input type="radio" name="myfrozen" value="0" @{[$frozen ? "" : qq(checked="checked")]} />@{[&Resource('Edit:DontFreeze',escape=>1)]}</label><br />)
571     : ""
572     ]}
573     @{[
574     $option{conflict} ? "" :
575     qq(
576     <label><input type="checkbox" name="mytouch" value="on" checked="checked" />@{[&Resource('Edit:UpdateTimeStamp',escape=>1)]}</label><br />
577     <label><input type="submit" name="mypreview_write" value="@{[&Resource('Edit:Save',escape=>1)]}" accesskey="S" /><kbd>S</kbd></label>
578     $afteredit
579     )
580     ]}
581     </form>
582     EOD
583     $f;
584     }
585    
586     sub is_editable {
587     my ($page) = @_;
588 wakaba 1.4 return 0 unless SuikaWiki::Name::Space::validate_name ($page);
589     return 0 if $page =~ /[\x00-\x20\[\]\x7F]/;
590 wakaba 1.2 1;
591 wakaba 1.1 }
592    
593     sub decode {
594     my ($s) = @_;
595     $s =~ tr/+/ /;
596     $s =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
597     return $s;
598     }
599    
600     sub encode {
601     my $s = shift;
602     $s =~ s/([^0-9A-Za-z_-])/sprintf '%%%02X', ord $1/ge;
603     $s;
604     }
605    
606     sub get_now {
607     my ($sec, $min, $hour, $day, $mon, $year) = localtime(time);
608     $year += 1900;
609     $mon++;
610     $mon = "0$mon" if $mon < 10;
611     $day = "0$day" if $day < 10;
612     $hour = "0$hour" if $hour < 10;
613     $min = "0$min" if $min < 10;
614     #$sec = "0$sec" if $sec < 10;
615     return "$year-$mon-$day $hour:$min";
616     }
617    
618     sub init_InterWikiName {
619     my @content = split /\n/, $database{InterWikiName};
620     for (@content) {
621     if (/^([^#]\S*)\s+(\S[^\x0A\x0D]+)/) {
622     $interwiki{$1} = $2;
623     }
624     }
625     $interwiki{'[[]]'} = 1; ## dummy
626     }
627    
628     sub frozen_reject {
629     my ($isfrozen) = $database->meta (IsFrozen => $form{mypage});
630     my ($willbefrozen) = $form{myfrozen};
631     if (not $isfrozen and not $willbefrozen) {
632     # You need no check.
633     return 0;
634     } elsif (valid_password($form{mypassword})) {
635     # You are admin.
636     return 0;
637     } else {
638     &_do_view_msg (-view => '-error', -page => $form{mypage},
639     error_message => &Resource ('Error:PasswordIsIncorrect'));
640     exit;
641     }
642     }
643    
644     sub is_frozen ($) { $database->meta (IsFrozen => $_[0]) ? 1 : 0 }
645    
646     sub do_comment {
647     my ($content) = $database{$form{mypage}};
648     my $default_name; ## this code is not strict.
649     $default_name = $1 if $content =~ /default-name="([^"]+)"/;
650     my $datestr = '[WEAK['.&get_now.']]';
651     my $namestr = $form{myname} || $default_name || &Resource('WikiForm:WikiComment:DefaultName');
652     ($namestr = '', $datestr = '') if $form{myname} eq 'nodate';
653     if ($namestr =~ /^(?:>>)?[0-9]/) {
654     $namestr = qq( ''$namestr'': );
655     } elsif (length $namestr) {
656     $namestr = qq( ''[[$namestr]]'': );
657     }
658     my $anchor = &get_new_anchor_index ($content);
659     my $i = 1; my $o = 0;
660     $content =~ s{(\[\[\#r?comment\]\])}{
661     my $embed = $1;
662     if ($i == $form{comment_index}) {
663     if ($embed ne '[[#rcomment]]') {
664     $embed = "- [$anchor] $datestr$namestr$form{mymsg}\n$embed"; $o = 1;
665     } else {
666     $embed .= "\n- [$anchor] $datestr$namestr$form{mymsg}"; $o = 1;
667     }
668     }
669     $i++; $embed;
670     }ge;
671     unless ($o) {
672     $content = "#?SuikaWiki/0.9\n\n" unless $content;
673     $content .= "\n" unless $content =~ /\n$/s;
674     $content .= "- [$anchor] $datestr$namestr$form{mymsg}\n";
675     }
676     $form{__comment_anchor_index} = $anchor;
677     if ($form{mymsg} || $form{myname}) {
678     $form{mymsg} = $content;
679     $form{mytouch} = 'on';
680     &do_write;
681     } else { ## Don't write
682     $form{mycmd} = 'default';
683     &do_view;
684     }
685     }
686    
687     sub get_new_anchor_index ($) {
688     my $content = shift;
689     my $anchor = 0;
690     $content =~ s/^(?:[-=]+\s*)?\[([0-9]+)\]/$anchor = $1 if $1 > $anchor; $&/mge;
691     $anchor + 1;
692     }
693    
694     sub load_formatter (@) {
695 wakaba 1.4 my $x = SuikaWiki::Plugin->feature ('SuikaWiki::Markup::XML');
696 wakaba 1.1 for my $t (@_) {
697     unless ($fmt{$t}) {
698     require Message::Util::Formatter;
699     $fmt{$t} = Message::Util::Formatter->new;
700     for (@{$SuikaWiki::Plugin::List{'wiki'.$t}||[]}) {
701     $_->load_formatter ($fmt{$t}, type => 'wiki'.$t);
702     }
703 wakaba 1.4 $fmt{$t}->option (return_class => 'SuikaWiki::Markup::XML') if $x;
704 wakaba 1.1 }
705     }
706     }
707    
708     sub do_wikiform {
709     my $content = $database{$form{mypage}};
710     my $anchor = &get_new_anchor_index ($content);
711     &load_formatter (qw/form_template form_option/);
712     my $write = 0;
713     my $i = 1;
714     $content =~ s{$embed_command{form}}{
715     my ($embed, $wfname, $template, $option) = ($&, $1, $3, $4);
716     if (($wfname && $wfname eq $form{wikiform_targetform})
717     || $i == $form{wikiform_index}) {
718     $template =~ s/\\([\\'])/$1/g;
719     $option =~ s/\\([\\'])/$1/g;
720     my $param = bless {depth=>10}, 'SuikaWiki::Plugin';
721     $param->{page} = $form{mypage};
722     $param->{form_index} = $i;
723     $param->{form_name} = $wfname;
724     $param->{anchor_index} = $anchor;
725     $param->{argv} = \%form;
726     $param->{default_name} = $1 if $content =~ /default-name="([^"]+)"/;
727     $param->{default_name} ||= &Resource('WikiForm:WikiComment:DefaultName');
728     $fmt{form_option}->replace ($option, $param);
729     my $t = 1;
730     for (keys %{$param->{require}||{}}) {
731     (undef $t, last) unless length $param->{argv}->{'wikiform__'.$_};
732     }
733     $t = $fmt{form_template}->replace ($template, $param) if $t;
734     if (length $t) {
735     if ($param->{output}->{reverse}) {
736     $embed .= "\n" . $t;
737     } else {
738     $embed = $t . "\n" . $embed;
739     }
740     $write = 1;
741     $form{__comment_anchor_index} = $anchor
742     if $param->{anchor_index_}; ## $anchor is used!
743     }
744     $form{__wikiform_anchor_index} = $i;
745     undef $form{wikiform_targetform}; ## Make sure never to match
746     undef $form{wikiform_index}; ## with WikiForm in rest of page!
747     }
748     $i++; $embed;
749     }ge;
750     unless ($write) {
751     #$content = "#?SuikaWiki/0.9\n\n" unless $content;
752     #$content .= "\n" unless $content =~ /\n$/s;
753     #
754     }
755     if ($write) {
756     $form{mymsg} = $content;
757     $form{mytouch} = 'on';
758     &do_write;
759     } else { ## Don't write!
760     $form{mycmd} = 'default';
761     &do_view;
762     }
763     }
764    
765     sub code_convert {
766     require Jcode;
767     my ($contentref, $code) = (shift, shift || $kanjicode);
768     if ($code =~ /euc/) { $code = 'euc' }
769     elsif ($code =~ /iso/) { $code = 'jis' }
770     elsif ($code =~ /shi/) { $code = 'sjis' }
771     elsif ($code =~ /utf/) { $code = 'utf8' }
772     $$contentref = Jcode->new ($contentref)->tr ("\xA3\xB0-\xA3\xB9\xA3\xC1-\xA3\xDA\xA3\xE1-\xA3\xFA\xA1\xF5\xA1\xA4\xA1\xA5\xA1\xA7\xA1\xA8\xA1\xA9\xA1\xAA\xA1\xAE\xA1\xB0\xA1\xB2\xA1\xBF\xA1\xC3\xA1\xCA\xA1\xCB\xA1\xCE\xA1\xCF\xA1\xD0\xA1\xD1\xA1\xDC\xA1\xF0\xA1\xF3\xA1\xF4\xA1\xF6\xA1\xF7\xA1\xE1\xA2\xAF\xA2\xB0\xA2\xB2\xA2\xB1\xA1\xE4\xA1\xE3\xA1\xC0\xA1\xA1" => q(0-9A-Za-z&,.:;?!`^_/|()[]{}+$%#*@='"~-><\ ))->$code;
773     return $$contentref;
774     }
775    
776     sub _rfc3339_date ($) {
777     my @time = gmtime (shift);
778     sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00', $time[5]+1900,$time[4]+1,@time[3,2,1,0];
779     }
780    
781     my %_Resource;
782     sub Resource ($;%) {
783     my ($s, %o) = @_;
784     unless (defined $_Resource{$s}) {
785     $_Resource{$_[0]} = &wiki::resource::get ($s, $_Resource{__option});
786     }
787     $o{escape} ? &escape ($_Resource{$s}) : $_Resource{$s};
788     }
789    
790     package wiki::referer;
791     sub add ($$) {
792     my $page = shift;
793     my $uri = shift;
794     unless (ref $uri) {
795     require URI;
796     $uri = URI->new ($uri);
797     ## Some schemes do not have query part.
798     eval q{ $uri->query (undef) if $uri->query =~ /^[0-9]{6,8}$/ };
799     $uri->fragment (undef);
800     }
801     $uri = $uri->canonical;
802     return unless $uri;
803     for my $regex (&get_dont_record) {
804     return if $uri =~ /$regex/;
805     }
806     my %list = get ($page);
807     $list{ $uri }++;
808     set ($page, \%list);
809     }
810     sub get ($) { split /"/, $main::database->meta (Referer => $_[0]) }
811     sub set ($%) {
812     my $page = shift;
813     my $list = shift;
814     $main::database->meta (Referer => $page => join '"', %$list);
815     }
816    
817     sub get_dont_record () {
818     map {s/\$/\\\$/g; s/\@/\\\@/g; $_}
819     grep !/^#/,
820     split /[\x0D\x0A]+/, $main::database{RefererDontRecord};
821     }
822     sub get_site_name () {
823     my @lines = grep /[^#]/, split /[\x0D\x0A]+/, $main::database{RefererSiteName};
824     my @item;
825     for (@lines) {
826     next if /^#/;
827     my ($uri, $name) = split /\s+/, $_, 2;
828     $uri =~ s/\$/\\\$/g; $uri =~ s/\@/\\\@/g; $uri =~ s/\//\\\//g;
829     $name =~ s!([()/\\])!\\$1!g; $name =~ s/\$([0-9]+)/).__decode (\${$1}).q(/g;
830     push @item, [$uri, qq(q($name))];
831     }
832     @item;
833     }
834    
835     sub list_html ($) {
836     my $page = shift;
837     my %list = get ($page);
838     my $r = '';
839     my @name = get_site_name ();
840     for my $uri (sort {$list{$b}<=>$list{$a}||$a cmp $b} keys %list) {
841     my $title;
842     for my $item (@name) {
843     if ($uri =~ /$item->[0]/) {
844     $title = $uri;
845     eval qq{\$title =~ s/^.*$item->[0].*\$/$item->[1]/e}
846     or die $@ ;#. qq{\$title =~ s/^.*$item->[0].*\$/$item->[1]/e};
847     last;
848     }
849     }
850     my $euri = main::escape ($uri);
851     if ($title) {
852     $r .= qq(<li>{$list{$uri}} <a href="$euri" title="URI: &lt;$euri&gt;">@{[main::escape ($title)]}</a></li>\n);
853     } else {
854     $r .= qq(<li>{$list{$uri}} &lt;<a href="$euri">$euri</a>&gt;</li>\n);
855     }
856     }
857     $r ? qq(<ul>$r</ul>\n) : '';
858     }
859    
860     sub __decode ($) {
861     my $s = shift;
862     $s =~ tr/+/ /;
863     $s =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/chr hex $1/ge;
864     main::code_convert (\$s);
865     }
866    
867     package wiki::useragent;
868     our $UseLog;
869    
870     sub add ($) {
871     my $s = shift;
872     return unless length $s;
873     return unless $UseLog;
874     $s =~ s/([\x00-\x08\x0A-\x1F\x25\x7F-\xFF])/sprintf '%%%02X', unpack 'C', $1/ge;
875     my %ua;
876     for (split /\n/, $main::database{$main::PageName{UserAgentList}}) {
877     if (/^-\[(\d+)\] (.+)$/) {
878     my ($t, $n) = ($1, $2);
879     $n =~ tr/\x0A\x0D//d;
880     $ua{$n} = $t;
881     }
882     }
883     $ua{$s}++;
884     my $s = qq(#?SuikaWiki/0.9\n);
885     for (sort {$ua{$a} <=> $ua{$b}} keys %ua) {
886     $s .= sprintf qq(-[%d] %s\n), $ua{$_}, $_;
887     }
888     $main::database->STORE ($main::PageName{UserAgentList} => $s, -touch => 0);
889     }
890    
891     package wiki::suikawikiconst;
892    
893     sub to_hash ($;$) {
894     my $page = shift;
895     my $h = shift || {};
896     my $val;
897     for my $line (split /\n/, $page) {
898     next if $line =~ /^#/;
899     $line =~ tr/\x0A\x0D//d;
900     if ($val && $line =~ s/^\s+\\?//) {
901     $h->{$val} .= length $h->{$val} ? "\n" . $line : $line;
902     } elsif ($line =~ /^(.+):/) {
903     $val = $1; $h->{$val} = '';
904     }
905     }
906     $h;
907     }
908    
909     package wiki::dummy;
910     sub mtime (@) {undef}
911     sub meta (@) {undef}
912     sub Yuki::YukiWikiDB2::meta (@) {undef}
913    
914     package SuikaWiki::Plugin;
915     sub escape ($$) { main::escape ($_[1]) }
916     sub unescape ($$) { main::unescape ($_[1]) }
917     sub encode ($$) { main::encode ($_[1]) }
918     sub decode ($$) { main::decode ($_[1]) }
919     sub __get_datetime ($) { main::get_now () }
920     sub resource ($$;%) { shift; &main::Resource (@_) }
921     sub uri ($$) { $main::uri{$_[1]} }
922     sub user_agent_names ($) { $main::UA }
923 wakaba 1.4 sub _path_to ($$) { $main::PathTo{$_[1]} }
924 wakaba 1.3
925 wakaba 1.1 sub formatter ($$) {
926     &main::load_formatter ($_[1]);
927     $main::fmt{$_[1]};
928     }
929     sub format_converter ($$$) {
930     &main::load_formatter ('format');
931     $main::fmt{format}->{($_[1]=~/([A-Za-z0-9]\S+)/?$1:'SuikaWiki/0.9').'_to_'.$_[2]}
932     || $main::fmt{format}->{($_[1]=~/([A-Za-z0-9](?:(?!\/)\S)+)/?$1:'SuikaWiki').'_to_'.$_[2]};
933     }
934 wakaba 1.4 sub formatter_replace_if_not_parsed_yet ($$$$;$) {
935     my ($o, $context, $p, $attr_name, $option) = @_;
936 wakaba 1.3 &main::load_formatter ($context);
937 wakaba 1.4 if ((ref $p->{$attr_name} && $p->{$attr_name}->flag ('parsed'))
938     || (!ref $p->{$attr_name} && index ($p->{-option}->{$attr_name}, 'p') > -1)) {
939     $p->{$attr_name}; ## Already parsed
940     } else {
941     $main::fmt{$context}->replace ($p->{$attr_name}, $o, {formatter => $main::fmt{$context}});
942     }
943 wakaba 1.3 }
944    
945 wakaba 1.1 sub cache ($$) {
946     our %Cache;
947     my (undef, $name, %option) = @_;
948     unless (ref $Cache{$name}) {
949     my %cache;
950     tie (%cache, 'Yuki::YukiWikiCache', -file => $main::PathTo{CachePrefix}.$name, %option);
951     $Cache{$name} = \%cache;
952     }
953     $Cache{$name};
954     }
955     sub _database ($) { $main::database }
956     sub _database_exist ($$) { exists $main::database{$_[1]} }
957     sub _html_wikilink ($$%) { shift; &main::make_wikilink (@_) }
958     sub _uri_wiki_page ($$%) {
959     my (undef, $page, %option) = @_;
960     $option{mode} ||= 'read';
961     length $page ? undef : ($page = $main::PageName{FrontPage});
962     $option{href} = $main::uri{wiki}.'?';
963     if ($option{up_to_date} || $option{mode} ne 'read' || $option{add_param}) {
964     $option{href} .= qq(mypage=@{[&main::encode($page)]};mycmd=@{[&main::encode($option{mode})]});
965     $option{href} .= ';'.$option{add_param} if $option{add_param};
966     $option{href} .= ';x-d='.time if $option{up_to_date};
967     $option{href} .= ';x-lm='.($main::database->mtime ($page)||0) if $option{with_lm};
968     } else {
969     $option{href} .= &main::encode ($page);
970     }
971     $option{href};
972     }
973    
974    
975     package wiki::conneg;
976    
977     ## BUG: this parser isn't strict.
978     sub get_accept_lang (;$) {
979     my $alang = shift || $main::ENV{HTTP_ACCEPT_LANGUAGE};
980     my %alang = (ja => 0.0002, en => 0.0001);
981     if ($main::UA =~ m#Mozilla/0\.#) {
982     $alang{ja} = 0.00001;
983     }
984     my $i = 0.1;
985     for (split /\s*,\s*/, $alang) {
986     tr/\x09\x0A\x0D\x20//d;
987     if (/((?:(?!;q=).)+)(?:;q="?([0-9.]+)"?)?/) {
988     my $l = lc $1; $l =~ tr/\x22\x5C//d;
989     $alang{$l} = (defined $2 ? $2 : 1.000)*1000;
990     $alang{$l} += $i unless $alang{$l} == 0;
991     $i -= 0.001;
992     }
993     }
994     \%alang;
995     }
996    
997     package wiki::resource;
998    
999     sub get ($;\%) {
1000     my ($resname, $option) = @_;
1001     $option->{accept_language} ||= &wiki::conneg::get_accept_lang ();
1002     $option->{resource} ||= {};
1003     my $v;
1004     for my $lang (sort {$option->{accept_language}->{$b} <=> $option->{accept_language}->{$a}} grep {$option->{accept_language}->{$_}!=0} keys %{$option->{accept_language}}) {
1005     while (length $lang) {
1006     unless ($option->{accept_language}->{defined $option->{accept_language}->{$lang} ? $lang : '*'} == 0) {
1007 wakaba 1.3 $option->{resource}->{$lang} ||= &wiki::suikawikiconst::to_hash ($main::database{$main::PageName{ResourceNS}.$lang});
1008 wakaba 1.1 $v = $option->{resource}->{$lang}->{$resname};
1009     last if defined $v;
1010     }
1011     $lang =~ s/[^+-]*$//; $lang =~ s/[+-]$//;
1012     }
1013     last if defined $v;
1014     }
1015     defined $v ? $v : $resname;
1016     }
1017    
1018     package main;
1019     &SuikaWiki::Plugin::import_plugins ();
1020     &main ();
1021    
1022     =head1 NAME
1023    
1024 wakaba 1.4 lib/suikawiki.pl --- SuikaWiki transitional library
1025 wakaba 1.1
1026     =head1 AUTHOR
1027    
1028 wakaba 1.4 Hiroshi Yuki <hyuki@hyuki.com> <http://www.hyuki.com/yukiwiki/> (YukiWiki)
1029 wakaba 1.1
1030 wakaba 1.4 Makio Tsukamoto <http://digit.que.ne.jp/> (WalWiki)
1031 wakaba 1.1
1032     Wakaba <w@suika.fam.cx>
1033    
1034     =head1 LICENSE
1035    
1036 wakaba 1.4 Copyright AUTHORS 2000-2003
1037 wakaba 1.1
1038     This program is free software; you can redistribute it and/or
1039     modify it under the same terms as Perl itself.
1040    
1041     =cut
1042    
1043 wakaba 1.4 1; # $Date: 2003/04/03 01:09:07 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24