updates
[torrus-plus.git] / src / lib / Torrus / PSGI.pm
blob16c25cc31239e64fc50613d2bf14ba1aa3953372
1 #!/usr/bin/perl
2 package Torrus::PSGI;
4 use strict;
5 use warnings;
7 # because Miyagawa knows much more than SS about perl, and Ive written mod_perl1/2/fcgi adapters enough times now in my life
8 use Plack::Request;
9 use Plack::Response;
11 use JSON ();
13 use Torrus::Log;
14 use Torrus::Renderer;
15 use Torrus::SiteConfig;
16 use Torrus::ACL;
18 our $VERSION = 0.1;
20 =head2 run_psgi
22 The outmost function, can probably be collapsed down in to I<do_process>
24 =cut
26 sub run_psgi
29 my $c = shift; # class name
31 my $env = shift; # PSGI env
33 my $q = Plack::Request->new($env);
35 my $res = __PACKAGE__->do_process($q);
37 # this is a travesty
38 &Torrus::DB::cleanupEnvironment();
40 return $res->finalize
44 =head2 do_process
46 Processes the web request itself
48 =cut
50 sub do_process
53 my $c = shift;
55 my $q = shift;
57 my $path_info = $q->path_info;
59 my @paramNames = $q->param();
61 &Torrus::Log::setLevel('debug')
62 if( $q->param('DEBUG') and not $Torrus::Renderer::globalDebug );
64 my %options;
65 for my $name ( @paramNames )
67 next if $name eq 'SESSION_ID';
68 $options{'variables'}->{$name} = $q->param($name)
69 if $name =~ m/^[A-Z]/;
72 my( $fname, $mimetype, $expires );
73 my @cookies;
75 my $renderer = Torrus::Renderer->new();
76 if( not defined( $renderer ) )
78 return report_error($q, 'Error initializing Renderer');
81 my $tree = _path_to_tree($q);
83 _determine_uid($q,\%options,\@cookies);
85 if( not $fname )
87 if( not $tree or not Torrus::SiteConfig::treeExists( $tree ) )
89 ( $fname, $mimetype, $expires ) =
90 $renderer->renderTreeChooser( %options );
92 else
94 if( $Torrus::CGI::authorizeUsers and
95 not $options{'acl'}->hasPrivilege( $options{'uid'}, $tree,
96 'DisplayTree' ) )
98 return report_error($q, 'Permission denied');
101 if( $Torrus::Renderer::displayReports and
102 defined( $q->param('htmlreport') ) )
104 if( $Torrus::CGI::authorizeUsers and
105 not $options{'acl'}->hasPrivilege( $options{'uid'}, $tree,
106 'DisplayReports' ) )
108 return report_error($q, 'Permission denied');
111 my $reportfname = $q->param('htmlreport');
112 # strip off leading slashes for security
113 $reportfname =~ s/^.*\///o;
115 $fname = $Torrus::Global::reportsDir . '/' . $tree .
116 '/html/' . $reportfname;
118 return report_error($q, 'No such file: ' . $reportfname)
119 if not -f $fname;
121 $mimetype = 'text/html';
122 $expires = '3600';
124 else
126 my $config_tree = Torrus::ConfigTree->new( -TreeName => $tree );
127 return report_error($q, 'Configuration is not ready')
128 if not defined $config_tree ;
130 my $token = $q->param('token');
131 if( not defined($token) )
133 my $path = $q->param('path');
134 if( not defined($path) )
136 my $nodeid = $q->param('nodeid');
137 if( defined($nodeid) )
139 $token = $config_tree->getNodeByNodeid( $nodeid );
140 return report_error($q, 'Cannot find nodeid: ' . $nodeid)
141 if not defined $token
143 else
145 $token = $config_tree->token('/');
148 else
150 $token = $config_tree->token($path);
151 return report_error($q, 'Invalid path')
152 if not defined $token
155 elsif( $token !~ m/^S/ and
156 not defined( $config_tree->path( $token ) ) )
158 return report_error($q, 'Invalid token')
161 my $view = $q->param('view');
162 $view = $q->param('v')
163 if not defined $view;
165 return report_error($q, 'Invalid view name: ' . $view)
166 if( defined $view and not $config_tree->viewExists($view) );
168 ( $fname, $mimetype, $expires ) =
169 $renderer->render( $config_tree, $token, $view, %options );
171 undef $config_tree;
176 &Torrus::DB::cleanupEnvironment();
178 if( defined( $options{'acl'} ) )
180 undef $options{'acl'};
183 if( defined($fname) )
185 if( not -e $fname )
187 return report_error($q, 'No such file or directory: ' . $fname);
190 Debug("Render returned $fname $mimetype $expires");
192 my $fh = IO::File->new( $fname );
193 if( defined( $fh ) )
195 print $q->header('-type' => $mimetype,
196 '-expires' => '+'.$expires.'s',
197 '-cookie' => \@cookies);
199 $fh->binmode(':raw');
200 my $buffer;
201 while( $fh->read( $buffer, 65536 ) )
203 print( $buffer );
205 $fh->close();
207 else
209 return report_error($q, 'Cannot open file ' . $fname . ': ' . $!);
212 else
214 return report_error($q, "Renderer returned error.\n" .
215 "Probably wrong directory permissions or " .
216 "directory missing:\n" .
217 $Torrus::Global::cacheDir);
220 if( not $Torrus::Renderer::globalDebug )
222 &Torrus::Log::setLevel('info');
224 return;
228 sub report_error
230 my $q = shift;
231 my $msg = shift;
233 my $v = $q->param('view');
234 if( defined $v and $v eq 'rpc' )
236 my $json = JSON->new();
237 $json->pretty;
238 $json->canonical;
239 my $res = Plack::Response->new(200,{'Content-Type' => 'application/json', 'Expires' => 'now'});
240 $res->body($json->encode({'success' => 0, 'error' => $msg}));
241 return $res
243 else
245 my $res = Plack::Response->new(500,{'Content-type' => 'text/plain', 'Expires' => 'now'});
246 $res->body('Error: ' . $msg);
247 return $res
250 return
253 sub _determine_uid
256 my $q = shift;
257 my $options = shift;
258 my $cookies = shift;
260 return 1 unless $Torrus::CGI::authorizeUsers;
262 my $uid;
264 $options->{acl} = Torrus::ACL->new();
266 my $hostauth = $q->param('hostauth');
267 if( defined( $hostauth ) )
269 $uid = $q->address();
270 $uid =~ s/\W/_/go;
271 my $password = $uid . '//' . $hostauth;
273 Debug('Host-based authentication for ' . $uid);
275 if( not $options->{acl}->authenticateUser( $uid, $password ) )
277 my $res = Plack::Response->new(403,{ 'Content-Type' => 'text/plain' });
278 $res->body('Host-based authentication failed for ' . $uid);
279 Info('Host-based authentication failed for ' . $uid);
280 return $res
283 Info('Host authenticated: ' . $uid);
284 $options->{uid} = $uid;
286 else
289 my $ses_id = $q->cookies->{'SESSION_ID'};
291 my $needs_new_session = 1;
292 my %session;
294 if( $ses_id )
296 # create a session object based on the cookie we got from the
297 # browser, or a new session if we got no cookie
298 my $eval_ret = eval
300 tie %session, 'Apache::Session::File', $ses_id, {
301 Directory => $Torrus::Global::sesStoreDir,
302 LockDirectory => $Torrus::Global::sesLockDir }
304 if( $eval_ret and not $@ )
306 if( $options->{'variables'}->{'LOGOUT'} )
308 tied( %session )->delete();
310 else
312 $needs_new_session = 0;
317 if( $needs_new_session )
319 tie %session, 'Apache::Session::File', undef, {
320 Directory => $Torrus::Global::sesStoreDir,
321 LockDirectory => $Torrus::Global::sesLockDir };
324 # might be a new session, so lets give them their cookie back
326 my %cookie = (-name => 'SESSION_ID',
327 -value => $session{'_session_id'});
329 if( $session{'uid'} )
331 $options->{'uid'} = $session{'uid'};
332 if( $session{'remember_login'} )
334 $cookie{'-expires'} = '+60d';
337 else
339 my $needsLogin = 1;
341 # POST form parameters
343 $uid = $q->param('uid');
344 my $password = $q->param('password');
345 if( defined( $uid ) and defined( $password ) )
347 if( $options->{acl}->authenticateUser( $uid, $password ) )
349 $session{'uid'} = $options->{uid} = $uid;
350 $needsLogin = 0;
351 Info('User logged in: ' . $uid);
353 if( $q->param('remember') )
355 $cookie{'-expires'} = '+60d';
356 $session{'remember_login'} = 1;
359 else
361 $options->{'authFailed'} = 1;
365 # if( $needsLogin )
367 # $options->{'urlPassTree'} = __PACKAGE__->_path_to_tree($q);
368 # for my $param ( 'token', 'path', 'nodeid',
369 # 'view', 'v' )
371 # my $val = $q->param( $param );
372 # $options->{'urlPassParams'}{$param} = $val
373 # if defined $val and $val ne ''
376 # ( $fname, $mimetype, $expires ) =
377 # $renderer->renderUserLogin( %$options );
379 # die('renderUserLogin returned undef') unless $fname;
382 untie %session;
384 push(@$cookies, $q->cookie(%cookie));
388 return $uid
392 sub _path_to_tree {
394 my $q = shift;
396 my $tree = $q->path_info;
397 $tree =~ s/^.*\/(.*)$/$1/;
399 return $tree
406 # Local Variables:
407 # mode: perl
408 # indent-tabs-mode: nil
409 # perl-indent-level: 4
410 # End: