~kosova/+junk/tuxfamily-twiki

« back to all changes in this revision

Viewing changes to foswiki/lib/Foswiki/Engine.pm

  • Committer: James Michael DuPont
  • Date: 2009-07-18 19:58:49 UTC
  • Revision ID: jamesmikedupont@gmail.com-20090718195849-vgbmaht2ys791uo2
added foswiki

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# See bottom of file for license and copyright information
 
2
=begin TML
 
3
 
 
4
---+!! package Foswiki::Engine
 
5
 
 
6
The engine class is a singleton that implements details about Foswiki's
 
7
execution mode. This is the base class and implements basic behavior.
 
8
 
 
9
Each engine should inherits from this and overload methods necessary
 
10
to achieve correct behavior.
 
11
 
 
12
=cut
 
13
 
 
14
package Foswiki::Engine;
 
15
 
 
16
use strict;
 
17
use Error qw( :try );
 
18
use Assert;
 
19
use Scalar::Util;
 
20
 
 
21
=begin TML
 
22
 
 
23
---++ ClassMethod new() -> $engine
 
24
 
 
25
Constructs an engine object.
 
26
 
 
27
=cut
 
28
 
 
29
sub new {
 
30
    my $proto = shift;
 
31
    my $class = ref($proto) || $proto;
 
32
    my $this  = {};
 
33
    return bless $this, $class;
 
34
}
 
35
 
 
36
=begin TML
 
37
 
 
38
---++ ObjectMethod run()
 
39
 
 
40
Start point to Runtime Engines.
 
41
 
 
42
=cut
 
43
 
 
44
sub run {
 
45
    my $this = shift;
 
46
    my $req  = $this->prepare;
 
47
    if ( defined $req ) {
 
48
        my $res = Foswiki::UI::handleRequest($req);
 
49
        $this->finalize( $res, $req );
 
50
    }
 
51
}
 
52
 
 
53
=begin TML
 
54
 
 
55
---++ ObjectMethod prepare() -> $req
 
56
 
 
57
Initialize a Foswiki::Request object by calling many preparation methods
 
58
and returns it, or a status code in case of error.
 
59
 
 
60
=cut
 
61
 
 
62
sub prepare {
 
63
    my $this = shift;
 
64
    my $req;
 
65
    try {
 
66
        $req = Foswiki::Request->new();
 
67
        $this->prepareConnection($req);
 
68
        $this->prepareQueryParameters($req);
 
69
        $this->prepareHeaders($req);
 
70
        $this->prepareCookies($req);
 
71
        $this->preparePath($req);
 
72
        $this->prepareBody($req);
 
73
        $this->prepareBodyParameters($req);
 
74
        $this->prepareUploads($req);
 
75
    }
 
76
    catch Foswiki::EngineException with {
 
77
        my $e   = shift;
 
78
        my $res = $e->{response};
 
79
        unless ( defined $res ) {
 
80
            $res = new Foswiki::Response();
 
81
            $res->header( -type => 'text/html', -status => $e->{status} );
 
82
            my $html = CGI::start_html( $e->{status} . ' Bad Request' );
 
83
            $html .= CGI::h1('Bad Request');
 
84
            $html .= CGI::p( $e->{reason} );
 
85
            $html .= CGI::end_html();
 
86
            $res->print($html);
 
87
        }
 
88
        $this->finalizeError($res);
 
89
        return $e->{status};
 
90
    }
 
91
    otherwise {
 
92
        my $e   = shift;
 
93
        my $res = Foswiki::Response->new();
 
94
        $res->header( -type => 'text/plain' );
 
95
        if (DEBUG) {
 
96
 
 
97
            # output the full message and stacktrace to the browser
 
98
            $res->print( $e->stringify() );
 
99
        }
 
100
        else {
 
101
            my $mess = $e->stringify();
 
102
            print STDERR $mess;
 
103
 
 
104
            # tell the browser where to look for more help
 
105
            my $text =
 
106
'Foswiki detected an internal error - please check your Foswiki logs and webserver logs for more information.'
 
107
              . "\n\n";
 
108
            $mess =~ s/ at .*$//s;
 
109
 
 
110
            # cut out pathnames from public announcement
 
111
            $mess =~ s#/[\w./]+#path#g;
 
112
            $text .= $mess;
 
113
            $res->print($text);
 
114
        }
 
115
        $this->finalizeError($res);
 
116
        return 500;    # Internal server error
 
117
    };
 
118
    return $req;
 
119
}
 
