1
# See bottom of file for license and copyright information
4
---+!! package Foswiki::UI
6
Coordinator of execution flow and service functions used by the UI packages
15
$Foswiki::cfg{SwitchBoard} ||= {};
17
# package - perl package that contains the method for this request
18
# function - name of the function in package
19
# context - hash of context vars to define
20
# allow - hash of HTTP methods to allow (all others are denied)
21
# deny - hash of HTTP methods that are denied (all others are allowed)
22
# 'deny' is not tested if 'allow' is defined
24
# The switchboard can contain entries either as hashes or as arrays.
25
# The array format specifies [0] package, [1] function, [2] context
26
# and should be used when declaring scripts from plugins that must work
27
# with Foswiki 1.0.0 and 1.0.4.
29
$Foswiki::cfg{SwitchBoard}{attach} = {
30
package => 'Foswiki::UI::Upload',
32
context => { attach => 1 },
34
$Foswiki::cfg{SwitchBoard}{changes} = {
35
package => 'Foswiki::UI::Changes',
36
function => 'changes',
37
context => { changes => 1 },
39
$Foswiki::cfg{SwitchBoard}{edit} = {
40
package => 'Foswiki::UI::Edit',
42
context => { edit => 1 },
44
$Foswiki::cfg{SwitchBoard}{login} = {
47
context => { ( login => 1, logon => 1 ) },
49
$Foswiki::cfg{SwitchBoard}{logon} = {
52
context => { ( login => 1, logon => 1 ) },
54
$Foswiki::cfg{SwitchBoard}{manage} = {
55
package => 'Foswiki::UI::Manage',
57
context => { manage => 1 },
58
allow => { POST => 1 },
60
$Foswiki::cfg{SwitchBoard}{oops} = {
61
package => 'Foswiki::UI::Oops',
62
function => 'oops_cgi',
63
context => { oops => 1 },
65
$Foswiki::cfg{SwitchBoard}{preview} = {
66
package => 'Foswiki::UI::Preview',
67
function => 'preview',
68
context => { preview => 1 },
70
$Foswiki::cfg{SwitchBoard}{rdiffauth} = {
71
package => 'Foswiki::UI::RDiff',
73
context => { diff => 1 },
75
$Foswiki::cfg{SwitchBoard}{rdiff} = {
76
package => 'Foswiki::UI::RDiff',
78
context => { diff => 1 },
80
$Foswiki::cfg{SwitchBoard}{register} = {
81
package => 'Foswiki::UI::Register',
82
function => 'register_cgi',
83
context => { register => 1 },
84
# method verify must allow GET; protect in Foswiki::UI::Register
85
#allow => { POST => 1 },
87
$Foswiki::cfg{SwitchBoard}{rename} = {
88
package => 'Foswiki::UI::Manage',
90
context => { rename => 1 },
91
# Rename is 2 stage; protect in Foswiki::UI::Rename
92
#allow => { POST => 1 },
94
$Foswiki::cfg{SwitchBoard}{resetpasswd} = {
95
package => 'Foswiki::UI::Register',
96
function => 'resetPassword',
97
context => { resetpasswd => 1 },
98
allow => { POST => 1 },
100
$Foswiki::cfg{SwitchBoard}{rest} = {
101
package => 'Foswiki::UI::Rest',
103
context => { rest => 1 },
105
$Foswiki::cfg{SwitchBoard}{save} = {
106
package => 'Foswiki::UI::Save',
108
context => { save => 1 },
109
allow => { POST => 1 },
111
$Foswiki::cfg{SwitchBoard}{search} = {
112
package => 'Foswiki::UI::Search',
113
function => 'search',
114
context => { search => 1 },
116
$Foswiki::cfg{SwitchBoard}{statistics} = {
117
package => 'Foswiki::UI::Statistics',
118
function => 'statistics',
119
context => { statistics => 1 },
121
$Foswiki::cfg{SwitchBoard}{upload} = {
122
package => 'Foswiki::UI::Upload',
123
function => 'upload',
124
context => { upload => 1 },
125
allow => { POST => 1 },
127
$Foswiki::cfg{SwitchBoard}{viewauth} = {
128
package => 'Foswiki::UI::View',
130
context => { view => 1 },
132
$Foswiki::cfg{SwitchBoard}{viewfile} = {
133
package => 'Foswiki::UI::View',
134
function => 'viewfile',
135
context => { viewfile => 1 },
137
$Foswiki::cfg{SwitchBoard}{view} = {
138
package => 'Foswiki::UI::View',
140
context => { view => 1 },
149
use Foswiki::Request ();
150
use Foswiki::Response ();
151
use Foswiki::OopsException ();
152
use Foswiki::EngineException ();
153
use Foswiki::ValidationException ();
154
use Foswiki::AccessControlException ();
155
use Foswiki::Validation ();
157
# Used to lazily load UI handler modules
158
our %isInitialized = ();
162
# Change to a 1 to trace passthrough
168
---++ StaticMethod handleRequest($req) -> $res
170
Main coordinator of request-process-response cycle.
178
my $dispatcher = $Foswiki::cfg{SwitchBoard}{ $req->action() };
179
unless ( defined $dispatcher ) {
180
$res = new Foswiki::Response();
181
$res->header( -type => 'text/html', -status => '404' );
182
my $html = CGI::start_html('404 Not Found');
183
$html .= CGI::h1('Not Found');
185
CGI::p( "The requested URL "
187
. " was not found on this server." );
188
$html .= CGI::end_html();
193
if (ref($dispatcher) eq 'ARRAY') {
194
# Old-style array entry in switchboard from a plugin
195
my @array = @$dispatcher;
197
package => $array[0],
198
function => $array[1],
199
context => $array[2],
203
if ( $dispatcher->{package} && !$isInitialized{$dispatcher->{package}} ) {
204
eval qq(use $dispatcher->{package});
206
$isInitialized{$dispatcher->{package}} = 1;
210
$sub = $dispatcher->{package} . '::' if $dispatcher->{package};
211
$sub .= $dispatcher->{function};
213
# Get the params cache from the path
215
my $path_info = $req->path_info();
216
if ($path_info =~ s#/foswiki_redirect_cache/([a-f0-9]{32})$##) {
218
$req->path_info( $path_info );
223
# Read cached post parameters
224
my $passthruFilename =
225
$Foswiki::cfg{WorkingDir} . '/tmp/passthru_' . $cache;
226
if ( open( F, '<', $passthruFilename ) ) {
228
if (TRACE_PASSTHRU) {
229
print STDERR "Passthru: Loading cache for ", $req->url(),
230
'?', $req->query_string(), "\n";
231
print STDERR <F>, "\n";
233
open( F, '<' . $passthruFilename );
237
unlink($passthruFilename);
238
$req->delete('foswiki_redirect_cache');
239
print STDERR "Passthru: Loaded and unlinked $passthruFilename\n"
242
$req->method('POST');
245
print STDERR "Passthru: Could not find $passthruFilename\n"
249
#print STDERR "INCOMING ".$req->method()." ".$req->url." -> ".$sub."\n";
250
#print STDERR "Validation: ".($req->param('validation_key')||'no key')."\n";
251
#require Data::Dumper;
252
#print STDERR Data::Dumper->Dump([$req]);
253
if ( UNIVERSAL::isa( $Foswiki::engine, 'Foswiki::Engine::CLI' ) ) {
254
$dispatcher->{context}->{command_line} = 1;
255
} elsif ( defined $req->method()
257
( defined $dispatcher->{allow}
258
&& !$dispatcher->{allow}->{uc($req->method())} )
260
( defined $dispatcher->{deny}
261
&& $dispatcher->{deny}->{uc($req->method())} )
264
$res = new Foswiki::Response();
265
$res->header( -type => 'text/html', -status => '405' );
266
$res->print('Bad Request: '.uc($req->method()).' denied for '
270
$res = _execute( $req, \&$sub, %{$dispatcher->{context}} );
276
---++ StaticMethod _execute($req, $sub, %initialContext) -> $res
278
Creates a Foswiki session object with %initalContext and calls
279
$sub method. Returns the Foswiki::Response object generated
284
my ( $req, $sub, %initialContext ) = @_;
286
# DO NOT pass in $req->remoteUser here (even though it appears to be right)
287
# because it may occlude the login manager.
288
my $session = new Foswiki( undef, $req, \%initialContext );
289
my $res = $session->{response};
291
$res->pushHeader( 'X-FoswikiAction' => $req->action() );
292
$res->pushHeader( 'X-FoswikiURI' => $req->uri() );
294
unless ( defined $session->{response}->status()
295
&& $session->{response}->status() =~ /^\s*3\d\d/ )
298
$session->{users}->{loginManager}->checkAccess();
301
catch Foswiki::ValidationException with {
302
my $query = $session->{request};
303
# Redirect with passthrough so we don't lose the
304
# original query params. We use the login script for
305
# validation because it already has the correct criteria
306
# in httpd.conf for Apache login.
307
my $url = $session->getScriptUrl(
308
0, 'login', $session->{webName}, $session->{topicName} );
309
$query->param( -name => 'action',
310
-value => 'validate' );
311
$query->param( -name => 'origurl',
312
-value => $session->{request}->uri );
313
$session->redirect( $url, 1 ); # with passthrough
315
catch Foswiki::AccessControlException with {
317
unless ( $session->{users}->{loginManager}->forceAuthentication() )
320
# Login manager did not want to authenticate, perhaps because
321
# we are already authenticated.
322
my $exception = new Foswiki::OopsException(
323
'accessdenied', status => 403,
325
topic => $e->{topic},
326
def => 'topic_access',
327
params => [ $e->{mode}, $e->{reason} ]
330
$exception->generate($session);
333
catch Foswiki::OopsException with {
334
shift->generate($session);
336
catch Error::Simple with {
338
$res = new Foswiki::Response;
339
$res->header( -type => 'text/plain' );
342
# output the full message and stacktrace to the browser
343
$res->print( $e->stringify() );
346
my $mess = $e->stringify();
348
$session->logger->log('warning',$mess);
350
# tell the browser where to look for more help
352
'Foswiki detected an internal error - please check your Foswiki logs and webserver logs for more information.'
354
$mess =~ s/ at .*$//s;
356
# cut out pathnames from public announcement
357
$mess =~ s#/[\w./]+#path#g;
362
catch Foswiki::EngineException with {
364
my $res = $e->{response};
365
unless ( defined $res ) {
366
$res = new Foswiki::Response();
367
$res->header( -type => 'text/html', -status => $e->{status} );
368
my $html = CGI::start_html( $e->{status} . ' Bad Request' );
369
$html .= CGI::h1('Bad Request');
370
$html .= CGI::p( $e->{reason} );
371
$html .= CGI::end_html();
374
$Foswiki::engine->finalizeError($res);
378
$res = new Foswiki::Response;
379
$res->header( -type => 'text/plain' );
380
$res->print("Unspecified error");
390
---++ StaticMethod logon($session)
392
Handler to "logon" action.
393
* =$session= is a Foswiki session object
399
if (($session->{request}->param('action') ||'') eq 'validate'
400
# Force login if not recognisably authenticated
401
&& $session->inContext('authenticated')) {
402
Foswiki::Validation::validate( $session );
404
$session->{users}->{loginManager}->login(
405
$session->{request}, $session );
411
---++ StaticMethod checkWebExists( $session, $web, $topic, $op )
413
Check if the web exists. If it doesn't, will throw an oops exception.
414
$op is the user operation being performed.
419
my ( $session, $webName, $topic, $op ) = @_;
420
ASSERT( $session->isa('Foswiki') ) if DEBUG;
422
unless ( $session->{store}->webExists($webName) ) {
423
throw Foswiki::OopsException(
424
'accessdenied', status => 403,
425
def => 'no_such_web',
435
---++ StaticMethod topicExists( $session, $web, $topic, $op ) => boolean
437
Check if the given topic exists, throwing an OopsException
438
if it doesn't. $op is the user operation being performed.
442
sub checkTopicExists {
443
my ( $session, $webName, $topic, $op ) = @_;
444
ASSERT( $session->isa('Foswiki') ) if DEBUG;
446
unless ( $session->{store}->topicExists( $webName, $topic ) ) {
447
throw Foswiki::OopsException(
448
'accessdenied', status => 403,
449
def => 'no_such_topic',
459
---++ StaticMethod checkAccess( $web, $topic, $mode, $user )
461
Check if the given mode of access by the given user to the given
462
web.topic is permissible, throwing a Foswiki::OopsException if not.
467
my ( $session, $web, $topic, $mode, $user ) = @_;
468
ASSERT( $session->isa('Foswiki') ) if DEBUG;
471
$session->security->checkAccessPermission(
472
$mode, $user, undef, undef, $topic, $web
476
throw Foswiki::OopsException(
477
'accessdenied', status => 403,
478
def => 'topic_access',
481
params => [ $mode, $session->security->getReason() ]
488
---++ StaticMethod readTemplateTopic( $session, $theTopicName ) -> ( $meta, $text )
490
Read a topic from the Foswiki web, or if that fails from the current
495
sub readTemplateTopic {
496
my ( $session, $theTopicName ) = @_;
497
ASSERT( $session->isa('Foswiki') ) if DEBUG;
499
my $web = $Foswiki::cfg{SystemWebName};
500
if ( $session->{store}->topicExists( $session->{webName}, $theTopicName ) )
503
# try to read from current web, if found
504
$web = $session->{webName};
506
return $session->{store}
507
->readTopic( $session->{user}, $web, $theTopicName, undef );
512
---++ StaticMethod checkValidationKey( $session )
514
Check the validation key for the given action. Throws an exception
515
if the validation key isn't valid (handled in _execute(), above)
517
See Foswiki::Validation for more information.
521
sub checkValidationKey {
524
# Check the nonce before we do anything else
525
my $nonce = $session->{request}->param('validation_key');
526
$session->{request}->delete('validation_key');
527
if ( !defined($nonce)
528
|| !Foswiki::Validation::isValidNonce( $session->getCGISession(),
531
throw Foswiki::ValidationException();
533
if ( defined($nonce) ) {
535
# Expire the nonce. If the user tries to use it again, they will
537
Foswiki::Validation::expireValidationKeys(
538
$session->getCGISession(),
539
$Foswiki::cfg{Validation}{ExpireKeyOnUse} ? $nonce : undef );
545
---++ StaticMethod run( $method, %context )
547
Supported for bin scripts that were written for Foswiki < 1.0. The parameters
548
are a function reference to the UI method to call and initial context.
550
In Foswiki >= 1.0 it should be replaced by a Config.spec entry such as:
553
# Bin script registration - do not modify
554
$Foswiki::cfg{SwitchBoard}{publish} = [ "Foswiki::Contrib::Publish", "publish", { publishing => 1 } ];
559
my ( $method, %context ) = @_;
561
if ( UNIVERSAL::isa( $Foswiki::engine, 'Foswiki::Engine::CLI' ) ) {
562
$context{command_line} = 1;
564
_execute( Foswiki::Request->new(), \&$method, %context );
569
# Module of Foswiki - The Free and Open Source Wiki, http://foswiki.org/
571
# Copyright (C) 2008-2009 Foswiki Contributors. Foswiki Contributors
572
# are listed in the AUTHORS file in the root of this distribution.
573
# NOTE: Please extend that file, not this notice.
575
# Additional copyrights apply to some or all of the code in this
578
# Copyright (C) 1999-2007 Peter Thoeny, peter@thoeny.org
579
# and TWiki Contributors. All Rights Reserved. TWiki Contributors
580
# are listed in the AUTHORS file in the root of this distribution.
581
# Copyright (C) 2005 Martin at Cleaver.org
582
# Copyright (C) 2005-2007 TWiki Contributors
584
# and also based/inspired on Catalyst framework, whose Author is
585
# Sebastian Riedel. Refer to
587
# http://search.cpan.org/~mramberg/Catalyst-Runtime-5.7010/lib/Catalyst.pm
589
# for more credit and liscence details.
591
# This program is free software; you can redistribute it and/or
592
# modify it under the terms of the GNU General Public License
593
# as published by the Free Software Foundation; either version 2
594
# of the License, or (at your option) any later version. For
595
# more details read LICENSE in the root of this distribution.
597
# This program is distributed in the hope that it will be useful,
598
# but WITHOUT ANY WARRANTY; without even the implied warranty of
599
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
601
# As per the GPL, removal of this notice is prohibited.