| blib/lib/CGI/pWiki.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 17 | 326 | 5.2 |
| branch | 1 | 170 | 0.5 |
| condition | 1 | 34 | 2.9 |
| subroutine | 5 | 29 | 17.2 |
| pod | 1 | 25 | 4.0 |
| total | 25 | 584 | 4.2 |
| line | stmt | bran | cond | sub | pod | time | code | |
|---|---|---|---|---|---|---|---|---|
| 1 | #!/usr/bin/perl | |||||||
| 2 | ||||||||
| 3 | 1 | 1 | 7135 | use 5.00503; | ||||
| 1 | 4 | |||||||
| 1 | 51 | |||||||
| 4 | package CGI::pWiki; | |||||||
| 5 | 1 | 1 | 5 | use strict; | ||||
| 1 | 2 | |||||||
| 1 | 39 | |||||||
| 6 | 1 | 1 | 1125 | use URI::Escape qw(uri_escape uri_unescape); | ||||
| 1 | 1529 | |||||||
| 1 | 126 | |||||||
| 7 | 1 | 1 | 6 | use vars qw($VERSION); $VERSION = "0.15"; | ||||
| 1 | 1 | |||||||
| 1 | 5246 | |||||||
| 8 | ||||||||
| 9 | #------------------------------------------------------------------------------# | |||||||
| 10 | ||||||||
| 11 | =pod | |||||||
| 12 | ||||||||
| 13 | =head1 NAME | |||||||
| 14 | ||||||||
| 15 | CGI::pWiki - Perl Wiki Environment | |||||||
| 16 | ||||||||
| 17 | =head1 SYNOPSIS | |||||||
| 18 | ||||||||
| 19 | #!/usr/bin/perl | |||||||
| 20 | use CGI::pWiki; | |||||||
| 21 | use strict; | |||||||
| 22 | my $pWiki = new CGI::pWiki()->server(); | |||||||
| 23 | 0; | |||||||
| 24 | ||||||||
| 25 | =head1 DESCRIPTION | |||||||
| 26 | ||||||||
| 27 | The B |
|||||||
| 28 | a WikiWikiWeb for virtual hosts and multiple databases. | |||||||
| 29 | ||||||||
| 30 | =head1 USAGE | |||||||
| 31 | ||||||||
| 32 | =head2 Installation | |||||||
| 33 | ||||||||
| 34 | At first install the CGI::pWiki module either on the CPAN, | |||||||
| 35 | or the Debian or by hand as usual with : | |||||||
| 36 | ||||||||
| 37 | perl Makefile.PL && | |||||||
| 38 | make && | |||||||
| 39 | make test && | |||||||
| 40 | su -c "make install" | |||||||
| 41 | ||||||||
| 42 | First check your /etc/apache/httpd.conf for the system wide | |||||||
| 43 | ScriptAlias path and directory path. | |||||||
| 44 | ||||||||
| 45 | ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ | |||||||
| 46 | ||||||||
| 47 | The pWiki distibution includes a pWiki.cgi to be symlinked | |||||||
| 48 | from your install point to your system wide cgi-bin directory. | |||||||
| 49 | ||||||||
| 50 | ln -s /usr/local/bin/pWiki.cgi /usr/lib/cgi-bin/ | |||||||
| 51 | ||||||||
| 52 | Next check your /etc/apache/httpd.conf to contain at least | |||||||
| 53 | those modules : | |||||||
| 54 | ||||||||
| 55 | LoadModule mime_module /usr/lib/apache/1.3/mod_mime.so | |||||||
| 56 | LoadModule dir_module /usr/lib/apache/1.3/mod_dir.so | |||||||
| 57 | LoadModule cgi_module /usr/lib/apache/1.3/mod_cgi.so | |||||||
| 58 | LoadModule alias_module /usr/lib/apache/1.3/mod_alias.so | |||||||
| 59 | LoadModule access_module /usr/lib/apache/1.3/mod_access.so | |||||||
| 60 | LoadModule auth_module /usr/lib/apache/1.3/mod_auth.so | |||||||
| 61 | LoadModule setenvif_module /usr/lib/apache/1.3/mod_setenvif.so | |||||||
| 62 | LoadModule action_module /usr/lib/apache/1.3/mod_actions.so | |||||||
| 63 | ||||||||
| 64 | Add a virtual host directive : | |||||||
| 65 | ||||||||
| 66 | NameVirtualHost * | |||||||
| 67 | |
|||||||
| 68 | ServerName test.copyleft.de | |||||||
| 69 | DocumentRoot /var/www/test.copyleft.de | |||||||
| 70 | DirectoryIndex index.wiki index.xml index.html index.htm index.text | |||||||
| 71 | Action wiki-script /cgi-bin/pWiki.cgi | |||||||
| 72 | # Some Apaches need the next line, also. | |||||||
| 73 | # ErrorDocument 404 /cgi-bin/pWiki.cgi | |||||||
| 74 | ||||||||
| 75 | AddHandler wiki-script .wiki | |||||||
| 76 | AddHandler wiki-script .text | |||||||
| 77 | AddHandler wiki-script .html | |||||||
| 78 | AddHandler wiki-script .htm | |||||||
| 79 | AddHandler wiki-script .pod | |||||||
| 80 | AddHandler wiki-script .xml | |||||||
| 81 | # The next line should be in 127.0.0.1 virtual hosts, only ! | |||||||
| 82 | # AddHandler wiki-script .xsl | |||||||
| 83 | ||||||||
| 84 | ||||||||
| 85 | There is no need to add any handler besides B<.wiki> and B<.text>, | |||||||
| 86 | if you dont want to manage the other files with B |
|||||||
| 87 | Handling B<.xsl> files in fact opens a wide security hole, and should | |||||||
| 88 | B |
|||||||
| 89 | ||||||||
| 90 | =head2 Security | |||||||
| 91 | ||||||||
| 92 | CGI::pWiki will offer users from outside to write files in the | |||||||
| 93 | document root of your webserver. It is therefore a possible | |||||||
| 94 | security hole. The minimal security is to constrain write access | |||||||
| 95 | by using the Unix C |
|||||||
| 96 | ||||||||
| 97 | mkdir /var/www/test.copyleft.de | |||||||
| 98 | echo "=location /open/index.wiki" /var/www/test.copyleft.de/index.wiki | |||||||
| 99 | mkdir /var/www/test.copyleft.de/open | |||||||
| 100 | touch /var/www/test.copyleft.de/open/index.wiki | |||||||
| 101 | chmod a+w /var/www/test.copyleft.de/open | |||||||
| 102 | chmod a+w /var/www/test.copyleft.de/open/index.wiki | |||||||
| 103 | ||||||||
| 104 | This will create a document root for the test site, installs | |||||||
| 105 | a relocation of the index page, and creates an open area and | |||||||
| 106 | its index page, and makes it world writeable, while other | |||||||
| 107 | areas will stay read only. | |||||||
| 108 | ||||||||
| 109 | A typical all public site for creating open content may want | |||||||
| 110 | to allow every directory to be writeable. Adopt the following | |||||||
| 111 | lines to migrate existing content. | |||||||
| 112 | ||||||||
| 113 | find /var/www/test.copyleft.de/ -print | xargs sudo chown kraehe.www-data | |||||||
| 114 | find /var/www/test.copyleft.de/ -type d -print | xargs chmod 6775 | |||||||
| 115 | find /var/www/test.copyleft.de/ ! -type d -print | xargs chmod 664 | |||||||
| 116 | ||||||||
| 117 | You may want to restrict edit access to the Wiki as a webmaster | |||||||
| 118 | by defining a directory directive : | |||||||
| 119 | ||||||||
| 120 | |
|||||||
| 121 | AuthUserFile /usr/local/etc/test.copyleft.de.htpasswd | |||||||
| 122 | AuthName "For Test Only" | |||||||
| 123 | AuthType Basic | |||||||
| 124 | |
|||||||
| 125 | require valid-user | |||||||
| 126 | ||||||||
| 127 | ||||||||
| 128 | ||||||||
| 129 | Or leave this as an option for .htaccess : | |||||||
| 130 | ||||||||
| 131 | AuthUserFile /usr/local/etc/test.copyleft.de.htpasswd | |||||||
| 132 | AuthName "For Test Only" | |||||||
| 133 | AuthType Basic | |||||||
| 134 | |
|||||||
| 135 | require valid-user | |||||||
| 136 | ||||||||
| 137 | ||||||||
| 138 | =head2 First Test | |||||||
| 139 | ||||||||
| 140 | You can now test the pWiki by reloading Apache. Create a directories | |||||||
| 141 | for your virtual host to contain a database called pWiki. The second | |||||||
| 142 | directory needs to be writeable by the webserver, as it contains the | |||||||
| 143 | shadow pages, if people change the content online. | |||||||
| 144 | ||||||||
| 145 | mkdir -p /var/www/test.copyleft.de/pWiki | |||||||
| 146 | mkdir -p /var/lib/pWiki/test.copyleft.de/pWiki | |||||||
| 147 | chmod a+w /var/lib/pWiki/test.copyleft.de/pWiki | |||||||
| 148 | ||||||||
| 149 | Browse at your fresh created test site and enter the URL : | |||||||
| 150 | ||||||||
| 151 | http://test.copyleft.de/pWiki/index.wiki | |||||||
| 152 | ||||||||
| 153 | This should show an edit window. Submit something like the following : | |||||||
| 154 | ||||||||
| 155 | This is a test for pWiki. | |||||||
| 156 | ||||||||
| 157 | Click on the pWiki and submit the following : | |||||||
| 158 | ||||||||
| 159 | The CGI_pWiki Perl_Module is an Apache_Handler acting as a | |||||||
| 160 | wrapper around a WikiWikiWeb for creating content in a | |||||||
| 161 | [comunity] on the fly. | |||||||
| 162 | ||||||||
| 163 | Benefits : | |||||||
| 164 | ||||||||
| 165 | * rapid content creation | |||||||
| 166 | * easy formatting rules | |||||||
| 167 | * multiple authors | |||||||
| 168 | ||||||||
| 169 | CGI_pWiki is able to handle the following extensions : | |||||||
| 170 | ||||||||
| 171 | | .html | normal hypertext pages | | |||||||
| 172 | | .text | preformated text pages | | |||||||
| 173 | | .wiki | pWiki formated hypertext pages | | |||||||
| 174 | | .xml | XSL formated hypertext pages | | |||||||
| 175 | | .pod | PlainOldDocumentation | | |||||||
| 176 | ||||||||
| 177 | Ensure that there are no leading white space when cut and paste. | |||||||
| 178 | ||||||||
| 179 | =head2 Adding Style | |||||||
| 180 | ||||||||
| 181 | The CGI-pWiki distribution contains an example database. | |||||||
| 182 | Copy it to your document root : | |||||||
| 183 | ||||||||
| 184 | cp htdocs/pWiki/* /var/www/test.copyleft.de/pWiki/ | |||||||
| 185 | ||||||||
| 186 | The style is defined in pairs of files with B<.lnx> and B<.moz> | |||||||
| 187 | extension. Copy the pWiki/content.{lnx,moz}-exam files to your | |||||||
| 188 | document root and define the main table of contents. | |||||||
| 189 | ||||||||
| 190 | =head2 METHODS | |||||||
| 191 | ||||||||
| 192 | =over | |||||||
| 193 | ||||||||
| 194 | =item new proto HASH | |||||||
| 195 | ||||||||
| 196 | Creates a new pWiki object. Default options are passed as key-value | |||||||
| 197 | pairs or as a single hash. Options may be changed directly in the | |||||||
| 198 | object. | |||||||
| 199 | ||||||||
| 200 | =head1 AUTHOR | |||||||
| 201 | ||||||||
| 202 | (c) 2002 GNU/GPL+Perl/Artistic Michael Koehne kraehe@copyleft.de | |||||||
| 203 | ||||||||
| 204 | =head1 SEE ALSO | |||||||
| 205 | ||||||||
| 206 | CGI | |||||||
| 207 | ||||||||
| 208 | =cut | |||||||
| 209 | ||||||||
| 210 | #------------------------------------------------------------------------------# | |||||||
| 211 | ||||||||
| 212 | my $ESCAPE1 = '(&|<|>|"|--)'; | |||||||
| 213 | my $ESCAPE2 = { | |||||||
| 214 | '&' => '&', | |||||||
| 215 | '<' => '<', | |||||||
| 216 | '>' => '>', | |||||||
| 217 | '"' => '"', | |||||||
| 218 | '--' => '--' | |||||||
| 219 | }; | |||||||
| 220 | my $TEMPLATE= { | |||||||
| 221 | 'edit' => ' | |||||||
| 222 | Edit: %TOPIC% |
|||||||
| 223 | ||||||||
| 224 | |
|||||||
| 225 | %TEXT% | |||||||
| 226 | ', | |||||||
| 227 | 'notfound' => ' | |||||||
| 228 | %TOPIC% was not found in pWiki.
|
|||||||
| 229 | This could be, because this page has moved, | |||||||
| 230 | or because nothing has been written yet.
|
|||||||
| 231 | ||||||||
| 232 | ||||||||
| 233 | You may want to | |||||||
| 234 | ||||||||
| 235 | for | |||||||
| 236 | ||||||||
| 237 | ||||||||
| 238 | ||||||||
| 239 | ||||||||
| 240 | ||||||||
| 241 | You may want to | |||||||
| 242 | ||||||||
| 243 | it now. | |||||||
| 244 | ||||||||
| 245 | ', | |||||||
| 246 | 'content' => '', | |||||||
| 247 | 'style' => '%HTML%' | |||||||
| 248 | }; | |||||||
| 249 | ||||||||
| 250 | #------------------------------------------------------------------------------# | |||||||
| 251 | ||||||||
| 252 | sub new { | |||||||
| 253 | 1 | 1 | 1 | 11 | my $proto = shift; | |||
| 254 | 1 | 50 | 5 | my $self = ($#_ == 0) ? { %{ (shift) } } : { @_ }; | ||||
| 0 | 0 | |||||||
| 255 | 1 | 33 | 8 | my $class = ref($proto) || $proto; | ||||
| 256 | ||||||||
| 257 | 1 | 2 | bless($self, $class); | |||||
| 258 | ||||||||
| 259 | 1 | 3 | return $self; | |||||
| 260 | } | |||||||
| 261 | ||||||||
| 262 | sub server { | |||||||
| 263 | 0 | 0 | 0 | my $self=shift; | ||||
| 264 | ||||||||
| 265 | 0 | $self->parse_request; | ||||||
| 266 | 0 | my $html = $self->translate; | ||||||
| 267 | ||||||||
| 268 | 0 | 0 | if ($html ne "") { | |||||
| 269 | 0 | print "Content-type: text/html\n\n"; | ||||||
| 270 | 0 | print $html; | ||||||
| 271 | } else { | |||||||
| 272 | 0 | $self->error("$self->{pt} not found"); | ||||||
| 273 | } | |||||||
| 274 | } | |||||||
| 275 | ||||||||
| 276 | #------------------------------------------------------------------------------# | |||||||
| 277 | ||||||||
| 278 | sub html { | |||||||
| 279 | 0 | 0 | 0 | my $self = shift; | ||||
| 280 | ||||||||
| 281 | 0 | $_ = $self->readfile($self->{pt}); | ||||||
| 282 | 0 | 0 | $self->{TITLE} = $1 if m! |
|||||
| 283 | 0 | 0 | $_ = $1 if m!]*>(.+)!is; | |||||
| 284 | ||||||||
| 285 | 0 | return $_; | ||||||
| 286 | } | |||||||
| 287 | ||||||||
| 288 | sub text { | |||||||
| 289 | 0 | 0 | 0 | my $self = shift; | ||||
| 290 | ||||||||
| 291 | 0 | $_ = "\n".$self->readfile($self->{pt}); | ||||||
| 292 | ||||||||
| 293 | 0 | return "$_"; |
||||||
| 294 | } | |||||||
| 295 | ||||||||
| 296 | sub wiki { | |||||||
| 297 | 0 | 0 | 0 | my $self = shift; | ||||
| 298 | 0 | my $html = ""; | ||||||
| 299 | ||||||||
| 300 | 0 | $_ = "\n".$self->readfile($self->{pt}); | ||||||
| 301 | ||||||||
| 302 | # convert old wiki tags | |||||||
| 303 | 0 | s! |
||||||
| 304 | 0 | s! |
||||||
| 305 | 0 | s! |
||||||
| 306 | ||||||||
| 307 | ||||||||
| 308 | # handle paragraphs, lists and tables. | |||||||
| 309 | 0 | foreach (split /\n\n+/) { | ||||||
| 310 | 0 | 0 | next, if /^[ \t\n]*$/; | |||||
| 311 | 0 | 0 | $_ = "\n$_" unless /^\n/; | |||||
| 312 | 0 | chomp; | ||||||
| 313 | 0 | 0 | $html .= $self->format_command($_), next | |||||
| 314 | if /^(\n=[^\n]+)+$/; | |||||||
| 315 | 0 | 0 | $html .= $self->format_list($_), next | |||||
| 316 | if /^(\n[ \t]*[*-][^\n]+)+$/; | |||||||
| 317 | 0 | 0 | $html .= $self->format_table($_), next | |||||
| 318 | if /^(\n[ \t]*[|][^\n]+[|][ \t]*)+$/; | |||||||
| 319 | 0 | 0 | $html .= $self->format_verbatim($_), next | |||||
| 320 | if /^(\n[ \t]+[^\n]+)+$/; | |||||||
| 321 | 0 | $html .= $self->format_ordinary($_); | ||||||
| 322 | } | |||||||
| 323 | ||||||||
| 324 | 0 | return "$html"; | ||||||
| 325 | } | |||||||
| 326 | ||||||||
| 327 | #------------------------------------------------------------------------------# | |||||||
| 328 | ||||||||
| 329 | sub error { | |||||||
| 330 | 0 | 0 | 0 | my $self = shift; | ||||
| 331 | 0 | my $reason = shift; | ||||||
| 332 | ||||||||
| 333 | 0 | print "Content-type: text/html\n\n"; | ||||||
| 334 | ||||||||
| 335 | 0 | print " |
||||||
| 336 | 0 | print $reason,"\n"; | ||||||
| 337 | 0 | print "\n\n"; |
||||||
| 338 | ||||||||
| 339 | 0 | foreach (keys %ENV) { print $_," = ",$ENV{$_}," \n" }; |
||||||
| 0 | ||||||||
| 340 | 0 | exit 0; | ||||||
| 341 | } | |||||||
| 342 | ||||||||
| 343 | sub notfound { | |||||||
| 344 | 0 | 0 | 0 | my $self = shift; | ||||
| 345 | ||||||||
| 346 | 0 | return $self->template('notfound'); | ||||||
| 347 | } | |||||||
| 348 | ||||||||
| 349 | sub checkwrite { | |||||||
| 350 | 0 | 0 | 0 | my $self = shift; | ||||
| 351 | ||||||||
| 352 | 0 | my $file = $self->{pt}; | ||||||
| 353 | 0 | my $dir = $self->{pt}; | ||||||
| 354 | 0 | $dir =~ s!/[^/]*$!!; | ||||||
| 355 | ||||||||
| 356 | 0 | 0 | return "this should be a POST event" unless $self->{rm} eq "POST"; | |||||
| 357 | ||||||||
| 358 | 0 | 0 | return "user $self->{ru} not authorized " |
|||||
| 359 | if $self->{ru} eq "unknown"; | |||||||
| 360 | 0 | 0 | return "directory $dir not writeable " |
|||||
| 361 | unless -w $dir; | |||||||
| 362 | 0 | 0 | 0 | return "file $self->{pt} not writeable " |
||||
| 363 | if -r $self->{pt} && ! -w $self->{pt}; | |||||||
| 364 | 0 | 0 | return "file $self->{pt} contains slashdot" | |||||
| 365 | if $self->{pt} =~ m!/[.]!; | |||||||
| 366 | 0 | 0 | return "file $self->{pt} contains funnychars" | |||||
| 367 | unless $self->{pt} =~ m!^[a-zA-Z0-9_./-]+$!; | |||||||
| 368 | ||||||||
| 369 | 0 | return; | ||||||
| 370 | } | |||||||
| 371 | ||||||||
| 372 | sub edit { | |||||||
| 373 | 0 | 0 | 0 | my $self = shift; | ||||
| 374 | ||||||||
| 375 | 0 | $_ = $self->checkwrite(); | ||||||
| 376 | 0 | 0 | return $_ if $_; | |||||
| 377 | ||||||||
| 378 | 0 | $_ = $self->readfile($self->{pt}); | ||||||
| 379 | 0 | s/$ESCAPE1/$ESCAPE2->{$1}/geo; | ||||||
| 0 | ||||||||
| 380 | 0 | $self->{TEXT}=$_; | ||||||
| 381 | ||||||||
| 382 | 0 | return $self->template('edit'); | ||||||
| 383 | } | |||||||
| 384 | ||||||||
| 385 | sub save { | |||||||
| 386 | 0 | 0 | 0 | my $self = shift; | ||||
| 387 | ||||||||
| 388 | 0 | $_ = $self->checkwrite(); | ||||||
| 389 | 0 | 0 | return $_ if $_; | |||||
| 390 | ||||||||
| 391 | 0 | 0 | if ($self->{VAL}->{text}) { | |||||
| 392 | 0 | $_ = $self->{VAL}->{text}; | ||||||
| 393 | 0 | s/\r//g; | ||||||
| 394 | ||||||||
| 395 | 0 | 0 | if (-f $self->{pt}) { | |||||
| 396 | 0 | 0 | rename($self->{pt}, $self->{pt}.'~') unless -f $self->{pt}.'~'; | |||||
| 397 | } else { | |||||||
| 398 | 0 | open OUT, ">$self->{pt}~"; print OUT "\n"; close OUT; | ||||||
| 0 | ||||||||
| 0 | ||||||||
| 399 | } | |||||||
| 400 | 0 | open OUT, ">$self->{pt}"; print OUT "$_\n"; close OUT; | ||||||
| 0 | ||||||||
| 0 | ||||||||
| 401 | } else { | |||||||
| 402 | 0 | $self->error("no text"); | ||||||
| 403 | } | |||||||
| 404 | ||||||||
| 405 | 0 | return $self->display(); | ||||||
| 406 | } | |||||||
| 407 | ||||||||
| 408 | sub search { | |||||||
| 409 | 0 | 0 | 0 | my $self = shift; | ||||
| 410 | 0 | my $want = $self->{qs}; | ||||||
| 411 | 0 | $want =~ s/^search=//; | ||||||
| 412 | 0 | 0 | $want = "pWiki" if $want eq ""; | |||||
| 413 | 0 | my $html = "Search Results\nmatching: $want\n"; |
||||||
| 414 | 0 | my $rslt = `find . -type f ! -name '*~' -print | fgrep -v /CVS/ | xargs egrep -iE '$want' 2>/dev/null`; | ||||||
| 415 | 0 | my $hits; | ||||||
| 416 | 0 | my $matches=0; | ||||||
| 417 | ||||||||
| 418 | 0 | SEARCHLOOP: foreach (split( /\n/, $rslt)) { | ||||||
| 419 | 0 | my ($file,$str) = split /:/, $_, 2; | ||||||
| 420 | 0 | $file =~ s/^\.//; | ||||||
| 421 | 0 | $str =~ s/<[^>]+>//g; | ||||||
| 422 | 0 | 0 | next SEARCHLOOP if $str =~ /^[ \t\r\n]*$/; | |||||
| 423 | 0 | my $qm = quotemeta $str; | ||||||
| 424 | 0 | 0 | $hits->{$file} .= "$str \n" if $hits->{$file} !~ m!$qm!; |
|||||
| 425 | } | |||||||
| 426 | ||||||||
| 427 | 0 | $html .= "
|
||||||
| 428 | 0 | foreach (sort keys %$hits) { | ||||||
| 429 | 0 | $matches++; | ||||||
| 430 | 0 | my $tag = $_; | ||||||
| 431 | 0 | $tag =~ s!^\/!!; | ||||||
| 432 | 0 | $tag =~ s![_/]! !g; | ||||||
| 433 | 0 | $tag =~ s![.].*$!!; | ||||||
| 434 | ||||||||
| 435 | 0 | $html .= " \n$hits->{$_}"; |
||||||
| 436 | } | |||||||
| 437 | 0 | $html .= ""; | ||||||
| 438 | ||||||||
| 439 | 0 | 0 | $html .= " ... $matches matches search complete." if ($matches); |
|||||
| 440 | 0 | 0 | $html .= " ... there are no matches." if (! $matches); |
|||||
| 441 | ||||||||
| 442 | 0 | return $html; | ||||||
| 443 | } | |||||||
| 444 | ||||||||
| 445 | sub diff { | |||||||
| 446 | 0 | 0 | 0 | my $self = shift; | ||||
| 447 | ||||||||
| 448 | 0 | my $html = "\npWiki Diff\n
|
||||||
| 449 | 0 | my $rslt = `find . -type f ! -name '*~' -print | fgrep -v /CVS/`; | ||||||
| 450 | ||||||||
| 451 | 0 | DIFFLOOP: foreach (split( /\n/, $rslt)) { | ||||||
| 452 | 0 | my $file = $_; $file =~ s!^[.]/!!; | ||||||
| 0 | ||||||||
| 453 | 0 | my $path = $_; $path =~ s!^[.]!!; | ||||||
| 0 | ||||||||
| 454 | 0 | my $old = $file."~"; | ||||||
| 455 | 0 | 0 | next DIFFLOOP unless -r $old; | |||||
| 456 | ||||||||
| 457 | 0 | my $diff = `diff -p $old $file`; | ||||||
| 458 | 0 | $diff =~ s/$ESCAPE1/$ESCAPE2->{$1}/geo; | ||||||
| 0 | ||||||||
| 459 | ||||||||
| 460 | 0 | $html .= " \n \n$diff\n"; |
||||||
| 461 | } | |||||||
| 462 | 0 | $html .= ""; | ||||||
| 463 | ||||||||
| 464 | 0 | return $html; | ||||||
| 465 | } | |||||||
| 466 | ||||||||
| 467 | #------------------------------------------------------------------------------# | |||||||
| 468 | ||||||||
| 469 | sub parse_request { | |||||||
| 470 | 0 | 0 | 0 | my $self = shift; | ||||
| 471 | ||||||||
| 472 | 0 | 0 | $self->{dr} = $ENV{DOCUMENT_ROOT} || $self->error('DOCUMENT_ROOT not defined'); | |||||
| 473 | 0 | 0 | $self->{hh} = $ENV{HTTP_HOST} || $self->error('HTTP_HOST not defined'); | |||||
| 474 | 0 | 0 | $self->{rm} = $ENV{REQUEST_METHOD} || $self->error('REQUEST_METHOD not defined'); | |||||
| 475 | 0 | 0 | $self->{sn} = $ENV{SCRIPT_NAME} || $self->error('SCRIPT_NAME not defined'); | |||||
| 476 | 0 | 0 | $self->{ur} = $ENV{REQUEST_URI} || $self->error('REQUEST_URI not defined'); | |||||
| 477 | 0 | 0 | $self->{ru} = $ENV{REMOTE_USER} || "unknown"; | |||||
| 478 | 0 | $self->{ua} = ($ENV{HTTP_USER_AGENT} =~ /(links|lynx)/i); | ||||||
| 479 | ||||||||
| 480 | 0 | 0 | if ($ENV{PATH_INFO}) { | |||||
| 481 | 0 | $self->{pi} = $ENV{PATH_INFO}; | ||||||
| 482 | } else { | |||||||
| 483 | 0 | $self->{pi} = $self->{ur}; | ||||||
| 484 | 0 | $self->{pi} =~ s/\?.*//; | ||||||
| 485 | } | |||||||
| 486 | ||||||||
| 487 | 0 | 0 | if ($ENV{QUERY_STRING}) { | |||||
| 488 | 0 | $self->{qs} = $ENV{QUERY_STRING}; | ||||||
| 489 | } else { | |||||||
| 490 | 0 | $self->{qs} = $self->{ur}; | ||||||
| 491 | 0 | $self->{qs} =~ s/^[^?]*\?//; | ||||||
| 492 | } | |||||||
| 493 | ||||||||
| 494 | 0 | 0 | if ($ENV{PATH_TRANSLATED}) { | |||||
| 495 | 0 | $self->{pt} = $ENV{PATH_TRANSLATED}; | ||||||
| 496 | } else { | |||||||
| 497 | 0 | $self->{pt} = $self->{dr}.$self->{ur}; | ||||||
| 498 | 0 | $self->{pt} =~ s/\?.*//; | ||||||
| 499 | } | |||||||
| 500 | ||||||||
| 501 | 0 | 0 | if ($self->{rm} eq "POST") { | |||||
| 502 | 0 | alarm(60); | ||||||
| 503 | 0 | my $contlen = 0+$ENV{CONTENT_LENGTH}; | ||||||
| 504 | 0 | 0 | $contlen = 0 if ($contlen < 1); | |||||
| 505 | 0 | my $query; | ||||||
| 506 | 0 | my $readlen = read(STDIN, $query, $contlen); | ||||||
| 507 | 0 | alarm(0); | ||||||
| 508 | ||||||||
| 509 | 0 | 0 | $self->error("POST failed") if $readlen != $contlen; | |||||
| 510 | 0 | $self->{QUERY_BODY} = $query; | ||||||
| 511 | ||||||||
| 512 | 0 | $query =~ tr/+/ /; # RFC1630 | ||||||
| 513 | 0 | my @parts = split(/&/, $query); | ||||||
| 514 | ||||||||
| 515 | 0 | $self->{VAL}={}; | ||||||
| 516 | 0 | foreach (@parts) { | ||||||
| 517 | 0 | my ($key, $val) = split(/=/,$_,2); | ||||||
| 518 | 0 | 0 | $val = (defined $val) ? uri_unescape($val) : ''; | |||||
| 519 | 0 | $key = uri_unescape($key); | ||||||
| 520 | 0 | $self->{VAL}->{$key} = $val; | ||||||
| 521 | } | |||||||
| 522 | ||||||||
| 523 | 0 | 0 | if ($self->{VAL}->{path}) { | |||||
| 524 | 0 | $self->{pi} = $self->{VAL}->{path}; | ||||||
| 525 | 0 | $self->{pt} = $self->{dr}.$self->{VAL}->{path}; | ||||||
| 526 | } | |||||||
| 527 | 0 | 0 | $self->{qs} = $self->{VAL}->{query} if $self->{VAL}->{query}; | |||||
| 528 | } | |||||||
| 529 | ||||||||
| 530 | 0 | 0 | $self->error("no path info") unless $self->{pi}; | |||||
| 531 | 0 | 0 | $self->error("no query string") unless $self->{qs}; | |||||
| 532 | 0 | 0 | $self->error("no path translated") unless $self->{pt}; | |||||
| 533 | 0 | 0 | $self->error("can not chdir to doc root") unless chdir $self->{dr}; | |||||
| 534 | 0 | umask 000; | ||||||
| 535 | } | |||||||
| 536 | ||||||||
| 537 | sub translate { | |||||||
| 538 | 0 | 0 | 0 | my $self = shift; | ||||
| 539 | 0 | my $html; | ||||||
| 540 | ||||||||
| 541 | 0 | $self->{URL} = "http://$self->{hh}$self->{pi}"; | ||||||
| 542 | 0 | $self->{SCR} = "http://$self->{hh}$self->{sn}"; | ||||||
| 543 | 0 | $self->{PATH} = $self->{pi}; | ||||||
| 544 | 0 | $self->{DIR} = $self->{pi}; | ||||||
| 545 | 0 | $self->{DIR} =~ s!/[^/]*$!!; | ||||||
| 546 | 0 | $self->{DIR} =~ s!^/!!; | ||||||
| 547 | 0 | $self->{TOPIC} = $self->{pi}; | ||||||
| 548 | 0 | $self->{TOPIC} =~ s!^.*/!!; | ||||||
| 549 | 0 | $self->{TOPIC} =~ s![.].*$!!; | ||||||
| 550 | 0 | $self->{TOPIC} =~ s!_! !g; | ||||||
| 551 | 0 | $self->{TITLE} = $self->{TOPIC}; | ||||||
| 552 | ||||||||
| 553 | 0 | 0 | QUERYCASE: { | |||||
| 554 | 0 | $html = $self->error(), last QUERYCASE if $self->{error}; | ||||||
| 555 | 0 | 0 | $html = $self->error(), last QUERYCASE if $self->{qs} =~ /^error/; | |||||
| 556 | 0 | 0 | $html = $self->search(), last QUERYCASE if $self->{qs} =~ /^search=/; | |||||
| 557 | 0 | 0 | $html = $self->diff(), last QUERYCASE if $self->{qs} eq "diff"; | |||||
| 558 | 0 | 0 | $html = $self->edit(), last QUERYCASE if $self->{qs} eq "edit"; | |||||
| 559 | 0 | 0 | $html = $self->save(), last QUERYCASE if $self->{qs} eq "save"; | |||||
| 560 | 0 | $html = $self->display(); | ||||||
| 561 | } | |||||||
| 562 | ||||||||
| 563 | 0 | $self->{HTML} = $html; | ||||||
| 564 | 0 | $self->{INDEX} = $self->template("content"); | ||||||
| 565 | ||||||||
| 566 | 0 | 0 | return $self->template("style") || $self->{HTML}; | |||||
| 567 | } | |||||||
| 568 | ||||||||
| 569 | sub display { | |||||||
| 570 | 0 | 0 | 0 | my $self = shift; | ||||
| 571 | ||||||||
| 572 | 0 | 0 | return $self->notfound() unless -r $self->{pt}; | |||||
| 573 | 0 | 0 | return $self->html() if $self->{pt} =~ /\.html$/; | |||||
| 574 | 0 | 0 | return $self->html() if $self->{pt} =~ /\.htm$/; | |||||
| 575 | 0 | 0 | return $self->wiki() if $self->{pt} =~ /\.wiki$/; | |||||
| 576 | 0 | 0 | return $self->wiki() if $self->{pt} =~ /\.pod$/; | |||||
| 577 | 0 | 0 | return $self->xml() if $self->{pt} =~ /\.xml$/; | |||||
| 578 | 0 | return $self->text(); | ||||||
| 579 | } | |||||||
| 580 | ||||||||
| 581 | sub readfile { | |||||||
| 582 | 0 | 0 | 0 | my $self = shift; | ||||
| 583 | 0 | my $file = shift; | ||||||
| 584 | ||||||||
| 585 | 0 | 0 | if (-r $file) { | |||||
| 586 | 0 | my $oirs = $/; | ||||||
| 587 | 0 | undef $/; | ||||||
| 588 | 0 | open IN, $file; | ||||||
| 589 | 0 | my $html = |
||||||
| 590 | 0 | close IN; | ||||||
| 591 | 0 | $/ = $oirs; | ||||||
| 592 | 0 | return $html; | ||||||
| 593 | } | |||||||
| 594 | 0 | return; | ||||||
| 595 | } | |||||||
| 596 | ||||||||
| 597 | sub template { | |||||||
| 598 | 0 | 0 | 0 | my $self = shift; | ||||
| 599 | 0 | my $temp = shift; | ||||||
| 600 | 0 | 0 | my $file = $self->{ua} ? "$temp.lnx" : "$temp.moz"; | |||||
| 601 | 0 | my $html = ""; | ||||||
| 602 | ||||||||
| 603 | 0 | 0 | TEMPLCASE: { | |||||
| 604 | 0 | $html = $self->readfile("$self->{DIR}/$file"), last TEMPLCASE | ||||||
| 605 | if -r "$self->{DIR}/$file"; | |||||||
| 606 | 0 | 0 | $html = $self->readfile("$self->{dr}/$file"), last TEMPLCASE | |||||
| 607 | if -r $file; | |||||||
| 608 | 0 | 0 | $html = $self->readfile("pWiki/$file"), last TEMPLCASE | |||||
| 609 | if -r "pWiki/$file"; | |||||||
| 610 | 0 | 0 | $html = $TEMPLATE->{$temp} || ""; | |||||
| 611 | } | |||||||
| 612 | 0 | $html =~ s!%([A-Z]+)%!$self->{$1}!geo; | ||||||
| 0 | ||||||||
| 613 | ||||||||
| 614 | 0 | return $html; | ||||||
| 615 | } | |||||||
| 616 | ||||||||
| 617 | sub autolink { | |||||||
| 618 | 0 | 0 | 0 | my ($self,$link) = @_; | ||||
| 619 | ||||||||
| 620 | 0 | 0 | return $link if $link =~ /:$/; # oups ... | |||||
| 621 | ||||||||
| 622 | 0 | $link =~ tr/[]//d; | ||||||
| 623 | 0 | my $url = $link; | ||||||
| 624 | 0 | my $tag = $link; | ||||||
| 625 | ||||||||
| 626 | 0 | 0 | if ($link =~ /(.*)[|](.*)/) { | |||||
| 627 | 0 | $url = $2; | ||||||
| 628 | 0 | $tag = $1; | ||||||
| 629 | 0 | $tag =~ s!_! !g; | ||||||
| 630 | 0 | $url =~ s!::!-!g; | ||||||
| 631 | 0 | 0 | $url .= ".pod" if $self->{pt} =~ /\.pod/; | |||||
| 632 | } else { | |||||||
| 633 | 0 | $url =~ s!/".*!!g; | ||||||
| 634 | 0 | 0 | $url =~ s!/!_!g if $self->{pt} =~ /\.wiki/; | |||||
| 635 | 0 | 0 | $url =~ s!/.*$!!g if $self->{pt} =~ /\.pod/; | |||||
| 636 | 0 | $url =~ s!:+!-!g; | ||||||
| 637 | 0 | 0 | $url = "$self->{DIR}/$url" if $self->{DIR}; | |||||
| 638 | 0 | 0 | $url = "/$url" if $url !~ m!^/!; | |||||
| 639 | 0 | $tag =~ s!_! !g; | ||||||
| 640 | ||||||||
| 641 | 0 | 0 | EXTCASE: { | |||||
| 642 | 0 | $url .= ".wiki", last EXTCASE if -r $self->{dr}.$url.".wiki"; | ||||||
| 643 | 0 | 0 | $url .= ".text", last EXTCASE if -r $self->{dr}.$url.".text"; | |||||
| 644 | 0 | 0 | $url .= ".html", last EXTCASE if -r $self->{dr}.$url.".html"; | |||||
| 645 | 0 | 0 | $url .= ".htm", last EXTCASE if -r $self->{dr}.$url.".htm"; | |||||
| 646 | 0 | 0 | $url .= ".pod", last EXTCASE if -r $self->{dr}.$url.".pod"; | |||||
| 647 | 0 | 0 | $url .= ".xml", last EXTCASE if -r $self->{dr}.$url.".xml"; | |||||
| 648 | ||||||||
| 649 | 0 | $_ = $self->{pt}; | ||||||
| 650 | 0 | m/\.([^.]+)$/; | ||||||
| 651 | 0 | $url .= ".$1"; | ||||||
| 652 | 0 | $tag = "?".$tag."?"; | ||||||
| 653 | } | |||||||
| 654 | } | |||||||
| 655 | ||||||||
| 656 | 0 | return "$tag"; | ||||||
| 657 | } | |||||||
| 658 | ||||||||
| 659 | sub expand { | |||||||
| 660 | 0 | 0 | 0 | my $self = shift; my $cmd = shift; $_ = shift; | ||||
| 0 | ||||||||
| 0 | ||||||||
| 661 | ||||||||
| 662 | 0 | s!([IBSCLFXE])<+(.*)!$self->expand($1,$2)!geo; | ||||||
| 0 | ||||||||
| 663 | ||||||||
| 664 | 0 | 0 | return "$_" if $cmd eq "I"; | |||||
| 665 | 0 | 0 | return "$_" if $cmd eq "B"; | |||||
| 666 | 0 | 0 | return "$_" if $cmd =~ /[CFX]/; |
|||||
| 667 | 0 | 0 | return $self->autolink($_) if $cmd eq "L"; | |||||
| 668 | 0 | 0 | 0 | return "&".$_.";" if ($cmd eq "E") && /^[^0-9]/; | ||||
| 669 | 0 | 0 | 0 | return "\\0".$_ if ($cmd eq "E") && /^[0-9]/; | ||||
| 670 | ||||||||
| 671 | 0 | 0 | s/ / /g if $cmd eq "S"; | |||||
| 672 | ||||||||
| 673 | 0 | return "$_"; | ||||||
| 674 | } | |||||||
| 675 | ||||||||
| 676 | sub wikify { | |||||||
| 677 | 0 | 0 | 0 | my $self = shift; $_ = shift; | ||||
| 0 | ||||||||
| 678 | ||||||||
| 679 | 0 | s!([IBSCLFXE])<+([^>]+)>+!$self->expand($1,$2)!geo; | ||||||
| 0 | ||||||||
| 680 | 0 | s!([\n\t ])(\[[0-9A-Za-z_/:-]+\]|[A-Za-z0-9]+[A-Z_/:-][0-9A-Za-z_/:-]*)!$1.$self->autolink($2)!geo; | ||||||
| 0 | ||||||||
| 681 | ||||||||
| 682 | 0 | return $_; | ||||||
| 683 | } | |||||||
| 684 | ||||||||
| 685 | #------------------------------------------------------------------------------# | |||||||
| 686 | ||||||||
| 687 | sub format_table { | |||||||
| 688 | 0 | 0 | 0 | my $self = shift; $_ = $self->wikify(shift); | ||||
| 0 | ||||||||
| 689 | ||||||||
| 690 | 0 | s!^[ \t]*[|]!\n | ||||||
| !g; | ||||||||
| 691 | 0 | s!\n[ \t]*[|]!\n | ||||||
| !g; | ||||||||
| 692 | 0 | s![|][ \t]*$! | ||||||
| 693 | 0 | s![|][ \t]*\n! | ||||||
| 694 | 0 | s![|]! | !g; | |||||
| 695 | ||||||||
| 696 | 0 | return "\n |
||||||
| 697 | } | |||||||
| 698 | ||||||||
| 699 | sub format_list { | |||||||
| 700 | 0 | 0 | 0 | my $self = shift; $_ = $self->wikify(shift); | ||||
| 0 | ||||||||
| 701 | ||||||||
| 702 | 0 | s!\n[ \t]*[*-] !\n |
||||||
| 703 | ||||||||
| 704 | 0 | return "\n
|
||||||
| 705 | } | |||||||
| 706 | ||||||||
| 707 | sub format_ordinary { | |||||||
| 708 | 0 | 0 | 0 | my $self = shift; $_ = $self->wikify(shift); | ||||
| 0 | ||||||||
| 709 | ||||||||
| 710 | 0 | s!\n[ \t]+!\n !g; |
||||||
| 711 | ||||||||
| 712 | 0 | return "\n$_\n \n"; |
||||||
| 713 | } | |||||||
| 714 | ||||||||
| 715 | sub format_verbatim { | |||||||
| 716 | 0 | 0 | 0 | my $self = shift; $_ = shift; | ||||
| 0 | ||||||||
| 717 | ||||||||
| 718 | 0 | s/$ESCAPE1/$ESCAPE2->{$1}/geo; | ||||||
| 0 | ||||||||
| 719 | ||||||||
| 720 | 0 | return "\n$_\n\n"; |
||||||
| 721 | } | |||||||
| 722 | ||||||||
| 723 | sub format_command { | |||||||
| 724 | 0 | 0 | 0 | my $self = shift; $_ = shift; | ||||
| 0 | ||||||||
| 725 | 0 | my $html = ""; | ||||||
| 726 | ||||||||
| 727 | 0 | 0 | if (/\n=location (.+)/i) { | |||||
| 728 | 0 | print "Location: $1\n\n"; | ||||||
| 729 | 0 | exit 0; | ||||||
| 730 | } | |||||||
| 731 | 0 | s!([IBSCLFXE])<([^>]+)>!$self->expand($1,$2)!geo; | ||||||
| 0 | ||||||||
| 732 | ||||||||
| 733 | 0 | 0 | $self->{TITLE} = $1 if /\n=title ([^\n]+)/i; | |||||
| 734 | 0 | 0 | $html .= "$1" if /\n=head1 ([^\n]+)/i; |
|||||
| 735 | 0 | 0 | $html .= "$1" if /\n=head2 ([^\n]+)/i; |
|||||
| 736 | 0 | 0 | $html .= "$1" if /\n=head3 ([^\n]+)/i; |
|||||
| 737 | 0 | 0 | $html .= "
|
|||||
| 738 | 0 | 0 | $html .= " |
|||||
| 739 | 0 | 0 | $html .= "" if /\n=back.*/i; | |||||
| 740 | ||||||||
| 741 | 0 | return $html; | ||||||
| 742 | } | |||||||
| 743 | ||||||||
| 744 | #------------------------------------------------------------------------------# | |||||||
| 745 | ||||||||
| 746 | 1; |