120
 
 
121
=begin TML
 
122
 
 
123
---++ ObjectMethod prepareConnection( $req )
 
124
 
 
125
Abstract method, must be defined by inherited classes.
 
126
   * =$req= - Foswiki::Request object to populate
 
127
 
 
128
Should fill remoteAddr, method and secure fields of =$req= object.
 
129
 
 
130
=cut
 
131
 
 
132
sub prepareConnection { }
 
133
 
 
134
=begin TML
 
135
 
 
136
---++ ObjectMethod prepareQueryParameters( $req, $queryString )
 
137
 
 
138
Should fill $req's query parameters field.
 
139
 
 
140
This method populates $req as it should if given $queryString parameter.
 
141
Subclasses may redefine this method and call SUPER with query string obtained.
 
142
 
 
143
=cut
 
144
 
 
145
sub prepareQueryParameters {
 
146
    my ( $this, $req, $queryString ) = @_;
 
147
    my @pairs = split /[&;]/, $queryString;
 
148
    my ( $param, $value, %params, @plist );
 
149
    foreach my $pair (@pairs) {
 
150
        ( $param, $value ) = split('=', $pair, 2);
 
151
        # url decode
 
152
        if ( defined $value ) {
 
153
            $value =~ tr/+/ /;
 
154
            $value =~ s/%([0-9A-F]{2})/chr(hex($1))/gei;
 
155
        }
 
156
        if ( defined $param ) {
 
157
            $param =~ tr/+/ /;
 
158
            $param =~ s/%([0-9A-F]{2})/chr(hex($1))/gei;
 
159
            push @{ $params{$param} }, $value;
 
160
            push @plist, $param;
 
161
        }
 
162
    }
 
163
    foreach my $param (@plist) {
 
164
        $req->queryParam( $param, $params{$param} );
 
165
    }
 
166
}
 
167
 
 
168
=begin TML
 
169
 
 
170
---++ ObjectMethod prepareHeaders( $req )
 
171
 
 
172
Abstract method, must be defined by inherited classes.
 
173
   * =$req= - Foswiki::Request object to populate
 
174
 
 
175
Should fill $req's headers and remoteUser fields.
 
176
 
 
177
=cut
 
178
 
 
179
sub prepareHeaders { }
 
180
 
 
181
=begin TML
 
182
 
 
183
---++ ObjectMethod preparePath( $req )
 
184
 
 
185
Abstract method, must be defined by inherited classes.
 
186
   * =$req= - Foswiki::Request object to populate
 
187
 
 
188
Should fill $req's uri and pathInfo fields.
 
189
 
 
190
=cut
 
191
 
 
192
sub preparePath { }
 
193
 
 
194
=begin TML
 
195
 
 
196
---++ ObjectMethod prepareCookies( $req )
 
197
 
 
198
   * =$req= - Foswiki::Request object to populate
 
199
 
 
200
Should fill $req's cookie field. This method take cookie data from
 
201
previously populated headers field and initializes from it. Maybe 
 
202
doesn't need to overload in children classes.
 
203
 
 
204
=cut
 
205
 
 
206
sub prepareCookies {
 
207
    my ( $this, $req ) = @_;
 
208
    eval { require CGI::Cookie };
 
209
    throw Error::Simple($@) if $@;
 
210
    $req->cookies( scalar CGI::Cookie->parse( $req->header('Cookie') ) )
 
211
      if $req->header('Cookie');
 
212
}
 
213
 
 
214
=begin TML
 
215
 
 
216
---++ ObjectMethod prepareBody( $req )
 
