source: project/wiki/eggref/4/spiffy @ 13306

Last change on this file since 13306 was 13306, checked in by felix winkelmann, 12 years ago

added xlib page and removed download links

File size: 24.9 KB
Line 
1[[tags: egg]]
2
3== Spiffy
4
5[[toc:]]
6
7=== Description
8
9A small web-server written in [[http://www.call-with-current-continuation.org|Chicken]].
10
11=== Author
12
13[[Felix Winkelmann]].  Currently maintained by [[Peter Bex]].
14
15=== Requirements
16
17Requires the [[intarweb]], [[uri-common]], [[defstruct]], [[matchable]]
18and [[sendfile]] extensions.
19
20=== Documentation
21
22Spiffy is a web-server library for the Chicken Scheme system. It's
23quite easy to set up and use (whether as a library or a standalone
24server application) and it can be customized in numerous ways.
25
26=== Starting the server
27
28<procedure>(start-server [port: port-number])</procedure>
29
30Starts the server, to listen on the given port. Other configuration
31can be tweaked through SRFI-39 parameters. These are listed below.
32Once the server is started, server behaviour can be controlled through
33these parameters as well. By default, Spiffy will only serve static
34files. On directories, it will give a "403 forbidden", unless there
35is an index-file. If there is, that file's contents will be shown.
36
37All arguments directly supplied to {{start-server}} override the
38configuration parameter values.
39
40{{Port-number}} defaults to the value of {{server-port}} (see below).
41
42=== Configuration parameters
43
44The following parameters can be used to control spiffy's behaviour.
45Besides these parameters, you can also influence spiffy's behaviour by
46tweaking the [[intarweb]] parameters.
47
48<parameter>(server-software [product])</parameter>
49
50The server software product description. This should be a valid
51product value as used in the server and user-agent headers by intarweb;
52this is a list of lists. The inner lists contain the product name,
53the product version and a comment, all either a string or {{#f}}.
54Default: {{(("Spiffy" "a.b" "Running on Chicken x.y"))}}, with {{a.b}}
55being the Spiffy major/minor version and {{x.y}} being Chicken's.
56
57<parameter>(root-path [path])</parameter>
58
59The path to the document root, for the current vhost.
60Defaults to {{"./web"}}.
61
62<parameter>(server-port [port-number])</parameter>
63
64The port number on which to listen. Defaults to 8080.
65
66<parameter>(spiffy-user [name-or-uid])</parameter>
67
68The name or UID of a user to switch to just after binding the
69port. This only works if you start Spiffy as root, so it can bind port
7080 and then drop privileges. If {{#f}}, no switch will occur.
71Defaults to {{#f}}.
72
73<parameter>(spiffy-group [name-or-gid])</parameter>
74
75The name or GID of a group to switch to just after binding the
76port. This only works if you start Spiffy as root, so it can bind port
7780 and then drop privileges. If {{#f}}, it will be set to the primary
78group of {{spiffy-user}} if the user was selected. Otherwise, no
79change will occur.  Defaults to {{#f}}.
80
81<parameter>(index-files [file-list])</parameter>
82
83A list of filenames which are to be used as index files to serve when
84the requested URL identifies a directory.  Defaults to
85{{'("index.html" "index.xhtml")}}
86
87<parameter>(mime-type-map [extension->mimetype-list])</parameter>
88
89An alist of extensions (strings) to mime-types (symbols), to use
90for the content-type header when serving up a static file. Defaults to
91  (("xml" . text/xml)
92   ("html" . text/html)
93   ("xhtml" . text/xhtml+xml)
94   ("js"  . text/javascript)
95   ("pdf" . application/pdf)
96   ("css" . text/css)
97   ("png" . image/png)
98   ("ico" . image/x-icon)
99   ("gif" . image/gif)
100   ("jpeg" . image/jpeg)
101   ("jpg" . image/jpeg)
102   ("svg" . image/svg+xml)
103   ("bmp" . image/bmp)
104   ("txt" . text/plain))
105
106<parameter>(default-mime-type [mime-type])</parameter>
107
108The mime-type (a symbol) to use if none was found in the
109{{mime-type-map}}. Defaults to {{'application/octet-stream}}
110
111<parameter>(default-host [hostname])</parameter>
112
113The host name to use when no virtual host could be determined from the
114request.  See the section on virtual hosts below.
115
116<parameter>(vhost-map [host-regex->vhost-handler])</parameter>
117
118A mapping of virtual hosts (regex) to handlers (procedures of one
119argument; a continuation thunk). See the section on virtual hosts
120below. Defaults to {{`((".*" . ,(lambda (continue) (continue))))}}
121
122<parameter>(file-extension-handlers [extension->handler-list])</parameter>
123
124An alist mapping file extensions (strings) to handler procedures
125(lambdas of one argument; the file name relative to the webroot).
126Defaults to {{'()}}. If no handler was found, defaults to just sending
127a static file.
128
129<parameter>(spiffy-access-log [log-file-or-port])</parameter>
130
131Filename (string) or port to append access log output to.  Default:
132{{#f}} (disabled)
133
134<parameter>(spiffy-error-log [log-file-or-port])</parameter>
135
136Filename (string) or port to which error messages from evaluated code
137should be output. Default: {{(current-error-port)}}
138
139<parameter>(spiffy-debug-log [log-file-or-port])</parameter>
140
141Filename (string) or port to write debugging messages to.  Default:
142{{#f}} (disabled)
143
144<parameter>(access-file [string])</parameter>
145
146The name of an access file, or {{#f}} if not applicable.  This file is
147read when the directory is entered by the directory traversal system,
148and allows you to write dynamic handlers that can assign new values
149for parameters only for resources below that directory, very much like
150adding parameters in code before calling a procedure.  See the section
151"Access files" for more information.
152
153=== Handlers
154
155Besides "static" configuration, Spiffy also has several handlers for
156when something is to be served.
157
158<parameter>(handle-directory [proc])</parameter>
159
160The handler for directory entries. If the requested URL points to a
161directory which has no index file, this handler is invoked. It is a
162procedure of one argument, the path (a string) relative to the
163webroot. Defaults to a procedure which returns a "403 forbidden".
164
165<parameter>(handle-file [proc])</parameter>
166
167The handler for files. If the requested URL points to a file, this
168handler is invoked to serve the file. It is a procedure of one
169argument, the path (a string) relative to the webroot. Defaults to a
170procedure which sets the content-type and determines a handler based
171on the {{file-extension-handlers}}, or {{send-static-file}} if none
172was found.
173
174<parameter>(handle-not-found [proc])</parameter>
175
176The handler for nonexisting files. If the requested URL does not point
177to an existing file or directory, this procedure is called. It is a
178procedure of one argument, the path (a string) that was
179requested. This path should be interpreted as being relative to the
180webroot (even though it points to no existing file). Defaults to a
181procedure which returns a "404 Not found".
182
183<parameter>(handle-exception [proc])</parameter>
184
185The handler for when an exception occurs. This defaults to a procedure
186that logs the error to the error log. While debugging or developing, it
187may be more convenient to use a procedure that sends the error back to
188the client:
189
190<enscript highlight=scheme>
191(handle-exception
192  (lambda (exn chain)
193    (send-status 500 "Internal server error" (build-error-message exn chain))))
194</enscript>
195
196<parameter>(handle-access-logging [proc])</parameter>
197
198The handler for access logging. This is a procedure of zero arguments
199which should write a line to the access log. Defaults to a procedure which
200writes a line to {{access-log}} which looks like this:
201
202   127.0.0.1 [Sun Nov 16 15:16:01 2008] "GET http://localhost:8080/foo HTTP/1.1" Links (2.2; NetBSD 5.99.01 macppc; x)
203
204=== Runtime information
205
206During the handling of a request, Spiffy adds more information to the
207environment by parameterizing the following parameters whenever the
208information becomes available:
209
210<parameter>(current-request [request])</parameter>
211
212An intarweb request-object that defines the current request. Available
213from the moment the request comes in and is parsed. Contains, among
214other things, the query parameters and the request-headers, in fully
215parsed form (as intarweb returns them).
216
217<parameter>(current-response [response])</parameter>
218
219An intarweb response-object that defines the current
220response. Available from the same time current-request is available.
221This keeps getting updated along the way, while the response data is
222being refined (like when headers are being added).
223
224<parameter>(current-file [path])</parameter>
225
226The path to the requested file (a string). Available from the moment
227Spiffy determined the requested URL points to a file (just before the
228{{handle-file}} procedure is called). This file is relative to the
229{{root-path}}.
230
231<parameter>(current-pathinfo [path])</parameter>
232
233The trailing path ''fragments'' (a list of strings) that were passed
234in the URL after the requested filename. Available from the moment
235Spiffy determined the requested URL points to a file (just before the
236{{handle-file}} procedure is called).
237
238<parameter>(remote-address [address])</parameter>
239
240The IP address (a string) of the user-agent performing the current
241request.
242
243<parameter>(local-address [address])</parameter>
244
245The IP address (a string) on which the current request came in.
246
247=== Virtual hosts
248
249Spiffy has support for virtual hosting, using the HTTP/1.1 Host
250header.  This allows you to use one Spiffy instance running on one IP
251address/port number to serve multiple webpages, as determined by the
252hostname that was requested.
253
254The virtual host is defined by a procedure, which can set arbitrary
255parameters on-the-fly. It is passed a continuation thunk, which it
256should explicitly call if it wants the processing to continue.  The
257most used parameter in virtual host setups is the {{root-path}}
258parameter, so that another docroot can be selected based on the
259requested hostname, showing different websites for different hosts:
260
261<examples><example>
262<expr>
263(vhost-map `(("foo\\.bar\\.com" .
264               ,(lambda (continue)
265                  (parameterize ((file-extension-handlers
266                                   `(("ssp" . ,ssp-handler) ("ws" . ,web-scheme-handler)))
267                                 (root-path "/var/www/domains/foo.bar.com"))
268                     (continue))))
269             (,(glob->regexp "*.domain.com") .
270                ,(lambda (continue)
271                   (parameterize ((file-extension-handlers
272                                    `(("php" . ,(cgi-handler* "/usr/pkg/bin/php"))))
273                                  (root-path "/var/www/domains/domain.com"))
274                     (continue))))))
275</expr>
276</example></examples>
277
278In this example, if a client accesses
279{{foo.bar.com/mumble/blah.html}}, the file
280{{/var/www/domains/foo.bar.com/mumble/blah.html}} will be served.  Any
281files ending in {{.ssp}} or {{.ws}} will be served by the
282corresponding file type handler.  If there's any PHP file, its source
283will simply be displayed.  In case of
284{{my.domain.com/something/bar.html}}, the file
285{{/var/www/domains/domain.com/something/bar.html}} will be served.  If
286there's a {{.ssp}} or {{.ws}} file there, it will not be interpreted.
287Its source will be displayed instead.  A {{.php}} file, on the other
288hand, will be passed via CGI to the program {{/usr/pkg/bin/php}}.
289
290Domain names are mapped to a lambda that sets up any parameters it
291wants to override from the defaults.  The host names are matched using
292{{string-match}}.  If the host name is not yet a regexp, it will be
293converted to a ''case-insensitive'' regexp.
294
295=== Access files
296
297Fine-grained access-control can be implemented by using so-called
298access files.  When a request for a specific file is made and a file
299with the name given in the {{access-file}} parameter exists in any
300directory between the {{root-dir}} of that vhost and the directory in
301which the file resides, then the access file is loaded as an
302s-expression containing a function and is evaluated with a single
303argument, the function that should be called to continue processing
304the request.
305
306This works just like vhosting.  The function that gets called can call
307{{parameterize}} to set additional constraints on the code that
308handles deeper directories.
309
310For example, if we evaluate {{(access-file ".access")}} before
311starting the server, and we put the following code in a file named
312{{.access}} into the root-directory, then all accesses from localhost
313to any file in the root-directory or any subdirectory will be denied:
314
315<enscript highlight=scheme>
316 (lambda (continue)
317   (if (string=? (remote-address (current-request)) "127.0.0.1")
318       (continue)
319       (send-status 403 "Forbidden" "Sorry, you're not allowed here")))
320</enscript>
321
322If we only want to deny access to files that start with an X, put this
323in the {{.access}} file:
324
325<enscript highlight=scheme>
326 (lambda (continue)
327   (let ((old-handler (handle-file)))
328     (parameterize ((handle-file
329                      (lambda (path)
330                        (if (not (string-prefix? "X" (pathname-file path)))
331                            (send-status 403 "Forbidden" "No X-files allowed!")
332                            (old-handler path)))))
333       (continue))))
334</enscript>
335
336Of course, access files can be used for much more than just access
337checks.  One can put anything in them that could be put in vhost
338configuration or in top-level configuration.
339
340They are very useful for making deployable web applications, so you
341can just drop a directory on your server which has its own
342configuration embedded in an access file in the root directory of the
343application, without having to edit the server's main configuration
344files.
345
346=== Procedures and macros
347
348The following procedures and macros can be used in dynamic web
349programs, or dynamic server configuration:
350
351<procedure>(with-headers new-headers thunk)</procedure>
352
353Call {{thunk}} with the header list {{new-headers}}. This
354parameterizes the current response to contain the new headers.  The
355existing headers are extended with {{new-headers}} through intarweb's
356{{headers}} procedure.
357
358<procedure>(write-logged-response)</procedure>
359
360This procedure simply writes {{current-response}} after calling
361{{handle-access-logging}}. Responses should always go through this
362procedure instead of directly using {{write-response}} from intarweb.
363
364<procedure>(log-to log format . rest)</procedure>
365
366Write a printf-style format string to the specified log (one of
367{{access-log}}, {{error-log}} or {{debug-log}}). {{format}} is a
368{{printf}}-style format string, and rest arguments should match
369the arguments one would pass to printf. A newline is appended to
370the end of the log message automatically.
371
372<procedure>(send-status code reason [message])</procedure>
373
374Easy way to send a page and a status code to the client.  The optional
375message is a string containing HTML to add in the body of the
376response. Example:
377
378<examples><example>
379<expr>
380(send-status 404 "Not found"
381 "Sorry, page not found! Please try <a href='/search.ws'>our search page</a>")
382</expr>
383</example></examples>
384
385<procedure>(send-static-file filename)</procedure>
386
387Send a file to the client. This sets the {{content-length}} header and
388tries to send the file as quickly as possible to the client. The
389filename is interpreted relative to {{root-path}}.
390
391<procedure>(restart-request request)</procedure>
392
393Restart the entire request-handling starting at the point where the
394request was just parsed. The argument is the new request to use.
395Be careful, this makes it very easy to introduce unwanted endless loops!
396
397<procedure>(htmlize string) => string</procedure>
398
399Encode "special" html symbols like tag and attribute characters so
400they will not be interpreted by the browser.
401
402<procedure>(build-error-message exn chain [raw-output])</procedure>
403
404Build an error message for the exception {{exn}}, with call chain
405{{chain}}. Defaults to HTML output, unless {{raw-output}} is given and
406nonfalse.
407
408<procedure>(server-root-uri)</procedure>
409
410An absolute URI (uri-common object) which can be used as a base for
411relative references (with absolute paths). It includes the scheme,
412current vhost, port and a root path of "/".
413
414=== Modules
415
416This section will describe what the various modules that come with
417Spiffy are and how they work.
418
419==== ssp-handler
420
421SSP, or Scheme Server Pages, are a way to embed Scheme in HTML
422pages. Files with an extension of {{.ssp}} are handled specifically,
423by replacing occurrences of certain reserved tags with Scheme code.
424There are two possible forms, either the long version, where all
425output is redirected to the HTTP response port, or the short, where
426the result of the embedded expression is displayed in the response.
427The tags default to {{<?scheme}} and {{<?}}, see Configuration for how
428to change them.
429
430<enscript highlight=scheme>
431   <html><body>
432   <ol><?scheme (for-each (lambda (i) (printf "<li>~S~%" i)) (iota 5))?></ol>
433   <br />
434   <b><?(call-with-values (lambda () (user-information (current-user-id))) (lambda (name . _) name))?><b>
435   </body></html>
436</enscript>
437
438would generate for example something like this:
439
440     1. 0
441     2. 1
442     3. 2
443     4. 3
444     5. 4
445
446  (felix x 500 100 /home/felix /bin/bash)
447
448When a {{.ssp}} file is loaded the first time, or when it has been
449modified, then a translation takes place that generates a loadable
450Scheme source file (with the extension {{.sspx}}, in the same
451directory as the original file) from the original data, so in the
452above example something like this would be generated:
453
454<enscript highlight=scheme>
455  (let ()
456    (display "<html><body>\n<ol>")
457    (for-each (lambda (i) (printf "<li>~S~%" i)) (iota 5))
458    (display "</ol>\n<br />\n<b>")
459    (display (call-with-values (lambda () (user-information (current-user-id))) (lambda (name . _) name)))
460    (display "<b>\n</body></html>\n") )
461</enscript>
462
463Note that the body is evaluated in a {{(let () ...)}} form.
464
465Note: each request runs in a separate thread, so code in {{.ssp}}
466pages should take care when using global variables.
467
468===== Configuration
469
470The SSP handler can be configured with the following options:
471
472<parameter>(ssp-short-open-tag [tag-regexp])</parameter>
473
474The opening tag for short fragments. Default: {{<?}}
475
476<parameter>(ssp-long-open-tag [tag-regexp])</parameter>
477
478The opening tag for long fragments. Default: {{<?scheme}}
479
480<parameter>(ssp-close-tag [tag-regexp])</parameter>
481
482The closing tag for Scheme fragments in {{.ssp}} files. Default: {{?>}}
483
484<parameter>(ssp-eval-environment [environment])</parameter>
485
486The environment passed to {{eval}} when evaluating Scheme code inside
487{{.ssp}}-pages.  Default: {{interaction-environment}}
488
489===== Runtime information
490
491For the duration of evaluating a SSP page, the following parameters
492will have a value assigned to them:
493
494<parameter>(current-workdir [path])</parameter>
495
496During execution, the current working directory of the SSP handler.
497Any of the "include" procedures (ssp-include, ssp-stringize) will
498interpret their file arguments to be relative to this directory.
499
500<parameter>(ssp-exit-handler [handler])</parameter>
501
502During execution of an ssp page, {{ssp-exit-handler}} is bound to a
503procedure that will finish the current page, ignoring any further
504content or code.
505
506===== Procedures
507
508The ssp-handler module adds the following procedures to the environment:
509
510<procedure>(ssp-handler filename)</procedure>
511
512The handler itself, which should be used in the {{file-extension-handlers}}
513parameter list.
514
515<procedure>(ssp-include filename)</procedure>
516
517Translates the file {{filename}} into Scheme by replacing {{<?scheme ... ?>}}
518and {{<? ... ?>}} sequences (if needed) and writes the translated contents
519to the current output-port.
520
521<procedure>(ssp-stringize FILENAME)</procedure>
522
523Similar to {{ssp-include}}, but instead of writing the translated
524text, the text is returned as a string.
525
526==== web-scheme-handler
527
528Another way of executing Scheme code to produce content are {{.ws}}
529files: these should contain a Scheme expression that is expected to
530evaluate to a string which will be directly written as the response to
531the current request. This facility is intended for Scheme code that
532uses the [[web-scheme]] extension.
533
534You can use the {{web-scheme-handler}} for any Scheme file which
535returns HTML as a string or which has a side-effect of outputting the
536HTML. If it's the latter, make sure the final statement in your file
537does not return a string or it will be appended to the output (just
538like in the {{csi}} REPL).
539
540'''Tip''' This handler type is perfect not only for web-scheme but
541also for when you're using {{SRV:send-reply}} with SXML or for example a
542wiki-to-string translator.
543
544Note: each request runs in a separate thread, so code in {{.ws}} pages
545should take care when using global variables.
546
547Note: web-scheme-handler is a separate extension and must be imported
548as such.
549
550<enscript highlight=scheme>
551(require-extension web-scheme-handler)
552</enscript>
553
554===== Configuration
555
556The Web-scheme handler can be configured with the following options:
557
558<parameter>(web-scheme-eval-environment [environment])</parameter>
559
560The environment passed to {{eval}} when evaluating Scheme code inside
561{{.ws}}-pages.  Default: {{interaction-environment}}
562
563===== Procedures
564
565The Web-scheme handler adds only one procedure to the environment:
566
567<procedure>(web-scheme-handler filename)</procedure>
568
569The handler itself, which should be used in the {{file-extension-handlers}}
570parameter list.
571
572
573==== cgi-handler
574
575Spiffy supports [[http://www.ietf.org/rfc/rfc3875|CGI/1.1 as specified by RFC 3875]].
576
577All request headers will be passed as environment variables to the CGI
578program, prefixed with {{"HTTP_"}}, and converted to uppercase, with
579hyphens ("-") replaced by an underscore ("_"). The CGI program will
580receive the request body in unparsed form from stdin and should write
581a complete HTTP response to stdout.  Any headers that are missing but
582required for HTTP will be added by Spiffy.  For more info on how a CGI
583script is called, consult the spec.
584
585The {{AUTH_TYPE}} and {{REMOTE_USER}} environment variables are
586currently not set during invocation of CGI subprocesses.
587The {{REMOTE_IDENT}} environment variable is not and never will be supported.
588
589===== Configuration
590
591CGI handler can be configured with the following parameters:
592
593<procedure>(cgi-default-environment [env-alist])</procedure>
594
595The environment variables that should be in the default environnment
596of every CGI program.  Variables like {{SCRIPT_NAME}} will be added
597dynamically to the end of this alist.
598
599Default:
600
601<enscript highlight=scheme>
602(("GATEWAY_INTERFACE" . "CGI/1.1"))
603</enscript>
604
605===== Procedures
606
607CGI-handler adds two procedures to the environment:
608
609<procedure>(cgi-handler filename [interpreter])</procedure>
610
611The cgi handler simply calls CGI scripts. It is assumed the
612requested file is executable if no interpreter is given.
613(If used as a regular handler, it will only receive the
614filename).
615
616<procedure>(cgi-handler* [interpreter])</procedure>
617
618The {{cgi-handler*}} procedure is usually more useful.  It allows you
619to define an interpreter to use for files and returns a new handler.
620See the example above for {{file-extension-handlers}}.
621
622
623==== simple-directory-handler
624
625In order to get directory listings, you can use
626{{simple-directory-handler}}.  Just assign the
627{{simple-directory-handler}} to {{handle-directory}} and you're set.
628
629===== Configuration
630
631The simple directory handler has a few configuration options:
632
633<procedure>(simple-directory-dotfiles? [dotfiles?])</procedure>
634
635Determines if dotfiles should show up in the directory listings.
636Default: {{#f}}
637
638<procedure>(simple-directory-display-file [displayer])</procedure>
639
640A lambda that accepts three arguments: the remote filename, the local
641filename and a boolean that says if the file is a directory.  This
642lambda should output a table row with the desired information.
643Defaults to a lambda that prints the name, size and date when the file
644was last modified.
645
646===== Procedures
647
648The simple-directory handler adds only one procedure to the environment:
649
650<procedure>(simple-directory-handler pathname)</procedure>
651
652The handler itself, which should be used in the {{handle-directory}}
653parameter.
654
655
656=== Changelog
657
658* 4.0 Rewrite from scratch, using Intarweb
659* pre-4.0 See the changelog for the old [[spiffy]]
660
661=== License
662
663  Copyright (c) 2005-2008, Felix L. Winkelmann and Peter Bex
664  All rights reserved.
665 
666  Redistribution and use in source and binary forms, with or without
667  modification, are permitted provided that the following conditions are
668  met:
669 
670  Redistributions of source code must retain the above copyright
671  notice, this list of conditions and the following disclaimer.
672 
673  Redistributions in binary form must reproduce the above copyright
674  notice, this list of conditions and the following disclaimer in the
675  documentation and/or other materials provided with the distribution.
676 
677  Neither the name of the author nor the names of its contributors may
678  be used to endorse or promote products derived from this software
679  without specific prior written permission.
680 
681  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
682  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
683  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
684  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
685  COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
686  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
687  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
688  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
689  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
690  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
691  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
692  OF THE POSSIBILITY OF SUCH DAMAGE.
Note: See TracBrowser for help on using the repository browser.