217
 
 
218
Abstract method, must be defined by inherited classes.
 
219
   * =$req= - Foswiki::Request object to populate
 
220
 
 
221
Should perform any initialization tasks related to body processing.
 
222
 
 
223
=cut
 
224
 
 
225
sub prepareBody { }
 
226
 
 
227
=begin TML
 
228
 
 
229
---++ ObjectMethod prepareBodyParameters( $req )
 
230
 
 
231
Abstract method, must be defined by inherited classes.
 
232
   * =$req= - Foswiki::Request object to populate
 
233
 
 
234
Should fill $req's body parameters.
 
235
 
 
236
=cut
 
237
 
 
238
sub prepareBodyParameters { }
 
239
 
 
240
=begin TML
 
241
 
 
242
---++ ObjectMethod prepareUploads( $req )
 
243
 
 
244
Abstract method, must be defined by inherited classes.
 
245
   * =$req= - Foswiki::Request object to populate
 
246
 
 
247
Should fill $req's uploads field. Its a hashref whose keys are
 
248
parameter names and values Foswiki::Request::Upload objects.
 
249
 
 
250
=cut
 
251
 
 
252
sub prepareUploads { }
 
253
 
 
254
=begin TML
 
255
 
 
256
---++ ObjectMethod finalize($res, $req)
 
257
 
 
258
Finalizes the request by calling many methods to send response to client and 
 
259
take any appropriate finalize actions, such as delete temporary files.
 
260
   * =$res= is the Foswiki::Response object
 
261
   * =$req= it the Foswiki::Request object.
 
262
 
 
263
=cut
 
264
 
 
265
sub finalize {
 
266
    my ( $this, $res, $req ) = @_;
 
267
    $this->finalizeUploads($res, $req);
 
268
    $this->finalizeHeaders( $res, $req );
 
269
    $this->finalizeBody($res);
 
270
}
 
271
 
 
272
=begin TML
 
273
 
 
274
---++ ObjectMethod finalizeUploads( $res, $req )
 
275
 
 
276
Abstract method, must be defined by inherited classes.
 
277
   * =$res= - Foswiki::Response object to get data from
 
278
   * =$req= - Foswiki::Request object to get data from
 
279
 
 
280
Should delete any temp files created in preparation phase.
 
281
 
 
282
=cut
 
283
 
 
284
sub finalizeUploads { }
 
285
 
 
286
=begin TML
 
287
 
 
288
---++ ObjectMethod finalizeError( $res )
 
289
 
 
290
Called if some engine especific error happens.
 
291
 
 
292
   * =$res= - Foswiki::Response object to get data from
 
293
 
 
294
=cut
 
295
 
 
296
sub finalizeError {
 
297
    my ( $this, $res ) = @_;
 
298
    $this->finalizeHeaders($res);
 
299
    $this->finalizeBody($res);
 
300
}
 
301
 
 
302
=begin TML
 
303
 
 
304
---++ ObjectMethod finalizeHeaders( $res, $req )
 
305
 
 
306
Base method, must be redefined by inherited classes. For convenience
 
307
this method deals with HEAD requests related stuff. Children classes
 
308
should call SUPER.
 
309
   * =$res= - Foswiki::Response object to get data from
 
310
   * =$req= - Foswiki::Request object to get data from
 
311
 
 
312
Should call finalizeCookies and then send $res' headers to client.
 
313
 
 
314
=cut
 
315
 
 
316
sub finalizeHeaders {
 
317
    my ( $this, $res, $req ) = @_;
 
318
    $this->finalizeCookies($res);
 
319
    if ( $req && $req->method() && uc($req->method()) eq 'HEAD' ) {
 
320
        $res->body('');
 
321
        $res->deleteHeader('Content-Length');
 
322
    }
 
323
}
 
324
 
 
325
=begin TML
 
326
 
 
327
---++ ObjectMethod finalizeCookies( $res )
 
328
 
 
329
   * =$res= - Foswiki::Response object to both get data from and populate
 
330
 
 
331
Should populate $res' headers field with cookies, if any.
 
332
 
 
333
=cut
 
334
 
 
335
sub finalizeCookies {
 
336
    my ( $this, $res ) = @_;
 
337
 
 
338
    # SMELL: Review comment below, from CGI:
 
339
    #    if the user indicates an expiration time, then we need
 
340
    #    both an Expires and a Date header (so that the browser is
 
341
    #    uses OUR clock)
 
342
    $res->pushHeader( 'Set-Cookie',
 
343
        Scalar::Util::blessed $_
 
344
          && $_->isa('CGI::Cookie') ? $_->as_string : $_ )
 
345
      foreach $res->cookies;
 
346
}
 
347
 
 
348
=begin TML
 
349
 
 
350
---++ ObjectMethod finalizeBody( $res )
 
351
 
 
352
   * =$res= - Foswiki::Response object to get data from
 
353
 
 
354
Should send $res' body to client. This method calls =write()=
 
355
as needed, sou engines should redefine that method insted of this one.
 
356
 
 
357
=cut
 
358
 
 
359
sub finalizeBody {
 
360
    my ( $this, $res ) = @_;
 
361
    my $body = $res->body;
 
362
    return unless $body;
 
363
    $this->prepareWrite($res);
 
364
    if ( Scalar::Util::blessed($body) && $body->can('read')
 
365
        or ref $body eq 'GLOB' )
 
366
    {
 
367
        while ( !eof $body ) {
 
368
            read $body, my $buffer, 4096;
 
369
            last unless $this->write($buffer);
 
370
        }
 
371
        close $body;
 
372
    }
 
373
    else {
 
374
        $this->write($body);
 
375
    }
 
376
}
 
377
 
 
378
=begin TML
 
379
 
 
380
---++ ObjectMethod prepareWrite( $res )
 
381
 
 
382
Abstract method, must be defined by inherited classes.
 
383
   * =$res= - Foswiki::Response object to get data from
 
384
 
 
385
Shold perform any task needed before writing.
 
386
That's ok if none needed ;-)
 
387
 
 
388
=cut
 
389
 
 
390
sub prepareWrite { }
 
391
 
 
392
=begin TML
 
393
 
 
394
---++ ObjectMethod write( $buffer )
 
395
 
 
396
Abstract method, must be defined by inherited classes.
 
397
   * =$buffer= - chunk of data to be sent
 
398
 
 
399
Should send $buffer to client.
 
400
 
 
401
=cut
 
402
 
 
403
sub write {
 
404
    ASSERT('Pure virtual method - should never be called');
 
405
}
 
406
 
 
407
1;
 
408
__DATA__
 
409
# Module of Foswiki - The Free and Open Source Wiki, http://foswiki.org/
 
410
#
 
411
# Copyright (C) 2008-2009 Foswiki Contributors. Foswiki Contributors
 
412
# are listed in the AUTHORS file in the root of this distribution.
 
413
# NOTE: Please extend that file, not this notice.
 
414
#
 
415
# Additional copyrights apply to some or all of the code in this
 
416
# file as follows:
 
417
#
 
418
# Copyright (C) 1999-2007 Peter Thoeny, peter@thoeny.org
 
419
# and TWiki Contributors. All Rights Reserved. TWiki Contributors
 
420
# are listed in the AUTHORS file in the root of this distribution.
 
421
#
 
422
# This module is based/inspired on Catalyst framework. Refer to
 
423
#
 
424
# http://search.cpan.org/~mramberg/Catalyst-Runtime-5.7010/lib/Catalyst.pm
 
425
#
 
426
# for credits and liscence details.
 
427
#
 
428
# This program is free software; you can redistribute it and/or
 
429
# modify it under the terms of the GNU General Public License
 
430
# as published by the Free Software Foundation; either version 2
 
431
# of the License, or (at your option) any later version. For
 
432
# more details read LICENSE in the root of this distribution.
 
433
#
 
434
# This program is distributed in the hope that it will be useful,
 
435
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
436
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
437
#
 
438
# As per the GPL, removal of this notice is prohibited.