Compare commits
1 Commits
main
...
surflet-se
Author | SHA1 | Date |
---|---|---|
![]() |
e1ec98d90b |
.gitignoreCOPYINGMakefileReadmestart-extended-web-serverstart-web-server
doc
html
latex
scheme
ftpd
httpd
access-control.scmcgi-server.scmcore.scmerror.scmfile-dir-handler.scmhandlers.scmhttp-top.scminfo-gateway.scmlogging.scmoptions.scmrequest.scmresponse.scmrman-gateway.scmserver.scmseval.scm
lib
cgi-script.scmcrlf-io.scmdns.scmformat-net.scmftp-library.scmftp.scmhandle-fatal-error.scmhtmlout.scmls.scmnetrc.scmnettime.scmparse-forms.scmpop3.scmrate-limit.scmrfc822.scmsmtp.scmsunet-utilities.scmuri.scmurl.scm
packages.scmxml
web-server
|
@ -1,31 +0,0 @@
|
|||
# CVS default ignores begin
|
||||
tags
|
||||
TAGS
|
||||
.make.state
|
||||
.nse_depinfo
|
||||
*~
|
||||
\#*
|
||||
.#*
|
||||
,*
|
||||
_$*
|
||||
*$
|
||||
*.old
|
||||
*.bak
|
||||
*.BAK
|
||||
*.orig
|
||||
*.rej
|
||||
.del-*
|
||||
*.a
|
||||
*.olb
|
||||
*.o
|
||||
*.obj
|
||||
*.so
|
||||
*.exe
|
||||
*.Z
|
||||
*.elc
|
||||
*.ln
|
||||
core
|
||||
# CVS default ignores end
|
||||
test-packages.scm
|
||||
test
|
||||
SSAX
|
27
COPYING
27
COPYING
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 1994 by Brian D. Carlstrom and Olin Shivers.
|
||||
Copyright (c) 1996-2001 by Mike Sperber.
|
||||
Copyright (c) 1999-2001 by Martin Gasbichler.
|
||||
Copyright (c) 1998-2001 by Eric Marsden.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
47
Makefile
47
Makefile
|
@ -1,47 +0,0 @@
|
|||
SHELL = /bin/sh
|
||||
|
||||
version_id = 1.0
|
||||
|
||||
TEMPDIR = /tmp
|
||||
|
||||
sunet_files = Readme \
|
||||
cgi-script.scm \
|
||||
cgi-server.scm \
|
||||
conditionals.scm \
|
||||
crlf-io.scm \
|
||||
htmlout.scm \
|
||||
http-top.scm \
|
||||
httpd/access-control.scm \
|
||||
httpd/core.scm \
|
||||
httpd/error.scm \
|
||||
httpd/handlers.scm \
|
||||
info-gateway.scm \
|
||||
rman-gateway.scm \
|
||||
modules.scm \
|
||||
parse-forms.scm \
|
||||
program-modules.scm \
|
||||
rfc822.scm \
|
||||
scheme-program-server.scm \
|
||||
server.scm \
|
||||
seval.scm \
|
||||
smtp.scm \
|
||||
stringhax.scm \
|
||||
su-httpd.txt \
|
||||
toothless.scm \
|
||||
uri.scm \
|
||||
url.scm
|
||||
|
||||
|
||||
sunet-$(version_id).tar.gz: $(sunet_files)
|
||||
sunet_root=`pwd`; \
|
||||
mkdir $(TEMPDIR)/sunet-$(version_id); \
|
||||
cp $(sunet_files) $(TEMPDIR)/sunet-$(version_id); \
|
||||
cd $(TEMPDIR); \
|
||||
tar czf sunet-$(version_id).tar.gz sunet-$(version_id); \
|
||||
mv sunet-$(version_id).tar.gz $$sunet_root; \
|
||||
rm -rf sunet-$(version_id)
|
||||
|
||||
.PHONY: tags
|
||||
tags:
|
||||
find . -name "*.scm" | etags -
|
||||
|
47
Readme
47
Readme
|
@ -1,47 +0,0 @@
|
|||
The SU Net package, version 1.0
|
||||
===============================
|
||||
|
||||
This directory contains my code for doing Net hacking from Scheme/scsh.
|
||||
It includes:
|
||||
An smtp client library.
|
||||
Forge mail from the comfort of your own Scheme process.
|
||||
|
||||
rfc822 header library
|
||||
Read email-style headers. Useful in several contexts (smtp, http, etc.)
|
||||
|
||||
Simple structured HTML output library
|
||||
Balanced delimiters, etc. htmlout.scm.
|
||||
|
||||
HTTP server library
|
||||
This is a complete implementation of an HTTP 1.0 server.
|
||||
The server is very extensible, via a mechanism called "path handlers."
|
||||
The library includes other standalone libraries that may be of use:
|
||||
+ URI and URL parsers and unparsers.
|
||||
+ A library to help writing CGI scripts in Scheme.
|
||||
+ Server extensions for interfacing to CGI scripts.
|
||||
+ Server extensions for uploading Scheme code.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Note well:
|
||||
- You can't do serious programming in Scheme within the bounds of R4RS.
|
||||
I work in Scheme 48 and scsh. Every file does have a comment header
|
||||
describing its non-R4RS dependencies, should you decide to try porting
|
||||
it to another Scheme.
|
||||
|
||||
- Only simple documentation, but my code is written in my usual style --
|
||||
voluminously commented.
|
||||
|
||||
-Olin
|
||||
-------------------------------------------------------------------------------
|
||||
Note further:
|
||||
|
||||
The net package is currently being maintained by Mike Sperber
|
||||
<sperber@informatik.uni-tuebingen.de>.
|
||||
|
||||
My main focus for further development is on making the HTTP server
|
||||
into a realistic full-blown package, but I'll gladly accept patches
|
||||
and suggestions for the other parts of the net package.
|
||||
|
||||
-Mike
|
||||
|
||||
And: See the doc directory for further informations.
|
|
@ -1,85 +0,0 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>The Scheme Underground Network Package</TITLE>
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<H1>The Scheme Underground Network Package</H1>
|
||||
I have written a set of libraries for doing Net hacking from Scheme/scsh.
|
||||
It includes:
|
||||
<DL>
|
||||
<DT> An smtp client library.
|
||||
<DD> Forge mail from the comfort of your own Scheme process.
|
||||
|
||||
<DT> rfc822 header library
|
||||
<DD> Read email-style headers. Useful in several contexts (smtp, http, etc.)
|
||||
|
||||
<DT> Simple structured HTML output library
|
||||
<DD> Balanced delimiters, etc.
|
||||
|
||||
<DT> The SU Web server
|
||||
<DD> This is a complete implementation of an HTTP 1.0 server in Scheme.
|
||||
The server contains other standalone packages that may separately be of
|
||||
use:
|
||||
<UL>
|
||||
<LI> URI and URL parsers and unparsers.
|
||||
<LI> A library to help writing CGI scripts in Scheme.
|
||||
<LI> Server extensions for interfacing to CGI scripts.
|
||||
<LI> Server extensions for uploading Scheme code.
|
||||
</UL>
|
||||
The server has three main design goals:
|
||||
<DL>
|
||||
<DT> Extensibility
|
||||
<DD> The server is in fact nothing but extensions, using a mechanism
|
||||
called "path handlers" to define URL-specific services. It has a toolkit
|
||||
of services that can be used as-is, extended or built upon.
|
||||
User extensions have exactly the same status as the base services.
|
||||
|
||||
<P>
|
||||
The extension mechanism allows for easy implementation of new services
|
||||
without the overhead of the CGI interface. Since the server is written
|
||||
on top of the Scheme shell, the full set of Unix system calls and
|
||||
program tools is available to the implementor.
|
||||
|
||||
<DT> Mobile code
|
||||
<DD> The server allows Scheme code to be uploaded for direct execution
|
||||
inside the server. The server has complete control over the code,
|
||||
and can safely execute it in restricted environments that do not
|
||||
provide access to potentially dangerous primitives (such as the
|
||||
"delete file" procedure.)
|
||||
|
||||
|
||||
<DT> Clarity
|
||||
<DD> I wrote this server to help myself understand the Web. It is voluminously
|
||||
commented, and I hope it will prove to be an aid in understanding the
|
||||
low-level details of the Web protocols.
|
||||
</DL>
|
||||
|
||||
<P>
|
||||
The S.U. server has the ability to upload code from Web clients and
|
||||
execute that code on behalf of the client in a protected environment.
|
||||
|
||||
<P>
|
||||
Some <A HREF="su-httpd.html">simple documentation</A> on the server
|
||||
is available.
|
||||
|
||||
</DL>
|
||||
|
||||
<H2>Obtaining the system</H2>
|
||||
The network code is available by
|
||||
<A HREF="ftp://ftp-swiss.ai.mit.edu/pub/scsh/contrib/net/net.tar.gz">ftp</A>.
|
||||
To run the server, you need our 0.4 release of
|
||||
<A HREF="http://www-swiss.ai.mit.edu/scsh/scsh.html">scsh</A>
|
||||
which has just been released.
|
||||
|
||||
Beyond actually running the server,
|
||||
the separate parser libraries and other utilites may be of use as separate
|
||||
modules.
|
||||
|
||||
<ADDRESS><A HREF="http://www.ai.mit.edu/people/shivers/">Olin Shivers</A>
|
||||
/ <A HREF="plan-file">shivers@ai.mit.edu</A></ADDRESS>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
|
|
@ -1,482 +0,0 @@
|
|||
<!-- check for *..* emphasis, etc., i.e., e.g. -->
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>The Scheme Underground Web system</TITLE>
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<H1>The Scheme Underground Web System</H1>
|
||||
|
||||
<ADDRESS><A HREF="http://www.ai.mit.edu/people/shivers/">Olin Shivers</A>
|
||||
/ <A HREF="plan-file">shivers@ai.mit.edu</A>
|
||||
</ADDRESS>
|
||||
July 1995
|
||||
|
||||
<BLOCKQUOTE>
|
||||
Note: Netscape typesets description lists in a manner that makes the
|
||||
procedure descriptions below blur together, even in the absence of the
|
||||
HTML COMPACT attribute. You may just wish to print out a simple
|
||||
<A HREF="su-httpd.txt">ASCII version</A> of this note, instead.
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
<!---------------------------------------------------------------------------->
|
||||
<H2>Introduction</H2>
|
||||
|
||||
The
|
||||
<A HREF="http://www.ai.mit.edu/projects/su/su.html">Scheme underground</A>
|
||||
Web system is a package of
|
||||
<A HREF="http://www-swiss.ai.mit.edu/scheme-home.html">Scheme</A>
|
||||
code that provides
|
||||
utilities for interacting with the
|
||||
<A HREF="http://www.w3.org/">World-Wide Web</A>.
|
||||
This includes:
|
||||
<UL>
|
||||
<LI> A Web server.
|
||||
<LI> URI and URL parsers and un-parsers.
|
||||
<LI> RFC822-style header parsers.
|
||||
<LI> Code for performing structured html output
|
||||
<LI> Code to assist in writing CGI Scheme programs
|
||||
that can be used by any CGI-compliant HTTP server
|
||||
(such as NCSA's httpd, or the S.U. Web server).
|
||||
</UL>
|
||||
|
||||
<P>
|
||||
The code can be obtained via
|
||||
<A HREF="ftp://ftp-swiss.ai.mit.edu/pub/scsh/contrib/net/net.tar.gz">
|
||||
anonymous ftp</A>
|
||||
and is implemented in
|
||||
<A HREF="http://www-swiss.ai.mit.edu/~jar/s48.html">Scheme 48</A>,
|
||||
using the system calls and support procedures of
|
||||
<A HREF="http://www-swiss.ai.mit.edu/scsh/scsh.html">scsh</A>,
|
||||
the Scheme Shell.
|
||||
The code was written to be clear and modifiable --
|
||||
it is voluminously commented and all non-R4RS dependencies are
|
||||
described at the beginning of each source file.
|
||||
|
||||
<P>
|
||||
I do not have the time to write detailed documentation for these packages.
|
||||
However, they are very thoroughly commented, and I strongly recommend
|
||||
reading the source files; they were written to be read, and the source
|
||||
code comments should provide a clear description of the system.
|
||||
The remainder of this note gives an overview of the server's basic
|
||||
architecture and interfaces.
|
||||
|
||||
<H2>The Scheme Underground Web Server</H2>
|
||||
|
||||
The server was designed with three principle goals in mind:
|
||||
<DL>
|
||||
<DT> Extensibility
|
||||
<DD> The server is designed to make it easy to extend the basic
|
||||
functionality. In fact, the server is nothing but extensions. There is
|
||||
no distinction between the set of basic services provided by the server
|
||||
implementation and user extensions -- they are both implemented in
|
||||
Scheme, and have equal status. The design is "turtles all the way down."
|
||||
|
||||
|
||||
<DT> Mobile code
|
||||
<DD> Because the server is written in Scheme 48, it is simple to use the
|
||||
Scheme 48 module system to upload programs to the server for safe
|
||||
execution within a protected, server-chosen environment. The server
|
||||
comes with a simple example upload service to demonstrate this
|
||||
capability.
|
||||
|
||||
|
||||
<DT> Clarity of implementation
|
||||
<DD> Because the server is written in a high-level language, it should make
|
||||
for a clearer exposition of the HTTP protocol and the associated URL
|
||||
and URI notations than one written in a low-level language such as C.
|
||||
This also should help to make the server easy to modify and adapt to
|
||||
different uses.
|
||||
</DL>
|
||||
|
||||
<!---------------------------------------------------------------------------->
|
||||
<H3>Basic server structure</H3>
|
||||
|
||||
The Web server is started by calling the <CODE>httpd</CODE> procedure,
|
||||
which takes one required and two optional arguments:
|
||||
<PRE>
|
||||
(httpd <VAR>path-handler</VAR> [<VAR>port</VAR> <VAR>working-directory</VAR>])
|
||||
</PRE>
|
||||
|
||||
The server accepts connections from the given port, which defaults to 80.
|
||||
The server runs with the working directory set to the given value,
|
||||
which defaults to
|
||||
<PRE>
|
||||
/usr/local/etc/httpd
|
||||
</PRE>
|
||||
|
||||
|
||||
<P>
|
||||
The server's basic loop is to wait on the port for a connection from an HTTP
|
||||
client. When it receives a connection, it reads in and parses the request into
|
||||
a special request data structure. Then the server forks a child process, who
|
||||
binds the current I/O ports to the connection socket, and then hands off to
|
||||
the top-level path handler (the first argument to <CODE>httpd</CODE>).
|
||||
The path-handler procedure is responsible for actually serving the request --
|
||||
it can be any arbitrary computation.
|
||||
Its output goes directly back to the HTTP client that sent the request.
|
||||
|
||||
<P>
|
||||
Before calling the path handler to service the request, the HTTP server
|
||||
installs an error handler that fields any uncaught error, sends an
|
||||
error reply to the client, and aborts the request transaction. Hence
|
||||
any error caused by a path-handler will be handled in a reasonable and
|
||||
robust fashion.
|
||||
|
||||
<P>
|
||||
The basic server loop, and the associated request data structure are the fixed
|
||||
architecture of the S.U. Web server; its flexibility lies in the notion of
|
||||
path handlers.
|
||||
|
||||
|
||||
<!---------------------------------------------------------------------------->
|
||||
<H3>Path handlers</H3>
|
||||
|
||||
A path handler is a procedure taking two arguments:
|
||||
<PRE>
|
||||
(path-handler <VAR>path</VAR> <VAR>req</VAR>)
|
||||
</PRE>
|
||||
|
||||
|
||||
The <VAR>req</VAR> argument is a request record giving all the details of the
|
||||
client's request; it has the following structure:
|
||||
<PRE>
|
||||
(define-record request
|
||||
method ; A string such as "GET", "PUT", etc.
|
||||
uri ; The escaped URI string as read from request line.
|
||||
url ; An http URL record (see url.scm).
|
||||
version ; A (major . minor) integer pair.
|
||||
headers ; An rfc822 header alist (see rfc822.scm).
|
||||
socket) ; The socket connected to the client.
|
||||
</PRE>
|
||||
|
||||
The <VAR>path</VAR> argument is the URL's path,
|
||||
parsed and split at slashes into a string list.
|
||||
For example, if the Web client dereferences URL
|
||||
<PRE>
|
||||
http://clark.lcs.mit.edu:8001/h/shivers/code/web.tar.gz
|
||||
</PRE>
|
||||
then the server would pass the following path to the top-level handler:
|
||||
<PRE>
|
||||
("h" "shivers" "code" "web.tar.gz")
|
||||
</PRE>
|
||||
|
||||
<P>
|
||||
The path argument's pre-parsed representation as a string list makes it easy
|
||||
for the path handler to implement recursive operations dispatch on URL paths.
|
||||
|
||||
<P>
|
||||
Path handlers can do anything they like to respond to HTTP requests; they have
|
||||
the full range of Scheme to implement the desired functionality. When
|
||||
handling HTTP requests that have an associated entity body (such as POST), the
|
||||
body should be read from the current input port. Path handlers should in all
|
||||
cases write their reply to the current output port. Path handlers should
|
||||
<EM>not</EM> perform I/O on the request record's socket.
|
||||
Path handlers are frequently called recursively, and doing I/O directly to the
|
||||
socket might bypass a filtering or other processing step interposed on the
|
||||
current I/O ports by some superior path handler.
|
||||
|
||||
<!---------------------------------------------------------------------------->
|
||||
<H3>Basic path handlers</H3>
|
||||
|
||||
Although the user can write any path-handler he likes, the S.U. server comes
|
||||
with a useful toolbox of basic path handlers that can be used and built upon:
|
||||
|
||||
<DL>
|
||||
|
||||
<DT>
|
||||
<CODE>(alist-path-dispatcher <VAR>ph-alist</VAR> <VAR>default-ph</VAR>) -> <VAR>path-handler</VAR>
|
||||
</CODE>
|
||||
<DD>
|
||||
This procedure takes a string->path-handler alist, and a default
|
||||
path handler, and returns a handler that dispatches on its path argument.
|
||||
When the new path handler is applied to a path
|
||||
<CODE>("foo" "bar" "baz")</CODE>,
|
||||
it uses the first element of the path -- <CODE>"foo"</CODE> -- to
|
||||
index into the alist.
|
||||
If it finds an associated path handler in the alist, it
|
||||
hands the request off to that handler, passing it the tail of the
|
||||
path, <CODE>("bar" "baz")</CODE>.
|
||||
On the other hand, if the path is empty, or the alist search does
|
||||
not yield a hit, we hand off to the default path handler,
|
||||
passing it the entire original path, <CODE>("foo" "bar" "baz")</CODE>.
|
||||
|
||||
<P>
|
||||
This procedure is how you say: "If the first element of the URL's path
|
||||
is `foo', do X; if it's `bar', do Y; otherwise, do Z." If one takes
|
||||
an object-oriented view of the process, an alist path-handler does
|
||||
method lookup on the requested operation, dispatching off to the
|
||||
appropriate method defined for the URL.
|
||||
|
||||
<P>
|
||||
The slash-delimited URI path structure implies an associated
|
||||
tree of names. The path-handler system and the alist dispatcher
|
||||
allow you to procedurally define the server's response to any arbitrary
|
||||
subtree of the path space.
|
||||
|
||||
<P>
|
||||
Example: <br>
|
||||
A typical top-level path handler is
|
||||
|
||||
<PRE>
|
||||
(define ph
|
||||
(alist-path-dispatcher
|
||||
`(("h" . ,(home-dir-handler "public_html"))
|
||||
("cgi-bin" . ,(cgi-handler "/usr/local/etc/httpd/cgi-bin"))
|
||||
("seval" . ,seval-handler))
|
||||
(rooted-file-handler "/usr/local/etc/httpd/htdocs")))
|
||||
</PRE>
|
||||
|
||||
This means:
|
||||
<UL>
|
||||
<LI> If the path looks like <CODE>("h" "shivers" "code" "web.tar.gz")</CODE>,
|
||||
pass the path <CODE>("shivers" "code" "web.tar.gz")</CODE> to a
|
||||
home-directory path handler.
|
||||
|
||||
|
||||
<LI> If the path looks like <CODE>("cgi-bin" "calendar")</CODE>,
|
||||
pass <CODE>("calendar")</CODE> off to the CGI path handler.
|
||||
|
||||
|
||||
<LI> If the path looks like <CODE>("seval" ...)</CODE>,
|
||||
the tail of the path is passed off to the code-uploading seval
|
||||
path handler.
|
||||
|
||||
<LI> Otherwise, the whole path is passed to a rooted file handler, who
|
||||
will convert it into a filename, rooted at
|
||||
<CODE>/usr/local/etc/httpd/htdocs</CODE>, and serve that file.
|
||||
</UL>
|
||||
|
||||
|
||||
<DT> <CODE>(home-dir-handler <VAR>subdir</VAR>) ->
|
||||
<VAR>path-handler</CODE></VAR>
|
||||
<DD>
|
||||
This procedure builds a path handler that does basic file serving
|
||||
out of home directories. If the resulting path handler is passed
|
||||
a path of <CODE>(<VAR>user</VAR> . <VAR>file-path</VAR>)</CODE>,
|
||||
then it serves the file
|
||||
<PRE>
|
||||
<VAR>user's-home-directory</VAR>/<VAR>subdir</VAR>/<VAR>file-path</VAR>
|
||||
</PRE>
|
||||
The path handler only handles GET requests; the filename is not
|
||||
allowed to contain <CODE>..</CODE> elements.
|
||||
|
||||
|
||||
<DT>
|
||||
<CODE>(tilde-home-dir-handler <VAR>subdir</VAR> <VAR>default-path-handler</VAR>)
|
||||
-> <VAR>path-handler</VAR>
|
||||
</CODE>
|
||||
<DD>
|
||||
This path handler examines the car of the path. If it is a string
|
||||
beginning with a tilde, <em>e.g.</em>, "<CODE>~ziggy</CODE>",
|
||||
then the string is taken
|
||||
to mean a home directory, and the request is served similarly to a
|
||||
<CODE>home-dir-handler</CODE> path handler.
|
||||
Otherwise, the request is passed off
|
||||
in its entirety to the default path handler.
|
||||
|
||||
<P>
|
||||
This procedure is useful for implementing servers that provide the
|
||||
semantics of the NCSA httpd server.
|
||||
|
||||
|
||||
<DT>
|
||||
<CODE>(cgi-handler <VAR>cgi-directory</VAR>) -> <VAR>path-handler</VAR>
|
||||
</CODE>
|
||||
<DD>
|
||||
This procedure returns a path-handler that passes the request off to some
|
||||
program using the CGI interface. The script name is taken from the
|
||||
car of the path; it is checked for occurrences of <CODE>..</CODE>'s.
|
||||
If the path is
|
||||
<PRE>
|
||||
("my-prog" "foo" "bar")
|
||||
</PRE>
|
||||
then the program executed is
|
||||
<PRE>
|
||||
<VAR>cgi-directory</VAR>/my-prog
|
||||
</PRE>
|
||||
<P>
|
||||
When the CGI path handler builds the process environment for the
|
||||
CGI script, several elements
|
||||
(<em>e.g.</em>, <CODE>$PATH</CODE> and <CODE>$SERVER_SOFTWARE</CODE>)
|
||||
are request-invariant, and can be computed at server start-up time.
|
||||
This can be done by calling
|
||||
<PRE>
|
||||
(initialise-request-invariant-cgi-env)
|
||||
</PRE>
|
||||
when the server starts up. This is <EM>not</EM> necessary,
|
||||
but will make CGI requests a little faster.
|
||||
|
||||
|
||||
<DT>
|
||||
<CODE>(rooted-file-handler <VAR>root-dir</VAR>) -> <VAR>path-handler</VAR>
|
||||
</CODE>
|
||||
<DD>
|
||||
Returns a path handler that serves files from a particular root
|
||||
in the file system. Only the GET operation is provided. The path
|
||||
argument passed to the handler is converted into a filename,
|
||||
and appended to <VAR>root-dir</VAR>.
|
||||
The file name is checked for <CODE>..</CODE> components,
|
||||
and the transaction is aborted if it does. Otherwise, the file is
|
||||
served to the client.
|
||||
|
||||
<DT>
|
||||
<CODE>(null-path-handler <VAR>path</VAR> <VAR>req</VAR>)</CODE>
|
||||
<DD>
|
||||
This path handler is useful as a default handler. It handles no requests,
|
||||
always returning a "404 Not found" reply to the client.
|
||||
|
||||
</DL>
|
||||
|
||||
<!---------------------------------------------------------------------------->
|
||||
<H3>HTTP errors</H3>
|
||||
|
||||
Authors of path-handlers need to be able to handle errors in a reasonably
|
||||
simple fashion. The S.U. Web server provides a set of error conditions that
|
||||
correspond to the error replies in the HTTP protocol. These errors can be
|
||||
raised with the <CODE>http-error</CODE> procedure.
|
||||
When the server runs a path handler,
|
||||
it runs it in the context of an error handler that catches these errors,
|
||||
sends an error reply to the client, and closes the transaction.
|
||||
|
||||
<DL>
|
||||
|
||||
<DT>
|
||||
<CODE>(http-error <VAR>reply-code</VAR> <VAR>req</VAR> [<VAR>extra</VAR> ...])</CODE>
|
||||
<DD>
|
||||
This raises an http error condition. The reply code is one of the
|
||||
numeric HTTP error reply codes, which are bound to the variables
|
||||
<CODE>http-reply/ok</CODE>, <CODE>http-reply/not-found</CODE>,
|
||||
<CODE>http-reply/bad-request</CODE>, and so
|
||||
forth. The <VAR>req</VAR> argument is the request record that caused
|
||||
the error.
|
||||
Any following <VAR>extra</VAR> args are passed along for
|
||||
informational purposes.
|
||||
Different HTTP errors take different types of extra arguments.
|
||||
For example, the "301 moved permanently" and "302 moved temporarily"
|
||||
replies use the first two <VAR>extra</VAR> values as the
|
||||
<CODE>URI:</CODE> and <CODE>Location:</CODE>
|
||||
fields in the reply header, respectively. See the clauses of the
|
||||
<CODE>send-http-error-reply</CODE> procedure for details.
|
||||
|
||||
|
||||
<DT>
|
||||
<CODE>(send-http-error-reply <VAR>reply-code</VAR> <VAR>request</VAR>
|
||||
[<VAR>extra</VAR> ...])
|
||||
</CODE>
|
||||
<DD>
|
||||
This procedure writes an error reply out to the current output
|
||||
port. If an error occurs during this process, it is caught, and
|
||||
the procedure silently returns. The http server's standard error
|
||||
handler passes all http errors raised during path-handler execution
|
||||
to this procedure to generate the error reply before aborting the
|
||||
request transaction.
|
||||
</DL>
|
||||
|
||||
<!---------------------------------------------------------------------------->
|
||||
<H3>Simple directory generation</H3>
|
||||
|
||||
Most path-handlers that serve files to clients eventually call an internal
|
||||
procedure named <CODE>file-serve</CODE>,
|
||||
which implements a simple directory-generation service using the
|
||||
following rules:
|
||||
<UL>
|
||||
<LI> If the filename has the <EM>form</EM> of a directory
|
||||
(<EM>i.e.</EM>, it ends with a slash),
|
||||
then <CODE>file-serve</CODE> actually looks for a
|
||||
file named "<CODE>index.html</CODE>" in that directory.
|
||||
|
||||
<LI> If the filename names a directory, but is not in directory form
|
||||
(<EM>i.e.</EM>, it doesn't end in a slash,
|
||||
as in "<CODE>/usr/include</CODE>" or "<CODE>/usr/raj</CODE>"),
|
||||
then <CODE>file-serve</CODE> sends back a "301 moved permanently"
|
||||
message,
|
||||
redirecting the client to a slash-terminated version of the original
|
||||
URL. For example, the URL
|
||||
<PRE>
|
||||
http://clark.lcs.mit.edu/~shivers
|
||||
</PRE>
|
||||
would be redirected to
|
||||
<PRE>
|
||||
http://clark.lcs.mit.edu/~shivers/
|
||||
</PRE>
|
||||
|
||||
<LI> If the filename names a regular file, it is served to the client.
|
||||
</UL>
|
||||
|
||||
|
||||
<!---------------------------------------------------------------------------->
|
||||
<H3>Support procs</H3>
|
||||
|
||||
The source files contain a host of support procedures which will be of utility
|
||||
to anyone writing a custom path-handler. Read the files first.
|
||||
|
||||
|
||||
<!---------------------------------------------------------------------------->
|
||||
<H3>Losing</H3>
|
||||
|
||||
Be aware of two Unix problems, which may require workarounds:
|
||||
<OL>
|
||||
|
||||
<LI>
|
||||
NeXTSTEP's Posix implementation of the <CODE>getpwnam()</CODE> routine
|
||||
will silently tell you that every user has uid 0. This means
|
||||
that if your server, running as root, does a
|
||||
<PRE>
|
||||
(set-uid (user->uid "nobody"))
|
||||
</PRE>
|
||||
it will essentially do a
|
||||
<PRE>
|
||||
(set-uid 0)
|
||||
</PRE>
|
||||
and you will thus still be running as root.
|
||||
|
||||
<P>
|
||||
The fix is to manually find out who user nobody is (he's -2 on my
|
||||
system), and to hard-wire this into the server:
|
||||
<PRE>
|
||||
(set-uid -2)
|
||||
</PRE>
|
||||
This problem is NeXTSTEP specific. If you are using not using NeXTSTEP,
|
||||
no problem.
|
||||
|
||||
|
||||
<LI>
|
||||
On NeXTSTEP, the ip-address->host-name translation routine
|
||||
(in C, <CODE>gethostbyaddr()</CODE>; in scsh,
|
||||
<CODE>(host-info addr)</CODE>) does not
|
||||
use the DNS system; it goes through NeXT's propietary Netinfo
|
||||
system, and may not return a fully-qualified domain name. For
|
||||
example, on my system, I get "amelia-earhart", when I want
|
||||
"amelia-earhart.lcs.mit.edu". Since the server uses this name
|
||||
to construct redirection URL's to be sent back to the Web client,
|
||||
they need to be FQDN's.
|
||||
|
||||
<P>
|
||||
This problem may occur on other OS's;
|
||||
I cannot determine if <CODE>gethostbyaddr()</CODE>
|
||||
is required to return a FQDN or not. (I would appreciate hearing the
|
||||
answer if you know; my local Internet guru's couldn't tell me.)
|
||||
|
||||
<P>
|
||||
If your system doesn't give you a complete Internet address when
|
||||
you say
|
||||
<PRE>
|
||||
(host-info:name (host-info (system-name)))
|
||||
</PRE>
|
||||
then you have this problem.
|
||||
|
||||
<P>
|
||||
The server has a workaround. There is a procedure exported from
|
||||
the httpd-core package:
|
||||
<PRE>
|
||||
(set-my-fqdn name)
|
||||
</PRE>
|
||||
Call this to crow-bar the server's idea of its own Internet host name
|
||||
before running the server, and all will be well.
|
||||
</OL>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -1,7 +0,0 @@
|
|||
*.aux
|
||||
*.toc
|
||||
*.dvi
|
||||
*.ps
|
||||
*.pdf
|
||||
*.log
|
||||
|
|
@ -1 +0,0 @@
|
|||
../../web-server/root/htdocs/sunet-manual
|
|
@ -1,24 +0,0 @@
|
|||
\chapter{Writing CGI Scripts in Scheme}\label{cha:cgi-scripts}
|
||||
%
|
||||
The \ex{cgi-scripts} structure provides functionality useful for
|
||||
writing CGI scripts in Scheme.
|
||||
|
||||
\defun{cgi-form-query}{}{data-alist}
|
||||
\begin{desc}
|
||||
CGI scripts receive their parameters in various ways, depending on
|
||||
how they were called (e.g.\ by \ex{GET} method).
|
||||
|
||||
This procedure translates the delivered form data into an alist of
|
||||
decoded strings, using the environment variables set by the server
|
||||
(\ex{REQUEST\_METHOD}, \ex{QUERY\_STRING} (for a \ex{GET} request),
|
||||
\ex{CONTENT\_LENGTH} (for a \ex{POST} request)). So a query string
|
||||
like \codex{button=on\&\ob{}reply=Oh,\ob{}\%20yes} becomes an alist
|
||||
\codex{(("button" . "on") ("reply" . "Oh, yes"))}
|
||||
|
||||
\ex{Cgi-form-query} only works for \ex{GET} and \ex{POST} methods.
|
||||
\end{desc}
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,296 +0,0 @@
|
|||
% code.sty: -*- latex -*-
|
||||
% Latex macros for a "weak" verbatim mode.
|
||||
% -- like verbatim, except \, {, and } have their usual meanings.
|
||||
|
||||
% Environments: code, tightcode, codeaux, codebox, centercode
|
||||
% Commands: \dcd, \cddollar, \cdmath, \cd, \codeallowbreaks, \codeskip, \^
|
||||
% Already defined in LaTeX, but of some relevance: \#, \$, \%, \&, \_, \{, \}
|
||||
|
||||
% Changelog at the end of the file.
|
||||
|
||||
% These commands give you an environment, code, that is like verbatim
|
||||
% except that you can still insert commands in the middle of the environment:
|
||||
% \begin{code}
|
||||
% for(x=1; x<loop_bound; x++)
|
||||
% y += x^3; /* {\em Add in {\tt x} cubed} */
|
||||
% \end{code}
|
||||
%
|
||||
% All characters are ordinary except \{}. To get \{} in your text,
|
||||
% you use the commands \\, \{, and \}.
|
||||
|
||||
% These macros mess with the definition of the special chars (e.g., ^_~%).
|
||||
% The characters \{} are left alone, so you can still have embedded commands:
|
||||
% \begin{code} f(a,b,\ldots,y,z) \end{code}
|
||||
% However, if your embedded commands use the formerly-special chars, as in
|
||||
% \begin{code} x := x+1 /* \mbox{\em This is $y^3$} */ \end{code}
|
||||
% then you lose. The $ and ^ chars are scanned in as non-specials,
|
||||
% so they don't work. If the chars are scanned *outside* the code env,
|
||||
% then you have no problem:
|
||||
% \def\ycube{$y^3$}
|
||||
% \begin{code} x := x+1 /* {\em This is \ycube} */ \end{code}
|
||||
% If you must put special chars inside the code env, you do it by
|
||||
% prefixing them with the special \dcd ("decode") command, that
|
||||
% reverts the chars to back to special status:
|
||||
% \begin{code} x := x+1 /* {\dcd\em This is $y^3$} */ \end{code}
|
||||
% \dcd's scope is bounded by its enclosing braces. It is only defined within
|
||||
% the code env. You can also turn on just $ with the \cddollar command;
|
||||
% you can turn on just $^_ with the \cdmath command. See below.
|
||||
%
|
||||
% Alternatively, just use \(...\) for $...$, \sp for ^, and \sb for _.
|
||||
|
||||
% WARNING:
|
||||
% Like \verb, you cannot put a \cd{...} inside an argument to a macro
|
||||
% or a command. If you try, for example,
|
||||
% \mbox{\cd{$x^y$}}
|
||||
% you will lose. That is because the text "\cd{$x^y$}" gets read in
|
||||
% as \mbox's argument before the \cd executes. But the \cd has to
|
||||
% have a chance to run before LaTeX ever reads the $x^y$ so it can
|
||||
% turn off the specialness of $ and ^. So, \cd has to appear at
|
||||
% top level, not inside an argument. Similarly, you can't have
|
||||
% a \cd or a \code inside a macro (Although you could use \gdef to
|
||||
% define a macro *inside* a \cd, which you could then use outside.
|
||||
% Don't worry about this if you don't understand it.)
|
||||
|
||||
% BUG: In the codebox env, the effect of a \dcd, \cddollar, or \cdmath
|
||||
% command is reset at the end of each line. This can be hacked by
|
||||
% messing with the \halign's preamble, if you feel up to it.
|
||||
|
||||
% Useage note: the initial newline after the \begin{code} or
|
||||
% \begin{codebox} is eaten, but the last newline is not.
|
||||
% So,
|
||||
% \begin{code}
|
||||
% foo
|
||||
% bar
|
||||
% \end{code}
|
||||
% leaves one more blank line after bar than does
|
||||
% \begin{code}
|
||||
% foo
|
||||
% bar\end{code}
|
||||
% Moral: get in the habit of terminating code envs without a newline
|
||||
% (as in the second example).
|
||||
%
|
||||
|
||||
% All this stuff tweaks the meaning of space, tab, and newline.
|
||||
%===============================================================================
|
||||
% \cd@obeyspaces
|
||||
% Turns all spaces into non-breakable spaces.
|
||||
% Note: this is like \@vobeyspaces except without spurious space in defn.
|
||||
% @xobeysp is basically a space; it's defined in latex.tex.
|
||||
%
|
||||
{\catcode`\ =\active\gdef\cd@obeyspaces{\catcode`\ =\active\let =\@xobeysp}}
|
||||
|
||||
|
||||
|
||||
% \cd@obeytabs
|
||||
% Turns all tabs into 8 non-breakable spaces (which is bogus).
|
||||
%
|
||||
{\catcode`\^^I=\active %
|
||||
\gdef\cd@obeytabs{\catcode`\^^I=\active\let^^I=\cd@tab}}
|
||||
|
||||
\def\cd@tab{\@xobeysp\@xobeysp\@xobeysp\@xobeysp\@xobeysp\@xobeysp\@xobeysp\@xobeysp}
|
||||
|
||||
|
||||
|
||||
% \cd@obeylines
|
||||
% Turns all cr's into linebreaks. Pagebreaks are not permitted between lines.
|
||||
% This is copied from lplain.tex's \obeylines, with the cr def'n changed.
|
||||
%
|
||||
{\catcode`\^^M=\active % these lines must end with %
|
||||
\gdef\cd@obeylines{\catcode`\^^M=\active\let^^M=\cd@cr}}
|
||||
|
||||
% What ^M turns into. This def'n keeps blank lines from being compressed out.
|
||||
\def\cd@cr{\par\penalty10000\leavevmode} % TeX magicness
|
||||
%\def\cd@cr{\par\penalty10000\mbox{}} % LaTeX
|
||||
|
||||
|
||||
% \codeallowbreaks
|
||||
% Same as \cd@obeylines, except pagebreaks are allowed.
|
||||
% Put this command inside a code env to allow pagebreaks.
|
||||
|
||||
{\catcode`\^^M=\active % these lines must end with %
|
||||
\gdef\codeallowbreaks{\catcode`\^^M\active\let^^M\cd@crbr}}
|
||||
|
||||
%\def\cd@crbr{\leavevmode\endgraf} % What ^M turns into.
|
||||
\def\cd@crbr{\par\leavevmode} % What ^M turns into.
|
||||
|
||||
|
||||
% \cd@obeycrsp
|
||||
% Turns cr's into non-breakable spaces. Used by \cd.
|
||||
|
||||
{\catcode`\^^M=\active % these lines must end with %
|
||||
\gdef\cd@obeycrsp{\catcode`\^^M=\active\let^^M=\@xobeysp}}
|
||||
|
||||
% =============================================================================
|
||||
|
||||
% Set up code environment, in which most of the common special characters
|
||||
% appearing in code are treated verbatim, namely: $&#^_~%
|
||||
% \ { } are still enabled so that macros can be called in this
|
||||
% environment. Use \\, \{, and \} to use these characters verbatim
|
||||
% in this environment.
|
||||
%
|
||||
% Inside a group, you can make
|
||||
% all the hacked chars special with the \dcd command
|
||||
% $ special with the \cddollar command
|
||||
% $^_ special with the \cdmath command.
|
||||
% If you have a bunch of math $..$'s in your code env, then a global \cddollar
|
||||
% or \cdmath at the beginning of the env can save a lot of trouble.
|
||||
% When chars are special (e.g., after a \dcd), you can still get #$%&_{} with
|
||||
% \#, \$, \%, \&, \_, \{, and \} -- this is standard LaTeX.
|
||||
% Additionally, \\ gives \ inside the code env, and when \cdmath
|
||||
% makes ^ special, it also defines \^ to give ^.
|
||||
|
||||
%The hacked characters can be made special again
|
||||
% within a group by using the \dcd command.
|
||||
|
||||
% Note: this environment allows no breaking of lines whatsoever; not
|
||||
% at spaces or hypens. To arrange for a break use the standard \- command,
|
||||
% or a \discretionary{}{}{} which breaks, but inserts nothing. This is useful,
|
||||
% for example for allowing hypenated identifiers to be broken, e.g.
|
||||
% \def\={\discretionary{}{}{}} %optional break
|
||||
% FOO-\=BAR.
|
||||
|
||||
\def\setupcode{\parsep=0pt\parindent=0pt%
|
||||
\normalfont\ttfamily\frenchspacing\catcode``=13\@noligs%
|
||||
\def\\{\char`\\}%
|
||||
\let\dcd=\cd@dcd\let\cddollar=\cd@dollarspecial\let\cdmath=\cd@mathspecial%
|
||||
\@makeother\$\@makeother\&\@makeother\#%
|
||||
\@makeother\^\@makeother\_\@makeother\~%
|
||||
\@makeother\%\cd@obeytabs\cd@obeyspaces}
|
||||
% other: $&#^_~%
|
||||
% left special: \{}
|
||||
% unnecessary: @`'"
|
||||
|
||||
|
||||
%% codebox, centercode
|
||||
%%=============================================================================
|
||||
%% The codebox env makes a box exactly as wide as it needs to be
|
||||
%% (i.e., as wide as the longest line of code is). This is useful
|
||||
%% if you want to center a chunk of code, or flush it right, or
|
||||
%% something like that. The optional argument to the environment,
|
||||
%% [t], [c], or [b], specifies how to vertically align the codebox,
|
||||
%% just as with arrays or other boxes. Default is [c].
|
||||
|
||||
%% Must be a newline immediately after "\begin{codebox}[t]"!
|
||||
|
||||
{\catcode`\^^M=\active % these lines must end with %
|
||||
\gdef\cd@obeycr{\catcode`\^^M=\active\let^^M=\cr}}
|
||||
|
||||
% If there is a [<letter>] option, then the following newline will
|
||||
% be read *after* ^M is bound to \cr, so we're cool. If there isn't
|
||||
% an option given (i.e., default to [c]), then the @\ifnextchar will
|
||||
% gobble up the newline as it gobbles whitespace. So we insert the
|
||||
% \cr explicitly. Isn't TeX fun?
|
||||
\def\codebox{\leavevmode\@ifnextchar[{\@codebox}{\@codebox[c]\cr}} %]
|
||||
|
||||
\def\@codebox[#1]%
|
||||
{\hbox\bgroup$\if #1t\vtop \else \if#1b\vbox \else \vcenter \fi\fi\bgroup%
|
||||
\tabskip\z@\setupcode\cd@obeycr% just before cd@obey
|
||||
\halign\bgroup##\hfil\span}
|
||||
|
||||
\def\endcodebox{\crcr\egroup\egroup\m@th$\egroup}
|
||||
|
||||
% Center the box on the page:
|
||||
\newenvironment{centercode}%
|
||||
{\begin{center}\begin{codebox}[c]}%
|
||||
{\end{codebox}\end{center}}
|
||||
|
||||
|
||||
%% code, codeaux, tightcode
|
||||
%%=============================================================================
|
||||
%% Code environment as described above. Lines are kept on one page.
|
||||
%% This actually works by setting a huge penalty for breaking
|
||||
%% between lines of code. Code is indented same as other displayed paras.
|
||||
%% Note: to increase left margin, use \begin{codeaux}{\leftmargin=1in}.
|
||||
|
||||
% To allow pagebreaks, say \codeallowbreaks immediately inside the env.
|
||||
% You can allow breaks at specific lines with a \pagebreak form.
|
||||
|
||||
%% N.B.: The \global\@ignoretrue command must be performed just inside
|
||||
%% the *last* \end{...} before the following text. If not, you will
|
||||
%% get an extra space on the following line. Blech.
|
||||
|
||||
%% This environment takes two arguments.
|
||||
%% The second, required argument is the \list parameters to override the
|
||||
%% \@listi... defaults.
|
||||
%% - Usefully set by clients: \topsep \leftmargin
|
||||
%% - Possible, but less useful: \partopsep
|
||||
%% The first, optional argument is the extra \parskip glue that you get around
|
||||
%% \list environments. It defaults to the value of \parskip.
|
||||
\def\codeaux{\@ifnextchar[{\@codeaux}{\@codeaux[\parskip]}} %]
|
||||
\def\@codeaux[#1]#2{%
|
||||
\bgroup\parskip#1%
|
||||
\begin{list}{}%
|
||||
{\parsep\z@\rightskip\z@\listparindent\z@\itemindent\z@#2}%
|
||||
\item[]\setupcode\cd@obeylines}%
|
||||
\def\endcodeaux{\end{list}\leavevmode\egroup\ignorespaces\global\@ignoretrue}
|
||||
|
||||
%% Code env is codeaux with the default margin and spacing \list params:
|
||||
\def\code{\codeaux{}} \let\endcode=\endcodeaux
|
||||
|
||||
%% Like code, but with no extra vertical space above and below.
|
||||
\def\tightcode{\codeaux[=0pt]{\topsep\z@}}%
|
||||
\let\endtightcode\endcodeaux
|
||||
% {\vspace{-1\parskip}\begin{codeaux}{\partopsep\z@\topsep\z@}}%
|
||||
% {\end{codeaux}\vspace{-1\parskip}}
|
||||
|
||||
|
||||
|
||||
% Reasonable separation between lines of code
|
||||
\newcommand{\codeskip}{\penalty0\vspace{2ex}}
|
||||
|
||||
|
||||
% \cd is used to build a code environment in the middle of text.
|
||||
% Note: only difference from display code is that cr's are taken
|
||||
% as unbreakable spaces instead of linebreaks.
|
||||
|
||||
\def\cd{\leavevmode\begingroup\ifmmode\let\startcode=\startmcode\else%
|
||||
\let\startcode\starttcode\fi%
|
||||
\setupcode\cd@obeycrsp\startcode}
|
||||
|
||||
\def\starttcode#1{#1\endgroup}
|
||||
\def\startmcode#1{\hbox{#1}\endgroup}
|
||||
|
||||
|
||||
% Restore $&#^_~% to their normal catcodes
|
||||
% Define \^ to give the ^ char.
|
||||
% \dcd points to this guy inside a code env.
|
||||
\def\cd@dcd{\catcode`\$=3\catcode`\&=4\catcode`\#=6\catcode`\^=7%
|
||||
\catcode`\_=8\catcode`\~=13\catcode`\%=14\def\^{\char`\^}}
|
||||
|
||||
% Selectively enable $, and $^_ as special.
|
||||
% \cd@mathspecial also defines \^ give the ^ char.
|
||||
% \cddollar and \cdmath point to these guys inside a code env.
|
||||
\def\cd@dollarspecial{\catcode`\$=3}
|
||||
\def\cd@mathspecial{\catcode`\$=3\catcode`\^=7\catcode`\_=8%
|
||||
\def\^{\char`\^}}
|
||||
|
||||
|
||||
% Change log:
|
||||
% Started off as some macros found in C. Rich's library.
|
||||
% Olin 1/90:
|
||||
% Removed \makeatletter, \makeatother's -- they shouldn't be there,
|
||||
% because style option files are read with makeatletter. The terminal
|
||||
% makeatother screwed things up for the following style options.
|
||||
% Olin 3/91:
|
||||
% Rewritten.
|
||||
% - Changed things so blank lines don't get compressed out (the \leavevmove
|
||||
% in \cd@cr and \cd@crwb).
|
||||
% - Changed names to somewhat less horrible choices.
|
||||
% - Added lots of doc, so casual hackers can more easily mess with all this.
|
||||
% - Removed `'"@ from the set of hacked chars, since they are already
|
||||
% non-special.
|
||||
% - Removed the bigcode env, which effect can be had with the \codeallowbreaks
|
||||
% command.
|
||||
% - Removed the \@noligs command, since it's already defined in latex.tex.
|
||||
% - Win big with the new \dcd, \cddollar, and \cdmath commands.
|
||||
% - Now, *only* the chars \{} are special inside the code env. If you need
|
||||
% more, use the \dcd command inside a group.
|
||||
% - \cd now works inside math mode. (But if you use it in a superscript,
|
||||
% it still comes out full size. You must explicitly put a \scriptsize\tt
|
||||
% inside the \cd: $x^{\cd{\scriptsize\tt...}}$. A \leavevmode was added
|
||||
% so that if you begin a paragraph with a \cd{...}, TeX realises you
|
||||
% are starting a paragraph.
|
||||
% - Added the codebox env. Tricky bit involving the first line hacked
|
||||
% with help from David Long.
|
||||
% Olin 8/94
|
||||
% Changed the font commands for LaTeX2e.
|
|
@ -1,105 +0,0 @@
|
|||
% css.t2p
|
||||
% Dorai Sitaram
|
||||
% 19 Jan 2001
|
||||
% A basic style for HTML documents generated
|
||||
% with tex2page.
|
||||
|
||||
\cssblock
|
||||
|
||||
body {
|
||||
color: black;
|
||||
background-color: #e5e5e5;
|
||||
/*background-color: beige;*/
|
||||
margin-top: 2em;
|
||||
margin-left: 8%;
|
||||
margin-right: 8%;
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6 {
|
||||
margin-top: .5em;
|
||||
}
|
||||
|
||||
.partheading {
|
||||
font-size: 70%;
|
||||
}
|
||||
|
||||
.chapterheading {
|
||||
font-size: 70%;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
ol ol {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
|
||||
ol ol ol {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
|
||||
ol ol ol ol {
|
||||
list-style-type: upper-alpha;
|
||||
}
|
||||
|
||||
.scheme {
|
||||
color: brown;
|
||||
}
|
||||
|
||||
.scheme .keyword {
|
||||
color: #990000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.scheme .builtin {
|
||||
color: #990000;
|
||||
}
|
||||
|
||||
.scheme .variable {
|
||||
color: navy;
|
||||
}
|
||||
|
||||
.scheme .global {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.scheme .selfeval {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.scheme .comment {
|
||||
color: teal;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
color: red;
|
||||
text-align: right;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.disable {
|
||||
/* color: #e5e5e5; */
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.smallcaps {
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
.smallprint {
|
||||
color: gray;
|
||||
font-size: 75%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.smallprint hr {
|
||||
text-align: left;
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
\endcssblock
|
|
@ -1,6 +0,0 @@
|
|||
% Loads cmtt fonts in on \tt. -*- latex -*-
|
||||
% I prefer these to the Courier fonts that latex gives you w/postscript styles.
|
||||
% Courier is too spidery and too wide -- it's hard to get 80 chars on a line.
|
||||
% -Olin
|
||||
|
||||
\renewcommand{\ttdefault}{cmtt}
|
|
@ -1,278 +0,0 @@
|
|||
\makeatletter
|
||||
\def\ie{\mbox{\emph{i.e.}}} % \mbox keeps the last period from
|
||||
\def\Ie{\mbox{\emph{I.e.}}} % looking like an end-of-sentence.
|
||||
\def\eg{\mbox{\emph{e.g.}}}
|
||||
\def\Eg{\mbox{\emph{E.g.}}}
|
||||
\def\etc{{\em etc.}}
|
||||
|
||||
\def\Lisp{\textsc{Lisp}}
|
||||
\def\CommonLisp{\textsc{Common Lisp}}
|
||||
\def\Ascii{\textsc{Ascii}}
|
||||
\def\Ansi{\textsc{Ansi}}
|
||||
\def\Unix{{Unix}} % Not smallcaps, according to Bart.
|
||||
\def\Scheme{{Scheme}}
|
||||
\def\scm{{Scheme 48}}
|
||||
\def\RnRS{R5RS}
|
||||
\def\Posix{\textsc{Posix}}
|
||||
|
||||
\def\sharpf{\textnormal{\texttt{\#f}}}
|
||||
\def\sharpt{\textnormal{\texttt{\#t}}}
|
||||
\newcommand{\synteq}{\textnormal{::=}}
|
||||
|
||||
\def\maketildeother{\catcode`\~=12}
|
||||
\def\maketildeactive{\catcode`\~=13}
|
||||
\def\~{\char`\~}
|
||||
|
||||
\newcommand{\evalsto}{\ensuremath{\Rightarrow}}
|
||||
|
||||
% One-line code examples
|
||||
%\newcommand{\codex}[1]% One line, centred. Tight spacing.
|
||||
% {$$\abovedisplayskip=.75ex plus 1ex minus .5ex%
|
||||
% \belowdisplayskip=\abovedisplayskip%
|
||||
% \abovedisplayshortskip=0ex plus .5ex%
|
||||
% \belowdisplayshortskip=\abovedisplayshortskip%
|
||||
% \hbox{\ttt #1}$$}
|
||||
%\newcommand{\codex}[1]{\begin{tightinset}\ex{#1}\end{tightinset}\ignorespaces}
|
||||
\newcommand{\codex}[1]{\begin{leftinset}\ex{#1}\end{leftinset}\ignorespaces}
|
||||
|
||||
\def\widecode{\codeaux{\leftmargin=0pt\topsep=0pt}}
|
||||
\def\endwidecode{\endcodeaux}
|
||||
|
||||
% For multiletter vars in math mode:
|
||||
\newcommand{\var}[1]{\mbox{\frenchspacing\it{#1}}}
|
||||
\newcommand{\vari}[2]{\ensuremath{\mbox{\it{#1}}_{#2}}}
|
||||
|
||||
%% What you frequently want when you say \tt:
|
||||
\def\ttchars{\catcode``=13\@noligs\frenchspacing}
|
||||
\def\ttt{\normalfont\ttfamily\ttchars}
|
||||
|
||||
% Works in math mode; all special chars remain special; cheaper than \cd.
|
||||
% Will not be correct size in super and subscripts, though.
|
||||
\newcommand{\ex}[1]{{\normalfont\texttt{\ttchars #1}}}
|
||||
|
||||
\newenvironment{inset}
|
||||
{\bgroup\parskip=1ex plus 1ex\begin{list}{}%
|
||||
{\topsep=0pt\rightmargin\leftmargin}%
|
||||
\item[]}%
|
||||
{\end{list}\leavevmode\egroup\global\@ignoretrue}
|
||||
|
||||
\newenvironment{leftinset}
|
||||
{\bgroup\parskip=1ex plus 1ex\begin{list}{}%
|
||||
{\topsep=0pt}%
|
||||
\item[]}%
|
||||
{\end{list}\leavevmode\egroup\global\@ignoretrue}
|
||||
|
||||
\newenvironment{tightinset}
|
||||
{\bgroup\parskip=0pt\begin{list}{}%
|
||||
{\topsep=0pt\rightmargin\leftmargin}%
|
||||
\item[]}%
|
||||
{\end{list}\leavevmode\egroup\global\@ignoretrue}
|
||||
|
||||
\newenvironment{tightleftinset}
|
||||
{\bgroup\parskip=0pt\begin{list}{}%
|
||||
{\topsep=0pt}%
|
||||
\item[]}%
|
||||
{\end{list}\leavevmode\egroup\global\@ignoretrue}
|
||||
|
||||
\long\def\remark#1{\bgroup\small\begin{quote}\textsl{Remark: } #1\end{quote}\egroup}
|
||||
\newenvironment{remarkenv}{\bgroup\small\begin{quote}\textsl{Remark: }}%
|
||||
{\end{quote}\egroup}
|
||||
\newcommand{\oops}[1]{\bgroup\small\begin{quote}\textsl{Oops: } #1\end{quote}\egroup}
|
||||
|
||||
\newcommand{\note}[1]{\{Note #1\}}
|
||||
|
||||
\newcommand{\itum}[1]{\item{\bf #1}\\*}
|
||||
|
||||
% For use in code. The \llap magicness makes the lambda exactly as wide as
|
||||
% the other chars in \tt; the \hskip shifts it right a bit so it doesn't
|
||||
% crowd the left paren -- which is necessary if \tt is cmtt.
|
||||
% Note that (\l{x y} (+ x y)) uses the same number of columns in TeX form
|
||||
% as it produces when typeset. This makes it easy to line up the columns
|
||||
% in your input. \l is bound to some useless command in LaTeX, so we have to
|
||||
% define it w/renewcommand.
|
||||
\let\oldl\l %Save the old \l on \oldl
|
||||
\renewcommand{\l}[1]{\ \llap{$\lambda$\hskip-.05em}\ (#1)}
|
||||
|
||||
% This one is for the rare (lambda x ...) case -- it doesn't have the
|
||||
% column-invariant property. Oh, well.
|
||||
\newcommand{\lx}[1]{\ \llap{$\lambda$\hskip-.05em}\ {#1}}
|
||||
|
||||
% For subcaptions
|
||||
\newcommand{\subcaption}[1]
|
||||
{\unskip\vspace{-2mm}\begin{center}\unskip\em#1\end{center}}
|
||||
|
||||
%%% T release notes stuff
|
||||
\newlength{\notewidth}
|
||||
\setlength{\notewidth}{\textwidth}
|
||||
\addtolength{\notewidth}{-1.25in}
|
||||
|
||||
%\newcommand{\remark} [1]
|
||||
% {\par\vspace{\parskip}
|
||||
% \parbox[t]{.75in}{\sc Remark:}
|
||||
% \parbox[t]{\notewidth}{\em #1}
|
||||
% \vspace{\parskip}
|
||||
% }
|
||||
|
||||
\newenvironment{optiontable}%
|
||||
{\begin{tightinset}\renewcommand{\arraystretch}{1.5}%
|
||||
\begin{tabular}{@{}>{\ttt}ll@{}}}%
|
||||
{\end{tabular}\end{tightinset}}%
|
||||
|
||||
\newenvironment{desctable}[1]%
|
||||
{\begin{inset}\renewcommand{\arraystretch}{1.5}%
|
||||
\begin{tabular}{lp{#1}}}%
|
||||
{\end{tabular}\end{inset}}
|
||||
|
||||
\def\*{{\ttt *}}
|
||||
|
||||
% Names of things
|
||||
|
||||
\newcommand{\keyword} [1]{\index{#1}{\normalfont\textsf{#1}}}
|
||||
|
||||
% \ex{#1} and also generates an index entry.
|
||||
\newcommand{\exi}[1]{\index{#1@\texttt{#1}}\ex{#1}}
|
||||
\newcommand{\indextt}[1]{\index{#1@\texttt{#1}}}
|
||||
|
||||
|
||||
\newcommand{\evalto}{$\Longrightarrow$\ }
|
||||
\renewcommand{\star}{$^*$\/}
|
||||
\newcommand{\+}{$^+$}
|
||||
|
||||
% Semantic domains, used to indicate the type of a value
|
||||
|
||||
\newcommand{\sem}{\normalfont\itshape} %semantic font
|
||||
\newcommand{\semvar}[1]{\textit{#1}} %semantic font
|
||||
\newcommand{\synvar}[1]{\textrm{\textit{$\left<\right.$#1$\left.\right>$}}} %syntactic font
|
||||
\newcommand{\type}{\sem}
|
||||
\newcommand{\zeroormore}[1]{{\sem #1$_1$ \ldots #1$_n$}}
|
||||
\newcommand{\oneormore}[1]{{\sem #1$_1$ #1$_2$ \ldots #1$_n$}}
|
||||
|
||||
\newcommand{\proc} {{\sem procedure}}
|
||||
\newcommand{\boolean} {{\sem boolean}}
|
||||
\newcommand{\true} {{\sem true}}
|
||||
\newcommand{\false} {{\sem false}}
|
||||
|
||||
\newcommand{\num} {{\sem number}}
|
||||
\newcommand{\fixnum} {{\sem fixnum}}
|
||||
\newcommand{\integer} {{\sem integer}}
|
||||
\newcommand{\real} {{\sem real}}
|
||||
|
||||
\newcommand{\character} {{\sem character}}
|
||||
\newcommand{\str} {{\sem string}}
|
||||
\newcommand{\sym} {{\sem symbol}}
|
||||
|
||||
\newcommand{\location} {{\sem location}}
|
||||
\newcommand{\object} {{\sem object}}
|
||||
|
||||
\newcommand{\error} {{\sem error}}
|
||||
\newcommand{\syntaxerror} {{\sem syntax error}}
|
||||
\newcommand{\readerror} {{\sem read error}}
|
||||
\newcommand{\undefined} {{\sem undefined}}
|
||||
\newcommand{\noreturn} {{\sem no return value}}
|
||||
|
||||
\newcommand{\port} {{\sem port}}
|
||||
|
||||
% semantic variables
|
||||
|
||||
\newcommand{\identifier} {{\sem identifier}}
|
||||
\newcommand{\identifiers} {\zeroormore{\<ident>}}
|
||||
\newcommand{\expr} {{\sem expression}}
|
||||
\newcommand{\body} {{\sem body}}
|
||||
\newcommand{\valueofbody} {{\sem value~of~body}}
|
||||
\newcommand{\emptylist} {{\sem empty~list}}
|
||||
\newcommand{\car} {\keyword{car}}
|
||||
\newcommand{\cdr} {\keyword{cdr}}
|
||||
\newcommand{\TMPDIR}{\texttt{\$TMPDIR}}
|
||||
|
||||
% generally useful things
|
||||
|
||||
% For line-breaking \tt stuff.
|
||||
\renewcommand{\=}{\discretionary{-}{}{-}}
|
||||
\newcommand{\ob}{\discretionary{}{}{}} % Optional break.
|
||||
|
||||
\newcommand{\indx}[1]{#1 \index{ #1 }}
|
||||
%\newcommand{\gloss}[1]{#1 \glossary{ #1 }}
|
||||
|
||||
% This lossage produces #2 if #1 is zero length, otw #3.
|
||||
% We use it to conditionally add a space between the procedure and
|
||||
% the args in procedure prototypes, but only if there are any args--
|
||||
% we want to produce "(read)", not "(read )".
|
||||
\newlength{\voidlen}
|
||||
\newcommand{\testvoid}[3]{\settowidth\voidlen{#1}\ifdim\voidlen>0in{#3}\else{#2}\fi}
|
||||
|
||||
|
||||
% Typeset a definition prototype line, e.g.:
|
||||
% (cons <arg1> <arg2>) -> pair procedure
|
||||
%
|
||||
% Five args are: proc-name args ret-value(s) type index-entry
|
||||
\newcommand{\dfnix}[5]
|
||||
{\hbox to \linewidth{\ttchars%
|
||||
{\ttt(#1\testvoid{#2}{}{\ }{\sem{#2}}\testvoid{#2}{}{\/})\hskip 1em minus
|
||||
0.5em$\longrightarrow$\hskip 1em minus 0.5em{\sem{#3}}\hfill\quad\textnormal{#4}}}\index{#5}}
|
||||
|
||||
\newcommand{\dfnx}[4] {\dfnix{#1}{#2}{#3}{#4}{#1@\texttt{#1}}}
|
||||
|
||||
\newcommand{\dfn} {\par\medskip\dfnx} % Takes 4 args, actually.
|
||||
\newcommand{\dfni} {\par\medskip\dfnix} % Takes 5 args, actually.
|
||||
|
||||
\newcommand{\defvar} {\par\medskip\defvarx} % Takes 4 args, actually.
|
||||
\newcommand{\defvarx}[2]%
|
||||
{\index{#1}
|
||||
\hbox to \linewidth{\ttchars{{\ttt{#1}} \hfill #2}}}%
|
||||
|
||||
% Typeset the protocol line, then do the following descriptive text indented.
|
||||
% If you want to group two procs together, do the first one with a \dfn,
|
||||
% then the second one, and the documentation, with a \defndescx.
|
||||
|
||||
% This one doesn't put whitespace above. Use it immediately after a \dfn
|
||||
% to group two prototype lines together.
|
||||
\newenvironment{dfndescx}[4]%
|
||||
{\dfnx{#1}{#2}{#3}{#4}\begin{desc}}{\end{desc}}
|
||||
|
||||
\newenvironment{dfndesc}[4] % This one puts whitespace above.
|
||||
{\par\medskip\begin{dfndescx}{#1}{#2}{#3}{#4}}
|
||||
{\end{dfndescx}}
|
||||
|
||||
\newenvironment{desc}%
|
||||
{\nopagebreak[2]%
|
||||
\smallskip
|
||||
\bgroup\begin{list}{}{\topsep=0pt\parskip=0pt}\item[]}
|
||||
{\end{list}\leavevmode\egroup\global\@ignoretrue}
|
||||
|
||||
\def\defun#1#2#3{\dfn{#1}{#2}{#3}{procedure}} % preskip
|
||||
\newcommand{\defunx}[3]{\dfnx{#1}{#2}{#3}{procedure}} % no skip
|
||||
|
||||
\newenvironment{defundescx}[3]%
|
||||
{\begin{dfndescx}{#1}{#2}{#3}{procedure}}
|
||||
{\end{dfndescx}}
|
||||
|
||||
\newenvironment{defundesc}[3]%
|
||||
{\begin{dfndesc}{#1}{#2}{#3}{procedure}}
|
||||
{\end{dfndesc}}
|
||||
|
||||
|
||||
\newenvironment{column}{\begin{tabular}[t]{@{}l@{}}}{\end{tabular}}
|
||||
|
||||
\newenvironment{exampletable}%
|
||||
{\begin{leftinset}%
|
||||
\newcommand{\header}[1]{\multicolumn{2}{@{}l@{}}{##1}\\}%
|
||||
\newcommand{\splitline}[2]%
|
||||
{\multicolumn{2}{@{}l@{}}{##1}\\\multicolumn{2}{@{}l@{}}{\qquad\evalto\quad{##2}}}
|
||||
\begin{tabular}{@{}l@{\quad\evalto\quad}l@{}}}%
|
||||
{\end{tabular}\end{leftinset}}
|
||||
|
||||
% Put on blank lines in a code env to allow a pagebreak.
|
||||
\newcommand{\cb}{\pagebreak[0]}
|
||||
|
||||
\newenvironment{boxedcode}
|
||||
{\begin{inset}\tabular{|l|}\hline}
|
||||
{\\ \hline \end{tabular}\end{inset}}
|
||||
|
||||
% A ragged-right decl that doesn't redefine \\ -- for use in tables.
|
||||
\newcommand{\raggedrightparbox}{\let\temp=\\\raggedright\let\\=\temp}
|
||||
|
||||
\newenvironment{boxedfigure}[1]%
|
||||
{\begin{figure}[#1]\begin{boxedminipage}{\linewidth}\vskip 1.5ex}
|
||||
{\end{boxedminipage}\end{figure}}
|
||||
|
||||
\makeatother
|
|
@ -1,428 +0,0 @@
|
|||
\chapter{DNS Client Library}\label{cha:dns}
|
||||
%
|
||||
\begin{description}
|
||||
\item[Used files:] dns.scm
|
||||
\item[Name of the package:] dns
|
||||
\end{description}
|
||||
%
|
||||
\section{Overview}
|
||||
The \ex{dns} structure contains a library for querying DNS servers.
|
||||
|
||||
Features:
|
||||
\begin{itemize}
|
||||
\item Parsing of \texttt{resolv.conf}, including \texttt{search}
|
||||
entries. This enables looking up the FQDN of a host.
|
||||
\end{itemize}
|
||||
|
||||
\section{Conditions}
|
||||
|
||||
The library defines a set of conditions raised by the procedures of
|
||||
the library. The supertype of these conditions is \exi{dns-error}.
|
||||
\defun{dns-error?}{thing}{\boolean}
|
||||
\begin{desc}
|
||||
The predicate for \ex{dns-error} conditions.
|
||||
\end{desc}
|
||||
\defun{dns-error->string} {dns-error-condition} {\str}
|
||||
\begin{desc}
|
||||
Returns a string with the description of the condition.
|
||||
\end{desc}
|
||||
|
||||
\defvar{parse-error}{condition}
|
||||
\defvarx{unexpected-eof-from-server}{condition}
|
||||
\defvarx{bad-address}{condition}
|
||||
\defvarx{no-nameservers}{condition}
|
||||
\defvarx{bad-nameserver}{condition}
|
||||
\defvarx{not-a-hostname}{condition}
|
||||
\defvarx{not-a-ip} {condition}
|
||||
|
||||
\begin{desc}
|
||||
|
||||
\end{desc}
|
||||
\defvar {dns-format-error} {condition}
|
||||
\defvarx {dns-server-failure} {condition}
|
||||
\defvarx {dns-name-error} {condition}
|
||||
\defvarx {dns-not-implemented} {condition}
|
||||
\defvarx {dns-refused} {condition}
|
||||
\begin{desc}
|
||||
These conditons correspond to errors returned by the DNS server.
|
||||
They are all subtypes of the \exi{dns-server-error} condition which
|
||||
in turn is a subtype of \ex{dns-error}.
|
||||
\end{desc}
|
||||
\defun{dns-server-error?}{thing}{\boolean}
|
||||
\begin{desc}
|
||||
The predicate for \ex{dns-server-error} conditions.
|
||||
\end{desc}
|
||||
|
||||
\defun{parse-error?}{thing} {\boolean}
|
||||
\defunx{unexpected-eof-from-server?}{thing} {\boolean}
|
||||
\defunx{bad-address?}{thing} {\boolean}
|
||||
\defunx{no-nameservers?}{thing} {\boolean}
|
||||
\defunx{bad-nameserver?}{thing} {\boolean}
|
||||
\defunx{not-a-hostname?}{thing} {\boolean}
|
||||
\defunx{not-a-ip?}{thing} {\boolean}
|
||||
\defunx{dns-format-error?} {thing} {\boolean}
|
||||
\defunx{dns-server-failure?} {thing} {\boolean}
|
||||
\defunx{dns-name-error?} {thing} {\boolean}
|
||||
\defunx{dns-not-implemented?} {thing} {\boolean}
|
||||
\defunx{dns-refused?} {thing} {\boolean}
|
||||
\begin{desc}
|
||||
The type predicates for the conditions above.
|
||||
\end{desc}
|
||||
|
||||
\section{High-level Interface}
|
||||
\def\ipaddr{\textnormal{IP-address\xspace}}
|
||||
\def\fqdn{\textnormal{FQDN\xspace}}
|
||||
|
||||
The library uses an internal store to cache data obtained from DNS
|
||||
servers. All procedures take a boolean flag \var{use-cache?} that
|
||||
indicates whether the cache should be used or not. \var{use-cache?}
|
||||
defaults to true.
|
||||
|
||||
\defun{dns-clear-cache!}{}{\undefined}
|
||||
\begin{desc}
|
||||
This procedure erases all information stored in the internal cache.
|
||||
\end{desc}
|
||||
|
||||
The library is further capable of parsing the contents of
|
||||
\texttt{/etc/resolv.conf} (see Section~\ref{sec:dns-rc}). The
|
||||
nameservers listed there are the default value for the optional
|
||||
argument \var{nameserver list} which many procedures of the library
|
||||
possess.
|
||||
|
||||
\defun{dns-lookup-ip}{\fqdn [nameserver list][use-cache?]}{\fqdn}
|
||||
\begin{desc}
|
||||
Given the FQDN of a host, \ex{dns-lookup-ip} returns the IP address.
|
||||
The optional argument specifes the name servers to query, it defaults
|
||||
to the ones found in \texttt{/etc/resolv.conf}.
|
||||
\end{desc}
|
||||
|
||||
\defun{dns-lookup-ip}{\ipaddr [nameserver list][use-cache?]}{\fqdn}
|
||||
\begin{desc}
|
||||
Looks up the FQDN for the given IP address. The optional argument
|
||||
specifes the name servers to query, it defaults to the ones found in
|
||||
\texttt{/etc/resolv.conf}. \oops{use-cache? is not implemented yet}
|
||||
\end{desc}
|
||||
|
||||
\defun{dns-lookup-nameserver}{name/\ipaddr [nameserver list][use-cache?]}{\ipaddr list}
|
||||
\begin{desc}
|
||||
Looks up an authoritative name server for a hostname, returns a list
|
||||
of name servers. The optional argument specifes the name servers to
|
||||
query, it defaults to the ones found in
|
||||
\texttt{/etc/resolv.conf}\oops{use-cache? is not implemented yet}
|
||||
\end{desc}
|
||||
|
||||
\defun{dns-lookup-mail-exchanger}{name/\ipaddr [nameserver list][use-cache?]}{\fqdn list}
|
||||
\begin{desc}
|
||||
Looks up mail-exchangers for a hostname und returns them in a list
|
||||
sorted by preference. \oops{use-cache? is not implemented yet}
|
||||
\end{desc}
|
||||
\defun{socket-address->fqdn}{socket-address [use-cache?]}{\fqdn}
|
||||
\begin{desc}
|
||||
Returns the FQDN for of the address bound to argument. The argument
|
||||
\var{cache?} indicates whether the internal cache may be queried to
|
||||
obtain the information.\oops{use-cache? is required by the implmentation}
|
||||
\end{desc}
|
||||
|
||||
\defun{maybe-dns-lookup-name}{name [nameserver list][use-cache?]}{\ipaddr or \sharpf}
|
||||
\defunx{maybe-dns-lookup-ip}{\ipaddr}{\fqdn{} or \sharpf}
|
||||
\begin{desc}
|
||||
These procedures provide the same functionality as
|
||||
\ex{dns-lookup-name} and \ex{dns-lookup-ip} but return \sharpf{} in
|
||||
case of an \ex{dns-error}.\oops{optional arguments not implemented yet}
|
||||
\end{desc}
|
||||
|
||||
\defun{host-fqdn} {name/socket-address [nameserver list][use-cache?]}{\fqdn}
|
||||
\defunx{system-fqdn}{[nameserver list][use-cache?]}{\fqdn}
|
||||
\begin{desc}
|
||||
\ex{host-fqdn} returns the fully qualified domain name (FQDN) for its
|
||||
argument which can be either a unqualified host name or a socket
|
||||
address. The procedure \ex{system-fqdn} returns the FQDN of the
|
||||
current host.\oops{optional arguments not implemented yet}
|
||||
\end{desc}
|
||||
|
||||
\section{Low-level Interface}
|
||||
|
||||
\defun{dns-lookup}{\fqdn/\ipaddr type [nameserver list][use-cache?]}{dns-message}
|
||||
\begin{desc}
|
||||
This is the most general way to submit a DNS query. The return value
|
||||
is a \ex{dns-message} structure:\oops{optional arguments not implemented yet}
|
||||
\end{desc}
|
||||
|
||||
\defun{dns-message?}{thing}{\boolean}
|
||||
\defunx{dns-message-query}{dns-message}{message}
|
||||
\defunx{dns-message-reply}{dns-message}{message}
|
||||
\defunx{dns-message-cache?}{dns-message}{\boolean}
|
||||
\defunx{dns-message-protocol}{dns-message}{'udp or 'tcp}
|
||||
\defunx{dns-message-tried-nameservers}{dns-message}{}
|
||||
\begin{desc}
|
||||
A \var{dns-message} records the query sent to the server and the
|
||||
reply from the server. It also contains information whether the
|
||||
library took the reply from the cache, which protocol was used and
|
||||
to which nameservers the query was sent.
|
||||
\end{desc}
|
||||
|
||||
\defun{pretty-print-dns-message}{dns-message [output-port]}{\undefined}
|
||||
\begin{desc}
|
||||
Pretty prints a DNS message to \var{out-port} which defaults to the
|
||||
current output port.
|
||||
\end{desc}
|
||||
|
||||
\defun{message?}{thing}{\boolean}{}
|
||||
\defunx{message-header}{message}{header}
|
||||
\defunx{message-questions}{message}{question list}
|
||||
\defunx{message-answers}{message}{rr list}
|
||||
\defunx{message-nameservers}{message}{rr list}
|
||||
\defunx{message-additionals}{message}{rr list}
|
||||
\defunx{message-source}{message}{char list}
|
||||
\begin{desc}
|
||||
A \ex{message} represents the data sent to the DNS server or
|
||||
received from the DNS server. The DNS protocol uses the same message
|
||||
format for queries and replies. In queries only the header and the
|
||||
questions is present, a reply may contain answers, name servers and
|
||||
and additional informations as resource records. \ex{message-source}
|
||||
returns the actual data sent over the network.
|
||||
\end{desc}
|
||||
|
||||
\defun{header?}{thing}{\boolean}
|
||||
\defunx{header-id}{header}{number}
|
||||
\defunx{header-flags}{header}{flags}
|
||||
\defunx{header-question-count}{header}{number}
|
||||
\defunx{header-answer-count}{header}{number}
|
||||
\defunx{header-nameserver-count}{header}{number}
|
||||
\defunx{header-additional-count}{header}{number}
|
||||
\begin{desc}
|
||||
Every DNS message contains a header which stores information about
|
||||
the data present in the message and contains flags for the query.
|
||||
\end{desc}
|
||||
|
||||
\defun{flags?}{thing}{\boolean}
|
||||
\defunx{flags-query-type}{flags}{'query or 'response}
|
||||
\defunx{flags-opcode}{flags}{number}
|
||||
\defunx{flags-authoritative?}{flags}{\boolean}
|
||||
\defunx{flags-truncated?}{flags}{\boolean}
|
||||
\defunx{flags-recursion-desired?}{flags}{\boolean}
|
||||
\defunx{flags-recursion-available?}{flags}{\boolean}
|
||||
\defunx{flags-z}{flags}{0}
|
||||
\defunx{flags-response-code}{flags}{number}
|
||||
\begin{desc}
|
||||
Flags occur within the header of a DNS message. The boolean value
|
||||
returned from \ex{flags-authoritative} indicates whether the message
|
||||
was sent from a authoritative server, \ex{flags-truncated?} should
|
||||
always be \sharpf as the library automatically uses the TCP protocol
|
||||
is the UDP message size is not sufficied.
|
||||
\end{desc}
|
||||
|
||||
\defun{question?}{thing}{\boolean}
|
||||
\defunx{question-name}{question}{\str}
|
||||
\defunx{question-type}{question}{message-type}
|
||||
\defunx{question-class}{question}{message-class}
|
||||
\begin{desc}
|
||||
A question sent to the DNS server.
|
||||
\end{desc}
|
||||
The type and class of the question and answer are elements of
|
||||
enumerated types: \textbf{class doesn't start at 0}
|
||||
|
||||
\dfn{message-class}{class-name}{message-class}{syntax}
|
||||
\defunx{message-class?}{thing}{\boolean}
|
||||
\defunx{message-class-name}{message-class}{symbol}
|
||||
\defunx{message-class-number}{message-class}{number}
|
||||
\begin{desc}
|
||||
\ex{message-class} constructs a member of the enumeration,
|
||||
\ex{message-class?} is the type predicate, \ex{message-class-name}
|
||||
returns the symbol and \ex{message-class-number} the number used for
|
||||
the class in the DNS protocol.
|
||||
\end{desc}
|
||||
The possible names for the classes are:
|
||||
\begin{description}
|
||||
\item[\ex{in}] The Internet
|
||||
\item[\ex{cs}] obsolete
|
||||
\item[\ex{ch}] the CHAOS class
|
||||
\item[\ex{hs}] Hesoid
|
||||
\end{description}
|
||||
|
||||
\dfn{message-type}{type-name}{message-type}{syntax}
|
||||
\defunx{message-type?}{thing}{\boolean}
|
||||
\defunx{message-type-name}{message-type}{symbol}
|
||||
\defunx{message-type-index}{message-type}{number}
|
||||
\begin{desc}
|
||||
\ex{message-type} constructs a member of the enumeration from name
|
||||
\synvar{type-name} listed in Table~\ref{tab:message-types}.
|
||||
\ex{message-type?} is the type predicate, \ex{message-type-name}
|
||||
returns the name, and \ex{message-type-number} the number used for
|
||||
the class the DNS protocol.
|
||||
|
||||
\end{desc}
|
||||
\begin{table}[htb]
|
||||
\centering
|
||||
\begin{tabular}{|l|l|}
|
||||
\hline
|
||||
\ex{a}& a host address\\\hline
|
||||
\ex{ns}&an authoritative name server\\\hline
|
||||
\ex{md}&(obsolete)\\\hline
|
||||
\ex{mf}&(obsolete)\\\hline
|
||||
\ex{cname}&the canonical name for an alias\\\hline
|
||||
\ex{soa}& marks the start of a zone of authority\\\hline
|
||||
\ex{mb}&(experimental)\\\hline
|
||||
\ex{mg}&(experimental)\\\hline
|
||||
\ex{mr}&(experimental)\\\hline
|
||||
\ex{null}& (experimental)\\\hline
|
||||
\ex{wks}& a well known service description\\\hline
|
||||
\ex{ptr}& a domain name pointer\\\hline
|
||||
\ex{hinfo}& host information\\\hline
|
||||
\ex{minfo}& (experimental)\\\hline
|
||||
\ex{mx}& mail exchange\\\hline
|
||||
\ex{txt}& text strings\\\hline
|
||||
\end{tabular}
|
||||
\caption{Message types}
|
||||
\label{tab:message-types}
|
||||
\end{table}
|
||||
|
||||
\defun{rr?}{thing}{\boolean}
|
||||
\defunx{rr-name}{rr}{\str}
|
||||
\defunx{rr-type}{rr}{message-type}
|
||||
\defunx{rr-class}{rr}{message-class}
|
||||
\defunx{rr-ttl}{rr}{number}
|
||||
\defunx{rr-data}{rr}{rr-data-X}
|
||||
\begin{desc}
|
||||
A resource record as returned from the DNS server. The actual data
|
||||
of the record is stored in the \texttt{rr-data} field.
|
||||
\end{desc}
|
||||
|
||||
\defun{rr-data-a?}{thing}{\boolean}
|
||||
\defunx{rr-data-a-ip}{rr-data-a}{\ipaddr}
|
||||
\begin{desc}
|
||||
An address resource record which holds an internet address.
|
||||
\end{desc}
|
||||
|
||||
\defun{rr-data-ns?}{thing}{\boolean}
|
||||
\defunx{rr-data-ns-name}{rr-data-ns}{\fqdn}
|
||||
\begin{desc}
|
||||
A name server resource record containing the FQDN of the name server.
|
||||
\end{desc}
|
||||
|
||||
\defun{rr-data-cname?}{thing}{\boolean}
|
||||
\defunx{rr-data-cname}{rr-data-cname}{\fqdn}
|
||||
\begin{desc}
|
||||
A canonical name resource record which contains the canonical or
|
||||
primary name of the owner.
|
||||
\end{desc}
|
||||
|
||||
\defun{rr-data-mx?}{thing}{\boolean}
|
||||
\defunx{rr-data-mx-preference}{rr-data-mx}{number}
|
||||
\defunx{rr-data-mx-exchanger}{rr-data-mx}{\fqdn}
|
||||
\begin{desc}
|
||||
A mail exchange resource record with the preference and the FQDN of
|
||||
a host willing to act as a mail exchange.
|
||||
\end{desc}
|
||||
|
||||
\defun{rr-data-ptr?}{thing}{\boolean}
|
||||
\defunx{rr-data-ptr-name}{rr-data-ptr}{\str}
|
||||
\begin{desc}
|
||||
A pointer resource record which points to some other domain name.
|
||||
\end{desc}
|
||||
|
||||
\defun{rr-data-soa?}{thing}{\boolean}
|
||||
\defunx{rr-data-soa-mname}{rr-data-soa}{\fqdn}
|
||||
\defunx{rr-data-soa-rname}{rr-data-soa}{\fqdn}
|
||||
\defunx{rr-data-soa-serial}{rr-data-soa}{number}
|
||||
\defunx{rr-data-soa-refresh}{rr-data-soa}{number}
|
||||
\defunx{rr-data-soa-retry}{rr-data-soa}{number}
|
||||
\defunx{rr-data-soa-expire}{rr-data-soa}{number}
|
||||
\defunx{rr-data-soa-minimum}{rr-data-soa}{number}
|
||||
\begin{desc}
|
||||
A zone of authority resource record.
|
||||
\end{desc}
|
||||
The protocol specifies other possiple values for the \texttt{rr-data}
|
||||
field but we where no able to find test cases for them.
|
||||
|
||||
|
||||
\defun{cache?}{thing}{\boolean}
|
||||
\defunx{cache-answer}{cache}{dns-message}
|
||||
\defunx{cache-ttl}{cache}{number}
|
||||
\defunx{cache-time}{cache}{number}
|
||||
\begin{desc}
|
||||
A cache data structure corresponds to a saved answer to a previous
|
||||
query. \ex{cache-answer} returns the saved message, \ex{cache-ttl}
|
||||
returns the time when the cache entry expires and \ex{cache-time}
|
||||
returns the time the entry was created.
|
||||
\end{desc}
|
||||
|
||||
|
||||
\section{Host Names}
|
||||
\defun{is-fqdn?}{\str}{\boolean}
|
||||
\begin{desc}
|
||||
Indicates whether the argument matches the grammar for a fully
|
||||
qualified domain name.
|
||||
\oops{The current implementation simply searches for a dot in the name}
|
||||
\end{desc}
|
||||
|
||||
\defun{unqualified-hostname?}{\str}{\boolean}
|
||||
\begin{desc}
|
||||
Returns true if the argument matches the grammar for a unqualified
|
||||
host name.
|
||||
\oops{This procedure isn't implemented yet}
|
||||
\end{desc}
|
||||
|
||||
\section{Parsing \texttt{/etc/resolv.conf}}
|
||||
\label{sec:dns-rc}
|
||||
|
||||
\defvar{resolv.conf-parse-error} {condition}
|
||||
\defun{resolv.conf-parse-error?}{thing}{\boolean}
|
||||
\begin{desc}
|
||||
The code signals the condition \var{resolv.conf-parse-error} if a
|
||||
parse error occurs while scanning \texttt{/etc/resolv.conf}. It is a
|
||||
subtype of the \var{dns-error} condition.
|
||||
\ex{resolv.conf-parse-error?} is the type predicate for this
|
||||
condition.
|
||||
\oops{this is not implemented yet}
|
||||
\end{desc}
|
||||
|
||||
\defun{resolv.conf}{}{{symbol$\rightarrow$string} alist}
|
||||
\begin{desc}
|
||||
Returns the contents of \texttt{/etc/resolv.conv} as an alist with
|
||||
the possible keys \texttt{nameserver}, \texttt{domain},
|
||||
\texttt{search}, \texttt{sortlist}, \texttt{options}.
|
||||
|
||||
Note that the library caches the contents of
|
||||
\texttt{/etc/resolv.conv} and \ex{resolv.conf} only really opens the
|
||||
file if its modification time is more recent than the modification
|
||||
time of the cache.
|
||||
\end{desc}
|
||||
\defun{parse-resolv.conf!}{}{\undefined}
|
||||
\begin{desc}
|
||||
Parses the contents of \texttt{/etc/resolv.conv} and updates the
|
||||
internal cache of the library.
|
||||
\end{desc}
|
||||
\defun{dns-find-nameserver-list}{}{\fqdn list}
|
||||
\begin{desc}
|
||||
Returns a list of name servers from \texttt{/etc/resolv.conf}
|
||||
\end{desc}
|
||||
\defun{dns-find-nameserver}{}{\fqdn}
|
||||
\begin{desc}
|
||||
Returns the first name servers found in \texttt{/etc/resolv.conf}.
|
||||
\ex{dns-find-nameserver} raises \ex{no-nameservers} if
|
||||
\texttt{/etc/resolv.conf} does not contain a \texttt{nameserver}
|
||||
entry.
|
||||
\end{desc}
|
||||
\defun{domains-for-search}{}{\str list}
|
||||
\begin{desc}
|
||||
Parses \texttt{/etc/resolv.conf} and extracts the domains specified
|
||||
by the \texttt{search} keyword.
|
||||
\end{desc}
|
||||
|
||||
|
||||
\section{IP Addresses as Dotted Strings}
|
||||
\textbf{Should live in its own package}
|
||||
\defun{address32->ip-string}{\ipaddr}{ip-string}
|
||||
|
||||
\defun{ip-string->address32}{ip-string}{\ipaddr}
|
||||
|
||||
\defun{ip-string?}{string}{\boolean}
|
||||
\begin{desc}
|
||||
Tests whether \var{string} represents a valid IPv4 address.
|
||||
\oops{not yet implemented}
|
||||
\end{desc}
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,163 +0,0 @@
|
|||
\chapter{FTP Client}\label{cha:ftp}
|
||||
|
||||
The \ex{ftp} structure lets you transfer files between networked
|
||||
machines from the Scheme Shell, using the File Transfer Protocol as
|
||||
described in RFC~959.
|
||||
|
||||
Some of the procedures in this module extract useful information from
|
||||
the server's reply, such as the size of a file, or the name of the
|
||||
directory we have moved to. These procedures return the extracted
|
||||
information, or, if the server's response doesn't match the expected
|
||||
code from the server, a catchable \ex{ftp-error} is raised.
|
||||
|
||||
\defun{ftp-connect}{host login password passive? [log-port]}{connection}
|
||||
\begin{desc}
|
||||
Open a command connection with the remote machine \var{host} and
|
||||
login on that server with \var{login} and \var{password}.
|
||||
\var{Login} and \var{password} can be \sharpf, in which case the
|
||||
information is extracted from the user's \ex{.netrc} file if necessary.
|
||||
|
||||
If \var{log-port} is specified, it must be an output port: this
|
||||
starts logging the conversation with the server to that port. Note
|
||||
that the log contains passwords in clear text.
|
||||
\end{desc}
|
||||
|
||||
\dfn{ftp-type}{\synvar{name}}{ftp-type}{syntax}
|
||||
\defunx{set-ftp-type!}{connection ftp-type}{undefined}
|
||||
\begin{desc}
|
||||
This change the transfer mode for future file transfers. The
|
||||
transfer mode is specfified by \var{ftp-type} which can be created
|
||||
with the \ex{ftp-type} macro. \synvar{Name} must be either
|
||||
\ex{binary} for binary data or \ex{ascii} for text.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-rename}{connection old new}{undefined}
|
||||
\begin{desc}
|
||||
This changes the name of \var{old} on the remote host to \var{new}
|
||||
(assuming sufficient permissions). \var{Old} and \var{new} are
|
||||
strings.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-delete}{connection file}{undefined}
|
||||
\begin{desc}
|
||||
This deletes \var{file} from the remote host (assuming the user has
|
||||
appropriate permissions).
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-cd}{connection dir}{undefined}
|
||||
\begin{desc}
|
||||
This changes the current directory on the server.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-cdup}{connection}{undefined}
|
||||
\begin{desc}
|
||||
This move to the parent directory on the server.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-pwd}{connection}{string}
|
||||
\begin{desc}
|
||||
Return the current directory on the remote host, as a string.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-ls}{connection [dir]}{list}
|
||||
\begin{desc}
|
||||
This returns a list of filenames on the remote host, either from the
|
||||
current directory (if \var{dir} is not specified), or from the
|
||||
directory specified by \var{dir}.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-dir}{connection [dir]}{status}
|
||||
\begin{desc}
|
||||
This returns a list of long-form file name entries on the remote
|
||||
host, either from the current directory (if \var{dir} is not
|
||||
specified), or from the directory specified by \var{dir}. (Note
|
||||
that the format for the long-form entries is not specified by the
|
||||
FTP standard.)
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-get}{connection remote-file proc}{undefined}
|
||||
\begin{desc}
|
||||
This downloads \var{remote-file} from the FTP server.
|
||||
\ex{Ftp-get} establishes a data conneciton to the server, attaches
|
||||
an input port to the data connection, and calls \var{proc} on that
|
||||
port.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-put}{connection remote-file proc}{undefined}
|
||||
\begin{desc}
|
||||
This uploads \var{remote-file} to the FTP server. \ex{Ftp-put}
|
||||
establishes a data conneciton to the server, attaches an output port
|
||||
to the data connection, and calls \var{proc} on that port.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-append}{connection remote-file proc}{undefined}
|
||||
\begin{desc}
|
||||
This appends data to \var{remote-file} on the FTP server.
|
||||
\ex{Ftp-append} establishes a data conneciton to the server,
|
||||
attaches an output port to the data connection, and calls \var{proc}
|
||||
on that port.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-rmdir}{connection dir}{undefined}
|
||||
\begin{desc}
|
||||
This removes the directory \var{dir} from the remote host (assuming
|
||||
sufficient permissions).
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-mkdir}{connection dir}{undefined}
|
||||
\begin{desc}
|
||||
This create a new directory named \var{dir} on the remote host
|
||||
(assuming sufficient permissions).
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-modification-time}{connection file}{date}
|
||||
\begin{desc}
|
||||
This requests the time of the last modification of \var{file} on the
|
||||
remote host, and on success return a Scsh date record. (This command
|
||||
is not part of RFC~959 and is not implemented by all servers, but is
|
||||
useful for mirroring.)
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-size}{connection file}{integer}
|
||||
\begin{desc}
|
||||
This returns the size of \var{file} in bytes. (This command is not
|
||||
part of RFC~959 and is not implemented by all servers.)
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-quit}{connection}{undefined}
|
||||
\begin{desc}
|
||||
This closes the connection to the remote host. The \var{connection}
|
||||
object is useless after a quit command.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-quot}{connection command}{status}
|
||||
\begin{desc}
|
||||
This sends a \var{command} verbatim to the remote server and wait
|
||||
for a response. The response text is returned verbatim.
|
||||
\end{desc}
|
||||
|
||||
\defun{ftp-error?}{thing}{boolean}
|
||||
\begin{desc}
|
||||
This returns \sharpt{} if \var{thing} is a \ex{ftp-error} object,
|
||||
otherwise \sharpf.
|
||||
\end{desc}
|
||||
|
||||
\defun{copy-port->port-binary}{input-port oputput-port}{undefined}
|
||||
\defunx{copy-port->port-ascii}{input-port oputput-port}{undefined}
|
||||
\defunx{copy-ascii-port->port}{input-port oputput-port}{undefined}
|
||||
\begin{desc}
|
||||
These procedures are useful for downloading and uploading data to an
|
||||
FTP connection via \ex{ftp-get}, \ex{ftp-get}, and \ex{ftp-append}.
|
||||
They all copy data from one port to another.
|
||||
\ex{Copy-port->port-binary} copies verbatim, while the other two
|
||||
perform CR/LF conversion for ASCII data transfers.
|
||||
\ex{Copy-port->port-ascii} adds CR/LFs at line endings on output,
|
||||
whereas \ex{Copy-ascii-port->port} removes CR/LFs at line endings
|
||||
end replaces them by ordinary LFs.
|
||||
\end{desc}
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
\chapter{FTP server}\label{cha:ftpd}
|
||||
|
||||
\begin{description}
|
||||
\item[Used files:] ftpd.scm
|
||||
\item[Name of the package:] ftpd
|
||||
\end{description}
|
||||
|
||||
\section{What users want to know}
|
||||
|
||||
\section{Entry points}
|
||||
|
||||
\defun {ftpd} {options} {\noreturn}
|
||||
\begin{defundescx}{ftp-inetd} {anonymous-home} {\noreturn}
|
||||
\ex{ftpd} starts the server, using \semvar{anonymous-home} as the
|
||||
root directory of the server. Usage of relative paths is not
|
||||
encouraged. \semvar{port} specifies the port the server is
|
||||
listening for connections. It defaults to 21. \ex{ftpd} makes a log
|
||||
entry for each file sent or retrieved. These logs are written to
|
||||
\semvar{logfile}, if given. If \semvar{dns-lookup?} is \sharpt, the
|
||||
logfile will contain the host names instead of their IP
|
||||
addresses. If \semvar{dns-lookup?} is not specified or \sharpf, the
|
||||
IP addresses are stored.
|
||||
|
||||
The log format of \ex{ftpd} is the same as the one of
|
||||
\ex{wuftpd}. The fields are seperated by spaces and contain
|
||||
following informations:
|
||||
\codex{Fri Apr 19 17:08:14 2002 4 134.2.2.171 56881 /files.lst b \_ i a nop@ssword ftp 0 *}
|
||||
\begin{enumerate}
|
||||
\item Current date and time. This field contains
|
||||
spaces and is 24 characters long.
|
||||
\item Transfer time in seconds.
|
||||
\item Remote host IP (wuftpd puts the name here).
|
||||
\item File size in bytes
|
||||
\item Name of file (spaces are converted to underscores)
|
||||
\item Transfer type: \underline{a}scii or \underline{b}inary (image type).
|
||||
\item Special action flags. As \ex{ftpd} does not support any special
|
||||
action, we are always `\ex{\_}' here.
|
||||
\item File was sent to user (\underline{o}utgoing) or received from user
|
||||
(\underline{i}ncoming)
|
||||
\item \underline{A}nonymous access
|
||||
\item Anonymous ftp password. We do not use anyone.
|
||||
\item Service name - always \ex{ftp}.
|
||||
\item Authentication mode (always none = `\ex{0}').
|
||||
\item Authenticated user ID (always not available = `\ex{*}')
|
||||
\end{enumerate}
|
||||
|
||||
As the procedure does not return, you have to do a \ex{fork} in
|
||||
order to have a ``real'' daemon: \codex{(fork (lambda () (ftpd
|
||||
"/data/ftp" 8080)))} \ex{ftpd-inetd} is the version to be used with
|
||||
a daemon like \ex{inetd}. If the server is started this way, it
|
||||
handles the connection through the current standard output and input
|
||||
ports.
|
||||
\end{defundescx}
|
||||
|
||||
\subsubsection*{Examples}
|
||||
|
||||
To start the server with the current home directory as root directory
|
||||
and listening on port 8080, use
|
||||
\codex{(ftpd (cwd) 8080)}
|
||||
|
||||
This is how the ftp server at the computing faculty of the university
|
||||
of Tuebingen\footnote{\texttt{archive.informatik.uni-tuebingen.de}} is
|
||||
started:
|
||||
\begin{alltt}
|
||||
#!/bin/sh /scsh-0.6-alpha/bin/scsh <<EOF
|
||||
,batch on
|
||||
,config ,load modules.scm
|
||||
,open ftpd
|
||||
,open threads
|
||||
(define (archive-ftpd args)
|
||||
(with-syslog-destination
|
||||
#f
|
||||
#f
|
||||
(syslog-facility local0)
|
||||
#f
|
||||
(lambda ()
|
||||
(ftpd "/data/archive/"))))
|
||||
(dump-scsh-program archive-ftpd "archive-ftpd.image")
|
||||
;; (dump-scsh "archive-ftpd.image")
|
||||
EOF
|
||||
\end{alltt}
|
||||
|
||||
Perhaps you have noticed the \ex{with-syslog-destination} command.
|
||||
\ex{ftpd} generates syslog-messages that can be controlled via this
|
||||
command. The following section gives you an overview of what is logged
|
||||
at which level. See \ex{man 3 syslog} or the
|
||||
\ex{with-syslog-destination} command in the scsh-manual for further
|
||||
details.
|
||||
|
||||
\subsubsection*{Syslog messages}
|
||||
|
||||
\ex{ftpd} outputs a lot of syslog-messages. A syslog-message may look like
|
||||
this:
|
||||
\codex{Jul 24 18:34:52 axl ftpd: (thread 21) anonymous user login (230)}
|
||||
|
||||
The log gives you following informations (including those delivered by
|
||||
the syslog-daemon):
|
||||
|
||||
\begin{enumerate}
|
||||
\item The date and time the log was made (here: Jul 24 18:34:52)
|
||||
\item The machine the log was made on (here: axl)
|
||||
\item The program, that output the log (ftpd)
|
||||
\item The thread the message concerns (here thread 21)
|
||||
|
||||
Each connection is linked with a thread, that handles the commands
|
||||
of this connection. When the thread is created, there is a entry in
|
||||
the log file containing the remote address and the thread number, so
|
||||
in future logs the thread number labels the connection. As at any
|
||||
given time the thread number is unique, this is a bijection. (Note
|
||||
that the thread numbers are not unique over a period of time).
|
||||
\item The log message (here: notification about an anonymous user login)
|
||||
\item The reply code returned by ftpd, if any (here: 230)
|
||||
\end{enumerate}
|
||||
|
||||
\subsubsection*{The Syslog-levels used\footnote{For further details
|
||||
on syslog levels see \ex{man 3 syslog}}}
|
||||
|
||||
Following events are logged as
|
||||
\begin{description}
|
||||
|
||||
\item[\ex{NOTICE} level:]
|
||||
\begin{itemize}
|
||||
\item Messages concerning \emph{connections} (establishing connection,
|
||||
connection refused, closing connection due to timeout, etc.)
|
||||
\item The execution of the \ex{STOR} command. Its success (\ie
|
||||
somebody is putting something on your server via ftp, also known as
|
||||
\ex{PUT}) is also logged at notice-level. In fact, the log is made
|
||||
before the storing is started actually.
|
||||
\item Internal errors
|
||||
\item Unix errors
|
||||
\item Reaching of actually unreachable case branches
|
||||
\end{itemize}
|
||||
|
||||
\item[\ex{INFO} level:] Messages concerning all \emph{other commands},
|
||||
including the \ex{RETR} command.
|
||||
|
||||
\item[\ex{DEBUG} level:] All other messages, including debug messages.
|
||||
If you want to debug ftpd, put all the messages in one single file,
|
||||
since the debug-messages may refer to messages of other levels.
|
||||
\end{description}
|
||||
|
||||
Success (as long as interesting) and failure of commands are logged at
|
||||
info-level, except the success of the STOR-command, that is logged at
|
||||
notice-level (as mentioned above).
|
||||
\subsubsection*{Supported commands}
|
||||
|
||||
For those of you who are intrested, the table \ref{ftpd-commands}
|
||||
shows the list of supported commands by \ex{ftpd} according to
|
||||
RFC~959:
|
||||
|
||||
%{Can there be a pagebreak in a table?}
|
||||
|
||||
|
||||
\begin{table}
|
||||
\label{ftpd-commands}
|
||||
\begin{center}
|
||||
\begin{tabular}{|lp{10cm}|}
|
||||
\hline
|
||||
\ex{ABOR} & abort connection \\
|
||||
\ex{CDUP} & move to parent directory \\
|
||||
\ex{CWD} & move to specified directory (relative paths may be used) \\
|
||||
\ex{DELE} & delete file \\
|
||||
\ex{LIST} & list files in current directory (long format) \\
|
||||
\ex{MDTM} & deliver modification time of a regular file \\
|
||||
\ex{MKD} & make directory \\
|
||||
\ex{MODE} & change mode (only stream mode (S) is supported) \\
|
||||
\ex{NLST} & list files in current directory (short format) \\
|
||||
\ex{NOOP} & do nothing \\
|
||||
\ex{PASS} & read in passphrase (\ex{ftpd} currently does not support
|
||||
non-anonymous logins) \\
|
||||
\ex{PASV} & change to passive mode \\
|
||||
\ex{PORT} & change connection port \\
|
||||
\ex{PWD} & return name of working directory (print working directory) \\
|
||||
\ex{QUIT} & quit session \\
|
||||
\ex{RETR} & return file (GET) \\
|
||||
\ex{RMD} & remove directory \\
|
||||
\ex{RNFR} & read in the name of a file to be renamed (use \ex{RNTO} next) \\
|
||||
\ex{RNTO} & rename file mentioned before in a \ex{RNFR} command \\
|
||||
\ex{SIZE} & return size of a regular file \\
|
||||
\ex{STOR} & store file (PUT) \\
|
||||
\ex{STRU}& change structure to transfer files
|
||||
(only the file structure is supported) \\
|
||||
\ex{SYST} & return system type \\
|
||||
\ex{TYPE} & change type (supported types: A is ascii mode,
|
||||
I or L8 are 8-bit binary mode) \\
|
||||
\ex{USER} & login user (only anonymous logins allowed,
|
||||
use ``anonymous'' or ``ftp'' as user name) \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
\caption{Supported RFC~959 commands by the server.}
|
||||
\end{center}
|
||||
\end{table}
|
||||
|
||||
|
||||
%\subsection{What programmers want to know}
|
||||
|
||||
%Let me know what you want to know. As long as you are waiting for my
|
||||
%answer, have a look in the source file (I'm sorry).
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,16 +0,0 @@
|
|||
% headings.tex -*- latex -*-
|
||||
% Quieter headings that the ones used in article.sty.
|
||||
% This is not a style option. Don't say [headings].
|
||||
% Instead, say \input{headings} after the \documentstyle.
|
||||
% -Olin 7/91
|
||||
|
||||
\makeatletter
|
||||
|
||||
\def\section{\@startsection {section}{1}{\z@}{-3.5ex plus -1ex minus
|
||||
-.2ex}{2.3ex plus .2ex}{\large\normalfont\bfseries}}
|
||||
\def\subsection{\@startsection{subsection}{2}{\z@}{-3.25ex plus -1ex minus
|
||||
-.2ex}{1.5ex plus .2ex}{\normalsize\normalfont\bfseries}}
|
||||
\def\subsubsection{\@startsection{subsubsection}{3}{\z@}{-3.25ex plus
|
||||
-1ex minus -.2ex}{1.5ex plus .2ex}{\normalsize\normalfont\bfseries}}
|
||||
|
||||
\makeatother
|
|
@ -1,649 +0,0 @@
|
|||
\chapter{HTTP server}\label{cha:httpd}
|
||||
%
|
||||
The SUnet HTTP Server is a complete industrial-strength implementation
|
||||
of the HTTP 1.0 protocol. It is highly configurable and allows the writing
|
||||
of dynamic web pages that run inside the server without going through
|
||||
complicated and slow protocols like CGI or Fast/CGI.
|
||||
|
||||
\section{Starting and configuring the server}
|
||||
|
||||
All procedures described in this section are exported by the
|
||||
\texttt{httpd} structure.
|
||||
|
||||
The Web server is started by calling the \ex{httpd} procedure, which takes
|
||||
one argument, an options value:
|
||||
|
||||
\defun{httpd}{options}{\noreturn}
|
||||
\begin{desc}
|
||||
This procedure starts the server. The \var{options} argument
|
||||
specifies various configuration parameters, explained below.
|
||||
|
||||
The server's basic loop is to wait on the port for a connection from
|
||||
an HTTP client. When it receives a connection, it reads in and
|
||||
parses the request into a special request data structure. Then the
|
||||
server forks a thread which binds the current I/O ports to the
|
||||
connection socket, and then hands off to the top-level
|
||||
request handler (which must be specified in the options). The
|
||||
request handler is responsible for actually serving
|
||||
the request---it can be any arbitrary computation. Its output goes
|
||||
directly back to the HTTP client that sent the request.
|
||||
|
||||
Before calling the request handler to service the request, the HTTP
|
||||
server installs an error handler that fields any uncaught error,
|
||||
sends an error reply to the client, and aborts the request
|
||||
transaction. Hence any error caused by a request handler will be
|
||||
handled in a reasonable and robust fashion.
|
||||
\end{desc}
|
||||
%
|
||||
The options argument can be constructed through a number of procedures
|
||||
with names of the form \texttt{with-\ldots}. Each of these procedures
|
||||
either creates a fresh options value or adds a configuration parameter
|
||||
to an old options argument. The configuration parameter value is
|
||||
always the first argument, the (old) options value the optional second
|
||||
one. Here they are:
|
||||
|
||||
\defun{with-port}{port [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies the port on which the server listens. Defaults to 80.
|
||||
\end{desc}
|
||||
|
||||
\defun{with-root-directory}{root-directory [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies the current directory of the server. Note that this
|
||||
is \emph{not} the document root directory. Defaults to \texttt{/}.
|
||||
\end{desc}
|
||||
|
||||
\defun{with-fqdn}{fqdn [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies the fully-qualified domain name the server uses in
|
||||
automatically generated replies, or \ex{\#f} if the server should
|
||||
query DNS for the fully-qualified domain name.. Defaults to \ex{\#f}.
|
||||
\end{desc}
|
||||
|
||||
\defun{with-reported-port}{reported-port [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies the port number the server uses in automatically
|
||||
generated replies or \ex{\#f} if the reported port is the same as
|
||||
the port the server is listening on. (This is useful if you're
|
||||
running the server through an accelerating proxy.) Defaults to
|
||||
\ex{\#f}.
|
||||
\end{desc}
|
||||
|
||||
\defun{with-server-admin}{mail-address [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies the email address of the server administrator the
|
||||
server uses in automatically generated replies. Defaults to \ex{\#f}.
|
||||
\end{desc}
|
||||
|
||||
\defun{with-icon-name}{icon-name [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies how to generate the links to various decorative icons
|
||||
for the listings. It can either be a procedure which gets passed an
|
||||
icon tag (a symbol) and is expected to return a link pointing to the icon. If
|
||||
it is a string, that is taken as prefix to which the icon tag are
|
||||
appended. If \ex{\#f}, just the plain file names will be used. Defaults to \ex{\#f}.
|
||||
|
||||
The valid icon tags, together with the default names of their icon
|
||||
files, are:
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{|l|l|}
|
||||
\hline
|
||||
\texttt{directory} & \texttt{directory.xbm}\\\hline
|
||||
\texttt{text} & \texttt{text.xbm}\\\hline
|
||||
\texttt{doc} & \texttt{doc.xbm}\\\hline
|
||||
\texttt{image} & \texttt{image.xbm}\\\hline
|
||||
\texttt{movie} & \texttt{movie.xbm}\\\hline
|
||||
\texttt{audio} & \texttt{sound.xbm}\\\hline
|
||||
\texttt{archive} & \texttt{tar.xbm}\\\hline
|
||||
\texttt{compressed} & \texttt{compressed.xbm}\\\hline
|
||||
\texttt{uu} & \texttt{uu.xbm}\\\hline
|
||||
\texttt{binhex} & \texttt{binhex.xbm}\\\hline
|
||||
\texttt{binary} & \texttt{binary.xbm}\\\hline
|
||||
\texttt{blank} & \texttt{blank.xbm}\\\hline
|
||||
\texttt{back} & \texttt{back.xbm}\\\hline
|
||||
unknown & \texttt{unknown.xbm}\\\hline
|
||||
\end{tabular}
|
||||
|
||||
Example icons can be found as part of the CERN httpd distribution
|
||||
at \url{http://www.w3.org/pub/WWW/Daemon/}.
|
||||
\end{center}
|
||||
\end{desc}
|
||||
|
||||
\defun{with-request-handler}{request-handler [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies the request handler of the server to which the server
|
||||
delegates the actual work. More on that subject below in
|
||||
Section~\ref{httpd:request-handlers}. This parameter must be specified.
|
||||
\end{desc}
|
||||
|
||||
\defun{with-simultaneous-requests}{requests [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies a limit on the number of simultaneous requests the
|
||||
server servers. If that limit is exceeded during operation, the
|
||||
server will hold off on new requests until the number of
|
||||
simultaneous requests has sunk below the limit again. If this
|
||||
parameter is \ex{\#f}, no limit is imposed. Defaults to \ex{\#f}.
|
||||
\end{desc}
|
||||
|
||||
\defun{with-logfile}{logfile [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies the name of a log file for the server where it writes
|
||||
Common Log Format logging information. It can also be a port in
|
||||
which case the information is logged to that port, or \ex{\#f} for
|
||||
no logging. Defaults to \ex{\#f}.
|
||||
|
||||
To allow rotation of logfiles, the server re-opens the logfile
|
||||
whenever it receives a \texttt{USR1} signal.
|
||||
\end{desc}
|
||||
|
||||
\defun{with-syslog?}{syslog? [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies whether the server will log information about
|
||||
incoming to the Unix syslog facility. Defaults to \ex{\#t}.
|
||||
\end{desc}
|
||||
|
||||
\defun{with-resolve-ip?}{resolve-ip? [options]}{options}
|
||||
\begin{desc}
|
||||
This specifies whether the server writes the domain names rather
|
||||
than numerical IPs to the output log it produces. Defaults to
|
||||
\ex{\#t}.
|
||||
\end{desc}
|
||||
|
||||
To avoid paranthitis, the \ex{make-httpd-options} procedure eases the
|
||||
construction of the options argument:
|
||||
|
||||
\defun{make-httpd-options}{transformer value \ldots}{options}
|
||||
\begin{desc}
|
||||
This constructs an options value from an argument list of parameter
|
||||
transformers and parameter values. The arguments come in pairs,
|
||||
each an option transformer from the list above, and a value for that
|
||||
parameter. \ex{Make-httpd-options} returns the resulting options value.
|
||||
\end{desc}
|
||||
|
||||
For example,
|
||||
\begin{alltt}
|
||||
(httpd (make-httpd-options
|
||||
with-request-handler (rooted-file-handler "/usr/local/etc/httpd")
|
||||
with-root-directory "/usr/local/etc/httpd"))
|
||||
\end{alltt}
|
||||
%
|
||||
starts the server on port 80 with
|
||||
\ex{/usr/local/etc/httpd} as its root directory and
|
||||
lets it serve any file out from this directory.
|
||||
% #### note about rooted-file-handler
|
||||
|
||||
|
||||
\section{Requests}
|
||||
\label{httpd:requests}
|
||||
|
||||
Request handlers operate on \textit{requests} which contain the
|
||||
information needed to generate a page. The relevant procedures to
|
||||
dissect requests are defined in the \texttt{httpd-requests} structure:
|
||||
|
||||
\defun{request?}{value}{boolean}
|
||||
\defunx{request-method}{request}{string}
|
||||
\defunx{request-uri}{request}{string}
|
||||
\defunx{request-url}{request}{url}
|
||||
\defunx{request-version}{request}{pair}
|
||||
\defunx{request-headers}{request}{list}
|
||||
\defunx{request-socket}{request}{socket}
|
||||
\begin{desc}
|
||||
The procedure inspect request values. \ex{Request?} is a predicate
|
||||
for requests. \ex{Request-method} extracts the method of the HTTP
|
||||
request; it's a string such as \verb|"GET"|, \verb|"PUT"|.
|
||||
\ex{Request-uri} returns the escaped URI string as read from request
|
||||
line. \ex{Request-url} returns an HTTP URL value (see the
|
||||
description of the \ex{url} structure in \ref{cha:url}).
|
||||
\ex{Request-version} returns \verb|(major . minor)| integer pair
|
||||
representing the version specified in the HTTP request.
|
||||
\ex{Request-headers} returns an association lists of header field
|
||||
names and their values, each represented by a list of strings, one
|
||||
for each line. \ex{Request-socket} returns the the socket connected
|
||||
to the client.\footnote{Request handlers should not perform I/O on the
|
||||
request record's socket. Request handlers are frequently called
|
||||
recursively, and doing I/O directly to the socket might bypass a
|
||||
filtering or other processing step interposed on the current I/O ports
|
||||
by some superior request handler.}
|
||||
\end{desc}
|
||||
|
||||
\section{Responses}
|
||||
\label{sec:http-responses}
|
||||
|
||||
A path handler must return a \textit{response} value representing the
|
||||
content to be sent to the client. The machinery presented here for
|
||||
constructing responses lives in the \ex{httpd-responses} structure.
|
||||
|
||||
\defun{make-response}{status-code maybe-message seconds mime extras
|
||||
body}{response}
|
||||
\begin{desc}
|
||||
This procedure constructs a response value. \var{Status-code} is an
|
||||
HTTP status code (more on that below). \var{Maybe-message} is a a
|
||||
message elaborating on the circumstances of the status code; it can
|
||||
also be \sharpf{} meaning that the server should send a default
|
||||
message associated with the status code. \var{Seconds} natural
|
||||
number indicating the time the content was created, typically the
|
||||
value of \verb|(time)|. \var{Mime} is a string indicating the MIME
|
||||
type of the response (such as \verb|"text/html"| or
|
||||
\verb|"application/octet-stream"|). \var{Extras} is an association
|
||||
list with extra headers to be added to the response; its elements
|
||||
are pairs, each of which consists of a symbol representing the field
|
||||
name and a string representing the field value. \var{Body}
|
||||
represents the body of the response; more on that below.
|
||||
\end{desc}
|
||||
|
||||
\defun{make-redirect-response}{location}{response}
|
||||
\begin{desc}
|
||||
This is a helper procedure for constructing HTTP redirections. The
|
||||
server will serve the new file indicated by \var{location}.
|
||||
\var{Location} must be URI-encoded and begin with a slash.
|
||||
\end{desc}
|
||||
|
||||
\defun{make-error-response}{status-code request [message] extras \ldots}{response}
|
||||
\begin{desc}
|
||||
This is a helper procedure for constructing error responses.
|
||||
\var{code} is status code of the response (see below). \var{Request}
|
||||
is the request that led to the error. \var{Message} is an optional
|
||||
string containing an error message written in HTML, and \var{extras}
|
||||
are further optional arguments containing further message lines to
|
||||
be added to the web page that's generated.
|
||||
|
||||
\ex{Make-error-response} constructs a response value which generates
|
||||
a web page containg a short explanatory message for the error at hand.
|
||||
\end{desc}
|
||||
|
||||
\begin{table}[htb]
|
||||
\centering
|
||||
\begin{tabular}{|l|l|l|}
|
||||
\hline
|
||||
ok & 200 & OK\\\hline
|
||||
created & 201 & Created\\\hline
|
||||
accepted & 202 & Accepted\\\hline
|
||||
prov-info & 203 & Provisional Information\\\hline
|
||||
no-content & 204 & No Content\\\hline
|
||||
|
||||
mult-choice & 300 & Multiple Choices\\\hline
|
||||
moved-perm & 301 & Moved Permanently\\\hline
|
||||
moved-temp & 302 & Moved Temporarily\\\hline
|
||||
method & 303 & Method (obsolete)\\\hline
|
||||
not-mod & 304 & Not Modified\\\hline
|
||||
|
||||
bad-request & 400 & Bad Request\\\hline
|
||||
unauthorized & 401 & Unauthorized\\\hline
|
||||
payment-req & 402 & Payment Required\\\hline
|
||||
forbidden & 403 & Forbidden\\\hline
|
||||
not-found & 404 & Not Found\\\hline
|
||||
method-not-allowed & 405 & Method Not Allowed\\\hline
|
||||
none-acceptable & 406 & None Acceptable\\\hline
|
||||
proxy-auth-required & 407 & Proxy Authentication Required\\\hline
|
||||
timeout & 408 & Request Timeout\\\hline
|
||||
conflict & 409 & Conflict\\\hline
|
||||
gone & 410 & Gone\\\hline
|
||||
internal-error & 500 & Internal Server Error\\\hline
|
||||
not-implemented & 501 & Not Implemented\\\hline
|
||||
bad-gateway & 502 & Bad Gateway\\\hline
|
||||
service-unavailable & 503 & Service Unavailable\\\hline
|
||||
gateway-timeout & 504 & Gateway Timeout\\\hline
|
||||
\end{tabular}
|
||||
\caption{HTTP status codes}
|
||||
\label{tab:status-code-names}
|
||||
\end{table}
|
||||
|
||||
\dfn{status-code}{\synvar{name}}{status-code}{syntax}
|
||||
\defunx{name->status-code}{symbol}{status-code}
|
||||
\defunx{status-code-number}{status-code}{integer}
|
||||
\defunx{status-code-message}{status-code}{string}
|
||||
\begin{desc}
|
||||
The \ex{status-code} syntax returns a status code where
|
||||
\synvar{name} is the name from Table~\ref{tab:status-code-names}.
|
||||
\ex{Name->status-code} also returns a status code for a name
|
||||
represented as a symbol. For a given status code,
|
||||
\ex{status-code-number} extracts its number, and
|
||||
\ex{status-code-message} extracts its associated default message.
|
||||
\end{desc}
|
||||
|
||||
\section{Response Bodies}
|
||||
\label{httpd:response-bodies}
|
||||
|
||||
A \textit{response body} represents the body of an HTTP response.
|
||||
There are several types of response bodies, depending on the
|
||||
requirements on content generation.
|
||||
|
||||
\defun{make-writer-body}{proc}{body}
|
||||
\begin{desc}
|
||||
This constructs a response body from a \textit{writer}---a procedure
|
||||
that prints the page contents to a port. The \var{proc} argument
|
||||
must be a procedure accepting an output port (to which \var{proc}
|
||||
prints the body) and the options value passed to the \ex{httpd}
|
||||
invocation.
|
||||
\end{desc}
|
||||
|
||||
\defun{make-reader-writer-body}{proc}{body}
|
||||
\begin{desc}
|
||||
This constructs a response body from a \textit{reader/writer}---a
|
||||
procedure that prints the page contents to a port, possibly after
|
||||
reading input from the socket of the HTTP connection. The
|
||||
\var{proc} argument must be a procedure accepting three arguments:
|
||||
an input port (associated with the HTTP connection socket), an
|
||||
output port (to which \var{proc} prints the body), and the options
|
||||
value passed to the \ex{httpd} invocation.
|
||||
\end{desc}
|
||||
|
||||
\section{Request Handlers}
|
||||
\label{httpd:request-handlers}
|
||||
|
||||
A request handler generates the actual content for a request; request
|
||||
handlers form a simple algebra and may be combined and composed in
|
||||
various ways.
|
||||
|
||||
|
||||
A request handler is a procedure of two arguments like this:
|
||||
\defun{request-handler}{path req}{response}
|
||||
\begin{desc}
|
||||
\var{Req} is a request. The \semvar{path} argument is the URL's
|
||||
path, parsed and split at slashes into a string list. For example,
|
||||
if the Web client dereferences URL
|
||||
%
|
||||
\begin{verbatim}
|
||||
http://clark.lcs.mit.edu:8001/h/shivers/code/web.tar.gz
|
||||
\end{verbatim}
|
||||
then the server would pass the following path to the top-level
|
||||
handler:
|
||||
%
|
||||
\begin{verbatim}
|
||||
("h" "shivers" "code" "web.tar.gz")
|
||||
\end{verbatim}
|
||||
%
|
||||
The \var{path} argument's pre-parsed representation as a string
|
||||
list makes it easy for the request handler to implement recursive
|
||||
operations dispatch on URL paths.
|
||||
|
||||
The request handler must return an HTTP response.
|
||||
\end{desc}
|
||||
|
||||
\subsection{Basic Request Handlers}
|
||||
|
||||
The web server comes with a useful toolbox of basic request handlers
|
||||
that can be used and built upon. The following procedures are
|
||||
exported by the \ex{httpd\=basic\=handlers} structure:
|
||||
|
||||
\defvar{null-request-handler}{request-handler}
|
||||
\begin{desc}
|
||||
This request handler always generated a \ex{not-found} error
|
||||
response, no patter what the request is.
|
||||
\end{desc}
|
||||
|
||||
\defun{make-predicate-handler}{predicate handler
|
||||
default-handler}{request-handler}
|
||||
\begin{desc}
|
||||
The request handler returned by this procedure first calls
|
||||
\var{predicate} on its path and request; it then acts like
|
||||
\var{handler} if the predicate returned a true vale, and like
|
||||
\var{default-handler} if the predicate returned \sharpf.
|
||||
\end{desc}
|
||||
|
||||
\defun{make-host-name-handler}{hostname handler default-handler}{request-handler}
|
||||
\begin{desc}
|
||||
The request handler returned by this procedure compares the host
|
||||
name specified in the request with \var{hostname}: if they match, it
|
||||
acts like \var{handler}, otherwise, it acts like
|
||||
\var{default-handler}.
|
||||
\end{desc}
|
||||
|
||||
\defun{make-path-predicate-handler}{predicate handler
|
||||
default-handler}{request-handler}
|
||||
\begin{desc}
|
||||
The request handler returned by this procedure first calls
|
||||
\var{predicate} on its path; it then acts like \var{handler} if the
|
||||
predicate returned a true vale, and like \var{default-handler} if
|
||||
the predicate returned \sharpf.
|
||||
\end{desc}
|
||||
|
||||
\defun{make-path-prefix-handler}{path-prefix handler default-handler}{request-handler}
|
||||
\begin{desc}
|
||||
This constructs a request handler that calls \var{handler} on its
|
||||
argument if \var{path-prefix} (a string) is the first element of the
|
||||
requested path; it calls \var{handler} on the rest of the path and
|
||||
the original request. Otherwise, the handler acts like
|
||||
\var{default-handler}.
|
||||
\end{desc}
|
||||
|
||||
\defun{alist-path-dispatcher}{handler-alist default-handler}{request-handler}
|
||||
\begin{desc}
|
||||
This procedure takes as arguments an alist mapping strings to path
|
||||
handlers, and a default request handler, and returns a handler that
|
||||
dispatches on its path argument. When the new request handler is
|
||||
applied to a path
|
||||
\begin{verbatim}
|
||||
("foo" "bar" "baz")
|
||||
\end{verbatim}
|
||||
it uses the
|
||||
first element of the path---\ex{foo}---to index into the
|
||||
alist. If it finds an associated request handler in the alist, it
|
||||
hands the request off to that handler, passing it the tail of the
|
||||
path, in this case
|
||||
\begin{verbatim}
|
||||
("bar" "baz")
|
||||
\end{verbatim}
|
||||
%
|
||||
On the other hand, if the path is
|
||||
empty, or the alist search does not yield a hit, we hand off to the
|
||||
default path handler, passing it the entire original path,
|
||||
\begin{verbatim}
|
||||
("foo" "bar" "baz")
|
||||
\end{verbatim}
|
||||
%
|
||||
This procedure is how you say: ``If the first element of the URL's
|
||||
path is `foo', do X; if it's `bar', do Y; otherwise, do Z.''
|
||||
The slash-delimited URI path structure implies an associated tree of
|
||||
names. The request-handler system and the alist dispatcher allow you to
|
||||
procedurally define the server's response to any arbitrary subtree
|
||||
of the path space.
|
||||
|
||||
Example: A typical top-level request handler is
|
||||
\begin{alltt}
|
||||
(define ph
|
||||
(alist-path-dispatcher
|
||||
`(("h" . ,(home-dir-handler "public\_html"))
|
||||
("cgi-bin" . ,(cgi-handler "/usr/local/etc/httpd/cgi-bin"))
|
||||
("seval" . ,seval-handler))
|
||||
(rooted-file-handler "/usr/local/etc/httpd/htdocs")))
|
||||
\end{alltt}
|
||||
|
||||
This means:
|
||||
\begin{itemize}
|
||||
\item If the path looks like \ex{("h"\ob{} "shivers"\ob{}
|
||||
"code"\ob{} "web.\ob{}tar.\ob{}gz")}, pass the path
|
||||
\ex{("shivers"\ob{} "code"\ob{} "web.\ob{}tar.\ob{}gz")} to a
|
||||
home-directory request handler.
|
||||
\item If the path looks like \ex{("cgi-\ob{}bin"\ob{} "calendar")},
|
||||
pass ("calendar") off to the CGI request handler.
|
||||
\item If the path looks like \ex{("seval"\ob{} \ldots)}, the tail
|
||||
of the path is passed off to the code-uploading \ex{seval} path
|
||||
handler.
|
||||
\item Otherwise, the whole path is passed to a rooted file handler,
|
||||
who will convert it into a filename, rooted at
|
||||
\ex{/usr/\ob{}lo\ob{}cal/\ob{}etc/\ob{}httpd/\ob{}htdocs},
|
||||
and serve that file.
|
||||
\end{itemize}
|
||||
\end{desc}
|
||||
|
||||
\subsection{Static Content Request Handlers}
|
||||
|
||||
The request handlers described in this section are for serving static
|
||||
content off directory trees in the file system. They live in the
|
||||
\ex{httpd-file-directory-handlers} structure.
|
||||
|
||||
The request handlers in this section eventually call an internal
|
||||
procedure named \ex{file\=serve} for serving files which implements a
|
||||
simple directory-generation service using the following rules:
|
||||
\begin{itemize}
|
||||
\item If the filename has the form of a directory (i.e., it ends with
|
||||
a slash), then \ex{file\=serve} actually looks for a file named
|
||||
\ex{index.html} in that directory.
|
||||
\item If the filename names a directory, but is not in directory form
|
||||
(i.e., it doesn't end in a slash, as in
|
||||
``\ex{/usr\ob{}in\ob{}clu\ob{}de}'' or ``\ex{/usr\ob{}raj}''),
|
||||
then \ex{file\=serve} sends back a ``301 moved permanently''
|
||||
message, redirecting the client to a slash-terminated version of the
|
||||
original URL. For example, the URL
|
||||
\ex{http://\ob{}clark.\ob{}lcs.\ob{}mit.\ob{}edu/\ob{}~shi\ob{}vers}
|
||||
would be redirected to
|
||||
\ex{http://\ob{}clark.\ob{}lcs.\ob{}mit.\ob{}edu/\ob{}~shi\ob{}vers/}
|
||||
\item If the filename names a regular file, it is served to the
|
||||
client.
|
||||
\end{itemize}
|
||||
|
||||
\defun{rooted-file-handler}{root-dir}{request-handler}
|
||||
\begin{desc}
|
||||
This returns a request handler that serves files from a particular
|
||||
root in the file system. Only the \ex{GET} operation is provided.
|
||||
The path argument passed to the handler is converted into a
|
||||
filename, and appended to root-dir. The file name is checked for
|
||||
\ex{..} components, and the transaction is aborted if it does.
|
||||
Otherwise, the file is served to the client.
|
||||
\end{desc}
|
||||
|
||||
\defun{rooted-file-or-directory-handler}{root}{request-handler}
|
||||
\begin{desc}
|
||||
Dito, but also serve directory indices for directories without
|
||||
\ex{index.html}.
|
||||
\end{desc}
|
||||
|
||||
\defun{home-dir-handler}{subdir}{request-handler}
|
||||
\begin{desc}
|
||||
This procedure builds a request handler that does basic file serving
|
||||
out of home directories. If the resulting \var{request-handler} is
|
||||
passed a path of the form \ex{(\var{user} . \var{file-path})}, then it serves the file
|
||||
\ex{\var{subdir}/\var{file-path}} inside the user's home directory.
|
||||
|
||||
The request handler only handles GET requests; the filename is not
|
||||
allowed to contain \ex{..} elements.
|
||||
\end{desc}
|
||||
|
||||
\defun{tilde-home-dir-handler}{subdir
|
||||
default-request-handler}{request-handler}
|
||||
\begin{desc}
|
||||
This returns request handler that examines the car of the path. If
|
||||
it is a string beginning with a tilde, e.g., \ex{"~ziggy"}, then the
|
||||
string is taken to mean a home directory, and the request is served
|
||||
similarly to a home-dir-handler request handler. Otherwise, the
|
||||
request is passed off in its entirety to the
|
||||
\var{default-request-handler}.
|
||||
\end{desc}
|
||||
|
||||
\section{CGI Server}
|
||||
|
||||
The procedure(s) described here live in the \ex{httpd-cgi-handlers}
|
||||
structure.
|
||||
|
||||
\defun{cgi-handler}{bin-dir [cgi-bin-path]}{request-handler}
|
||||
\begin{desc}
|
||||
Returns a request handler for CGI scripts located in
|
||||
\var{bin-dir}. \var{Cgi-bin-dir} specifies the value of the
|
||||
\ex{PATH} variable of the environment the CGI scripts run in. It defaults
|
||||
to
|
||||
\begin{verbatim}
|
||||
/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin
|
||||
\end{verbatim}
|
||||
The CGI scripts are called as specified by CGI/1.1\footnote{see
|
||||
\url{http://hoohoo.ncsa.uiuc.edu/cgi/interface.html} for a sort of
|
||||
specification.}.
|
||||
|
||||
Note that the CGI handler looks at the name of the CGI script to
|
||||
determine how it should be handled:
|
||||
\begin{itemize}
|
||||
\item If the name of the script starts with `\ex{nph-}', its reply
|
||||
is read, the RFC~822-fields like \ex{Content-Type} and \ex{Status}
|
||||
are parsed and the client is sent back a real HTTP reply,
|
||||
containing the rest of the script's output.
|
||||
|
||||
\item If the name of the script doesn't start with `\ex{nph-}',
|
||||
its output is sent back to the client directly. If its return code
|
||||
is not zero, an error message is generated.
|
||||
\end{itemize}
|
||||
\end{desc}
|
||||
|
||||
\section{Scheme-Evaluating Request Handlers}
|
||||
|
||||
The \ex{httpd-seval-handlers} structure contains a handler which
|
||||
demonstrates how to safely evaluate Scheme code uploaded from the
|
||||
client to the server.
|
||||
|
||||
\defvar{seval-handler}{request-handler}
|
||||
\begin{desc}
|
||||
This request handler is suitable for receiving code entered into an
|
||||
HTML text form. The Scheme code being uploaded is being \ex{POST}ed
|
||||
to the server (from a form). The code should be URI-encoded in the
|
||||
URL as \texttt{program=}$\left<\mathrm{stuff}\right>$.
|
||||
$\mathrm{stuff}$ must be an (URI-encoded) Scheme expression which
|
||||
the handler evaluates in a separate subprocess. (It waits for 10
|
||||
seconds for a result, then kills the subprocess.) The handler then
|
||||
prints the return values of the Scheme code.
|
||||
\end{desc}
|
||||
|
||||
The following structures define environments that are \RnRS without
|
||||
features that could examine or effect the file system. You can also
|
||||
use them as models of how to execute code in other protected
|
||||
environments in \scm.
|
||||
|
||||
\subsection{The \protect{\texttt{loser}} structure}
|
||||
The \ex{loser} package exports only one procedure:
|
||||
|
||||
\begin{defundesc}{loser}{name}{nothing}
|
||||
Raises an error like ``Illegal call \var{name}''.
|
||||
\end{defundesc}
|
||||
|
||||
\subsection{The \protect{\texttt{toothless}} structure}
|
||||
The \ex{toothless} structure contains everything of \RnRS except
|
||||
that following procedure cause an error if called:
|
||||
\begin{itemize}
|
||||
\item \ex{call-with-input-file}
|
||||
\item \ex{call-with-output-file}
|
||||
\item \ex{load}
|
||||
\item \ex{open-input-file}
|
||||
\item \ex{open-output-file}
|
||||
\item \ex{transcript-on}
|
||||
\item \ex{with-input-from-file}
|
||||
\item \ex{with-input-to-file}
|
||||
\item \ex{eval}
|
||||
\item \ex{interaction-environment}
|
||||
\item \ex{scheme-report-environment}
|
||||
\end{itemize}
|
||||
|
||||
\subsection{The \protect{\texttt{toothless-eval}} structure}
|
||||
|
||||
\begin{defundesc}{eval-safely} {expression} {any result}
|
||||
Creates a brand-new structure, imports the \ex{toothless} structure,
|
||||
and evaluates \semvar{expression} in it. When the evaluation is
|
||||
done, the environment is thrown away, so \semvar{expression}'s
|
||||
side-effects don't persist from one \ex{eval\=safely} call to the
|
||||
next. If \semvar{expression} raises an error exception,
|
||||
\ex{eval-safely} returns \sharpf.
|
||||
\end{defundesc}
|
||||
|
||||
\section{Writing Request Handlers}
|
||||
|
||||
\subsection{Parsing HTML Forms}
|
||||
|
||||
In HTML forms, field data are turned into a single string, of the form
|
||||
\texttt{\synvar{name}=\synvar{val}\&\synvar{name}=\synvar{val}\ldots}.
|
||||
The \ex{parse-html-forms} structure provides simple functionality to
|
||||
parse these strings.
|
||||
|
||||
\defun{parse-html-form-query}{string}{alist}
|
||||
\begin{desc}
|
||||
This parses \verb|"foo=x&bar=y"| into \verb|(("foo" . "x") ("bar" .
|
||||
"y"))|. Substrings are plus-decoded (i.-e.\ plus characters are
|
||||
turned into spaces) and then URI-decoded.
|
||||
|
||||
This implementation is
|
||||
slightly sleazy as it will successfully parse a string like
|
||||
\verb|"a&b=c&d=f"| into \verb|(("a&b" . "c") ("d" . "f"))| without
|
||||
a complaint.
|
||||
\end{desc}
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,71 +0,0 @@
|
|||
\chapter{Overview}\label{sec:intro}
|
||||
|
||||
\section{What's SUnet?}
|
||||
|
||||
The Scheme Untergrund Networking Package (SUnet, for short) contains a
|
||||
set of libraries for doing Internet hacking from scsh. It includes:
|
||||
|
||||
\begin{description}
|
||||
\item[The SUnet Web server]
|
||||
This is a complete implementation of an HTTP 1.0 server in Scheme.
|
||||
The server is accompanied some libraries which may also be used separately:
|
||||
\begin{itemize}
|
||||
\item URI and URL parsers and unparsers
|
||||
\item a library for writing CGI scripts in Scheme
|
||||
\item server extensions for interfacing to CGI scripts
|
||||
\item server extensions for uploading Scheme code
|
||||
\item simple structured HTML output library
|
||||
\end{itemize}
|
||||
\item[The SUnet ftp daemon]
|
||||
This is a complete anonymous ftp server in Scheme.
|
||||
\item[ftp client library] This library allows you to access ftp
|
||||
servers programmatically.
|
||||
\item[netrc library] This library parses authentication information
|
||||
contained in \verb|~/.netrc|.
|
||||
\item[SMTP client library] This library allows you to forge mail from
|
||||
the comfort of your own Scheme process.
|
||||
\item[POP3 client library]
|
||||
This library allows you to access your POP3 mailbox from inside scsh.
|
||||
\item[RFC822 header library] This library parses email-style headers.
|
||||
\item[Daytime and Time protocol client library]
|
||||
This library lets you find out what time it is without paying for a
|
||||
Rolex.
|
||||
\item[DNS client library] This is a fairly complete, multithreaded DNS
|
||||
library.
|
||||
\item[An \texttt{ls} clone] This library Displays Unix-style directory
|
||||
listings without running \texttt{ls}.
|
||||
\end{description}
|
||||
|
||||
\section{Obtaining the system}
|
||||
|
||||
The SUnet code is available
|
||||
\urlhd{http://www.scsh.net/sunet/}{here}{from
|
||||
\url{http://www.scsh.net/sunet/}}. To run the code, you need
|
||||
version 0.6.3 or later of \urlhd{http://www.scsh.net/}{scsh}{scsh from
|
||||
\url{http://www.scsh.net/}}.
|
||||
|
||||
\section{How to use the packages}
|
||||
|
||||
Untar the SUnet distribution somewhere. Fire up scsh and load the
|
||||
SUnet \texttt{packages.scm} file into the configuration package.
|
||||
After that, all structures defined by SUnet are available:
|
||||
%
|
||||
\begin{alltt}
|
||||
atari-2600[72] scsh-0.6.3
|
||||
Welcome to scsh 0.6.3 (Health Reform)
|
||||
Type ,? for help.
|
||||
> ,config ,load packages.scm
|
||||
modules.scm
|
||||
> ,open ftp
|
||||
Load structure ftp (y/n)? y
|
||||
[netrc netrc.scm]
|
||||
[ftp ftp.scm]
|
||||
> ; call library code
|
||||
> ,exit
|
||||
atari-2600[73]
|
||||
\end{alltt}
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,133 +0,0 @@
|
|||
% man.t2p
|
||||
% Dorai Sitaram
|
||||
% Feb 6, 2000
|
||||
|
||||
% This file contains the tex2page macros needed to process
|
||||
% the scsh LaTeX document scsh-n.n.n/doc/scsh-manual/man.tex.
|
||||
% Copy (or link) this file alongside man.tex and run
|
||||
%
|
||||
% tex2page man
|
||||
|
||||
\input css.t2p
|
||||
\htmlmathstyle{no-image}
|
||||
|
||||
\let\pagebreak\relax
|
||||
|
||||
\let\small\relax
|
||||
|
||||
%\let\PRIMtableofcontents\tableofcontents
|
||||
%\def\tableofcontents{\chapter*{Contents}\PRIMtableofcontents}
|
||||
|
||||
\def\subtitle#1{\def\savesubtitle{#1}}
|
||||
|
||||
\def\maketitle{
|
||||
\subject{\TIIPtitle}
|
||||
{\bf \hr}
|
||||
\rightline{\savesubtitle}
|
||||
\bigskip\bigskip
|
||||
\bigskip\bigskip
|
||||
{\bf\TIIPauthor}
|
||||
{\bf\hr}
|
||||
}
|
||||
|
||||
\let\PRIMdocument\document
|
||||
|
||||
\def\document{\PRIMdocument
|
||||
|
||||
\let\ttchars\relax
|
||||
\let\ttt\tt
|
||||
|
||||
%\def\~{\rawhtml~\endrawhtml}
|
||||
\def\~{\char`\~}
|
||||
\def\cd#1{{\tt\def\\{\char`\\}\defcsactive\${\char`\$}\defcsactive\~{\char`\~}\defcsactive\&{\char`\&}#1}}
|
||||
\def\cddollar{\undefcsactive\$}
|
||||
\def\cdmath{\undefcsactive\$}
|
||||
\def\codeallowbreaks{\relax}
|
||||
\def\defvarx#1#2{\index{#1}\leftline{{\tt #1} \qquad #2}}
|
||||
|
||||
\let\PRIMflushright\flushright
|
||||
|
||||
\def\flushright{\PRIMflushright\TIIPtabularborder=0 }
|
||||
|
||||
\let\PRIMfigure\figure
|
||||
\let\PRIMendfigure\endfigure
|
||||
|
||||
\def\figure{\par\hrule\PRIMfigure}
|
||||
\def\endfigure{\PRIMendfigure\hrule\par}
|
||||
|
||||
\let\PRIMtable\table
|
||||
\let\PRIMendtable\endtable
|
||||
|
||||
\def\table{\par\hrule\PRIMtable}
|
||||
\def\endtable{\PRIMendtable\hrule\par}
|
||||
|
||||
\imgdef\vdots{\bf.\par.\par.}
|
||||
|
||||
%\evalh{
|
||||
%
|
||||
%(define all-blanks?
|
||||
% (lambda (s)
|
||||
% (andmap
|
||||
% char-whitespace?
|
||||
% (string->list s))))
|
||||
%
|
||||
%}
|
||||
%
|
||||
%
|
||||
%\def\spaceifnotempty{\evalh{
|
||||
%
|
||||
%(let ((x (ungroup (get-token))))
|
||||
% (unless (all-blanks? x)
|
||||
% (emit #\space)))
|
||||
%
|
||||
%}}
|
||||
|
||||
\def\spaceifnotempty#1{%
|
||||
\def\TEMP{#1}%
|
||||
\ifx\TEMP\empty\else\ \fi}
|
||||
|
||||
\def\dfnix#1#2#3#4{\leftline{{\tt(#1\spaceifnotempty{#2}{\it#2})} \quad $\longrightarrow$ \quad {\it #3} \qquad (#4)} \index}
|
||||
|
||||
%\def\ex#1{{\tt #1}}
|
||||
%\let\ex\texttt
|
||||
\def\l#1{lambda (#1)}
|
||||
\def\lx#1{lambda {#1}}
|
||||
%\def\notenum#1{}
|
||||
%\def\project#1{}
|
||||
%\def\var#1{{\it #1\/}}
|
||||
%\let\var\textit
|
||||
%\def\vari#1#2{\mbox{{\it #1\/}\undefcsactive\$$_{#2}$}}
|
||||
%\def\vari#1#2{\textit{#1}$_{#2}$}
|
||||
|
||||
\renewenvironment{boxedfigure}{\def\srecomment#1{\\#1\\}%
|
||||
\begin{figure}\pagestyle}{\end{figure}}
|
||||
|
||||
\newenvironment{centercode}{\begin{code}}{\end{code}}
|
||||
|
||||
\def\setupcode{\tt%
|
||||
\def\\{\char`\\}%
|
||||
\defcsactive\${\$}%
|
||||
\def\evalto{==> }%
|
||||
\defcsactive\%{\%}\obeywhitespace}
|
||||
|
||||
\newenvironment{code}{\begin{quote}\setupcode\GOBBLEOPTARG}
|
||||
{\end{quote}}
|
||||
|
||||
\newenvironment{codebox}{\begin{tableplain}\bgroup\setupcode\GOBBLEOPTARG}
|
||||
{\egroup\end{tableplain}}
|
||||
|
||||
\renewenvironment{desc}{\begin{quote}}{\end{quote}}
|
||||
|
||||
\renewenvironment{exampletable}{%
|
||||
\def\header#1{\\\leftline{#1}\\}%
|
||||
\def\splitline#1#2{\\\leftline{#1}\\\leftline{#2}}%
|
||||
\begin{tabular}{}}{\end{tabular}}
|
||||
|
||||
\newenvironment{tightcode}{\begin{code}}{\end{code}}
|
||||
\renewenvironment{widecode}{\begin{code}}{\end{code}}
|
||||
|
||||
\renewenvironment{inset}{\begin{quote}}{\end{quote}}
|
||||
\renewenvironment{leftinset}{\begin{quote}}{\end{quote}}
|
||||
\renewenvironment{tightinset}{\begin{quote}}{\end{quote}}
|
||||
\renewenvironment{tightleftinset}{\begin{quote}}{\end{quote}}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
% -*- latex -*-
|
||||
|
||||
% This is the reference manual for the Scheme Untergrund Networking Package.
|
||||
|
||||
\documentclass[twoside]{report}
|
||||
\usepackage{code,boxedminipage,makeidx,palatino,ct,
|
||||
headings,mantitle,array,matter,mysize10,tex2page}
|
||||
|
||||
\usepackage[latin1]{inputenc}
|
||||
\usepackage{alltt}
|
||||
\usepackage{xspace}
|
||||
|
||||
\texonly
|
||||
% tex2page defines \url and hyperref loads the package url
|
||||
% but setting \url to \relax satisfies \newcommand
|
||||
\let\url\relax
|
||||
\input{pdfcond}
|
||||
\ifpdf
|
||||
\usepackage[pdftex,hyperindex,
|
||||
pdftitle={sunet manual, release 2.0},
|
||||
pdfauthor={Olin Shivers, Mike Sperber, Martin Gasbichler, Eric Marsden
|
||||
and Andreas Bernauer}
|
||||
colorlinks=true,linkcolor=blue,pagecolor=blue,urlcolor=blue,
|
||||
pdfstartview=FitH,pdfview=FitH]{hyperref}
|
||||
\usepackage{thumbpdf}
|
||||
\usepackage{tocbibind}
|
||||
\else
|
||||
\usepackage[dvipdfm,hyperindex,hypertex,
|
||||
pdftitle={sunet manual, release 2.0},
|
||||
pdfauthor={Olin Shivers, Mike Sperber, Martin Gasbichler, Eric Marsden
|
||||
and Andreas Bernauer}
|
||||
colorlinks=true,linkcolor=blue,pagecolor=blue,urlcolor=blue,
|
||||
pdfstartview=FitH,pdfview=FitH]{hyperref}
|
||||
\fi
|
||||
\endtexonly
|
||||
|
||||
% Style issues
|
||||
\parskip = 3pt plus 3pt
|
||||
\sloppy
|
||||
|
||||
\input{decls}
|
||||
\makeindex
|
||||
%%% End preamble
|
||||
|
||||
|
||||
\begin{document}
|
||||
|
||||
\frontmatter
|
||||
\title{SUnet Reference Manual}
|
||||
\subtitle{For SUnet release 2.0}
|
||||
\author{Dr. S, Dr. S, Martin Gasbichler, Eric Marsden, Andreas Bernauer}
|
||||
\date{January 2003}
|
||||
|
||||
\mainmatter
|
||||
\maketitle
|
||||
\begin{abstract}
|
||||
\noindent The Scheme Untergrund Network Package (\textit{SUnet} for short) is
|
||||
a set of libraries for Internet hacking. Among the implemented
|
||||
protocols are server-side http, client-side ftp, server-side ftp,
|
||||
client-side DNS, client-side nettime, client-side POP3, client-side
|
||||
SMTP. SUnet also contains a number of libraries for Web
|
||||
programming: a small package for outputting HTML, a CGI library, as
|
||||
well as some other utilities.
|
||||
\end{abstract}
|
||||
\tableofcontents
|
||||
|
||||
\include{intro}
|
||||
\include{httpd}
|
||||
\include{cgi-script}
|
||||
\include{ftpd}
|
||||
\include{ftp}
|
||||
\include{netrc}
|
||||
\include{uri}
|
||||
\include{url}
|
||||
\include{rfc822}
|
||||
\include{nettime}
|
||||
\include{smtp}
|
||||
\include{pop3}
|
||||
\include{dns}
|
||||
|
||||
\backmatter
|
||||
\printindex
|
||||
|
||||
\end{document}
|
|
@ -1,76 +0,0 @@
|
|||
% This is the title page style stolen from the Texinfo design,
|
||||
% and expressed as a LaTeX style option. It is useful for manuals.
|
||||
%
|
||||
% Note that I play some *really* revolting games here to override
|
||||
% the vertical and horizontal margins temporarily for the title page.
|
||||
% The layout assumes you have 8.5" x 11" paper. You'd have to redo this
|
||||
% for A4 or another size.
|
||||
% -Olin 7/94
|
||||
|
||||
|
||||
% Fonts for title page:
|
||||
\DeclareFixedFont{\titlefont}%
|
||||
{\encodingdefault}{\familydefault}{bx}{\shapedefault}{20.5pt}
|
||||
\DeclareFixedFont{\authorfnt}%
|
||||
{\encodingdefault}{\familydefault}{bx}{\shapedefault}{14.4pt}
|
||||
\DeclareFixedFont{\subtitlefnt}%
|
||||
{\encodingdefault}{\familydefault}{m}{\shapedefault}{11}
|
||||
|
||||
%\def\authorrm{\normalfont\selectfont\fontseries{bx}\fontsize{14.4}{14.4}}
|
||||
%\def\subtitlefnt{\normalfont\selectfont\fontsize{11}{11}}
|
||||
|
||||
\newskip\titlepagetopglue \titlepagetopglue = 2.5in
|
||||
|
||||
|
||||
\newlength{\widewidth}
|
||||
\setlength{\widewidth}{6.5in}
|
||||
\newlength{\negwidemargin}
|
||||
\setlength{\negwidemargin}{-\oddsidemargin} % Reset the margin
|
||||
\addtolength{\negwidemargin}{-1in} % to edge of page
|
||||
\addtolength{\negwidemargin}{1in} % Then move right one inch.
|
||||
|
||||
%\def\wideline#1{\hbox to 0pt{\hspace\negwidemargin\hbox to\widewidth{#1}}}
|
||||
\def\wideline#1{\hbox{\makebox[0pt][l]{\hspace\negwidemargin\hbox to\widewidth{#1}}}}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\def\maketitle{\begin{titlepage}
|
||||
\thispagestyle{empty}
|
||||
\let\footnotesize\small \let\footnoterule\relax
|
||||
\null
|
||||
\parindent=0pt
|
||||
\def\subtitlefont{\normalbaselineskip = 13pt \normalbaselines \subtitlefnt}%
|
||||
\def\authorfont{\normalbaselineskip = 16pt \normalbaselines \authorfnt}%
|
||||
%
|
||||
% Leave some space at the very top of the page.
|
||||
\vspace*{-1in}\vspace*{-\topmargin}\vspace*{-\headheight}\vspace*{-\headsep}
|
||||
\vglue\titlepagetopglue
|
||||
%
|
||||
\wideline{\titlefont \@title \hfill} % title
|
||||
% \vskip4pt
|
||||
\vskip -0.3\baselineskip
|
||||
\wideline{\leaders\hrule height 4pt\hfill}
|
||||
\wideline{\hfill\subtitlefont\begin{tabular}[t]{@{}r@{}}\@subtitle%
|
||||
\\\@date%
|
||||
\end{tabular}} % subtitle
|
||||
%
|
||||
% author
|
||||
\vskip 0pt plus 1filll
|
||||
\wideline{\authorfont \begin{tabular}[t]{@{}c@{}}\@author
|
||||
\end{tabular}\hfill}
|
||||
%
|
||||
% \vskip4pt
|
||||
\vskip -0.3\baselineskip
|
||||
\wideline{\leaders\hrule height 2pt\hfill}
|
||||
|
||||
% This weirdness puts the bottom line 2.75 in from the bottom of
|
||||
% an 11in page.
|
||||
\vskip \textheight \vskip \headsep \vskip \headheight
|
||||
\vskip \topmargin \vskip 1in \vskip -11in \vskip 2.75in
|
||||
|
||||
\gdef\@author{}\gdef\@title{}\gdef\@subtitle{}\let\maketitle\relax
|
||||
\end{titlepage}
|
||||
\setcounter{page}{2}
|
||||
}
|
||||
|
||||
\def\subtitle#1{\gdef\@subtitle{#1}}
|
||||
\def\@subtitle{}
|
|
@ -1,16 +0,0 @@
|
|||
%&latex -*- latex -*-
|
||||
% Implement the \frontmatter, \mainmatter, and \backmatter macros,
|
||||
% so I can use them in reports, not just books.
|
||||
|
||||
\newif\if@mainmatter \@mainmattertrue
|
||||
|
||||
\newcommand\frontmatter{%
|
||||
\cleardoublepage\@mainmatterfalse\pagenumbering{roman}}
|
||||
|
||||
\newcommand\mainmatter{%
|
||||
\cleardoublepage\@mainmattertrue%
|
||||
\pagenumbering{arabic}\setcounter{page}{1}}
|
||||
|
||||
\newcommand\backmatter{%
|
||||
\if@openright\cleardoublepage\else\clearpage\fi%
|
||||
\@mainmatterfalse}
|
|
@ -1,22 +0,0 @@
|
|||
%&latex -*- latex -*-
|
||||
\if@twoside
|
||||
\oddsidemargin 44pt
|
||||
\evensidemargin 82pt
|
||||
\marginparwidth 107pt
|
||||
\else
|
||||
\oddsidemargin 63pt
|
||||
\evensidemargin 63pt
|
||||
\marginparwidth 90pt
|
||||
\fi
|
||||
\marginparsep 11pt
|
||||
|
||||
\topmargin 27pt
|
||||
\headheight 12pt
|
||||
\headsep 25pt
|
||||
\topskip = 10pt
|
||||
\footskip 30pt
|
||||
|
||||
\textheight = 43\baselineskip
|
||||
\advance\textheight by \topskip
|
||||
\textwidth 345pt
|
||||
\endinput
|
|
@ -1,61 +0,0 @@
|
|||
\chapter{Parsing Netrc Files}\label{cha:netrc}
|
||||
%
|
||||
The \ex{netrc} structures provides procedures to parse authentication
|
||||
information contained in \ex{~/.netrc}.
|
||||
|
||||
On Unix systems the netrc file may contain information allowing
|
||||
automatic login to remote hosts. The format of the file is defined in
|
||||
the \ex{ftp(1)} manual page. Example lines are
|
||||
%
|
||||
\begin{verbatim}
|
||||
machine ondine.cict.fr login marsden password secret
|
||||
default login anonymous password user@site
|
||||
\end{verbatim}
|
||||
%
|
||||
The netrc file should be protected by appropriate permissions, and
|
||||
(like \ex{/usr/bin/ftp}) this library will refuse to read the file if it is
|
||||
badly protected. (unlike \ex{ftp} this library will always refuse
|
||||
to read the file----\ex{ftp} refuses it only if the password is
|
||||
given for a non-default account). Appropriate permissions are set if
|
||||
only the user has permissions on the file.
|
||||
|
||||
\defun{netrc-machine-entry}{host accept-default? [file-name]}{netrc-entry-or-\sharpf}
|
||||
\begin{desc}
|
||||
This procedure looks for the entry related to given host in the
|
||||
user's netrc file. The host is specified in \var{host}.
|
||||
\var{Accept-default?} specifies whether \ex{netrc-machine-entry}
|
||||
should fall back to the default entry if there is no macht for
|
||||
\var{host} in the netrc file. If specified, \var{file-name}
|
||||
specifies an alternate file name for the netrc data. It defaults to
|
||||
\ex{.netrc} in the current user's home directory.
|
||||
|
||||
\ex{Netrc-machine-entry} returns a netrc entry (see below) if it was
|
||||
able to find the requested information; if not, it returns \sharpf.
|
||||
|
||||
If the netrc file had inappropriate permissions, \ex{netrc-machine-entry}
|
||||
raises an error.
|
||||
\end{desc}
|
||||
|
||||
\defun{netrc-entry?}{thing}{boolean}
|
||||
\defunx{netrc-entry-machine}{netrc-entry}{string}
|
||||
\defunx{netrc-entry-login}{netrc-entry}{string-or-\sharpf}
|
||||
\defunx{netrc-entry-password}{netrc-entry}{string-or-\sharpf}
|
||||
\defunx{netrc-entry-account}{netrc-entry}{string-or-\sharpf}
|
||||
\begin{desc}
|
||||
\ex{Netrc-entry?} is the predicate for netrc entries. The other
|
||||
procedures are selectors for netrc entries as returned by
|
||||
\ex{netrc-machine-entry}. They return \sharpf{} if the netrc file
|
||||
didn't contain a binding for the corresponding field.
|
||||
\end{desc}
|
||||
|
||||
\defun{netrc-macro-definitions}{[file-name]}{alist}
|
||||
\begin{desc}
|
||||
This returns the macro definitions from the netrc files, represented
|
||||
as an alist mapping macro names---represented as strings---to
|
||||
definitions---represented as lists of strings.
|
||||
\end{desc}
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,57 +0,0 @@
|
|||
\chapter{Time and Daytime}\label{cha:ntp}
|
||||
|
||||
Many Unix hosts provide a RFC~867 Daytime service which sends the
|
||||
current date and time as a human-readable character string. The
|
||||
daytime service is typically served on port 13 as both TCP and UDP.
|
||||
|
||||
The RFC~868 Time protocol provides a site-independent, machine
|
||||
readable date and time. The Time service is typically served
|
||||
on port 37 as TCP and UDP. The idea is that you can confirm your
|
||||
system's idea of the time by polling several independent sites on the
|
||||
network.
|
||||
|
||||
\section{Daytime}
|
||||
|
||||
The \ex{rfc867} structure contains an interface to Daytime protocol.
|
||||
|
||||
\defun{rfc867-daytime/tcp}{host}{string}
|
||||
\defunx{rfc867-daytime/udp}{host [timeout-or-\sharpf]}{string-or-\sharpf}
|
||||
\begin{desc}
|
||||
These procedures asks \var{host} about the current daytime and
|
||||
return the host's answer (e.g., ``Thursday, April 4,
|
||||
2'').
|
||||
|
||||
\ex{Rfc867-daytime/tcp} uses the TCP variant of the protocol.
|
||||
\ex{Rfc867-daytime/udp} uses UDP and sends a single request to the
|
||||
server. It allows the specification of an optional timeout; if not
|
||||
specified or \sharpf{}, \ex{Rfc867-daytime/udp} will wait
|
||||
indefinitely for an answer. If the answer from the server doesn't
|
||||
arrive within the specified time, \ex{rfc867-daytime/udp} returns
|
||||
\sharpf.
|
||||
\end{desc}
|
||||
|
||||
\section{Time}
|
||||
|
||||
The \ex{rfc868} structure contains an interface to the Time protocol.
|
||||
|
||||
\defun{rfc868-time/tcp}{host}{string}
|
||||
\defunx{rfc868-time/udp}{host [timeout-or-\sharpf]}{string-or-\sharpf}
|
||||
\begin{desc}
|
||||
These procedures asks \var{host} about the current time and return
|
||||
the host's answer. This is the number of second since 1970, just as
|
||||
with scsh's \texttt{time} procedure.
|
||||
|
||||
\ex{rfc868-time/tcp} uses the TCP variant of the protocol.
|
||||
\ex{rfc868-time/udp} uses UDP and sends a single request to the
|
||||
server. It allows the specification of an optional timeout; if not
|
||||
specified or \sharpf{}, \ex{rfc868-time/udp} will wait
|
||||
indefinitely for an answer. If the answer from the server doesn't
|
||||
arrive within the specified time, \ex{rfc868-time/udp} returns
|
||||
\sharpf.
|
||||
\end{desc}
|
||||
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,14 +0,0 @@
|
|||
\newif\ifpdf
|
||||
\ifx\pdfoutput\undefined
|
||||
\pdffalse % we are not running PDFLaTeX
|
||||
\else
|
||||
\pdfoutput=1 % we are running PDFLaTeX
|
||||
\pdftrue
|
||||
\fi
|
||||
% Then use your new variable \ifpdf
|
||||
% \ifpdf
|
||||
% \usepackage[pdftex]{graphicx}
|
||||
% \pdfcompresslevel=9
|
||||
% \else
|
||||
% \usepackage{graphicx}
|
||||
% \fi
|
|
@ -1,98 +0,0 @@
|
|||
\chapter{Using POP3}\label{cha:pop3}
|
||||
%
|
||||
The \ex{pop3} structure provides a client for the POP3 protocol that
|
||||
allows access to email on a maildrop server. It is often used in
|
||||
configurations where users connect from a client machine which doesn't
|
||||
have a permanent network connection or isn't always turned on,
|
||||
situations which make local SMTP delivery impossible. It is the most
|
||||
common form of email access provided by ISPs.
|
||||
|
||||
Two types of authentication are commonly used. The first, most basic
|
||||
type involves sending a user's password in clear over the network, and
|
||||
should be avoided. (Unfortunately, many POP3 clients only implement this
|
||||
basic authentication.) The digest authentication system involves the
|
||||
server sending the client a ``challenge'' token; the client encodes
|
||||
this token with the pass phrase and sends the coded information to the
|
||||
server. This method avoids sending sensitive information over the
|
||||
network. Both methods are implemented by \ex{pop3}.
|
||||
|
||||
Once connected, a client may request information about the number and
|
||||
size of the messages waiting on the server, download selected messages
|
||||
(either their headers or the entire content), and delete selected
|
||||
messages.
|
||||
|
||||
The procedures defined here raise an error detectable via
|
||||
\ex{pop3-error?} upon protocol errors with the POP3 server.
|
||||
|
||||
\defun{pop3-connect}{[host-or-\sharpf] [login-or-\sharpf]
|
||||
[password-or-\sharpf] [log-port]}{connection}
|
||||
\begin{desc}
|
||||
This procedure connects to the maildrop server named \var{host},
|
||||
and logs in using the provided login name and password. Any of
|
||||
these can be omitted or \sharpf, in which case the procedure uses
|
||||
defaults: \ex{MAILHOST} for the host, and \ex{~/.netrc}-provided
|
||||
values for login and password. If \var{log-port} is provided, the
|
||||
conversation to the server is logged to the specified output port.
|
||||
|
||||
\ex{Pop3-connect} returns a value representing the connection to the
|
||||
POP3 server, to be used in the procedures below.
|
||||
\end{desc}
|
||||
|
||||
\defun{pop3-stat}{connection}{number bytes}
|
||||
\begin{desc}
|
||||
This returns the number of messages and the number of bytes waiting in the
|
||||
maildrop.
|
||||
\end{desc}
|
||||
|
||||
Most of the following procedures accept a \var{msgid} argument which
|
||||
specifies a message number, which ranges from 1 for the first message
|
||||
to the number returned by \ex{pop3-stat}.
|
||||
|
||||
\defun{pop3-retrieve-message}{connection msgid}{headers message}
|
||||
\begin{desc}
|
||||
This downloads message number \var{msgid} from the mailhost.
|
||||
It returns the headers as an alist of field names and bodies; the
|
||||
names are symbols, the bodies are strings. (These are obtained
|
||||
using the \ex{rfc822} structure, see Section~\ref{cha:rfc822}.)
|
||||
The message is returned as a list of strings, each string
|
||||
representing a line of the message.
|
||||
\end{desc}
|
||||
|
||||
\defun{pop3-retrieve-headers}{connection msgid}{headers}
|
||||
\begin{desc}
|
||||
This downloads the headers of message number \var{msgid}. It
|
||||
returns the headers in the same format as \ex{pop3-retrieve-message}.
|
||||
\end{desc}
|
||||
|
||||
\defun{pop3-last}{connection}{msgid}
|
||||
\begin{desc}
|
||||
This returns the highest accessed message-id number for the current
|
||||
session. (This isn't in the RFC, but seems to be supported by several
|
||||
servers.)
|
||||
\end{desc}
|
||||
|
||||
\defun{pop3-delete}{connection msgid}{undefined}
|
||||
\begin{desc}
|
||||
This mark message number \var{msgid} for deletion. The message will
|
||||
not be deleted until the client logs out.
|
||||
\end{desc}
|
||||
|
||||
\defun{pop3-reset}{connection}{undefined}
|
||||
\begin{desc}
|
||||
This marks any messages which have been marked for deletion.
|
||||
\end{desc}
|
||||
|
||||
\begin{desc}{pop3-quit}{connection}{undefined}
|
||||
This closes the connection with the mailhost.
|
||||
\end{desc}
|
||||
|
||||
\defun{pop3-error?}{thing}{boolean}
|
||||
\begin{desc}
|
||||
This returns \sharpt{} if \var{thing} is a \ex{pop3-error} object,
|
||||
otherwise \sharpf.
|
||||
\end{desc}
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,107 +0,0 @@
|
|||
\chapter{RFC~822 Library}\label{cha:rfc822}
|
||||
%
|
||||
The \ex{rfc822} structure provides rudimentary support for parsing
|
||||
headers according to RFC~822 \textit{Standard for the format of ARPA
|
||||
Internet text messages}. These headers show up in SMTP messages,
|
||||
HTTP headers, etc.
|
||||
|
||||
An RFC~822 header field consists of a \textit{field name} and a
|
||||
\textit{field body}, like so:
|
||||
%
|
||||
\begin{verbatim}
|
||||
Subject: RFC 822 can format itself in the ARPA
|
||||
\end{verbatim}
|
||||
%
|
||||
Here, the field name is `\ex{Subject}', and the field name is `\ex{
|
||||
RFC 822 can format itself in the ARPA}' (note the leading space).
|
||||
The field body can be spread over several lines:
|
||||
%
|
||||
\begin{verbatim}
|
||||
Subject: RFC 822 can format itself
|
||||
in the ARPA
|
||||
\end{verbatim}
|
||||
%
|
||||
In this case, RFC~822 specifies that the meaning of the field body is
|
||||
actually all the lines of the body concatenated, without the
|
||||
intervening line breaks.
|
||||
|
||||
The \ex{rfc822} structure provides two sets of parsing
|
||||
procedures---one represents field bodies in the RFC-822-specified
|
||||
meaning, as a single string, the other (with \ex{-with-line-breaks}
|
||||
appended to the names) reflects the line breaks and represents the
|
||||
bodies as a list of string, one for each line. The latter set only
|
||||
marginally useful---mainly for code that needs to output headers in
|
||||
the same form as they were originally provided.
|
||||
|
||||
\defun{read-rfc822-field}{[port] [read-line]}{name body}
|
||||
\defun{read-rfc822-field-with-line-breaks}{[port] [read-line]}{name body-lines}
|
||||
\begin{desc}
|
||||
|
||||
Read one field from the port, and return two values:
|
||||
%
|
||||
\begin{description}
|
||||
\item[\var{name}] This is a symbol describing the field
|
||||
name, such as \ex{subject} or \ex{to}. The symbol consists of all
|
||||
lower-case letters.\footnote{In fact, it \ex{read-rfc822-field}
|
||||
uses the preferred case for symbols of the underlying Scheme
|
||||
implementation which, in the case of scsh, happens to be lower-case.}
|
||||
\item[\var{body} or \var{body-lines}] This is the field body.
|
||||
\var{Body} is a single string, \var{body-lines} is a list of
|
||||
strings, one for each line of the body. In each case,
|
||||
the terminating \ex{cr}/\ex{lf}'s (but nothing else) are
|
||||
trimmed from each string.
|
||||
\end{description}
|
||||
%
|
||||
When there are no more fields---EOF or a blank line has terminated
|
||||
the header section---then both procedures returns [\sharpf\
|
||||
\sharpf].
|
||||
|
||||
\var{Port} is an optional input port to read from---it defaults to
|
||||
the value of \ex{(current-input-port)}.
|
||||
|
||||
\var{Read-line} is an optional parameter specifying a procedure of
|
||||
one argument (the input port) used to read the raw header lines.
|
||||
The default used by these procedures terminates lines with
|
||||
either \ex{cr}/\ex{lf} or just \ex{lf}, and it trims the terminator
|
||||
from the line. This procedure should trim the terminator of the
|
||||
line, so an empty line is returned as an empty string.
|
||||
|
||||
The procedure raises an error if the syntax of the read field (the
|
||||
line returned by the read-line-function) is illegal according to
|
||||
RFC~822.
|
||||
\end{desc}
|
||||
|
||||
\defun{read-rfc822-headers} {[port] [read-line]} {alist}
|
||||
\defunx{read-rfc822-headers-with-line-breaks} {[port] [read-line]} {alist}
|
||||
\begin{desc}
|
||||
This procedure reads in and parses a section of text that looks like
|
||||
the header portion of an RFC~822 message. It returns an association
|
||||
list mapping field names (a symbol such as \ex{date} or \ex{subject}) to
|
||||
field bodies. The representation of the field bodies is as with
|
||||
\ex{read-rfc822-field} and \ex{read-rfc822-field-with-line-breaks}.
|
||||
|
||||
These procedures preserve the order of the header fields. Note that
|
||||
several header fields might share the same field name---in that
|
||||
case, the returned alist will contain several entries with the same
|
||||
\ex{car}.
|
||||
|
||||
\var{Port} and \var{read-line} are as with \ex{read-rfc822-field}
|
||||
and \ex{read-rfc822-field-with-line-breaks}.
|
||||
\end{desc}
|
||||
|
||||
\defun{rfc822-time->string}{time}{string}
|
||||
\begin{desc}
|
||||
This formats a time value (as returned by scsh's \ex{time})
|
||||
according to the requirements of the RFC~822 \ex{Date} header
|
||||
field. The format looks like this:
|
||||
%
|
||||
\begin{verbatim}
|
||||
Sun, 06 Nov 1994 08:49:37 GMT
|
||||
\end{verbatim}
|
||||
\end{desc}
|
||||
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,8 +0,0 @@
|
|||
\section{Section-Title}
|
||||
%
|
||||
\begin{description}
|
||||
\item[Used files:]
|
||||
\item[Name of the package:]
|
||||
\end{description}
|
||||
%
|
||||
Not implemented yet.
|
|
@ -1,123 +0,0 @@
|
|||
\chapter{SMTP Client}\label{cha:smtp}
|
||||
%
|
||||
The \ex{smtp} structure provides an client library for the Simple Mail
|
||||
Transfer Protocol, commonly used for sending email on the Internet.
|
||||
This library provides a simple wrapper for sending complete emails as
|
||||
well as procedures for composing custom SMTP transactions.
|
||||
|
||||
Some of the procedures described here return an SMTP reply code. For
|
||||
details, see RFC~821.
|
||||
|
||||
\defun{smtp-send-mail}{from to-list headers body [host]}{undefined}
|
||||
\defunx{smtp-error?}{thing}{boolean}
|
||||
\defunx{smtp-recipients-rejected-error?}{thing}{boolean}
|
||||
\begin{desc}
|
||||
This emails message \var{body} with headers \var{headers} to
|
||||
recipients in list \var{to-list}, using a sender address \var{from}.
|
||||
The email is handed off to the SMTP server running on \var{host};
|
||||
default is the local host. \var{Body} is either a list of strings
|
||||
representing the lines of the message body or an input port which is
|
||||
exhausted to determine the message body. \var{Headers} is an
|
||||
association lists, mapping symbols representing RFC~822 field names
|
||||
to strings representing field bodies.
|
||||
|
||||
If some transaction-related error happens, \ex{smtp-send-mail}
|
||||
signals an \ex{smtp-error} condition with predicate
|
||||
\ex{smtp-error?}. More specifically, it raises an
|
||||
\ex{smtp-recipients-rejected-error} (a subtype of \ex{smtp-error})
|
||||
if some recipients were rejected. For \ex{smtp-error}, the
|
||||
arguments to the \ex{signal} call are the error code and the error
|
||||
message, represented as a list of lines. For
|
||||
\ex{smtp-recipients-rejected-error}, the arguments are reply code
|
||||
700 and an association list whose elements are of the form
|
||||
\ex{(\var{loser-recipient} \var{code} . \var{text})}---that is, for
|
||||
each recipient refused by the server, you get the error data sent
|
||||
back for that guy. The success check is \ex{(< code 400)}.
|
||||
\end{desc}
|
||||
|
||||
\defun{smtp-expand}{name host}{code text}
|
||||
\defunx{smtp-verify}{name host}{code text}
|
||||
\defunx{smtp-get-help}{host [details]}{code text-list}
|
||||
\begin{desc}
|
||||
These three are simple queries of the server as stated in the
|
||||
RFC~821: \ex{smtp-expann} asks the server to confirm that the
|
||||
argument identifies a mailing list, and if so, to return the
|
||||
membership of that list. The full name of the users (if known) and
|
||||
the fully specified mailboxes are returned in a multiline reply.
|
||||
\ex{Smtp-verify} asks the receiver to confirm that the argument
|
||||
identifies a user. If it is a user name, the full name of the user
|
||||
(if known) and the fully specified mailbox are returned.
|
||||
\ex{Smtp-get-help} causes the server to send helpful information.
|
||||
The command may take an argument (\var{details}) (e.g., any command
|
||||
name) and return more specific information as a response.
|
||||
\end{desc}
|
||||
|
||||
\defun{smtp-connect}{host [port]}{smtp-connection}
|
||||
\begin{desc}
|
||||
\ex{Smtp-connect} returns an SMTP connection value that represents
|
||||
a connection to the SMTP server.
|
||||
\end{desc}
|
||||
|
||||
\defun{smtp-transactions}{smtp-connection transaction1 ...}{code text-list}
|
||||
\defunx{smtp-transactions/no-close}{smtp-connection transaction1 ...}{code text-list}
|
||||
\begin{desc}
|
||||
These procedures make it easy to do simple sequences of SMTP
|
||||
commands. \var{Smtp-connection} must be an SMTP connection as
|
||||
returned by \ex{smtp-connect}. The \var{transaction} arguments must
|
||||
be transactions as returned by the procedures below.
|
||||
\ex{Smtp-transactions} and \ex{smtp-transactions/no-close} execute
|
||||
the transactions specified by the arguments.
|
||||
|
||||
For each transaction,
|
||||
\begin{itemize}
|
||||
\item If the transaction's reply code is 221 or 421 (meaning the socket has
|
||||
been closed), then the transaction sequence is aborted, and
|
||||
\ex{smtp-transactions}/\ex{smtp-transactions/no-close} return the
|
||||
reply code and text from that transaction.
|
||||
\item If the reply code is an error code (in the four- or five-hundred range),
|
||||
the transaction sequence is aborted, and the fatal transaction's code
|
||||
and text values are returned. \ex{Smtp-transactions} will additionally
|
||||
close the socket for you; \ex{smtp-transactions/no-close} will not.
|
||||
\item If the transaction is the last in the transaction sequence,
|
||||
its reply code and text are returned.
|
||||
\item Otherwise, we throw away the current reply code and text, and
|
||||
proceed to the next transaction.
|
||||
\end{itemize}
|
||||
%
|
||||
\ex{Smtp-transactions} closes the socket after the transaction. (The
|
||||
\ex{smtp-quit} transaction, when executed, also closes the transaction.)
|
||||
|
||||
If the socket should be kept open in the case of an abort, use
|
||||
\ex{Smtp-transactions/no-close}.
|
||||
\end{desc}
|
||||
|
||||
\defunx{smtp-helo}{local-host-name}{smtp-transaction}
|
||||
\defunx{smtp-mail}{sender-address}{smtp-transaction}
|
||||
\defunx{smtp-rcpt}{destination-address}{smtp-transaction}
|
||||
\defunx{smtp-data}{socket message}{smtp-transaction}
|
||||
\defunx{smtp-send}{sender-address}{smtp-transaction}
|
||||
\defunx{smtp-soml}{sender-address}{smtp-transaction}
|
||||
\defunx{smtp-saml}{sender-address}{smtp-transaction}
|
||||
\defvarx{smtp-rset}{smtp-transaction}
|
||||
\defunx{smtp-vrfy}{user}{smtp-transaction}
|
||||
\defunx{smtp-expn}{user}{smtp-transaction}
|
||||
\defunx{smtp-help}{details}{smtp-transaction}
|
||||
\defvarx{smtp-noop}{smtp-transaction}
|
||||
\defvarx{smtp-quit}{smtp-transaction}
|
||||
\defvarx{smtp-turn}{smtp-transaction}
|
||||
\begin{desc}
|
||||
These transactions represent the commands of the SMTP protocol for
|
||||
use in \ex{smtp-transactions} and \ex{smtp-transactions/no-close},
|
||||
i.e.\ they send the corresponding command along with the argument(s),
|
||||
if any. For details, consult RFC~821.
|
||||
|
||||
The \ex{smtp-quit} transaction, in addition to sending a \ex{QUIT}
|
||||
command to the SMTP server, also closes the socket of its SMTP
|
||||
connection.
|
||||
\end{desc}
|
||||
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,168 +0,0 @@
|
|||
\chapter{Parsing and Processing URIs}\label{cha:uri}
|
||||
|
||||
The \ex{uri} structure contains a library for dealing with URIs.
|
||||
|
||||
\section{Notes on URI Syntax}
|
||||
|
||||
A URI (Uniform Resource Identifier) is of following syntax:
|
||||
%
|
||||
\begin{inset}
|
||||
[\var{scheme}] \verb|:| \var{path} [\verb|?| \var{search}] [\verb|#| \var{fragid}]
|
||||
\end{inset}
|
||||
%
|
||||
Parts in brackets may be omitted.
|
||||
|
||||
The URI contains characters like \verb|:| to indicate its different
|
||||
parts. Some special characters are \emph{escaped} if they are a
|
||||
regular part of a name and not indicators for the structure of a URI.
|
||||
Escape sequences are of following scheme: \verb|%|\var{h}\var{h} where \var{h}
|
||||
is a hexadecimal digit. The hexadecimal number refers to the
|
||||
ASCII of the escaped character, e.g.\ \verb|%20| is space (ASCII
|
||||
32) and \verb|%61| is `a' (ASCII 97). This module
|
||||
provides procedures to escape and unescape strings that are meant to
|
||||
be used in a URI.
|
||||
|
||||
\section{Procedures}
|
||||
|
||||
\defun{parse-uri} {uri-string } {scheme path search
|
||||
frag-id} \label{proc:parse-uri}
|
||||
\begin{desc}
|
||||
Parses an \var{uri\=string} into its four fields.
|
||||
The fields are \emph{not} unescaped, as the rules for
|
||||
parsing the \var{path} component in particular need unescaped
|
||||
text, and are dependent on \var{scheme}. The URL parser is
|
||||
responsible for doing this. If the \var{scheme}, \var{search}
|
||||
or \var{fragid} portions are not specified, they are \sharpf.
|
||||
Otherwise, \var{scheme}, \var{search}, and \var{fragid} are
|
||||
strings. \var{path} is a non-empty string list---the path split
|
||||
at slashes.
|
||||
\end{desc}
|
||||
|
||||
Here is a description of the parsing technique. It is inwards from
|
||||
both ends:
|
||||
\begin{itemize}
|
||||
\item First, the code searches forwards for the first reserved
|
||||
character (\verb|=|, \verb|;|, \verb|/|, \verb|#|, \verb|?|,
|
||||
\verb|:| or \verb|space|). If it's a colon, then that's the
|
||||
\var{scheme} part, otherwise there is no \var{scheme} part. At
|
||||
all events, it is removed.
|
||||
\item Then the code searches backwards from the end for the last reserved
|
||||
char. If it's a sharp, then that's the \var{fragid} part---remove it.
|
||||
\item Then the code searches backwards from the end for the last reserved
|
||||
char. If it's a question-mark, then that's the \var{search}
|
||||
part----remove it.
|
||||
\item What's left is the path. The code split it at slashes. The
|
||||
empty string becomes a list containing the empty string.
|
||||
\end{itemize}
|
||||
%
|
||||
This scheme is tolerant of the various ways people build broken
|
||||
URI's out there on the Net\footnote{So it does not absolutely conform
|
||||
to RFC~1630.}, e.g.\ \verb|=| is a reserved character, but used
|
||||
unescaped in the search-part. It was given to me\footnote{That's
|
||||
Olin Shivers.} by Dan Connolly of the W3C and slightly modified.
|
||||
|
||||
\defun{unescape-uri}{string [start] [end]}{string}
|
||||
\begin{desc}
|
||||
\ex{Unescape-uri} unescapes a string. If \var{start} and/or \var{end} are
|
||||
specified, they specify start and end positions within \var{string}
|
||||
should be unescaped.
|
||||
\end{desc}
|
||||
%
|
||||
This procedure should only be used \emph{after} the URI was parsed,
|
||||
since unescaping may introduce characters that blow up the
|
||||
parse---that's why escape sequences are used in URIs.
|
||||
|
||||
\defvar{uri-escaped-chars}{char-set}
|
||||
\begin{desc}
|
||||
This is a set of characters (in the sense of SRFI~14) which are
|
||||
escaped in URIs. These are the
|
||||
following characters: \verb|$|, \verb|-|, \verb|_|, \verb|@|, %$
|
||||
\verb|.|, \verb|&|, \verb|!|, \verb|*|, \verb|\|, \verb|"|,
|
||||
\verb|'|, \verb|(|, \verb|)|, \verb|,|, \verb|+|, and all other
|
||||
characters that are neither letters nor digits (such as space and
|
||||
control characters).
|
||||
\end{desc}
|
||||
|
||||
\defun{escape-uri} {string [escaped-chars]} {string}
|
||||
\begin{desc}
|
||||
This procedure escapes characters of \var{string} that are in
|
||||
\var{escaped\=chars}. \var{Escaped\=chars} defaults to
|
||||
\ex{uri\=escaped\=chars}.
|
||||
\end{desc}
|
||||
%
|
||||
Be careful with using this procedure to chunks of text with
|
||||
syntactically meaningful reserved characters (e.g., paths with URI
|
||||
slashes or colons)---they'll be escaped, and lose their special
|
||||
meaning. E.g.\ it would be a mistake to apply \ex{escape-uri} to
|
||||
\begin{verbatim}
|
||||
//lcs.mit.edu:8001/foo/bar.html
|
||||
\end{verbatim}
|
||||
%
|
||||
because the sla\-shes and co\-lons would be escaped.
|
||||
|
||||
\defun{split-uri}{uri start end} {list}
|
||||
\begin{desc}
|
||||
This procedure splits \var{uri} at slashes. Only the substring given
|
||||
with \var{start} (inclusive) and \var{end} (exclusive) as indices is
|
||||
considered. \var{start} and $\var{end} - 1$ have to be within the
|
||||
range of \var{uri}. Otherwise an \ex{index-out-of-range} exception
|
||||
will be raised.
|
||||
|
||||
Example: \codex{(split-uri "foo/bar/colon" 4 11)} returns
|
||||
\codex{("bar" "col")}
|
||||
\end{desc}
|
||||
|
||||
\defun{uri-path->uri}{path}{string}
|
||||
\begin{desc}
|
||||
This procedure generates a path out of a URI path list by inserting
|
||||
slashes between the elements of \var{plist}.
|
||||
\end{desc}
|
||||
%
|
||||
If you want to use the resulting string for further operation, you
|
||||
should escape the elements of \var{plist} in case they contain
|
||||
slashes, like so:
|
||||
%
|
||||
\begin{verbatim}
|
||||
(uri-path->uri (map escape-uri pathlist))
|
||||
\end{verbatim}
|
||||
|
||||
\defun{simplify-uri-path}{path}{list}
|
||||
\begin{desc}
|
||||
This procedure simplifies a URI path. It removes \verb|"."| and
|
||||
\verb|"/.."| entries from path, and removes parts before a root.
|
||||
The result is a list, or \sharpf{} if the path tries to back up past
|
||||
root.
|
||||
\end{desc}
|
||||
%
|
||||
According to RFC~2396, relative paths are considered not to start with
|
||||
\verb|/|. They are appended to a base URL path and then simplified.
|
||||
So before you start to simplify a URL try to find out if it is a
|
||||
relative path (i.e. it does not start with a \verb|/|).
|
||||
|
||||
Examples:
|
||||
%
|
||||
\begin{alltt}
|
||||
(simplify-uri-path (split-uri "/foo/bar/baz/.." 0 15))
|
||||
\(\Rightarrow\) ("" "foo" "bar")
|
||||
|
||||
(simplify-uri-path (split-uri "foo/bar/baz/../../.." 0 20))
|
||||
\(\Rightarrow\) ()
|
||||
|
||||
(simplify-uri-path (split-uri "/foo/../.." 0 10))
|
||||
\(\Rightarrow\) #f
|
||||
|
||||
(simplify-uri-path (split-uri "foo/bar//" 0 9))
|
||||
\(\Rightarrow\) ("")
|
||||
|
||||
(simplify-uri-path (split-uri "foo/bar/" 0 8))
|
||||
\(\Rightarrow\) ("")
|
||||
|
||||
(simplify-uri-path (split-uri "/foo/bar//baz/../.." 0 19))
|
||||
\(\Rightarrow\) #f
|
||||
\end{alltt}
|
||||
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
|
@ -1,113 +0,0 @@
|
|||
\chapter{Parsing and Processing URLs}\label{cha:url}
|
||||
%
|
||||
This modules contains procedures to parse and unparse URLs. Until
|
||||
now, only the parsing of HTTP URLs is implemented.
|
||||
|
||||
\section{Server Records}
|
||||
|
||||
A \textit{server} value describes path prefixes of the form
|
||||
\var{user}:\var{password}@\var{host}:\var{port}. These are
|
||||
frequently used as the initial prefix of URLs describing Internet
|
||||
resources.
|
||||
|
||||
\defun{make-server}{user password host port}{server}
|
||||
\defunx{server?}{thing}{boolean}
|
||||
\defunx{server-user}{server}{string-or-\sharpf}
|
||||
\defunx{server-password}{server}{string-or-\sharpf}
|
||||
\defunx{server-host}{server}{string-or-\sharpf}
|
||||
\defunx{server-port}{server}{string-or-\sharpf}
|
||||
\begin{desc}
|
||||
\ex{Make-server} creates a new server record. Each slot is a
|
||||
decoded string or \sharpf. (\var{Port} is also a string.)
|
||||
|
||||
\ex{server?} is the corresponding predicate, \ex{server-user},
|
||||
\ex{server-password}, \ex{server-host} and \ex{server-port}
|
||||
are the correspondig selectors.
|
||||
\end{desc}
|
||||
|
||||
\defun{parse-server}{path default}{server}
|
||||
\defunx{server->string}{server}{string}
|
||||
\begin{desc}
|
||||
\ex{Parse-server} parses a URI path \var{path} (a list representing
|
||||
a path, not a string) into a server value. Default values are taken
|
||||
from the server \var{default} except for the host. The values
|
||||
are unescaped and stored into a server record that is returned.
|
||||
\ex{Fatal-syntax-error} is called, if the specified path has no
|
||||
initial to slashes (i.e., it starts with `//\ldots').
|
||||
|
||||
\ex{server->string} just does the inverse job: it unparses
|
||||
\var{server} into a string. The elements of the record
|
||||
are escaped before they are put together.
|
||||
|
||||
Example:
|
||||
\begin{alltt}
|
||||
> (define default (make-server "andreas" "se ret" "www.sf.net" "80"))
|
||||
> (server->string default)
|
||||
"andreas:se\%20ret@www.sf.net:80"
|
||||
> (parse-server '("" "" "foo\%20bar@www.scsh.net" "docu" "index.html")
|
||||
default)
|
||||
'#{server}
|
||||
> (server->string ##)
|
||||
"foo\%20bar:se\%20ret@www.scsh.net:80"
|
||||
\end{alltt}
|
||||
%
|
||||
For details about escaping and unescaping see Chapter~\ref{cha:uri}.
|
||||
\end{desc}
|
||||
|
||||
\section{HTTP URLs}
|
||||
|
||||
\defun{make-http-url}{server path search frag-id}{http-url}
|
||||
\defunx{http-url?}{thing}{boolean}
|
||||
\defunx{http-url-server}{http-url}{server}
|
||||
\defunx{http-url-path}{http-url}{list}
|
||||
\defunx{http-url-search}{http-url}{string-or-\sharpf}
|
||||
\defunx{http-url-frag-ment-identifier}{http-url}{string-or-\sharpf}
|
||||
%
|
||||
\begin{desc}
|
||||
\ex{Make-http-url} creates a new \ex{httpd-url} record.
|
||||
\var{Server} is a record, containing the initial part of the address
|
||||
(like \ex{anonymous@clark.lcs.mit.edu:80}). \var{Path} contains the
|
||||
URL's URI path ( a list). These elements are in raw, unescaped
|
||||
format. To convert them back to a string, use
|
||||
\ex{(uri-path-list->path (map escape-uri pathlist))}. \var{Search}
|
||||
and \var{frag-id} are the last two parts of the URL. (See
|
||||
Chapter~\ref{cha:uri} about parts of an URI.)
|
||||
|
||||
\ex{Http-url?} is the predicate for HTTP URL values, and
|
||||
\ex{http-url-server}, \ex{http-url-path}, \ex{http-url-search} and
|
||||
\ex{http-url-fragment-identifier} are the corresponding selectors.
|
||||
\end{desc}
|
||||
|
||||
\defun{parse-http-url}{path search frag-id}{http-url}
|
||||
\begin{defundescx}{http-url->string}{http-url}{string}
|
||||
This constructs an HTTP URL record from a URI path (a list of path
|
||||
components), a search, and a frag-id component.
|
||||
|
||||
\ex{Http-url->string} just does the inverse job. It converts an
|
||||
HTTP URL record into a string.
|
||||
\end{defundescx}
|
||||
%
|
||||
Note: The URI parser \ex{parse-uri} maps a string to four parts:
|
||||
\var{scheme}, \var{path}, \var{search} and \var{frag-id} (see
|
||||
Section~\ref{proc:parse-uri} for details). If \var{scheme} is
|
||||
\ex{http}, then the other three parts can be passed to
|
||||
\ex{parse-http-url}, which parses them into a \ex{http-url} record.
|
||||
All strings come back from the URI parser encoded. \var{Search} and
|
||||
\var{frag-id} are left that way; this parser decodes the path
|
||||
elements. The first two list elements of the path indicating the
|
||||
leading double-slash are omitted.
|
||||
|
||||
The following procedure combines the jobs of \ex{parse-uri} and
|
||||
\ex{parse-http-url}:
|
||||
|
||||
\defun{parse-http-url-string}{string}{http-url}
|
||||
\begin{desc}
|
||||
This parses an HTTP URL and returns the corresponding URL value; it
|
||||
calls \ex{fatal-syntax-error} if the URL string doesn't have an
|
||||
\ex{http} scheme.
|
||||
\end{desc}
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "man"
|
||||
%%% End:
|
1324
scheme/ftpd/ftpd.scm
1324
scheme/ftpd/ftpd.scm
File diff suppressed because it is too large
Load Diff
|
@ -1,76 +0,0 @@
|
|||
;;; http server in the Scheme Shell -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1996 by Mike Sperber. <sperber@informatik.uni-tuebingen.de>
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; This code is very rudimentary at the moment and up for some expansion.
|
||||
;;; Right now, it is primarily useful for running the server through a
|
||||
;;; web accelerator
|
||||
|
||||
(define (access-denier . hosts)
|
||||
(lambda (info)
|
||||
(and (any (lambda (host)
|
||||
(host-matches? info host))
|
||||
hosts)
|
||||
'deny)))
|
||||
|
||||
(define (access-allower . hosts)
|
||||
(lambda (info)
|
||||
(and (any (lambda (host)
|
||||
(host-matches? info host))
|
||||
hosts)
|
||||
'allow)))
|
||||
|
||||
(define (access-controller . controls)
|
||||
(lambda (info)
|
||||
(let loop ((controls controls))
|
||||
(and (pair? controls)
|
||||
(or ((car controls) info)
|
||||
(loop (cdr controls)))))))
|
||||
|
||||
(define (access-controlled-handler control ph)
|
||||
(lambda (path req)
|
||||
(if (eq?
|
||||
(control (host-info (socket-remote-address (request-socket req))))
|
||||
'deny)
|
||||
(http-error (status-code forbidden) req)
|
||||
(ph path req))))
|
||||
|
||||
(define (address->list address)
|
||||
(list (arithmetic-shift (bitwise-and address #xff000000) -24)
|
||||
(arithmetic-shift (bitwise-and address #xff0000) -16)
|
||||
(arithmetic-shift (bitwise-and address #xff00) -8)
|
||||
(bitwise-and address #xff)))
|
||||
|
||||
(define (host-matches? info host)
|
||||
(cond
|
||||
((list? host)
|
||||
(let ((len (length host)))
|
||||
(any (lambda (address)
|
||||
(equal? (take len (address->list address)) host))
|
||||
(host-info:addresses info))))
|
||||
(else ; (string? host)
|
||||
(any (lambda (name)
|
||||
(string-match host (string-map char-downcase name)))
|
||||
(cons (host-info:name info)
|
||||
(host-info:aliases info))))))
|
||||
|
||||
(define normalize-host
|
||||
(let ((split (infix-splitter (make-regexp "\\.")))
|
||||
(number (make-regexp "[0-9]+")))
|
||||
(lambda (host)
|
||||
(let ((components (split host)))
|
||||
(if (every (lambda (component)
|
||||
(regexp-exec number component))
|
||||
components)
|
||||
(map string->number components)
|
||||
host)))))
|
||||
|
||||
(define (take n l)
|
||||
(let loop ((n n) (l l) (r '()))
|
||||
(if (zero? n)
|
||||
(reverse r)
|
||||
(loop (- n 1) (cdr l) (cons (car l) r)))))
|
|
@ -1,302 +0,0 @@
|
|||
;;; Server support for NCSA's WWW Common Gateway Interface -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; See http://hoohoo.ncsa.uiuc.edu/cgi/interface.html for a sort of "spec".
|
||||
|
||||
;;; PROBLEMS:
|
||||
;;; - The handlers could be made -- closed over their parameters
|
||||
;;; (e.g., root vars, etc.)
|
||||
|
||||
;;; This code provides a request handler for the HTTP server that implements
|
||||
;;; a CGI interface to external programs for doing HTTP transactions.
|
||||
|
||||
;;; About HTML forms
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; This info is in fact independent of CGI, but important to know about,
|
||||
;;; as many CGI scripts are written for responding to forms-entry in
|
||||
;;; HTML browsers.
|
||||
;;;
|
||||
;;; The form's field data are turned into a single string, of the form
|
||||
;;; name=val&name=val
|
||||
;;; where the <name> and <val> parts are URI encoded to hide their
|
||||
;;; &, =, and + chars, among other things. After URI encoding, the
|
||||
;;; space chars are converted to + chars, just for fun. It is important
|
||||
;;; to encode the spaces this way, because the perfectly general %xx escape
|
||||
;;; mechanism might be insufficiently confusing. This variant encoding is
|
||||
;;; called "form-url encoding."
|
||||
;;;
|
||||
;;; If the form's method is POST,
|
||||
;;; Browser sends the form's field data in the entity block, e.g.,
|
||||
;;; "button=on&ans=yes". The request's Content-type: is application/
|
||||
;;; x-www-form-urlencoded, and the request's Content-length: is the
|
||||
;;; number of bytes in the form data.
|
||||
;;;
|
||||
;;; If the form's method is GET,
|
||||
;;; Browser sends the form's field data in the URL's <search> part.
|
||||
;;; (So the server will pass to the CGI script as $QUERY_STRING,
|
||||
;;; and perhaps also on in argv[]).
|
||||
;;;
|
||||
;;; In either case, the data is "form-url encoded" (as described above).
|
||||
|
||||
;;; ISINDEX queries:
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; (Likewise for ISINDEX URL queries from browsers.)
|
||||
;;; Browser url-form encodes the query (see above), which then becomes the
|
||||
;;; ?<search> part of the URI. (Hence the CGI script will split the individual
|
||||
;;; fields into argv[].)
|
||||
|
||||
|
||||
;;; CGI interface:
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; - The URL's <search> part is assigned to env var $QUERY_STRING, undecoded.
|
||||
;;; - If it contains no raw "=" chars, it is split at "+" chars. The
|
||||
;;; substrings are URI decoded, and become the elts of argv[].
|
||||
;;; - The CGI script is run with stdin hooked up to the socket. If it's going
|
||||
;;; to read the entity, it should read $CONTENT_LENGTH bytes worth.
|
||||
;;; - A bunch of env vars are set; see below.
|
||||
;;; - If the script begins with "nph-" its output is the entire response.
|
||||
;;; Otherwise, it replies to the server, we peel off a little header
|
||||
;;; that is used to construct the real header for the response.
|
||||
;;; See the "spec" for further details. (URL above).
|
||||
;;;
|
||||
;;; The "spec" also talks about PUT, but when I tried this on a dummy script,
|
||||
;;; the NSCA httpd server generated buggy output. So I am only implementing
|
||||
;;; the POST and GET ops; any other op generates a "405 Method not allowed"
|
||||
;;; response.
|
||||
|
||||
;;; Parameters
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;; path for scripts
|
||||
(define cgi-default-bin-path "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin")
|
||||
|
||||
;;; The request handler for CGI scripts. (car path) is the script to run.
|
||||
;;; cgi-bin-path is used, if PATH-variable isn't defined
|
||||
|
||||
(define (cgi-handler bin-dir . maybe-cgi-bin-path)
|
||||
(let-optionals
|
||||
maybe-cgi-bin-path
|
||||
((cgi-bin-path cgi-default-bin-path))
|
||||
|
||||
(let ((request-invariant-cgi-env ; environment variables that never change
|
||||
`(("PATH" . ,cgi-bin-path)
|
||||
("SERVER_SOFTWARE" . ,sunet-version-identifier)
|
||||
("SERVER_NAME" . ,(host-info:name (host-info (system-name))))
|
||||
("GATEWAY_INTERFACE" . "CGI/1.1"))))
|
||||
(lambda (path req)
|
||||
(if (pair? path) ; Got to have at least one elt.
|
||||
(compute-cgi path req bin-dir request-invariant-cgi-env)
|
||||
(make-error-response (status-code bad-request) req "Empty CGI script"))))))
|
||||
|
||||
(define (compute-cgi path req bin-dir request-invariant-cgi-env)
|
||||
(let* ((prog (car path))
|
||||
|
||||
(filename (or (dotdot-check bin-dir (list prog))
|
||||
(http-error (status-code bad-request) req
|
||||
"CGI scripts may not contain \"..\" elements.")))
|
||||
|
||||
(nph? (string-prefix? "nph-" prog)) ; PROG starts with "nph-" ?
|
||||
; why did we had (string-suffix? "-nph" prog) here?
|
||||
|
||||
(search (http-url-search (request-url req))) ; Compute the
|
||||
(argv (if (and search (not (string-index search #\=))) ; argv list.
|
||||
(split-and-decode-search-spec search)
|
||||
'()))
|
||||
|
||||
(env (cgi-env req bin-dir (cdr path) request-invariant-cgi-env))
|
||||
|
||||
(doit (lambda ()
|
||||
(dup->inport (socket:inport (request-socket req)) 0)
|
||||
(dup->outport (current-output-port) 1)
|
||||
(dup 1 2)
|
||||
(apply exec/env filename env argv))))
|
||||
|
||||
(http-syslog (syslog-level debug) "[cgi-server] search: ~s, argv: ~s~%" search argv)
|
||||
(let ((request-method (request-method req)))
|
||||
(cond
|
||||
((or (string=? request-method "GET")
|
||||
(string=? request-method "POST")) ; Could do others also.
|
||||
(case (file-not-executable? filename)
|
||||
((search-denied permission)
|
||||
(make-error-response (status-code forbidden) req
|
||||
"Permission denied."))
|
||||
((no-directory nonexistent)
|
||||
(make-error-response (status-code not-found) req
|
||||
"File or directory doesn't exist."))
|
||||
(else
|
||||
(if nph?
|
||||
(cgi-make-nph-response (run/port* doit))
|
||||
(cgi-make-response (run/port* doit) path req)))))
|
||||
|
||||
(else
|
||||
(make-error-response (status-code method-not-allowed) req request-method))))))
|
||||
|
||||
|
||||
(define (split-and-decode-search-spec s)
|
||||
(let recur ((i 0))
|
||||
(cond
|
||||
((string-index s #\+ i) => (lambda (j) (cons (unescape-uri s i j)
|
||||
(recur (+ j 1)))))
|
||||
(else (list (unescape-uri s i (string-length s)))))))
|
||||
|
||||
|
||||
;;; Compute the CGI scripts' process environment by adding the standard CGI
|
||||
;;; environment var bindings to the current process env -- return result
|
||||
;;; as an alist.
|
||||
;;;
|
||||
;;; You are also supposed to add the headers as env vars in a particular
|
||||
;;; format, but are allowed to bag it if the environment var storage
|
||||
;;; requirements might overload the OS. I don't know what you can rely upon
|
||||
;;; in Unix, so I am just bagging it, period.
|
||||
;;;
|
||||
;;; Suppose the URL is
|
||||
;;; //machine/cgi-bin/test-script/foo/bar?quux%20a+b=c
|
||||
;;; then:
|
||||
;; PATH_INFO -- extra info after the script-name path prefix. "/foo/bar"
|
||||
;;; PATH_TRANSLATED -- non-virtual version of above. "/u/Web/foo/bar/"
|
||||
;;; SCRIPT_NAME virtual path to script "/cgi-bin/test-script"
|
||||
;;; QUERY_STRING -- not decoded "quux%20a+b=c"
|
||||
;;; The first three of these vars are *not* encoded, so information is lost
|
||||
;;; if the URL's path elements contain encoded /'s (%2F). CGI loses.
|
||||
|
||||
(define (cgi-env req bin-dir path-suffix request-invariant-cgi-env)
|
||||
(let* ((sock (request-socket req))
|
||||
(raddr (socket-remote-address sock))
|
||||
|
||||
(headers (request-headers req))
|
||||
|
||||
;; Compute the $PATH_INFO and $PATH_TRANSLATED strings.
|
||||
(path-info (uri-path->uri path-suffix)) ; No encode or .. check.
|
||||
(path-translated (path-list->file-name path-info bin-dir))
|
||||
|
||||
;; Compute the $SCRIPT_PATH string.
|
||||
(url-path (http-url-path (request-url req)))
|
||||
(script-path (take (- (length url-path) (length path-suffix))
|
||||
url-path))
|
||||
(script-name (uri-path->uri script-path)))
|
||||
|
||||
(receive (rhost rport)
|
||||
(socket-address->internet-address raddr)
|
||||
(receive (lhost lport)
|
||||
(socket-address->internet-address (socket-local-address sock))
|
||||
|
||||
`(("SERVER_PROTOCOL" . ,(version->string (request-version req)))
|
||||
("SERVER_PORT" . ,(number->string lport))
|
||||
("REQUEST_METHOD" . ,(request-method req))
|
||||
|
||||
("PATH_INFO" . ,path-info)
|
||||
("PATH_TRANSLATED" . ,path-translated)
|
||||
("SCRIPT_NAME" . ,script-name)
|
||||
|
||||
("REMOTE_ADDR" . ,(format-internet-host-address rhost))
|
||||
|
||||
;; ("AUTH_TYPE" . xx) ; Random authentication
|
||||
;; ("REMOTE_USER" . xx) ; features I don't understand.
|
||||
;; ("REMOTE_IDENT" . xx)
|
||||
|
||||
,@request-invariant-cgi-env ; Stuff that never changes (see cgi-handler).
|
||||
|
||||
,@(cond ((http-url-search (request-url req)) =>
|
||||
(lambda (srch) `(("QUERY_STRING" . ,srch))))
|
||||
(else '()))
|
||||
|
||||
,@(cond ((get-header headers 'content-type) =>
|
||||
(lambda (ct) `(("CONTENT_TYPE" . ,ct))))
|
||||
(else '()))
|
||||
|
||||
,@(cond ((get-header headers 'content-length) =>
|
||||
(lambda (cl) ; Skip initial whitespace (& other non-digits).
|
||||
(let ((first-digit (string-index cl char-set:digit))
|
||||
(cl-len (string-length cl)))
|
||||
(if first-digit
|
||||
`(("CONTENT_LENGTH" . ,(substring cl first-digit cl-len)))
|
||||
(http-error (status-code bad-request) req
|
||||
"Illegal `Content-length:' header.")))))
|
||||
|
||||
(else '()))
|
||||
|
||||
. ,(env->alist))))))
|
||||
|
||||
|
||||
(define (take n lis)
|
||||
(if (zero? n) '()
|
||||
(cons (car lis) (take (- n 1) (cdr lis)))))
|
||||
|
||||
(define (drop n lis)
|
||||
(if (zero? n) lis
|
||||
(drop (- n 1) (cdr lis))))
|
||||
|
||||
|
||||
;;; Script's output for request REQ is available on SCRIPT-PORT.
|
||||
;;; The script isn't an "nph-" script, so we read the response, and mutate
|
||||
;;; it into a real HTTP response, which we then send back to the HTTP client.
|
||||
|
||||
(define (cgi-make-response script-port path req)
|
||||
(let* ((headers (read-rfc822-headers script-port))
|
||||
(ctype (get-header headers 'content-type))
|
||||
(loc (get-header headers 'location))
|
||||
(stat (extract-status-code-and-text (get-header headers 'status)
|
||||
req))
|
||||
(extra-headers (delete-headers (delete-headers (delete-headers headers
|
||||
'content-type)
|
||||
'location)
|
||||
'status)))
|
||||
|
||||
(http-syslog (syslog-level debug) "[cgi-server] headers: ~s~%" headers)
|
||||
(http-syslog (syslog-level debug) "[cgi-server] request-method=~a~%"
|
||||
(request-method req))
|
||||
|
||||
(if loc
|
||||
(if (uri-has-protocol? (string-trim loc))
|
||||
(make-error-response (status-code moved-perm) req
|
||||
loc loc)
|
||||
(make-redirect-response (string-trim loc)))
|
||||
;; Send the response header back to the client
|
||||
(make-response ;code message seconds mime extras body
|
||||
(number->status-code (car stat))
|
||||
(cdr stat) ; text
|
||||
(time)
|
||||
ctype
|
||||
extra-headers
|
||||
(make-writer-body
|
||||
(lambda (out options)
|
||||
(copy-inport->outport script-port out)
|
||||
(close-input-port script-port)))))))
|
||||
|
||||
(define (get-header headers tag)
|
||||
(cond
|
||||
((assq tag headers) => cdr)
|
||||
(else
|
||||
(http-error (status-code bad-gateway) #f
|
||||
(string-append "CGI script didn't generate "
|
||||
(symbol->string tag)
|
||||
" header")))))
|
||||
|
||||
(define (delete-headers headers tag)
|
||||
(alist-delete tag headers))
|
||||
|
||||
(define (cgi-make-nph-response script-port)
|
||||
(make-nph-response
|
||||
(make-writer-body (lambda (out options)
|
||||
(copy-inport->outport script-port out)))))
|
||||
|
||||
(define (uri-has-protocol? loc)
|
||||
(receive (proto path search frag)
|
||||
(parse-uri loc)
|
||||
(if proto #t #f)))
|
||||
|
||||
(define (extract-status-code-and-text status req)
|
||||
(with-fatal-error-handler*
|
||||
(lambda (c d)
|
||||
(http-error (status-code bad-gateway) req
|
||||
"CGI script generated an invalid status header."
|
||||
status c))
|
||||
(lambda ()
|
||||
(let ((status (string-trim status)))
|
||||
(cons (string->number (substring status 0 3)) ; number
|
||||
(substring/shared status 4)))))) ; text
|
|
@ -1,366 +0,0 @@
|
|||
;;; http server in the Scheme Shell -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1994 by Brian D. Carlstrom and Olin Shivers.
|
||||
;;; Copyright (c) 1996-2002 by Mike Sperber.
|
||||
;;; Copyright (c) 2000-2002 by Martin Gasbichler.
|
||||
;;; Copyright (c) 2002 by Andreas Bernauer.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
|
||||
;;; This file implements the core of an HTTP server: code to establish
|
||||
;;; net connections, read and parse requests, and handler errors.
|
||||
;;; It does not have the code to actually handle requests. That's up
|
||||
;;; to other modules, and could vary from server to server. To build
|
||||
;;; a complete server, you need to define request handlers (see below) --
|
||||
;;; they determine how requests are to be handled.
|
||||
;;;
|
||||
;;; The RFC detailing the HTTP 1.0 protocol, RFC 1945, can be found at
|
||||
;;; http://www.w3.org/Protocols/rfc1945/rfc1945
|
||||
|
||||
(define server/protocol "HTTP/1.0")
|
||||
|
||||
(define (httpd options)
|
||||
(let ((port (httpd-options-port options))
|
||||
(root-dir (httpd-options-root-directory options))
|
||||
(rate-limiter
|
||||
(cond
|
||||
((httpd-options-simultaneous-requests options)
|
||||
=> make-rate-limiter)
|
||||
(else #f))))
|
||||
(let-thread-fluid
|
||||
logging
|
||||
(make-logging)
|
||||
(lambda ()
|
||||
|
||||
(init-http-log! options)
|
||||
(with-syslog-destination
|
||||
"httpd" #f #f #f
|
||||
(lambda ()
|
||||
(with-cwd
|
||||
root-dir
|
||||
(bind-listen-accept-loop
|
||||
protocol-family/internet
|
||||
;; Why is the output socket unbuffered? So that if the client
|
||||
;; closes the connection, we won't lose when we try to close the
|
||||
;; socket by trying to flush the output buffer.
|
||||
(lambda (sock addr)
|
||||
(if rate-limiter
|
||||
(begin
|
||||
(rate-limit-block rate-limiter)
|
||||
(rate-limit-open rate-limiter)))
|
||||
|
||||
(with-fatal-error-handler
|
||||
(lambda (c decline)
|
||||
(http-syslog (syslog-level notice) "error during connection negotiation~%")
|
||||
(if rate-limiter
|
||||
(rate-limit-close rate-limiter)))
|
||||
(call-with-values
|
||||
(lambda ()
|
||||
(socket-address->internet-address (socket-remote-address sock)))
|
||||
(lambda (host-address service-port)
|
||||
(if (and rate-limiter (http-syslog?))
|
||||
(http-syslog (syslog-level info) "<~a>~a: concurrent request #~a~%"
|
||||
(pid)
|
||||
(format-internet-host-address host-address)
|
||||
(rate-limiter-current-requests rate-limiter)))
|
||||
|
||||
(set-port-buffering (socket:outport sock) bufpol/none) ; No buffering
|
||||
(fork-thread
|
||||
(lambda ()
|
||||
(set-port-buffering (socket:inport sock) bufpol/none)
|
||||
(process-toplevel-request sock host-address options)
|
||||
(if (http-syslog?)
|
||||
(http-syslog (syslog-level debug) "<~a>~a [closing]~%"
|
||||
(pid)
|
||||
(format-internet-host-address host-address)))
|
||||
(with-fatal-error-handler
|
||||
(lambda (c decline)
|
||||
(if (http-syslog?)
|
||||
(http-syslog (syslog-level notice) "<~a>~a [error closing (~a)]~%"
|
||||
(pid)
|
||||
(format-internet-host-address host-address)
|
||||
c)))
|
||||
(close-socket sock))
|
||||
(if rate-limiter
|
||||
(rate-limit-close rate-limiter))
|
||||
(if (http-syslog?)
|
||||
(http-syslog (syslog-level info) "<~a>~a [closed]~%"
|
||||
(pid)
|
||||
(format-internet-host-address host-address)))))))))
|
||||
port))))))))
|
||||
|
||||
|
||||
;;; Top-level http request processor
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Read, parse, and handle a single http request. The only thing that makes
|
||||
;;; this complicated is handling errors -- as a server, we can't just let the
|
||||
;;; standard error handlers toss us into a breakpoint. We have to catch the
|
||||
;;; error, send an error response back to the client if we can, and then keep
|
||||
;;; on trucking. This means using the S48's condition system to catch and
|
||||
;;; handle the various errors, which introduces a major point of R5RS
|
||||
;;; incompatibiliy -- R5RS has no exception system. So if you were to port
|
||||
;;; this code to some other Scheme, you'd really have to sit down and think
|
||||
;;; about this issue for a minute.
|
||||
|
||||
(define (process-toplevel-request sock host-address options)
|
||||
;; This top-level error-handler catches *all* uncaught errors and warnings.
|
||||
;; If the error condition is a reportable HTTP error, we send a response back
|
||||
;; to the client. In any event, we abort the transaction, and return from
|
||||
;; PROCESS-TOPLEVEL-REQUEST.
|
||||
;;
|
||||
;; We *oughta* map non-http-errors into replies anyway.
|
||||
(with-fatal-error-handler*
|
||||
(lambda (c decline)
|
||||
(http-syslog (syslog-level notice) "<~a>~a: error: ~s~%"
|
||||
(pid)
|
||||
(format-internet-host-address host-address)
|
||||
c)
|
||||
(with-fatal-error-handler*
|
||||
(lambda (c decline)
|
||||
(http-syslog (syslog-level notice) "<~a>~a [error shutting down: ~s]~%"
|
||||
(pid)
|
||||
(format-internet-host-address host-address)
|
||||
c))
|
||||
(lambda ()
|
||||
(shutdown-socket sock shutdown/sends+receives)
|
||||
(http-syslog (syslog-level info) "<~a>~a [shut down]~%"
|
||||
(pid)
|
||||
(format-internet-host-address host-address)))))
|
||||
(lambda ()
|
||||
(call-with-values
|
||||
(lambda ()
|
||||
(with-fatal-error-handler*
|
||||
(lambda (c decline)
|
||||
(http-syslog (syslog-level notice) "<~a>~a: error: ~s~%"
|
||||
(pid)
|
||||
(format-internet-host-address host-address)
|
||||
c)
|
||||
(cond
|
||||
((http-error? c)
|
||||
(apply (lambda (status-code req . args)
|
||||
(values req
|
||||
(apply make-error-response
|
||||
status-code req
|
||||
args)))
|
||||
(condition-stuff c)))
|
||||
((fatal-syntax-error? c)
|
||||
(values #f
|
||||
(apply make-error-response (status-code bad-request)
|
||||
#f ; No request yet.
|
||||
"Request parsing error -- report to client maintainer."
|
||||
(condition-stuff c))))
|
||||
((not (and (exception? c)
|
||||
(eq? (exception-reason c)
|
||||
(enum exception os-error))))
|
||||
|
||||
;; try to send bug report to client
|
||||
(values #f
|
||||
(apply make-error-response (status-code internal-error)
|
||||
#f ; don't know
|
||||
"Internal error occured while processing request"
|
||||
c)))
|
||||
(else
|
||||
(decline))))
|
||||
(lambda ()
|
||||
(let ((initial-req (parse-http-request sock options)))
|
||||
(let redirect-loop ((req initial-req))
|
||||
(let ((response ((httpd-options-request-handler options)
|
||||
(http-url-path (request-url req))
|
||||
req)))
|
||||
(cond
|
||||
((nph-response? response)
|
||||
(values req response))
|
||||
((eq? (response-code response) (status-code redirect))
|
||||
(redirect-loop (redirect-request req response sock options)))
|
||||
(else
|
||||
(values req response)))))))))
|
||||
(lambda (req response)
|
||||
|
||||
(send-http-response req response
|
||||
(socket:inport sock)
|
||||
(socket:outport sock)
|
||||
options)
|
||||
)))))
|
||||
|
||||
(define (redirect-request req response socket options)
|
||||
(let* ((new-location-uri (redirect-body-location (response-body response)))
|
||||
(url (with-fatal-error-handler*
|
||||
(lambda (c decline)
|
||||
(if (fatal-syntax-error? c)
|
||||
(http-error (status-code internal-error) req
|
||||
(format #f "Bad redirection out from CGI program: ~%~a"
|
||||
(cdr c)))
|
||||
(decline c)))
|
||||
(lambda ()
|
||||
;; (future) NOTE: With this, a redirection may change the
|
||||
;; protocol in use (currently, the server only supports one of
|
||||
;; it). This might be inapplicable.
|
||||
(parse-http-servers-url-fragment new-location-uri socket options)))))
|
||||
|
||||
(make-request "GET"
|
||||
new-location-uri
|
||||
url
|
||||
(request-version req) ; did not change
|
||||
'() ; no rfc822 headers
|
||||
(request-socket req))))
|
||||
|
||||
;;;; HTTP request parsing
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; This code provides procedures to read requests from an input
|
||||
;;;; port.
|
||||
|
||||
;;; Read and parse an http request from INPORT.
|
||||
;;;
|
||||
;;; Note: this parser parses the URI into an http URL record. If the URI
|
||||
;;; isn't an http URL, the parser fails. This may not be right. There's
|
||||
;;; nothing in the http protocol to prevent you from passing a non-http
|
||||
;;; URI -- what this would mean, however, is not clear. Like so much of
|
||||
;;; the Web, the protocols are redundant, underconstrained, and ill-specified.
|
||||
|
||||
(define (parse-http-request sock options)
|
||||
(let ((line (read-crlf-line (socket:inport sock))))
|
||||
;; Blat out some logging info.
|
||||
(if (http-syslog?)
|
||||
(call-with-values
|
||||
(lambda ()
|
||||
(socket-address->internet-address (socket-remote-address sock)))
|
||||
(lambda (host-address service-port)
|
||||
(http-syslog (syslog-level info) "<~a>~a: ~a~%"
|
||||
(pid)
|
||||
(format-internet-host-address host-address)
|
||||
line))))
|
||||
|
||||
(if (eof-object? line)
|
||||
(fatal-syntax-error "EOF while parsing request.")
|
||||
|
||||
(let* ((elts (string->words line)) ; Split at white-space.
|
||||
(version (case (length elts)
|
||||
((2) '(0 . 9))
|
||||
((3) (parse-http-version (caddr elts)))
|
||||
(else (fatal-syntax-error "Bad HTTP version.")))))
|
||||
|
||||
(let* ((meth (car elts))
|
||||
(uri-string (cadr elts))
|
||||
(url (parse-http-servers-url-fragment uri-string sock options))
|
||||
(headers (if (equal? version '(0 . 9))
|
||||
'()
|
||||
(read-rfc822-headers (socket:inport sock)))))
|
||||
(make-request meth uri-string url version headers sock))))))
|
||||
|
||||
;;; Parse the URL, but if it begins without the "http://host:port"
|
||||
;;; prefix, interpolate one from SOCKET. It would be sleazier but
|
||||
;;; faster if we just computed the default host and port at
|
||||
;;; server-startup time, instead of on every request.
|
||||
;;; REDIRECT-REQUEST relys on that nothing is read out from SOCKET.
|
||||
|
||||
(define (parse-http-servers-url-fragment uri-string socket options)
|
||||
(receive (scheme path search frag-id) (parse-uri uri-string)
|
||||
(if frag-id ; Can't have a #frag part.
|
||||
(fatal-syntax-error "HTTP URL contains illegal #<fragment> suffix."
|
||||
uri-string)
|
||||
|
||||
(if scheme
|
||||
(if (string-ci=? scheme "http") ; Better be an http url.
|
||||
(parse-http-url path search #f)
|
||||
(fatal-syntax-error "Non-HTTP URL" uri-string))
|
||||
|
||||
;; Interpolate the server struct from our net connection.
|
||||
(if (and (pair? path) (string=? (car path) ""))
|
||||
(let* ((addr (socket-local-address socket))
|
||||
(local-name (or (httpd-options-fqdn options)
|
||||
(socket-address->fqdn addr #t)))
|
||||
(portnum (or (httpd-options-reported-port options)
|
||||
(my-reported-port addr))))
|
||||
(make-http-url (make-server #f #f
|
||||
local-name
|
||||
(number->string portnum))
|
||||
(map unescape-uri (cdr path)) ; Skip initial /.
|
||||
search
|
||||
#f))
|
||||
|
||||
(fatal-syntax-error "Path fragment must begin with slash"
|
||||
uri-string))))))
|
||||
|
||||
|
||||
(define parse-http-version
|
||||
(let ((re (make-regexp "^HTTP/([0-9]+)\\.([0-9]+)$"))
|
||||
(lose (lambda (s) (fatal-syntax-error "Bad HTTP version" s))))
|
||||
(lambda (vstring)
|
||||
(let ((m (regexp-exec re vstring)))
|
||||
(if m
|
||||
(cons (or (string->number (match:substring m 1) 10) (lose vstring))
|
||||
(or (string->number (match:substring m 2) 10) (lose vstring)))
|
||||
(lose vstring))))))
|
||||
|
||||
|
||||
;;; Split string into a list of whitespace-separated strings.
|
||||
;;; This could have been trivially defined in scsh as (field-splitter " \t\n")
|
||||
;;; but I hand-coded it because it's short, and I didn't want invoke the
|
||||
;;; regexp machinery for something so simple.
|
||||
|
||||
(define non-whitespace (char-set-complement char-set:whitespace))
|
||||
|
||||
(define (string->words s)
|
||||
(let recur ((start 0))
|
||||
(cond ((string-index s non-whitespace start) =>
|
||||
(lambda (start)
|
||||
(cond ((string-index s char-set:whitespace start) =>
|
||||
(lambda (end)
|
||||
(cons (substring s start end)
|
||||
(recur end))))
|
||||
(else (list (substring s start (string-length s)))))))
|
||||
(else '()))))
|
||||
|
||||
(define (send-http-headers response port)
|
||||
(display server/protocol port)
|
||||
(write-char #\space port)
|
||||
(display (status-code-number (response-code response)) port)
|
||||
(write-char #\space port)
|
||||
(display (or (response-message response)
|
||||
(status-code-message (response-code response)))
|
||||
port)
|
||||
(write-crlf port)
|
||||
|
||||
(send-http-header-fields
|
||||
(list (cons 'server (string-append "Scheme Untergrund " sunet-version-identifier))
|
||||
(cons 'content-type (response-mime response))
|
||||
(cons 'date (rfc822-time->string (response-seconds response))))
|
||||
port)
|
||||
(send-http-header-fields (response-extras response) port)
|
||||
|
||||
(write-crlf port))
|
||||
|
||||
(define (send-http-response request response input-port output-port options)
|
||||
(cond
|
||||
((not request)
|
||||
;; We have a bad request error. Try to report this headerless.
|
||||
(display-http-body (response-body response) input-port output-port options)
|
||||
;; no CLF-logging
|
||||
)
|
||||
((nph-response? response)
|
||||
(display-http-body (nph-response-body response) input-port output-port options)
|
||||
(http-log request (status-code ok))); guess the status code
|
||||
(else
|
||||
(if (not (v0.9-request? request))
|
||||
(send-http-headers response output-port))
|
||||
|
||||
(if (not (string=? (request-method request) "HEAD"))
|
||||
(display-http-body (response-body response) input-port output-port options))
|
||||
|
||||
(http-log request (response-code response)))))
|
||||
|
||||
(define (send-http-header-fields headers port)
|
||||
(for-each (lambda (pair)
|
||||
(display (car pair) port)
|
||||
(write-char #\: port)
|
||||
(display (cdr pair) port)
|
||||
(write-crlf port))
|
||||
headers))
|
||||
|
||||
(define (my-reported-port addr)
|
||||
(receive (ip-addr portnum) (socket-address->internet-address addr)
|
||||
portnum))
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
;;; Error stuff for the http server. -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; An http error condition is a data structure with the following pieces:
|
||||
;;; (error-code request message . irritants)
|
||||
;;; You recognise one with HTTP-ERROR?, and retrieve the pieces with
|
||||
;;; CONDITION-STUFF.
|
||||
;;;
|
||||
|
||||
;;; HTTP error condition
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Define a sub-type of the S48 error condition, the HTTP error condition.
|
||||
;;; An HTTP error is one that corresponds to one of the HTTP error response
|
||||
;;; codes, so you can reliably use an HTTP error condition to construct an
|
||||
;;; error response message to send back to the HTTP client.
|
||||
|
||||
(define-condition-type 'http-error '(error))
|
||||
|
||||
(define http-error? (condition-predicate 'http-error))
|
||||
|
||||
(define (http-error status-code req . args)
|
||||
(apply signal 'http-error status-code req args))
|
||||
|
||||
;;; Syntax error condition
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Scheme 48 has a "syntax error" error condition, but it isn't an error
|
||||
;;; condition! It's a warning condition. I don't understand this.
|
||||
;;; We define a *fatal* syntax error here for the parsers to use.
|
||||
|
||||
(define-condition-type 'fatal-syntax-error '(error))
|
||||
|
||||
(define fatal-syntax-error? (condition-predicate 'fatal-syntax-error))
|
||||
|
||||
(define (fatal-syntax-error msg . irritants)
|
||||
(apply signal 'fatal-syntax-error msg irritants))
|
||||
|
|
@ -1,496 +0,0 @@
|
|||
;;; http server in the Scheme Shell -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1994 by Brian D. Carlstrom and Olin Shivers.
|
||||
;;; Copyright (c) 1996-2002 by Mike Sperber.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
(define server/buffer-size 8192) ; WTF
|
||||
|
||||
;;; (home-dir-handler user-public-dir) -> handler
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Return a request handler that looks things up in a specific directory
|
||||
;;; in the user's home directory. If ph = (home-dir-handler "public_html")
|
||||
;;; then ph is a request handler that serves files out of peoples' public_html
|
||||
;;; subdirectory. So
|
||||
;;; (ph '("shivers" "hk.html") req)
|
||||
;;; will serve the file
|
||||
;;; ~shivers/public_html/hk.html
|
||||
;;; The request handler treats the URL path as (<user> . <file-path>),
|
||||
;;; serving
|
||||
;;; ~<user>/<user-public-dir>/<file-path>
|
||||
|
||||
(define (home-dir-handler user-public-dir)
|
||||
(lambda (path req)
|
||||
(if (null? path)
|
||||
(make-error-response (status-code bad-request)
|
||||
req
|
||||
"Path contains no home directory.")
|
||||
(make-rooted-file-path-response (string-append (http-homedir (car path) req)
|
||||
"/"
|
||||
user-public-dir)
|
||||
(cdr path)
|
||||
file-serve-response
|
||||
req))))
|
||||
|
||||
;;; (tilde-home-dir-handler user-public-dir default-request-handler)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; If the car of the path is a tilde-marked home directory (e.g., "~kgk"),
|
||||
;;; do home-directory service as in HOME-DIR-HANDLER, otherwise punt to the
|
||||
;;; default handler.
|
||||
|
||||
(define (tilde-home-dir? path req)
|
||||
(and (not (null? path))
|
||||
(let ((head (car path))) ; home-directory path?
|
||||
(and (> (string-length head) 0)
|
||||
(char=? (string-ref head 0) #\~)))))
|
||||
|
||||
(define (tilde-home-dir-handler user-public-dir default-handler)
|
||||
(make-predicate-handler
|
||||
tilde-home-dir?
|
||||
(lambda (path req)
|
||||
(let* ((tilde-home (car path)) ; Yes.
|
||||
(slen (string-length tilde-home))
|
||||
(subdir (string-append
|
||||
(http-homedir (substring tilde-home 1 slen) req)
|
||||
"/"
|
||||
user-public-dir)))
|
||||
(make-rooted-file-path-response subdir (cdr path) file-serve-response req)))
|
||||
default-handler))
|
||||
|
||||
|
||||
;;; Make a handler that serves files relative to a particular root
|
||||
;;; in the file system. You may follow symlinks, but you can't back up
|
||||
;;; past ROOT with ..'s.
|
||||
|
||||
(define (rooted-file-handler root)
|
||||
(lambda (path req)
|
||||
(make-rooted-file-path-response root path file-serve-response req)))
|
||||
|
||||
;;; Dito, but also serve directory indices for directories without
|
||||
;;; index.html.
|
||||
|
||||
(define (rooted-file-or-directory-handler root)
|
||||
(lambda (path req)
|
||||
(make-rooted-file-path-response root path
|
||||
file-serve-and-dir-response
|
||||
req)))
|
||||
|
||||
|
||||
;;;; Support procs for the path handlers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
;;; (MAKE-ROOTED-FILE-PATH-RESPONSE root file-path req)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Do a request for a file. The file-name is determined by appending the
|
||||
;;; the FILE-PATH list the string ROOT. E.g., if
|
||||
;;; ROOT = "/usr/shivers" FILE-PATH = ("a" "b" "c" "foo.html")
|
||||
;;; then we serve file
|
||||
;;; /usr/shivers/a/b/c/foo.html
|
||||
;;; Elements of FILE-PATH are *not allowed* to contain .. elements.
|
||||
;;; (N.B.: Although the ..'s can appear in relative URI's, /foo/../ path
|
||||
;;; sequences are processed away by the browser when the URI is converted
|
||||
;;; to an absolute URI before it is sent off to the server.)
|
||||
;;; It is possible to sneak a .. past this kind of front-end resolving by
|
||||
;;; encoding it (e.g., "foo%2F%2E%2E" for "foo/.."). If the client tries
|
||||
;;; this, MAKE-ROOTED-FILE-PATH-RESPONSE will catch it, and abort the transaction.
|
||||
;;; So you cannot make the reference back up past ROOT. E.g., this is
|
||||
;;; not allowed:
|
||||
;;; FILE-PATH = ("a" "../.." "c" "foo.html")
|
||||
;;;
|
||||
;;; Only GET and HEAD ops are provided.
|
||||
;;; The URL's <search> component must be #f.
|
||||
;;; The file is served if the server has read or stat(2) access to it,
|
||||
;;; respectively. If the server is run as root, this might be a problem.
|
||||
;;;
|
||||
;;; FILE-SERVE is a procedure which gets passed the file name, the
|
||||
;;; path, and the HTTP request to serve the file propert after the
|
||||
;;; security checks. Look in ROOTED-FILE-HANDLER and
|
||||
;;; ROOTED-FILE-OR-DIRECTORY-HANDLER for examples on how to feed this.
|
||||
|
||||
(define (make-rooted-file-path-response root file-path file-serve-response req)
|
||||
(if (http-url-search (request-url req))
|
||||
(make-error-response (status-code bad-request) req
|
||||
"Indexed search not provided for this URL.")
|
||||
(cond ((dotdot-check root file-path) =>
|
||||
(lambda (fname)
|
||||
(file-serve-response fname file-path req)))
|
||||
(else
|
||||
(make-error-response (status-code bad-request) req
|
||||
"URL contains unresolvable ..'s.")))))
|
||||
|
||||
|
||||
;; Just (file-info fname) with error handling.
|
||||
|
||||
(define (stat-carefully fname req)
|
||||
(with-errno-handler
|
||||
((errno packet)
|
||||
((errno/noent)
|
||||
(http-error (status-code not-found) req))
|
||||
((errno/acces)
|
||||
(http-error (status-code forbidden) req)))
|
||||
(file-info fname #t)))
|
||||
|
||||
;;; A basic file request handler -- ship the dude the file. No fancy path
|
||||
;;; checking. That has presumably been taken care of. This handler only
|
||||
;;; takes care of GET and HEAD methods.
|
||||
|
||||
(define (file-serve-or-dir-response fname file-path req directory-serve-response)
|
||||
(if (file-name-directory? fname) ; Simple index generation.
|
||||
(directory-serve-response fname file-path req)
|
||||
|
||||
(let ((request-method (request-method req)))
|
||||
(cond
|
||||
((or (string=? request-method "GET")
|
||||
(string=? request-method "HEAD")) ; Absolutely.
|
||||
(let ((info (stat-carefully fname req)))
|
||||
(case (file-info:type info)
|
||||
|
||||
((regular fifo socket)
|
||||
(send-file-response fname info req))
|
||||
|
||||
((directory) ; Send back a redirection "foo" -> "foo/"
|
||||
(make-error-response
|
||||
(status-code moved-perm) req
|
||||
(string-append (request-uri req) "/")
|
||||
(string-append (http-url->string (request-url req))
|
||||
"/")))
|
||||
|
||||
(else (make-error-response (status-code forbidden) req)))))
|
||||
|
||||
(else
|
||||
(make-error-response (status-code method-not-allowed) req
|
||||
request-method))))))
|
||||
|
||||
(define (directory-index-serve-response fname file-path req)
|
||||
(file-serve-response (string-append fname "index.html") file-path req))
|
||||
|
||||
(define (file-serve-response fname file-path req)
|
||||
(file-serve-or-dir-response fname file-path req
|
||||
directory-index-serve-response))
|
||||
|
||||
(define (tag->alt tag)
|
||||
(case tag
|
||||
((directory) "[DIR]")
|
||||
((text) "[TXT]")
|
||||
((doc) "[DOC]")
|
||||
((image) "[IMG]")
|
||||
((movie) "[MVI]")
|
||||
((audio) "[AU ]")
|
||||
((archive) "[TAR]")
|
||||
((compressed) "[ZIP]")
|
||||
((uu) "[UU ]")
|
||||
((binhex) "[HQX]")
|
||||
((binary) "[BIN]")
|
||||
(else "[ ]")))
|
||||
|
||||
;; These icons can, for example, be found in the cern-httpd-3.0
|
||||
;; distribution at http://www.w3.org/pub/WWW/Daemon/
|
||||
|
||||
(define (tag->icon tag)
|
||||
(case tag
|
||||
((directory) "directory.xbm")
|
||||
((text) "text.xbm")
|
||||
((doc) "doc.xbm")
|
||||
((image) "image.xbm")
|
||||
((movie) "movie.xbm")
|
||||
((audio) "sound.xbm")
|
||||
((archive) "tar.xbm")
|
||||
((compressed) "compressed.xbm")
|
||||
((uu) "uu.xbm")
|
||||
((binhex) "binhex.xbm")
|
||||
((binary) "binary.xbm")
|
||||
((blank) "blank.xbm")
|
||||
((back) "back.xbm")
|
||||
(else "unknown.xbm")))
|
||||
|
||||
(define (file-extension->tag fname)
|
||||
(let ((ext (file-name-extension fname)))
|
||||
(cond
|
||||
((string-ci=? ext ".txt") 'text)
|
||||
((or (string-ci=? ext ".doc")
|
||||
(string-ci=? ext ".htm")
|
||||
(string-ci=? ext ".html")
|
||||
(string-ci=? ext ".rtf")
|
||||
(string-ci=? ext ".pdf")
|
||||
(string-ci=? ext ".dvi")
|
||||
(string-ci=? ext ".ps")
|
||||
(string-ci=? ext ".tex")) 'doc)
|
||||
((or (string-ci=? ext ".bmp")
|
||||
(string-ci=? ext ".gif")
|
||||
(string-ci=? ext ".png")
|
||||
(string-ci=? ext ".jpg")
|
||||
(string-ci=? ext ".jpeg")
|
||||
(string-ci=? ext ".tiff")
|
||||
(string-ci=? ext ".tif")) 'image)
|
||||
((or (string-ci=? ext ".mpeg")
|
||||
(string-ci=? ext ".mpg")) 'movie)
|
||||
((or (string-ci=? ext ".au")
|
||||
(string-ci=? ext ".snd")
|
||||
(string-ci=? ext ".mp3")
|
||||
(string-ci=? ext ".wav")) 'audio)
|
||||
((or (string-ci=? ext ".tar")
|
||||
(string-ci=? ext ".zip")
|
||||
(string-ci=? ext ".zoo")) 'archive)
|
||||
((or (string-ci=? ext ".gz")
|
||||
(string-ci=? ext ".Z")
|
||||
(string-ci=? ext ".z")) 'compressed)
|
||||
((string-ci=? ext ".uu") 'uu)
|
||||
((string-ci=? ext ".hqx") 'binhex)
|
||||
(else 'binary))))
|
||||
|
||||
(define (file-tag fname type)
|
||||
(case type
|
||||
((regular fifo socket) (file-extension->tag fname))
|
||||
((directory) 'directory)
|
||||
(else 'unknown)))
|
||||
|
||||
(define (time->directory-index-date-string time)
|
||||
(format-date "~d-~b-~y ~H:~M:~S GMT" (date time 0)))
|
||||
|
||||
(define (read-max-lines fname max)
|
||||
(call-with-input-file
|
||||
fname
|
||||
(lambda (port)
|
||||
(let loop ((r "") (i max))
|
||||
(if (zero? i)
|
||||
r
|
||||
(let ((line (read-line port)))
|
||||
(if (eof-object? line)
|
||||
r
|
||||
(loop (string-append r " " line) (- i 1)))))))))
|
||||
|
||||
(define (string-cut s n)
|
||||
(if (>= (string-length s) n)
|
||||
(substring s 0 n)
|
||||
s))
|
||||
|
||||
(define html-file-header
|
||||
(let ((title-tag-regexp (make-regexp "<[Tt][Ii][Tt][Ll][Ee]>"))
|
||||
(title-close-tag-regexp (make-regexp "</[Tt][Ii][Tt][Ll][Ee]>")))
|
||||
(lambda (fname n)
|
||||
(let ((stuff (read-max-lines fname 10)))
|
||||
(cond
|
||||
((regexp-exec title-tag-regexp stuff)
|
||||
=> (lambda (open-match)
|
||||
(cond
|
||||
((regexp-exec title-close-tag-regexp stuff
|
||||
(match:end open-match 0))
|
||||
=> (lambda (close-match)
|
||||
(string-cut (substring stuff
|
||||
(match:end open-match 0)
|
||||
(match:start close-match 0))
|
||||
n)))
|
||||
(else (string-cut (substring stuff
|
||||
(match:end open-match 0)
|
||||
(string-length stuff))
|
||||
n)))))
|
||||
(else ""))))))
|
||||
|
||||
(define (file-documentation fname n)
|
||||
(cond
|
||||
((file-extension->content-type fname)
|
||||
=> (lambda (content-type)
|
||||
(if (and (string=? content-type "text/html" )
|
||||
(file-readable? fname))
|
||||
(html-file-header fname n)
|
||||
"")))
|
||||
(else "")))
|
||||
|
||||
(define (directory-index req dir icon-name port)
|
||||
|
||||
(define (pad-file-name file)
|
||||
(write-string (make-string (max (- 21 (string-length file))
|
||||
1)
|
||||
#\space)
|
||||
port))
|
||||
|
||||
(define (emit-file-name file)
|
||||
(let ((l (string-length file)))
|
||||
(if (<= l 20)
|
||||
(emit-text file port)
|
||||
(emit-text (substring file 0 20) port))))
|
||||
|
||||
(define (index-entry file)
|
||||
(let* ((fname (directory-as-file-name (string-append dir file)))
|
||||
(info (file-info fname #t))
|
||||
(type (file-info:type info))
|
||||
(size (file-info:size info))
|
||||
(tag (file-tag file type)))
|
||||
(emit-tag port 'img
|
||||
(cons 'src (icon-name tag))
|
||||
(cons 'alt (tag->alt tag)))
|
||||
(with-tag port a ((href file))
|
||||
(emit-file-name file))
|
||||
(pad-file-name file)
|
||||
(emit-text (time->directory-index-date-string (file-info:mtime info)) port)
|
||||
(if size
|
||||
(let* ((size-string
|
||||
(string-append (number->string (quotient size 1024))
|
||||
"K"))
|
||||
(size-string
|
||||
(if (<= (string-length size-string) 7)
|
||||
size-string
|
||||
(string-append (number->string (quotient size (* 1024 1024)))
|
||||
"M")))
|
||||
(size-string
|
||||
(if (<= (string-length size-string) 8)
|
||||
(string-append
|
||||
(make-string (- 8 (string-length size-string)) #\space)
|
||||
size-string)
|
||||
size-string)))
|
||||
(write-string size-string port))
|
||||
(write-string (make-string 8 #\space) port))
|
||||
(write-char #\space port)
|
||||
(emit-text (file-documentation fname 24) port)
|
||||
(write-crlf port)))
|
||||
|
||||
(let ((files (directory-files dir)))
|
||||
(for-each index-entry files)
|
||||
(length files)))
|
||||
|
||||
(define (directory-serve-response fname file-path req)
|
||||
(let ((request-method (request-method req)))
|
||||
(cond
|
||||
((or (string=? request-method "GET")
|
||||
(string=? request-method "HEAD"))
|
||||
|
||||
(if (not (eq? 'directory
|
||||
(file-info:type (file-info fname #t))))
|
||||
(make-error-response (status-code forbidden) req)
|
||||
(make-response
|
||||
(status-code ok)
|
||||
#f
|
||||
(time)
|
||||
"text/html"
|
||||
'()
|
||||
(make-writer-body
|
||||
(lambda (port options)
|
||||
(let* ((icon-option (httpd-options-icon-name options))
|
||||
(icon-name
|
||||
(cond
|
||||
((procedure? icon-option) icon-option)
|
||||
((string? icon-option)
|
||||
(lambda (tag)
|
||||
(string-append icon-option (tag->icon tag))))
|
||||
(else tag->icon))))
|
||||
(with-tag port html ()
|
||||
(let ((title (string-append "Index of /"
|
||||
(string-join file-path "/"))))
|
||||
(with-tag port head ()
|
||||
(emit-title port title))
|
||||
(with-tag port body ()
|
||||
(emit-header port 1 title)
|
||||
(with-tag port pre ()
|
||||
(emit-tag port 'img
|
||||
(cons 'src (icon-name 'blank))
|
||||
(cons 'alt " "))
|
||||
(write-string "Name " port)
|
||||
(write-string "Last modified " port)
|
||||
(write-string "Size " port)
|
||||
(write-string "Description" port)
|
||||
(emit-tag port 'hr)
|
||||
(emit-tag port 'img
|
||||
(cons 'src (icon-name 'back))
|
||||
(cons 'alt "[UP ]"))
|
||||
(if (not (null? file-path))
|
||||
(begin
|
||||
(with-tag port a ((href ".."))
|
||||
(write-string "Parent directory" port))
|
||||
(write-crlf port)))
|
||||
(let ((n-files (directory-index req fname icon-name port)))
|
||||
(emit-tag port 'hr)
|
||||
(format port "~d files" n-files))))))))))))
|
||||
(else
|
||||
(make-error-response (status-code method-not-allowed) req
|
||||
request-method)))))
|
||||
|
||||
(define (index-or-directory-serve-response fname file-path req)
|
||||
(let ((index-fname (string-append fname "index.html")))
|
||||
(if (file-readable? index-fname)
|
||||
(file-serve-response index-fname file-path req)
|
||||
(directory-serve-response fname file-path req))))
|
||||
|
||||
(define (file-serve-and-dir-response fname file-path req)
|
||||
(file-serve-or-dir-response fname file-path req
|
||||
index-or-directory-serve-response))
|
||||
|
||||
;;; Look up user's home directory, generating an HTTP error response if you lose.
|
||||
|
||||
(define (http-homedir username req)
|
||||
(with-fatal-error-handler (lambda (c decline)
|
||||
(apply http-error (status-code bad-request) req
|
||||
"Couldn't find user's home directory."
|
||||
(condition-stuff c)))
|
||||
|
||||
(home-dir username)))
|
||||
|
||||
|
||||
(define (send-file-response filename info req)
|
||||
(if (file-not-readable? filename) ; #### double stats are no good
|
||||
(make-error-response (status-code not-found) req)
|
||||
(receive (stripped-filename content-encoding)
|
||||
(file-extension->content-encoding filename)
|
||||
(make-response (status-code ok)
|
||||
#f
|
||||
(time)
|
||||
(file-extension->content-type stripped-filename)
|
||||
(append (if content-encoding
|
||||
(cons 'content-encoding content-encoding)
|
||||
'())
|
||||
(list
|
||||
(cons 'last-modified
|
||||
(time->http-date-string
|
||||
(file-info:mtime info)))
|
||||
(cons 'content-length (file-info:size info))))
|
||||
(make-writer-body
|
||||
(lambda (port options)
|
||||
(call-with-input-file filename
|
||||
(lambda (in)
|
||||
(copy-inport->outport in port)))))))))
|
||||
|
||||
|
||||
(define (file-extension->content-type fname)
|
||||
(let ((ext (file-name-extension fname)))
|
||||
(cond
|
||||
((string-ci=? ext ".htm") "text/html")
|
||||
((string-ci=? ext ".html") "text/html")
|
||||
((string-ci=? ext ".txt") "text/plain")
|
||||
((string-ci=? ext ".doc") "application/msword")
|
||||
((string-ci=? ext ".gif") "image/gif")
|
||||
((string-ci=? ext ".png") "image/png")
|
||||
((string-ci=? ext ".bmp") "image/bmp")
|
||||
((or (string-ci=? ext ".jpg")
|
||||
(string-ci=? ext ".jpeg")) "image/jpeg")
|
||||
((or (string-ci=? ext ".tiff")
|
||||
(string-ci=? ext ".tif")) "image/tif")
|
||||
((string-ci=? ext ".rtf") "text/rtf")
|
||||
((or (string-ci=? ext ".mpeg")
|
||||
(string-ci=? ext ".mpg")) "video/mpeg")
|
||||
((or (string-ci=? ext ".au")
|
||||
(string-ci=? ext ".snd")) "audio/basic")
|
||||
((string-ci=? ext ".wav") "audio/x-wav")
|
||||
((string-ci=? ext ".dvi") "application/x-dvi")
|
||||
((or (string-ci=? ext ".tex")
|
||||
(string-ci=? ext ".latex")) "application/latex")
|
||||
((string-ci=? ext ".zip") "application/zip")
|
||||
((string-ci=? ext ".tar") "application/tar")
|
||||
((string-ci=? ext ".hqx") "application/mac-binhex40")
|
||||
((string-ci=? ext ".ps") "application/postscript")
|
||||
((string-ci=? ext ".pdf") "application/pdf")
|
||||
(else "application/octet-stream"))))
|
||||
|
||||
(define (file-extension->content-encoding fname)
|
||||
(cond
|
||||
((let ((ext (file-name-extension fname)))
|
||||
(cond
|
||||
((string-ci=? ext ".Z") "x-compress")
|
||||
((string-ci=? ext ".gz") "x-gzip")
|
||||
(else #f)))
|
||||
=> (lambda (encoding)
|
||||
(values (file-name-sans-extension fname) encoding)))
|
||||
(else (values fname #f))))
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
;;; http server in the Scheme Shell -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; Copyright (c) 1996-2002 by Mike Sperber.
|
||||
;;; Copyright (c) 2002 by Andreas Bernauer.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; Path handlers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Path handlers are the guys that actually perform the requested operation
|
||||
;;; on the URL. The handler interface is
|
||||
;;; (handler path-list request)
|
||||
;;; The path-list is a URL path list that is a suffix of REQUEST's url's
|
||||
;;; path-list. Path handlers can decide how to handle an operation by
|
||||
;;; recursively keying off of the elements in path-list.
|
||||
;;;
|
||||
;;; The object-oriented view:
|
||||
;;; One way to look at this is to think of the request's METHOD as a
|
||||
;;; generic operation on the URL. Recursive request handlers do method
|
||||
;;; lookup to determine how to implement a given operation on a particular
|
||||
;;; path.
|
||||
;;;
|
||||
;;; The REQUEST is a request record, as defined in httpd-core.scm, containing
|
||||
;;; the details of the client request.
|
||||
|
||||
;; general request handler combinator:
|
||||
;; predicate: path x request --> boolean
|
||||
;; if #t, handler is called
|
||||
;; if #f, default-handler is called
|
||||
(define (make-predicate-handler predicate handler default-handler)
|
||||
(lambda (path req)
|
||||
(if (predicate path req)
|
||||
(handler path req)
|
||||
(default-handler path req))))
|
||||
|
||||
;; same as MAKE-PREDICATE-HANDLER except that the predicate is only
|
||||
;; called with the path:
|
||||
;; predicate: path --> boolean
|
||||
(define (make-path-predicate-handler predicate handler default-handler)
|
||||
(make-predicate-handler
|
||||
(lambda (path req) (predicate path)) handler default-handler))
|
||||
|
||||
;; selects handler according to host-field of http-request
|
||||
(define (make-host-name-handler hostname handler default-handler)
|
||||
(make-predicate-handler
|
||||
(lambda (path req)
|
||||
;; we expect only one host-header-field
|
||||
(string=? hostname (string-trim (get-header (request-headers req) 'host))))
|
||||
handler default-handler))
|
||||
|
||||
(define (get-header headers tag)
|
||||
(cond
|
||||
((assq tag headers) => cdr)
|
||||
(else
|
||||
(http-error (status-code bad-request) #f
|
||||
(string-append "Request did not contain "
|
||||
(symbol->string tag)
|
||||
" header")))))
|
||||
|
||||
;; selects handler according to path-prefix
|
||||
;; if path-prefix matches, handler is called without the path-prefix
|
||||
(define (make-path-prefix-handler path-prefix handler default-handler)
|
||||
(lambda (path req)
|
||||
(if (and (pair? path) (string=? path-prefix (car path)))
|
||||
(handler (cdr path) req)
|
||||
(default-handler path req))))
|
||||
|
||||
;;; (alist-path-dispatcher handler-alist default-handler) -> handler
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; This function creates a table-driven request handler that dispatches off
|
||||
;;; of the car of the request path. The handler uses the car to index into
|
||||
;;; a request handler alist. If it finds a hit, it recurses using the table's
|
||||
;;; request handler. If no hits, it handles the path with a default handler.
|
||||
;;; An alist handler is passed the tail of the original path; the
|
||||
;;; default handler gets the entire original path.
|
||||
;;;
|
||||
;;; This procedure is how you say: "If the first element of the URL's
|
||||
;;; path is 'foo', do X; if it's 'bar', do Y; otherwise, do Z."
|
||||
|
||||
(define (alist-path-dispatcher handler-alist default-handler)
|
||||
(fold-right
|
||||
(lambda (handler-pair default-handler)
|
||||
(make-path-prefix-handler
|
||||
(car handler-pair)
|
||||
(cdr handler-pair)
|
||||
default-handler))
|
||||
default-handler
|
||||
handler-alist))
|
||||
|
||||
;;; The null request handler -- handles nothing, sends back an error response.
|
||||
;;; Can be useful as the default in table-driven request handlers.
|
||||
|
||||
(define (null-request-handler path req)
|
||||
(make-error-response (status-code not-found) req))
|
|
@ -1,59 +0,0 @@
|
|||
;;; Scheme Untergrund Web Server -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; This file contains a few example top-level request handlers and
|
||||
;;; other useful fragments.
|
||||
|
||||
;;; - /h/<user>/<file-path> => serve <file-path> from ~user/public_html.
|
||||
;;; - /seval You may POST Scheme code to this URL, and receive the output.
|
||||
;;; - Otherwise, serve files from the standard HTTP demon repository.
|
||||
|
||||
(define rh1
|
||||
(alist-path-dispatcher
|
||||
`(("h" . ,(home-dir-handler "public_html"))
|
||||
("seval" . ,seval-handler)
|
||||
("cgi-bin" . ,(cgi-handler "/usr/local/etc/httpd/cgi-bin")))
|
||||
(rooted-file-handler "/usr/local/etc/httpd/htdocs")))
|
||||
|
||||
|
||||
;;; Do a rough approximation of NCSA httpd server semantics:
|
||||
;;; - /~shivers/... serves file ~shivers/public_html/...
|
||||
;;; - /cgi-bin/<prog> passes control to script
|
||||
;;; /usr/local/etc/httpd/cgi-bin/<prog>
|
||||
;;; - Otherwise, just serve files out of the standard directory.
|
||||
|
||||
(define rh2
|
||||
(alist-path-dispatcher
|
||||
`(("cgi-bin" . ,(cgi-handler "/usr/local/etc/httpd/cgi-bin")))
|
||||
(tilde-home-dir-handler "public_html"
|
||||
(rooted-file-handler "/usr/local/etc/httpd/htdocs"))))
|
||||
|
||||
;;; Greatest hits request handler.
|
||||
|
||||
(define rh3
|
||||
(alist-path-dispatcher
|
||||
`(("h" . ,(home-dir-handler "public_html"))
|
||||
("seval" . ,seval-handler)
|
||||
("cgi-bin" . ,(cgi-handler "/usr/local/etc/httpd/cgi-bin")))
|
||||
(tilde-home-dir-handler "public_html"
|
||||
(rooted-file-handler "/usr/local/etc/httpd/htdocs"))))
|
||||
|
||||
|
||||
|
||||
;;; Crank up a server on port 8001, first resetting our identity to
|
||||
;;; user "nobody". Initialise the request-invariant part of the CGI
|
||||
;;; env before starting.
|
||||
|
||||
(define (httpd1)
|
||||
(set-gid (->uid "nobody"))
|
||||
(set-uid (->gid "nobody"))
|
||||
(initialise-request-invariant-cgi-env)
|
||||
(httpd (make-httpd-options with-request-handler rh3
|
||||
with-port 8001
|
||||
with-root-directory "/usr/local/etc/httpd/htdocs")))
|
||||
|
|
@ -1,655 +0,0 @@
|
|||
;;; GNU info -> HTML gateway for the SU web server. -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1996 by Mike Sperber.
|
||||
;;; based on code with the same purpose by Gaebe Engelhart
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
|
||||
;;; (info-handler parse-info reference find-icon address) -> handler
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; This function creates a request handler that converts GNU info pages
|
||||
;;; on-the-fly. It is highly parameterizable to accomodate a wide
|
||||
;;; range of environments. The parameters specify how to find the
|
||||
;;; source code for the info pages, and how to generate certain
|
||||
;;; elements of the generated HTML output.
|
||||
;;;
|
||||
;;; PARSE-INFO specifies how to parse the URLs that end up in the
|
||||
;;; handler.
|
||||
;;; It can be:
|
||||
;;;
|
||||
;;; * a procedure which is called with the URL as its parameters.
|
||||
;;; It is expected to return with two values, FIND-ENTRY and
|
||||
;;; NODE-NAME. FIND-ENTRY, in turn, can be either a procedure
|
||||
;;; which gets passed the file name of an info node and is
|
||||
;;; supposed to return with an absolute name of same. If it is a
|
||||
;;; list, that list is taken as a list of directories in which to
|
||||
;;; search for the info files. NODE-NAME is supposed to be the
|
||||
;;; name of an info node of the form (<file>)<node>, extracted
|
||||
;;; from the URL.
|
||||
;;;
|
||||
;;; * a list, in which case that is taken as a list of
|
||||
;;; directories in which to search for the info files. The node
|
||||
;;; name of a node is extracted from the URL by just taking the
|
||||
;;; search component of the URL.
|
||||
;;;
|
||||
;;; * #f in which case the info path is taken from the environment
|
||||
;;; variable INFOPATH, and the node name extraction works as
|
||||
;;; above.
|
||||
;;;
|
||||
;;; REFERENCE specifies how to generate cross-references to other info
|
||||
;;; nodes. It can be:
|
||||
;;;
|
||||
;;; * a procedure which gets called with the URL of the info page
|
||||
;;; which contains the reference, and the node name of the node
|
||||
;;; to be referenced. The procedure is expected to return the
|
||||
;;; text for a link.
|
||||
;;;
|
||||
;;; * a string, in which case that is to be a prefix to which the
|
||||
;;; node name is simply appended to yield the new link.
|
||||
;;;
|
||||
;;; * #f in which case all references have the form
|
||||
;;; info?<node-name>.
|
||||
;;;
|
||||
;;; FIND-ICON specifies to to find the various icons used to decorate
|
||||
;;; info pages. It can be:
|
||||
;;;
|
||||
;;; * a procedure which gets passed one of the tags in
|
||||
;;; DEFAULT-ICON-ALIST and is supposed to return a link for the
|
||||
;;; appropriate icon (or #f if no icon is to be used)
|
||||
;;;
|
||||
;;; * a string which is taken as a prefix to which one of the
|
||||
;;; appropriate icon name from DEFAULT-ICON-ALIST is appended.
|
||||
;;; (Note that these icon names were stolen from the
|
||||
;;; cern-httpd-3.0 distribution at
|
||||
;;; http://www.w3.org/pub/WWW/Daemon/.)
|
||||
;;;
|
||||
;;; * a list which is taken as an alist of the same format as
|
||||
;;; DEFAULT-ICON-ALIST.
|
||||
;;;
|
||||
;;; * #f in which case no icons are used.
|
||||
;;;
|
||||
;;; ADDRESS a string to be appended at the bottom of all info pages
|
||||
;;;
|
||||
;;; To install a vanilla info handler for a prefix "info?" that looks
|
||||
;;; in the environment variable INFOPATH, just use something like
|
||||
;;; (info-handler #f #f #f "Generated by info-gateway")
|
||||
|
||||
;;; TODO: write a CGI version of this
|
||||
|
||||
(define-condition-type 'info-gateway-error '(error))
|
||||
|
||||
(define info-gateway-error? (condition-predicate 'info-gateway-error))
|
||||
|
||||
(define (info-gateway-error msg . irritants)
|
||||
(apply signal 'info-gateway-error msg irritants))
|
||||
|
||||
(define default-icon-alist
|
||||
'((info . "infodoc.gif")
|
||||
(up . "up.gif")
|
||||
(next . "next.gif")
|
||||
(previous . "prev.gif")
|
||||
(menu . "menu.gif")))
|
||||
|
||||
(define (info-handler parse-info reference find-icon address)
|
||||
(let ((icon-name
|
||||
(cond
|
||||
((procedure? find-icon) find-icon)
|
||||
((string? find-icon)
|
||||
(let ((alist
|
||||
(map (lambda (entry)
|
||||
(cons (car entry)
|
||||
(string-append find-icon (cdr entry))))
|
||||
default-icon-alist)))
|
||||
(lambda (tag)
|
||||
(cond ((assq tag alist) => cdr)
|
||||
(else #f)))))
|
||||
((list? find-icon)
|
||||
(lambda (tag)
|
||||
(cond ((assq tag find-icon) => cdr)
|
||||
(else #f))))
|
||||
(else (lambda (tag) #f))))
|
||||
(parse-info-url
|
||||
(cond
|
||||
((procedure? parse-info) parse-info)
|
||||
((list? parse-info) ; it's an info path
|
||||
(lambda (url)
|
||||
(values parse-info
|
||||
(unescape-uri (http-url-search url)))))
|
||||
(else
|
||||
(let ((info-path ((infix-splitter ":") (getenv "INFOPATH"))))
|
||||
(lambda (url)
|
||||
(values info-path
|
||||
(unescape-uri (http-url-search url))))))))
|
||||
(make-reference
|
||||
(cond
|
||||
((procedure? reference) reference)
|
||||
((string? reference)
|
||||
(lambda (url node-name)
|
||||
(string-append reference node-name)))
|
||||
(else
|
||||
(lambda (url node-name)
|
||||
(string-append "info?" node-name))))))
|
||||
|
||||
(lambda (path req)
|
||||
(let ((request-method (request-method req)))
|
||||
(cond
|
||||
((string=? request-method "GET")
|
||||
(with-fatal-error-handler
|
||||
(lambda (c decline)
|
||||
(cond
|
||||
((info-gateway-error? c)
|
||||
(apply http-error (status-code bad-gateway) req
|
||||
(condition-stuff c)))
|
||||
((http-error? c)
|
||||
(apply http-error (car (condition-stuff c)) req
|
||||
(cddr (condition-stuff c))))
|
||||
(else
|
||||
(decline))))
|
||||
|
||||
(make-response
|
||||
(status-code ok)
|
||||
#f
|
||||
(time)
|
||||
"text/html"
|
||||
'()
|
||||
(make-writer-body
|
||||
(lambda (out options)
|
||||
|
||||
(receive (find-entry node-name) (parse-info-url (request-url req))
|
||||
(display-node node-name
|
||||
(file-finder find-entry)
|
||||
(referencer make-reference (request-url req) out)
|
||||
icon-name
|
||||
out))
|
||||
(with-tag out address ()
|
||||
(write-string address out)))))))
|
||||
|
||||
(else
|
||||
(make-error-response (status-code method-not-allowed) req
|
||||
request-method)))))))
|
||||
|
||||
(define split-header-line
|
||||
(let ((split (infix-splitter (make-regexp "(, *)|( +)|( *\t *)")))
|
||||
(split-field (infix-splitter (make-regexp ": *"))))
|
||||
(lambda (l)
|
||||
(let ((fields (map split-field (split l))))
|
||||
|
||||
(define (search-field regexp)
|
||||
(cond
|
||||
((find (lambda (field)
|
||||
(string-match regexp (car field)))
|
||||
fields)
|
||||
=> cadr)
|
||||
(else #f)))
|
||||
|
||||
(values (search-field "[F|f]ile")
|
||||
(search-field "[N|n]ode")
|
||||
(search-field "[U|u]p")
|
||||
(search-field "[P|p]rev(ious)?")
|
||||
(search-field "[N|n]ext"))))))
|
||||
|
||||
(define (replace-if-empty-string s v)
|
||||
(if (zero? (string-length s))
|
||||
v
|
||||
s))
|
||||
|
||||
(define (string-newline->space s)
|
||||
(string-map (lambda (c)
|
||||
(if (char=? c #\newline)
|
||||
#\space
|
||||
c))
|
||||
s))
|
||||
|
||||
(define (parse-node-name node-name)
|
||||
(cond
|
||||
((string-match "^\\((.*)\\)(.*)$" (string-newline->space node-name))
|
||||
=> (lambda (match)
|
||||
(values
|
||||
(replace-if-empty-string (match:substring match 1) #f)
|
||||
(replace-if-empty-string (match:substring match 2) "Top"))))
|
||||
(else (values #f (string-newline->space node-name)))))
|
||||
|
||||
|
||||
(define (unparse-node-name file node)
|
||||
(let* ((ext (file-name-extension file))
|
||||
(file (if (string=? ext ".info")
|
||||
(file-name-sans-extension file)
|
||||
file)))
|
||||
(receive (file node) (if (and (string=? "dir" file)
|
||||
(not (string=? "" node))
|
||||
(not (string=? "Top" node)))
|
||||
(values node "Top")
|
||||
(values file node))
|
||||
(string-append "(" file ")" node))))
|
||||
|
||||
(define (display-icon file alt out)
|
||||
(emit-tag out 'img
|
||||
(cons 'src file)
|
||||
(cons 'alt alt)
|
||||
(cons 'align "bottom")))
|
||||
|
||||
(define (referencer make-reference old-entry out)
|
||||
(lambda (file node-name label . maybe-icon)
|
||||
(receive (node-file node) (parse-node-name node-name)
|
||||
(let ((file (or node-file file)))
|
||||
(with-tag out a ((href (make-reference
|
||||
old-entry
|
||||
(escape-uri (unparse-node-name file node)))))
|
||||
(if (and (not (null? maybe-icon))
|
||||
(car maybe-icon))
|
||||
(display-icon (car maybe-icon) (cadr maybe-icon) out))
|
||||
(emit-text label out))))))
|
||||
|
||||
(define node-prologue (ascii->char 31))
|
||||
(define node-epilogue-regexp
|
||||
(make-regexp
|
||||
(string-append (regexp-quote (string node-prologue))
|
||||
"|"
|
||||
(regexp-quote (string (ascii->char 12))))))
|
||||
|
||||
(define (string-starts-with-char? s c)
|
||||
(and (not (zero? (string-length s)))
|
||||
(char=? c (string-ref s 0))))
|
||||
|
||||
(define (node-prologue? s)
|
||||
(string-starts-with-char? s node-prologue))
|
||||
(define (node-epilogue? s)
|
||||
(regexp-exec node-epilogue-regexp s))
|
||||
|
||||
;; Document title
|
||||
|
||||
(define (display-title file node up previous next
|
||||
display-reference icon-name out)
|
||||
|
||||
(define (maybe-display-header header icon alt)
|
||||
(if header
|
||||
(begin
|
||||
(newline out)
|
||||
(with-tag out b ()
|
||||
(display-reference file header header icon alt)))))
|
||||
|
||||
(emit-title out (string-append "Info Node: "
|
||||
(unparse-node-name file node)))
|
||||
(with-tag out h1 ()
|
||||
(emit-tag out 'img
|
||||
(cons 'src (icon-name 'info))
|
||||
(cons 'alt "Info Node")
|
||||
(cons 'align 'bottom))
|
||||
(write-string (unparse-node-name file node) out))
|
||||
(emit-tag out 'hr)
|
||||
(maybe-display-header next (icon-name 'next) "[Next]")
|
||||
(maybe-display-header previous (icon-name 'previous) "[Previous]")
|
||||
(maybe-display-header up (icon-name 'up) "[Up]")
|
||||
|
||||
(if (or next previous up)
|
||||
(emit-tag out 'hr)))
|
||||
|
||||
;; Text
|
||||
|
||||
|
||||
;; Dealing with cross references
|
||||
;; info sucks
|
||||
|
||||
(define xref-marker-regexp (make-regexp "\\*[Nn]ote([ \n]|$)"))
|
||||
(define xref-regexp (make-regexp "\\*[Nn]ote *([^:]*): *([^\t\n,.;:?!]*)"))
|
||||
|
||||
(define max-xref-lines 3)
|
||||
|
||||
(define complete-line
|
||||
(let ((split-xref-markers (field-splitter xref-marker-regexp))
|
||||
(split-xrefs (field-splitter xref-regexp))
|
||||
(cr (string #\newline)))
|
||||
(lambda (line port)
|
||||
(let loop ((line line) (count max-xref-lines))
|
||||
(let ((xref-markers (split-xref-markers line))
|
||||
(xrefs (split-xrefs line)))
|
||||
(if (= (length xref-markers) (length xrefs))
|
||||
line
|
||||
(if (zero? count)
|
||||
(info-gateway-error "invalid cross reference")
|
||||
(let ((new-line (read-line port)))
|
||||
(if (eof-object? new-line)
|
||||
(info-gateway-error
|
||||
"unexpected end of info file inside cross reference"))
|
||||
(loop (string-append line cr new-line) (- count 1))))))))))
|
||||
|
||||
|
||||
(define (display-xref xref file display-reference out)
|
||||
(let* ((match (regexp-exec xref-regexp xref))
|
||||
(note (match:substring match 1))
|
||||
(node-name (match:substring match 2))
|
||||
(node-name (if (string=? "" node-name) note node-name))
|
||||
(node-name (substring node-name
|
||||
(string-skip node-name char-set:whitespace)
|
||||
(string-length node-name))))
|
||||
(emit-text "See " out)
|
||||
(display-reference file node-name note)))
|
||||
|
||||
(define display-text
|
||||
(let ((split-xrefs (infix-splitter xref-regexp #f 'split)))
|
||||
(lambda (line port file display-reference out)
|
||||
(let* ((line (complete-line line port))
|
||||
(components (split-xrefs line)))
|
||||
;; in components, every 2nd element is a cross reference
|
||||
;; also, it always has odd length or length zero
|
||||
(if (not (null? components))
|
||||
(let loop ((components components))
|
||||
(emit-text (car components) out)
|
||||
(if (not (null? (cdr components)))
|
||||
(begin
|
||||
(display-xref (cadr components) file display-reference out)
|
||||
(loop (cddr components))))))
|
||||
(newline out)))))
|
||||
|
||||
;; Menus
|
||||
|
||||
(define menu-regexp (make-regexp "^\\* +Menu:"))
|
||||
(define menu-item-regexp (make-regexp "^\\* +"))
|
||||
|
||||
(define (char-splitter c)
|
||||
(lambda (s)
|
||||
(cond ((string-index s c)
|
||||
=> (lambda (i)
|
||||
(values (substring s 0 i)
|
||||
(substring s (+ 1 i) (string-length s)))))
|
||||
(else (values s "")))))
|
||||
|
||||
(define colon-split (char-splitter #\:))
|
||||
|
||||
(define (display-menu-item-header line port file display-reference icon-name out)
|
||||
(let ((menu-line-split (infix-splitter menu-item-regexp)))
|
||||
(receive (note rest) (colon-split (cadr (menu-line-split line)))
|
||||
(receive (node-name text)
|
||||
(cond
|
||||
((string-match ": *(.*)" rest)
|
||||
=> (lambda (match)
|
||||
(values note (match:substring match 1))))
|
||||
((string-match "^ *([^.]*)\\.? *(.*)" rest)
|
||||
=> (lambda (match)
|
||||
(values (match:substring match 1)
|
||||
(match:substring match 2))))
|
||||
(else
|
||||
(info-gateway-error "invalid menu item")))
|
||||
(emit-tag out 'dt)
|
||||
(display-reference file node-name note (icon-name 'menu) "*")
|
||||
(newline out)
|
||||
(if (and (not (string=? "" text))
|
||||
(not (string=? "." text)))
|
||||
(begin
|
||||
(emit-tag out 'dd)
|
||||
(display-text text port file display-reference out)))))))
|
||||
|
||||
(define (display-menu line port file display-reference icon-name out)
|
||||
(emit-close-tag out 'pre)
|
||||
|
||||
(with-tag out dl ()
|
||||
(let loop ((line line))
|
||||
(if (eof-object? line)
|
||||
(info-gateway-error "unexpected end of info file"))
|
||||
|
||||
(display-menu-item-header line port file display-reference icon-name out)
|
||||
|
||||
(let finish-item-loop ()
|
||||
(if (eof-object? line)
|
||||
(info-gateway-error "unexpected end of info file"))
|
||||
|
||||
(let ((line (read-line port)))
|
||||
(cond
|
||||
((or (eof-object? line)
|
||||
(node-epilogue? line)
|
||||
(string=? "" line))
|
||||
(emit-tag out 'pre)
|
||||
(dispatch-line line port file display-reference icon-name out))
|
||||
((regexp-exec menu-item-regexp line)
|
||||
(loop line))
|
||||
(else
|
||||
(display-text line port file display-reference out)
|
||||
(finish-item-loop))))))))
|
||||
|
||||
;; Central dispatch
|
||||
|
||||
(define (dispatch-line line port file display-reference icon-name out)
|
||||
(cond
|
||||
((or (eof-object? line) (node-epilogue? line)) #f)
|
||||
((string=? "" line) (emit-p out) #t)
|
||||
((regexp-exec menu-regexp line) #t) ;; this should probably be expanded
|
||||
((regexp-exec menu-item-regexp line)
|
||||
(display-menu line port file display-reference icon-name out))
|
||||
(else
|
||||
(display-text line port file display-reference out) #t)))
|
||||
|
||||
(define (display-body port file display-reference icon-name out)
|
||||
(let loop ()
|
||||
(let ((line (read-line port)))
|
||||
(if (dispatch-line line port file display-reference icon-name out)
|
||||
(loop)))))
|
||||
|
||||
(define (display-node node-name find-file display-reference icon-name out)
|
||||
(receive (file node) (parse-node-name node-name)
|
||||
(receive (port file-header node-header up-header prev-header next-header)
|
||||
(find-node file node find-file)
|
||||
|
||||
(with-tag out html ()
|
||||
(with-tag out head ()
|
||||
(display-title file node-header up-header
|
||||
prev-header next-header
|
||||
display-reference icon-name
|
||||
out))
|
||||
(with-tag out body ()
|
||||
(with-tag out pre ()
|
||||
(display-body port file display-reference icon-name out))))
|
||||
|
||||
(close-input-port port))))
|
||||
|
||||
;; Finding nodes
|
||||
|
||||
(define (ensure-node-prologue port msg)
|
||||
(let ((line (read-line port)))
|
||||
(if (or (eof-object? line)
|
||||
(not (node-prologue line)))
|
||||
(info-gateway-error "invalid info file" msg))))
|
||||
|
||||
(define (ensure-regexp-line port regexp msg)
|
||||
(let ((line (read-line port)))
|
||||
(if (or (eof-object? line)
|
||||
(not (string-match regexp line)))
|
||||
(info-gateway-error "invalid info file" msg))))
|
||||
|
||||
(define (ensure-tag-table-node port)
|
||||
(ensure-regexp-line port "^Tag Table:" "no tag table"))
|
||||
(define (ensure-indirect-tag-table-header port)
|
||||
(ensure-regexp-line port "^\\(Indirect\\)" "no indirect tag"))
|
||||
|
||||
(define split-indirection (infix-splitter (make-regexp " *: *")))
|
||||
(define (parse-indirection line)
|
||||
(let ((l (split-indirection line)))
|
||||
(if (null? (cdr l))
|
||||
(info-gateway-error "invalid indirection entry in info file")
|
||||
(let ((file (car l))
|
||||
(seek-pos (string->number (cadr l))))
|
||||
(if (not seek-pos)
|
||||
(info-gateway-error "invalid indirection entry in info file"))
|
||||
(cons file seek-pos)))))
|
||||
|
||||
(define (read-indirection-table port)
|
||||
(let loop ((table '()))
|
||||
(let ((line (read-line port)))
|
||||
(if (eof-object? line)
|
||||
(info-gateway-error "invalid info file"))
|
||||
(if (node-epilogue? line)
|
||||
(reverse table)
|
||||
(loop (cons (parse-indirection line) table))))))
|
||||
|
||||
(define tag-seek-separator (ascii->char 127))
|
||||
|
||||
(define parse-tag
|
||||
(let ((split (infix-splitter (make-regexp ", *")))
|
||||
(split-field (infix-splitter ": "))
|
||||
(split-node-info
|
||||
(infix-splitter (string tag-seek-separator))))
|
||||
|
||||
(define (barf)
|
||||
(info-gateway-error "invalid tag entry in info file"))
|
||||
|
||||
(lambda (line)
|
||||
(let* ((fields (map split-field (split line)))
|
||||
(file (cond
|
||||
((assoc "File" fields)
|
||||
=> (lambda (p)
|
||||
(if (null? (cdr p)) (barf))
|
||||
(cadr p)))
|
||||
(else #f))))
|
||||
(cond
|
||||
((assoc "Node" fields)
|
||||
=> (lambda (p)
|
||||
(if (null? (cdr p)) (barf))
|
||||
(let ((s (split-node-info (cadr p))))
|
||||
(if (null? (cdr p)) (barf))
|
||||
(let* ((node (car s))
|
||||
(seek (string->number (cadr s))))
|
||||
(if (not seek) (barf))
|
||||
(values node file seek)))))
|
||||
(else (barf)))))))
|
||||
|
||||
(define (find-tag node port)
|
||||
(let loop ()
|
||||
(let ((line (read-line port)))
|
||||
(if (eof-object? line)
|
||||
(info-gateway-error "invalid info file"))
|
||||
(if (regexp-exec node-epilogue-regexp line)
|
||||
(http-error (status-code not-found) #f "node not found"))
|
||||
(receive (entry-node file seek) (parse-tag line)
|
||||
(if (string=? node entry-node)
|
||||
(cons file seek)
|
||||
(loop))))))
|
||||
|
||||
(define (find-indirection-entry seek-pos indirection-table)
|
||||
(let loop ((table indirection-table))
|
||||
(if (null? table)
|
||||
(http-error (status-code not-found) #f "node not found"))
|
||||
(let* ((entry (car table))
|
||||
(pos (cdr entry)))
|
||||
(if (and (>= seek-pos pos)
|
||||
(or (null? (cdr table))
|
||||
(let* ((next-entry (cadr table))
|
||||
(next-pos (cdr next-entry)))
|
||||
(< seek-pos next-pos))))
|
||||
entry
|
||||
(loop (cdr table))))))
|
||||
|
||||
(define (file-finder with)
|
||||
(cond ((procedure? with) with)
|
||||
((list? with)
|
||||
(lambda (file)
|
||||
(find-info-file file with)))))
|
||||
|
||||
(define (find-node-port-with-tag-entry node tag-entry ? find-file)
|
||||
(let* ((port (if (input-port? ?) ? #f))
|
||||
(indirection-table (if port #f ?))
|
||||
(seek-pos (cdr tag-entry))
|
||||
(indirection-entry
|
||||
(and indirection-table
|
||||
(find-indirection-entry seek-pos indirection-table)))
|
||||
(seek-pos (if indirection-entry
|
||||
(- seek-pos (cdr indirection-entry))
|
||||
seek-pos))
|
||||
;; that's what the documentation says ...
|
||||
(seek-pos (if (>= seek-pos 1000)
|
||||
(- seek-pos 1000)
|
||||
0))
|
||||
(file (or (car tag-entry)
|
||||
(and indirection-entry
|
||||
(car indirection-entry))))
|
||||
(port (if file
|
||||
(begin
|
||||
(if port (close-input-port port))
|
||||
(open-input-file (find-file file)))
|
||||
port)))
|
||||
(seek port seek-pos)
|
||||
port))
|
||||
|
||||
(define (find-node file node find-file)
|
||||
(if (not file)
|
||||
(http-error (status-code not-found) #f
|
||||
"no file in info node specification"))
|
||||
|
||||
(let* ((fname (find-file file))
|
||||
(port (open-input-file fname)))
|
||||
(let loop ((port port))
|
||||
(let ((line (read-line port)))
|
||||
(if (eof-object? line)
|
||||
(http-error (status-code not-found) #f "info node not found"))
|
||||
(if (node-prologue? line)
|
||||
(let ((header (read-line port)))
|
||||
(if (eof-object? header)
|
||||
(info-gateway-error "invalid info file"))
|
||||
(cond
|
||||
|
||||
((string-match "^Indirect:" header)
|
||||
(let ((indirection-table
|
||||
(read-indirection-table port)))
|
||||
(ensure-tag-table-node port)
|
||||
(ensure-indirect-tag-table-header port)
|
||||
(let ((tag-entry (find-tag node port)))
|
||||
(close-input-port port)
|
||||
(loop (find-node-port-with-tag-entry
|
||||
node tag-entry indirection-table find-file)))))
|
||||
|
||||
((string-match "^Tag Table:" header)
|
||||
(let ((tag-entry (find-tag node port)))
|
||||
(loop (find-node-port-with-tag-entry
|
||||
node tag-entry port find-file))))
|
||||
|
||||
((string-match "^File:" header)
|
||||
(receive
|
||||
(file-header node-header up-header prev-header next-header)
|
||||
(split-header-line header)
|
||||
(if (string=? node-header node)
|
||||
(values port
|
||||
file-header node-header
|
||||
up-header prev-header next-header)
|
||||
(loop port))))
|
||||
(else (loop port))))
|
||||
(loop port))))))
|
||||
|
||||
;; Finding files
|
||||
|
||||
(define (info-file-alternative-names file)
|
||||
(receive (dir base ext) (parse-file-name file)
|
||||
(let* ((base
|
||||
(cond ((string-match "(.*)-info$" base)
|
||||
=> (lambda (match)
|
||||
(match:substring match 1)))
|
||||
(else base)))
|
||||
(base-ci (string-map char-downcase base))
|
||||
(alts-1 (if (string=? base base-ci)
|
||||
(list base)
|
||||
(list base base-ci)))
|
||||
(alts (append alts-1
|
||||
(map (lambda (base)
|
||||
(string-append base ".info"))
|
||||
alts-1)))
|
||||
(alts (append alts
|
||||
(map (lambda (base)
|
||||
(string-append base "-info"))
|
||||
alts-1)))
|
||||
(alts (map (lambda (f) (string-append dir f)) alts))
|
||||
(alts (cons file alts)))
|
||||
alts)))
|
||||
|
||||
(define (find-info-file file info-path)
|
||||
(let ((alts (info-file-alternative-names file)))
|
||||
(let path-loop ((path info-path))
|
||||
(if (null? path)
|
||||
(http-error (status-code not-found) #f "info file not found"))
|
||||
(let alt-loop ((alts alts))
|
||||
(if (null? alts)
|
||||
(path-loop (cdr path))
|
||||
(let ((try (string-append (file-name-as-directory (car path))
|
||||
(car alts))))
|
||||
(if (file-exists? try)
|
||||
try
|
||||
(alt-loop (cdr alts)))))))))
|
|
@ -1,196 +0,0 @@
|
|||
;;; logging.scm
|
||||
;;; logging functionality for web server
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 2002 by Martin Gasbichler.
|
||||
;;; Copyright (c) 2002 by Andreas Bernauer.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
(define do-nothing-proc (lambda a #f))
|
||||
|
||||
(define-record-type logging :logging
|
||||
(really-make-logging log-port log-proc
|
||||
syslog? syslog-proc
|
||||
dns-lookup?)
|
||||
logging?
|
||||
;; port to perform CLF-logging
|
||||
(log-port logging-log-port set-logging-log-port!)
|
||||
;; proc to run for CLF-logging (req status-code)
|
||||
(log-proc logging-log-proc set-logging-log-proc!)
|
||||
;; do syslogging?
|
||||
(syslog? logging-syslog? set-logging-syslog?!)
|
||||
;; proc to run for syslog (level fmt . args)
|
||||
(syslog-proc logging-syslog-proc set-logging-syslog-proc!)
|
||||
;; perform dns lookups?
|
||||
(dns-lookup? logging-dns-lookup? set-logging-dns-lookup?!))
|
||||
|
||||
(define (make-logging)
|
||||
(really-make-logging #f
|
||||
do-nothing-proc
|
||||
#f
|
||||
do-nothing-proc
|
||||
#f))
|
||||
|
||||
(define logging (make-preserved-thread-fluid #f))
|
||||
|
||||
(define (make-fluid-selector selector)
|
||||
(lambda () (selector (thread-fluid logging))))
|
||||
|
||||
(define (make-fluid-setter setter)
|
||||
(lambda (value)
|
||||
(setter (thread-fluid logging) value)))
|
||||
|
||||
(define logging-http-log-proc (make-fluid-selector logging-log-proc))
|
||||
(define logging-http-syslog-proc (make-fluid-selector logging-syslog-proc))
|
||||
(define logging-http-syslog? (make-fluid-selector logging-syslog?))
|
||||
(define logging-http-log-port (make-fluid-selector logging-log-port))
|
||||
(define logging-dns-lookup? (make-fluid-selector logging-dns-lookup?))
|
||||
|
||||
(define set-logging-http-log-proc (make-fluid-setter set-logging-log-proc!))
|
||||
(define set-logging-http-syslog-proc (make-fluid-setter set-logging-syslog-proc!))
|
||||
(define set-logging-http-syslog? (make-fluid-setter set-logging-syslog?!))
|
||||
(define set-logging-http-log-port (make-fluid-setter set-logging-log-port!))
|
||||
(define set-logging-dns-lookup? (make-fluid-setter set-logging-dns-lookup?!))
|
||||
|
||||
(define http-syslog
|
||||
(lambda a
|
||||
(apply (logging-http-syslog-proc) a)))
|
||||
|
||||
(define http-log
|
||||
(lambda a
|
||||
(apply (logging-http-log-proc) a)))
|
||||
|
||||
(define (http-syslog?)
|
||||
(logging-http-syslog?))
|
||||
|
||||
(define (init-http-log! options)
|
||||
;; syslog has to be initialized before CLF-logging
|
||||
;; because the latter may generate syslog-messages
|
||||
(init-http-syslog! (httpd-options-syslog? options))
|
||||
(init-http-port-log! (httpd-options-logfile options))
|
||||
(if (httpd-options-resolve-ips? options)
|
||||
(set-logging-dns-lookup? #t)
|
||||
(set-logging-dns-lookup? #f)))
|
||||
|
||||
(define (init-http-syslog! syslog?)
|
||||
(if syslog?
|
||||
(let ((http-syslog-lock (make-lock)))
|
||||
(set-logging-http-syslog? #t)
|
||||
(set-logging-http-syslog-proc
|
||||
(lambda (level fmt . args)
|
||||
(with-lock http-syslog-lock
|
||||
(lambda ()
|
||||
(syslog level
|
||||
(apply format #f fmt args)))))))
|
||||
(begin
|
||||
(set-logging-http-syslog? #f)
|
||||
(set-logging-http-syslog-proc do-nothing-proc))))
|
||||
|
||||
(define (init-http-port-log! logfile)
|
||||
(let ((logport
|
||||
(cond
|
||||
((string? logfile) ; try to open logfile for appending (output)
|
||||
(open-logfile logfile))
|
||||
((output-port? logfile) ; we were given an output port, so let's use it
|
||||
logfile)
|
||||
((eq? logfile #f) ; no logging demanded
|
||||
#f)
|
||||
; unexpected value of logfile;
|
||||
(else
|
||||
(http-syslog
|
||||
(syslog-level warning)
|
||||
"[httpd] Warning: Logfile was not specified correctly (given: ~S).~% No CLF logging."
|
||||
logfile)
|
||||
(make-null-output-port)))))
|
||||
|
||||
(if logfile ; if logging was specified, set up the logger
|
||||
(let ((http-log-lock (make-lock)))
|
||||
(set-logging-http-log-port logport)
|
||||
(if (string? logfile)
|
||||
(spawn (make-logfile-rotator logfile http-log-lock)))
|
||||
(set-logging-http-log-proc (make-http-log-proc http-log-lock))))))
|
||||
|
||||
(define (make-http-log-proc http-log-lock)
|
||||
(lambda (req status-code)
|
||||
(if req
|
||||
(with-lock http-log-lock
|
||||
(lambda ()
|
||||
(display (make-CLF
|
||||
(receive (host-address _)
|
||||
(socket-address->internet-address
|
||||
(socket-remote-address (request-socket req)))
|
||||
(format-internet-host-address host-address))
|
||||
(request-method req) ; request method
|
||||
(uri-path->uri
|
||||
(http-url-path (request-url req))) ; requested file
|
||||
(version->string (request-version req)) ; protocol version
|
||||
(status-code-number status-code)
|
||||
23 ; filesize (unknown)
|
||||
(get-header (request-headers req) 'referer)
|
||||
(get-header (request-headers req) 'user-agent))
|
||||
(logging-http-log-port))
|
||||
(force-output (logging-http-log-port)))))))
|
||||
|
||||
(define (get-header headers tag)
|
||||
(cond
|
||||
((assq tag headers) => cdr)
|
||||
(else "unknown")))
|
||||
|
||||
;; does the logfile rotation on signal USR1
|
||||
(define (make-logfile-rotator logfile http-log-lock)
|
||||
(set-interrupt-handler interrupt/usr1 #f)
|
||||
(lambda ()
|
||||
(on-interrupt
|
||||
interrupt/usr1
|
||||
(lambda ()
|
||||
(with-lock http-log-lock
|
||||
(lambda ()
|
||||
(close-output-port (logging-http-log-port))
|
||||
(set-logging-http-log-port (open-logfile logfile))))))))
|
||||
|
||||
(define (open-logfile logfile)
|
||||
(with-errno-handler*
|
||||
(lambda (errno packet)
|
||||
(http-syslog (syslog-level warning)
|
||||
"[httpd] Warning: An error occured while opening ~S for writing (~A).~%Send signal USR1 when the problem is fixed.~%"
|
||||
logfile
|
||||
(car packet))
|
||||
(make-null-output-port))
|
||||
(lambda ()
|
||||
(open-output-file logfile
|
||||
(bitwise-ior open/create open/append)))))
|
||||
|
||||
; returns a string for a CLF entry (Common Log Format)
|
||||
; note: till now, we do not log the user's time zone code
|
||||
(define (make-CLF remote-ip request-type requested-file protocol http-code filesize referer user-agent)
|
||||
(format #f "~A - - ~A ~S ~A ~A ~S ~S~%"
|
||||
(or (maybe-dns-lookup remote-ip) "-")
|
||||
(format-date "[~d/~b/~Y:~H:~M:~S +0000]" (date)) ; +0000 as we don't know
|
||||
(string-join (list request-type
|
||||
(string-append "/" requested-file)
|
||||
protocol))
|
||||
; Unfortunately, we first split the request line into
|
||||
; method/request-type etc. and put it together here.
|
||||
; Files conform to CLF are expected to print the original line.
|
||||
(or http-code "-")
|
||||
(or filesize "-")
|
||||
(if (string? referer) (string-trim referer) "")
|
||||
(if (string? user-agent)
|
||||
(string-trim user-agent char-set:whitespace)
|
||||
"")))
|
||||
|
||||
|
||||
(define (maybe-dns-lookup remote-ip)
|
||||
(if (logging-dns-lookup?)
|
||||
(or (with-fatal-error-handler*
|
||||
(lambda (condition decline)
|
||||
(http-syslog (syslog-level debug)
|
||||
"An error occured while resolving IP ~A: ~A"
|
||||
remote-ip condition)
|
||||
remote-ip)
|
||||
(lambda ()
|
||||
(dns-lookup-ip remote-ip)))
|
||||
remote-ip)
|
||||
remote-ip))
|
|
@ -1,138 +0,0 @@
|
|||
;;; http server in the Scheme Shell -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 2002 by Mike Sperber.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; This package manages options to the http server as an abstract
|
||||
;;; data type.
|
||||
|
||||
(define-record-type httpd-options :httpd-options
|
||||
(really-make-httpd-options port
|
||||
root-directory
|
||||
icon-name
|
||||
fqdn
|
||||
reported-port
|
||||
request-handler
|
||||
server-admin
|
||||
simultaneous-requests
|
||||
logfile
|
||||
syslog?
|
||||
resolve-ips?)
|
||||
httpd-options?
|
||||
(port httpd-options-port
|
||||
set-httpd-options-port!)
|
||||
(root-directory httpd-options-root-directory
|
||||
set-httpd-options-root-directory!)
|
||||
;; ICON-NAME specifies how to generate the links to
|
||||
;; various decorative icons for the listings. It can either be a
|
||||
;; procedure which gets passed one of the icon tags in TAG->ICON and
|
||||
;; is expected to return a link pointing to the icon. If it is a
|
||||
;; string, that is taken as prefix to which the names from TAG->ICON
|
||||
;; are appended.
|
||||
(icon-name httpd-options-icon-name
|
||||
set-httpd-options-icon-name!)
|
||||
(fqdn httpd-options-fqdn
|
||||
set-httpd-options-fqdn!)
|
||||
(reported-port httpd-options-reported-port
|
||||
set-httpd-options-reported-port!)
|
||||
(request-handler httpd-options-request-handler
|
||||
set-httpd-options-request-handler!)
|
||||
(server-admin httpd-options-server-admin
|
||||
set-httpd-options-server-admin!)
|
||||
(simultaneous-requests httpd-options-simultaneous-requests
|
||||
set-httpd-options-simultaneous-requests!)
|
||||
(logfile httpd-options-logfile set-httpd-options-logfile!)
|
||||
(syslog? httpd-options-syslog? set-httpd-options-syslog?!)
|
||||
(resolve-ips? httpd-options-resolve-ips? set-httpd-options-resolve-ips?!))
|
||||
|
||||
; default httpd-options generation
|
||||
(define (make-default-httpd-options)
|
||||
(really-make-httpd-options 80 ; port
|
||||
"/" ; root-directory
|
||||
#f ; icon-name
|
||||
#f ; fqdn
|
||||
#f ; reported-port
|
||||
#f ; request-handler
|
||||
#f ; server-admin
|
||||
#f ; simultaneous-requests
|
||||
#f
|
||||
; string: filename of logfile (directory must exist)
|
||||
; output-port: log to this port (e.g. (current-error-port))
|
||||
; #f: no logging
|
||||
#t ; Do syslogging?
|
||||
#t)) ; Write host names instead of IPs in logfiles?
|
||||
|
||||
; creates a copy of a given httpd-option
|
||||
|
||||
(define (copy-httpd-options options)
|
||||
(let ((new-options (make-default-httpd-options)))
|
||||
(set-httpd-options-port! new-options
|
||||
(httpd-options-port options))
|
||||
(set-httpd-options-root-directory! new-options
|
||||
(httpd-options-root-directory options))
|
||||
(set-httpd-options-icon-name! new-options
|
||||
(httpd-options-icon-name options))
|
||||
(set-httpd-options-fqdn! new-options
|
||||
(httpd-options-fqdn options))
|
||||
(set-httpd-options-reported-port! new-options
|
||||
(httpd-options-reported-port options))
|
||||
(set-httpd-options-request-handler! new-options
|
||||
(httpd-options-request-handler options))
|
||||
(set-httpd-options-server-admin! new-options
|
||||
(httpd-options-server-admin options))
|
||||
(set-httpd-options-simultaneous-requests!
|
||||
new-options
|
||||
(httpd-options-simultaneous-requests options))
|
||||
(set-httpd-options-logfile! new-options (httpd-options-logfile options))
|
||||
(set-httpd-options-syslog?! new-options (httpd-options-syslog? options))
|
||||
(set-httpd-options-resolve-ips?! new-options (httpd-options-resolve-ips? options))
|
||||
new-options))
|
||||
|
||||
; (make-httpd-options-transformer set-option!) -> lambda (new-value [httpd-option])
|
||||
; creates a transformer for httpd-options
|
||||
; the returned procedure is called with the new value for the option
|
||||
; and optionally with the httpd-option to change
|
||||
(define (make-httpd-options-transformer set-option!)
|
||||
(lambda (new-value . stuff)
|
||||
(let ((new-options (if (not (null? stuff))
|
||||
(copy-httpd-options (car stuff))
|
||||
(make-default-httpd-options))))
|
||||
(set-option! new-options new-value)
|
||||
new-options)))
|
||||
|
||||
; several transformers for port, root-directory, etc.
|
||||
(define with-port
|
||||
(make-httpd-options-transformer set-httpd-options-port!))
|
||||
(define with-root-directory
|
||||
(make-httpd-options-transformer set-httpd-options-root-directory!))
|
||||
(define with-icon-name
|
||||
(make-httpd-options-transformer set-httpd-options-icon-name!))
|
||||
(define with-fqdn
|
||||
(make-httpd-options-transformer set-httpd-options-fqdn!))
|
||||
(define with-reported-port
|
||||
(make-httpd-options-transformer set-httpd-options-reported-port!))
|
||||
(define with-request-handler
|
||||
(make-httpd-options-transformer set-httpd-options-request-handler!))
|
||||
(define with-server-admin
|
||||
(make-httpd-options-transformer set-httpd-options-server-admin!))
|
||||
(define with-simultaneous-requests
|
||||
(make-httpd-options-transformer set-httpd-options-simultaneous-requests!))
|
||||
(define with-logfile
|
||||
(make-httpd-options-transformer set-httpd-options-logfile!))
|
||||
(define with-syslog?
|
||||
(make-httpd-options-transformer set-httpd-options-syslog?!))
|
||||
(define with-resolve-ips?
|
||||
(make-httpd-options-transformer set-httpd-options-resolve-ips?!))
|
||||
|
||||
(define (make-httpd-options . stuff)
|
||||
(let loop ((options (make-default-httpd-options))
|
||||
(stuff stuff))
|
||||
(if (null? stuff)
|
||||
options
|
||||
(let* ((transformer (car stuff))
|
||||
(value (cadr stuff)))
|
||||
(loop (transformer value options)
|
||||
(cddr stuff))))))
|
|
@ -1,47 +0,0 @@
|
|||
;;;; HTTP request
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
;;; Copyright (c) 1996 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;;; This code defines the http REQUEST data structure
|
||||
|
||||
(define-record-type request :request
|
||||
(make-request method uri url version headers socket)
|
||||
request?
|
||||
(method request-method) ; A string such as "GET", "PUT", etc.
|
||||
(uri request-uri) ; The escaped URI string as read from request line.
|
||||
(url request-url) ; An http URL record (see url.scm).
|
||||
(version request-version) ; A (major . minor) integer pair.
|
||||
(headers request-headers) ; An rfc822 header alist (see rfc822.scm).
|
||||
(socket request-socket)) ; The socket connected to the client.
|
||||
|
||||
(define-record-discloser :request
|
||||
(lambda (req)
|
||||
(list 'request
|
||||
(request-method req)
|
||||
(request-uri req)
|
||||
(request-url req)
|
||||
(request-version req)
|
||||
(request-headers req)
|
||||
(request-socket req))))
|
||||
;;; A http protocol version is an integer pair: (major . minor).
|
||||
|
||||
(define (version< v1 v2)
|
||||
(or (< (car v1) (car v2))
|
||||
(and (= (car v1) (car v2))
|
||||
(< (cdr v1) (cdr v2)))))
|
||||
|
||||
(define (version<= v1 v2) (not (version< v2 v1)))
|
||||
|
||||
(define (v0.9-request? req)
|
||||
(version<= (request-version req) '(0 . 9)))
|
||||
|
||||
|
||||
(define (version->string v)
|
||||
(string-append "HTTP/"
|
||||
(number->string (car v))
|
||||
"."
|
||||
(number->string (cdr v))))
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1994 by Brian D. Carlstrom and Olin Shivers.
|
||||
;;; Copyright (c) 2002 by Mike Sperber.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
(define-record-type http-response :http-response
|
||||
(make-response code message seconds mime extras body)
|
||||
response?
|
||||
(code response-code)
|
||||
(message response-message)
|
||||
(seconds response-seconds)
|
||||
(mime response-mime)
|
||||
(extras response-extras)
|
||||
(body response-body))
|
||||
|
||||
;; This is mainly for nph-... CGI scripts.
|
||||
;; This means that the body will output the entire MIME message, not
|
||||
;; just the part after the headers.
|
||||
|
||||
(define-record-type http-nph-response :http-nph-response
|
||||
(make-nph-response body)
|
||||
nph-response?
|
||||
(body nph-response-body))
|
||||
|
||||
(define-record-type http-writer-body :http-writer-body
|
||||
(make-writer-body proc)
|
||||
writer-body?
|
||||
(proc writer-body-proc))
|
||||
|
||||
(define-record-type http-reader-writer-body :http-reader-writer-body
|
||||
(make-reader-writer-body proc)
|
||||
reader-writer-body?
|
||||
(proc reader-writer-body-proc))
|
||||
|
||||
(define-record-type http-redirect-body :http-redirect-body
|
||||
(make-redirect-body location)
|
||||
redirect-body?
|
||||
(location redirect-body-location))
|
||||
|
||||
(define (display-http-body body iport oport options)
|
||||
(cond
|
||||
((writer-body? body)
|
||||
((writer-body-proc body) oport options))
|
||||
((reader-writer-body? body)
|
||||
((reader-writer-body-proc body) iport oport options))))
|
||||
|
||||
(define-finite-type status-code :http-status-code
|
||||
(number message)
|
||||
status-code?
|
||||
status-codes
|
||||
status-code-name
|
||||
status-code-index
|
||||
(number status-code-number)
|
||||
(message status-code-message)
|
||||
(
|
||||
(ok 200 "OK")
|
||||
(created 201 "Created")
|
||||
(accepted 202 "Accepted")
|
||||
(prov-info 203 "Provisional Information")
|
||||
(no-content 204 "No Content")
|
||||
|
||||
(mult-choice 300 "Multiple Choices")
|
||||
(moved-perm 301 "Moved Permanently")
|
||||
(moved-temp 302 "Moved Temporarily")
|
||||
(method 303 "Method (obsolete)")
|
||||
(not-mod 304 "Not Modified")
|
||||
|
||||
(bad-request 400 "Bad Request")
|
||||
(unauthorized 401 "Unauthorized")
|
||||
(payment-req 402 "Payment Required")
|
||||
(forbidden 403 "Forbidden")
|
||||
(not-found 404 "Not Found")
|
||||
(method-not-allowed 405 "Method Not Allowed")
|
||||
(none-acceptable 406 "None Acceptable")
|
||||
(proxy-auth-required 407 "Proxy Authentication Required")
|
||||
(timeout 408 "Request Timeout")
|
||||
(conflict 409 "Conflict")
|
||||
(gone 410 "Gone")
|
||||
|
||||
(internal-error 500 "Internal Server Error")
|
||||
(not-implemented 501 "Not Implemented")
|
||||
(bad-gateway 502 "Bad Gateway")
|
||||
(service-unavailable 503 "Service Unavailable")
|
||||
(gateway-timeout 504 "Gateway Timeout")
|
||||
|
||||
(redirect -301 "Internal redirect")))
|
||||
|
||||
(define (name->status-code name)
|
||||
(if (not (symbol? name))
|
||||
(call-error name->status-code (list name))
|
||||
(let loop ((i 0))
|
||||
(cond ((= i (vector-length status-codes))
|
||||
#f)
|
||||
((eq? name
|
||||
(status-code-name (vector-ref status-codes i)))
|
||||
(vector-ref status-codes i))
|
||||
(else
|
||||
(loop (+ i 1)))))))
|
||||
|
||||
(define (number->status-code number)
|
||||
(if (not (number? number))
|
||||
(call-error number->status-code (list number))
|
||||
(let loop ((i 0))
|
||||
(cond ((= i (vector-length status-codes))
|
||||
#f)
|
||||
((= number
|
||||
(status-code-number (vector-ref status-codes i)))
|
||||
(vector-ref status-codes i))
|
||||
(else
|
||||
(loop (+ i 1)))))))
|
||||
|
||||
;;; (make-error-response status-code req [message . extras])
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; As a special case, request REQ is allowed to be #f, meaning we haven't
|
||||
;;; even had a chance to parse and construct the request. This is only used
|
||||
;;; for 400 BAD-REQUEST error report.
|
||||
|
||||
(define (make-error-response code req . args)
|
||||
(let* ((message (and (pair? args) (car args)))
|
||||
(extras (if (pair? args) (cdr args) '()))
|
||||
|
||||
(generic-title (lambda (port)
|
||||
(title-html port
|
||||
(status-code-message code))))
|
||||
(send-message (lambda (port)
|
||||
(if message
|
||||
(format port "<BR>~%Further Information: ~A<BR>~%" message))))
|
||||
(close-html (lambda (port)
|
||||
(for-each (lambda (x) (format port "<BR>~s~%" x)) extras)
|
||||
(write-string "</BODY>\n" port)))
|
||||
|
||||
(create-response
|
||||
(lambda (headers writer-proc)
|
||||
(make-response code
|
||||
#f
|
||||
(time)
|
||||
"text/html"
|
||||
headers
|
||||
(make-writer-body writer-proc)))))
|
||||
|
||||
(cond
|
||||
;; This error response requires two args: message is the new URI: field,
|
||||
;; and the first EXTRA is the older Location: field.
|
||||
((or (eq? code (status-code moved-temp))
|
||||
(eq? code (status-code moved-perm)))
|
||||
(create-response
|
||||
(list (cons 'uri message)
|
||||
(cons 'location (car extras)))
|
||||
(lambda (port options)
|
||||
(title-html port "Document moved")
|
||||
(format port
|
||||
"This document has ~A moved to a <A HREF=\"~A\">new location</A>.~%"
|
||||
(if (eq? code (status-code moved-temp))
|
||||
"temporarily"
|
||||
"permanently")
|
||||
message)
|
||||
(close-html port))))
|
||||
|
||||
((eq? code (status-code bad-request))
|
||||
(create-response
|
||||
'()
|
||||
(lambda (port options)
|
||||
(generic-title port)
|
||||
(write-string "<P>Client sent a query that this server could not understand.\n"
|
||||
port)
|
||||
(send-message port)
|
||||
(close-html port))))
|
||||
|
||||
((eq? code (status-code unauthorized))
|
||||
(create-response
|
||||
(list (cons 'WWW-Authenticate message)) ; Vas is das?
|
||||
;; Vas das is? See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.47
|
||||
;; message should be a challenge(?)
|
||||
(lambda (port options)
|
||||
(title-html port "Authorization Required")
|
||||
(write-string "<P>Browser not authentication-capable or\n" port)
|
||||
(write-string "authentication failed.\n" port)
|
||||
(send-message port)
|
||||
(close-html port))))
|
||||
|
||||
((eq? code (status-code forbidden))
|
||||
(create-response
|
||||
'()
|
||||
(lambda (port options)
|
||||
(title-html port "Request not allowed.")
|
||||
(format port
|
||||
"Your client does not have permission to perform a ~A~%"
|
||||
(request-method req))
|
||||
(format port "operation on url ~a.~%" (request-uri req))
|
||||
(send-message port)
|
||||
(close-html port))))
|
||||
|
||||
((eq? code (status-code not-found))
|
||||
(create-response
|
||||
'()
|
||||
(lambda (port options)
|
||||
(title-html port "URL not found")
|
||||
(write-string
|
||||
"<P>The requested URL was not found on this server.\n"
|
||||
port)
|
||||
(send-message port)
|
||||
(close-html port))))
|
||||
|
||||
((eq? code (status-code internal-error))
|
||||
(create-response
|
||||
'()
|
||||
(lambda (port options)
|
||||
(generic-title port)
|
||||
(format port "The server encountered an internal error or
|
||||
misconfiguration and was unable to complete your request.
|
||||
<P>
|
||||
Please inform the server administrator, ~A, of the circumstances leading to
|
||||
the error, and time it occured.~%"
|
||||
(or (httpd-options-server-admin options)
|
||||
"[no mail address available]"))
|
||||
(send-message port)
|
||||
(close-html port))))
|
||||
|
||||
((eq? code (status-code not-implemented))
|
||||
(create-response
|
||||
'()
|
||||
(lambda (port options)
|
||||
(generic-title port)
|
||||
(format port "This server does not currently implement
|
||||
the requested method (~A).~%"
|
||||
(request-method req))
|
||||
(send-message port)
|
||||
(close-html port))))
|
||||
|
||||
((eq? code (status-code bad-gateway))
|
||||
(create-response
|
||||
'()
|
||||
(lambda (port options)
|
||||
(generic-title port)
|
||||
(format port "An error occured while waiting for the
|
||||
response of a gateway.~%")
|
||||
(send-message port)
|
||||
(close-html port)))))))
|
||||
|
||||
(define (title-html out message)
|
||||
(format out "<HEAD>~%<TITLE>~%~A~%</TITLE>~%</HEAD>~%~%" message)
|
||||
(format out "<BODY>~%<H1>~A</H1>~%" message))
|
||||
|
||||
;; Creates a redirect response. The server will serve the new file indicated by
|
||||
;; NEW-LOCATION. NEW-LOCATION must be uri-encoded and begin with a slash.
|
||||
(define (make-redirect-response new-location)
|
||||
(make-response
|
||||
(status-code redirect)
|
||||
#f
|
||||
(time)
|
||||
""
|
||||
'()
|
||||
(make-redirect-body new-location)))
|
|
@ -1,190 +0,0 @@
|
|||
;;; man page -> HTML gateway for the SU web server. -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1996-2003 by Mike Sperber.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; This uses RosettaMan
|
||||
;;; (based at ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z)
|
||||
|
||||
(define (rman-handler man-binary
|
||||
nroff-binary
|
||||
rman-binary
|
||||
gzcat-binary
|
||||
finder referencer address . maybe-man)
|
||||
(let ((parse-man-url
|
||||
(cond
|
||||
((procedure? finder) finder)
|
||||
((list? finder)
|
||||
(lambda (url)
|
||||
(values finder
|
||||
(unescape-uri (http-url-search url))
|
||||
'())))
|
||||
(else
|
||||
(let ((man-path ((infix-splitter ":") (getenv "MANPATH"))))
|
||||
(lambda (url)
|
||||
(values man-path
|
||||
(unescape-uri (http-url-search url))
|
||||
'()))))))
|
||||
(reference-template
|
||||
(cond
|
||||
((procedure? referencer) referencer)
|
||||
((string? referencer) (lambda (entry section) referencer))
|
||||
(else (lambda (entry section) "man?%s(%s)"))))
|
||||
(man (:optional maybe-man man)))
|
||||
|
||||
(lambda (path req)
|
||||
(let ((request-method (request-method req)))
|
||||
(cond
|
||||
((string=? request-method "GET")
|
||||
(with-fatal-error-handler
|
||||
(lambda (c decline)
|
||||
(cond
|
||||
((http-error? c)
|
||||
(apply http-error (car (condition-stuff c)) req
|
||||
(cddr (condition-stuff c))))
|
||||
(else
|
||||
(decline))))
|
||||
|
||||
(make-response
|
||||
(status-code ok)
|
||||
#f
|
||||
(time)
|
||||
"text/html"
|
||||
'()
|
||||
(make-writer-body
|
||||
(lambda (out options)
|
||||
(receive (man-path entry and-then)
|
||||
(parse-man-url (request-url req))
|
||||
(emit-man-page man-binary nroff-binary rman-binary
|
||||
gzcat-binary
|
||||
entry man man-path and-then reference-template out))
|
||||
|
||||
(with-tag out address ()
|
||||
(display address out)))))))
|
||||
(else
|
||||
(make-error-response (status-code method-not-allowed) req
|
||||
request-method)))))))
|
||||
|
||||
(define (cat-man-page key section out)
|
||||
(let ((title (if section
|
||||
(format #f "~a(~a) manual page" key section)
|
||||
(format #f "~a manual page" key))))
|
||||
(emit-title out title)
|
||||
(emit-header out 1 title)
|
||||
(newline out)
|
||||
(with-tag out body ()
|
||||
(with-tag out pre ()
|
||||
(copy-inport->outport (current-input-port)
|
||||
out)))))
|
||||
|
||||
(define (emit-man-page man-binary nroff-binary rman-binary
|
||||
gzcat-binary
|
||||
entry man man-path and-then reference-template out)
|
||||
(receive (key section) (parse-man-entry entry)
|
||||
(let ((status
|
||||
(cond
|
||||
((procedure? and-then)
|
||||
(run (| (begin (man man-binary nroff-binary gzcat-binary
|
||||
section key man-path))
|
||||
(begin (and-then key section)))
|
||||
(= 1 ,out)
|
||||
(= 2 ,out)))
|
||||
(else
|
||||
(run (| (begin (man man-binary nroff-binary gzcat-binary
|
||||
section key man-path))
|
||||
(,rman-binary "-fHTML"
|
||||
,@and-then
|
||||
"-r" ,(reference-template entry section)))
|
||||
(= 1 ,out)
|
||||
(= 2 ,out))))))
|
||||
|
||||
(if (not (zero? status))
|
||||
(error "internal error emitting man page")))))
|
||||
|
||||
(define parse-man-entry
|
||||
(let ((entry-regexp (make-regexp "(.*)\\((.)\\)")))
|
||||
(lambda (s)
|
||||
(cond
|
||||
((regexp-exec entry-regexp s)
|
||||
=> (lambda (match)
|
||||
(values (match:substring match 1)
|
||||
(match:substring match 2))))
|
||||
(else (values s #f))))))
|
||||
|
||||
(define (man man-binary nroff-binary gzcat-binary section key man-path)
|
||||
(cond
|
||||
((procedure? man-path) (man-path))
|
||||
((find-man-file key section "cat" man-path) =>
|
||||
(lambda (file)
|
||||
(cat-n-decode gzcat-binary file)))
|
||||
((find-man-file key section "man" man-path) =>
|
||||
(lambda (file)
|
||||
(nroff-n-decode nroff-binary file)))
|
||||
(else
|
||||
(if (not (zero?
|
||||
(with-env (("MANPATH" . ,(string-join man-path ":")))
|
||||
(run (,man-binary "-man" ,@(if section `(,section) '()) ,key)
|
||||
stdports))))
|
||||
(http-error (status-code not-found) #f "man page not found")))))
|
||||
|
||||
(define man-default-sections
|
||||
'("1" "2" "3" "4" "5" "6" "7" "8" "9" "o" "l" "n" "p"))
|
||||
|
||||
(define (find-man-file name section cat-man man-path . maybe-sections)
|
||||
|
||||
(define (section-dir section)
|
||||
(lambda (dir)
|
||||
(file-name-as-directory
|
||||
(string-append (file-name-as-directory dir)
|
||||
cat-man
|
||||
section))))
|
||||
|
||||
(let* ((prefix (if section
|
||||
(string-append name "." section)
|
||||
(string-append name ".")))
|
||||
(pattern (string-append (glob-quote prefix) "*"))
|
||||
(sections (:optional maybe-sections man-default-sections))
|
||||
(path (if section
|
||||
(map (section-dir section) man-path)
|
||||
(apply append
|
||||
(map (lambda (dir)
|
||||
(map (lambda (section)
|
||||
((section-dir section) dir))
|
||||
sections))
|
||||
man-path)))))
|
||||
|
||||
(let loop ((path path))
|
||||
(and (not (null? path))
|
||||
(let ((matches (glob (string-append (car path) pattern))))
|
||||
(if (not (null? matches))
|
||||
(car matches)
|
||||
(loop (cdr path))))))))
|
||||
|
||||
(define (file->man-directory file)
|
||||
(path-list->file-name
|
||||
(reverse
|
||||
(cdr
|
||||
(reverse
|
||||
(split-file-name
|
||||
(file-name-directory file)))))))
|
||||
|
||||
(define (cat-n-decode gzcat-binary file)
|
||||
(let ((ext (file-name-extension file)))
|
||||
(cond
|
||||
((string=? ".gz" ext) (run (,gzcat-binary ,file) stdports))
|
||||
((string=? ".Z" ext) (run (,gzcat-binary ,file) stdports))
|
||||
(else (call-with-input-file
|
||||
file
|
||||
(lambda (port)
|
||||
(copy-inport->outport port (current-output-port))))))))
|
||||
|
||||
(define (nroff-n-decode nroff-binary gzcat-binary file)
|
||||
(if (not (zero? (run (| (begin (cat-n-decode gzcat-binary file))
|
||||
(begin
|
||||
(with-cwd (file->man-directory file)
|
||||
(exec-epf (,nroff-binary "-man")))))
|
||||
stdports)))
|
||||
(http-error (status-code not-found) #f "man page not found")))
|
|
@ -1,50 +0,0 @@
|
|||
#!/bin/sh
|
||||
IFS=" "
|
||||
exec scsh -lm ../packages.scm -dm -o http-top -e top -s "$0" "$@"
|
||||
!#
|
||||
|
||||
;;; Scheme Underground Web Server -*- Scheme -*-
|
||||
;;; Olin Shivers
|
||||
|
||||
;;; To compile as a heap-image:
|
||||
;;; ,open http-top
|
||||
;;; (dump-scsh-program top "server")
|
||||
;;; then insert a #! trigger.
|
||||
|
||||
(define-structure http-top (export top)
|
||||
(open httpd-core
|
||||
httpd-make-options
|
||||
httpd-cgi-server
|
||||
httpd-basic-handlers
|
||||
httpd-seval-handlers
|
||||
scheme-with-scsh)
|
||||
(begin
|
||||
|
||||
;; Kitchen-sink request handler.
|
||||
|
||||
(define rh
|
||||
(alist-path-dispatcher
|
||||
`(("h" . ,(home-dir-handler "public_html"))
|
||||
("seval" . ,seval-handler)
|
||||
("cgi-bin" . ,(cgi-handler "/usr/local/etc/httpd/cgi-bin")))
|
||||
(tilde-home-dir-handler "public_html"
|
||||
(rooted-file-handler "/usr/local/etc/httpd/htdocs"))))
|
||||
|
||||
|
||||
|
||||
;; Crank up a server on port 8001, first resetting our identity to
|
||||
;; user "nobody". Initialise the request-invariant part of the CGI
|
||||
;; env before starting.
|
||||
|
||||
(define (top args)
|
||||
(display "We be jammin, now.\n") (force-output)
|
||||
(cond ((zero? (user-uid))
|
||||
(set-gid (->gid "nobody"))
|
||||
(set-uid (->uid "nobody"))))
|
||||
;; invariant environment is know initilialized by cgi-handler itself
|
||||
;; (initialise-request-invariant-cgi-env)
|
||||
(httpd (with-request-handler
|
||||
rh
|
||||
(with-port
|
||||
8001
|
||||
(with-root-directory "/usr/local/etc/httpd")))))))
|
|
@ -1,104 +0,0 @@
|
|||
;;; Path handler for uploading Scheme code to the SU web server -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; This is really just an handler example demonstrating how to upload code
|
||||
;;; into the server.
|
||||
|
||||
;;; (do/timeout secs thunk)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Run THUNK, and gun it down if it hasn't finished in SECS seconds.
|
||||
;;; Returns nothing useful, and THUNK gets executed in a subprocess,
|
||||
;;; so its side-effects are invisible, as well. This is a clever kludge --
|
||||
;;; it uses three subprocesses -- but I don't have interrupts, so I'm hosed.
|
||||
|
||||
(define (do/timeout* secs thunk)
|
||||
(run (begin (let ((timer (fork (lambda () (sleep secs))))
|
||||
(worker (fork thunk)))
|
||||
(receive (process status) (wait-any)
|
||||
(ignore-errors
|
||||
(lambda ()
|
||||
(signal-process (proc:pid (if (eq? worker process)
|
||||
timer
|
||||
worker))
|
||||
signal/kill))))))))
|
||||
(define-syntax do/timeout
|
||||
(syntax-rules ()
|
||||
((do/timeout secs body ...) (do/timeout* secs (lambda () body ...)))))
|
||||
|
||||
;;; The request handler for seval ops.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define (seval-handler path req)
|
||||
(let ((request-method (request-method req)))
|
||||
(cond
|
||||
((string=? request-method "POST") ; Could do others also.
|
||||
(seval path req))
|
||||
(else
|
||||
(make-error-response (status-code method-not-allowed) req request-method)))))
|
||||
|
||||
(define (seval path req)
|
||||
(make-response
|
||||
(status-code ok)
|
||||
#f
|
||||
(time)
|
||||
"text/html"
|
||||
'()
|
||||
(make-reader-writer-body
|
||||
(lambda (iport oport options)
|
||||
(let ((sexp (read-request-sexp req iport)))
|
||||
(http-syslog (syslog-level debug) "read sexp: ~a" sexp)
|
||||
(with-tag oport HEAD ()
|
||||
(newline oport)
|
||||
(emit-title oport "Scheme program output"))
|
||||
(newline oport)
|
||||
|
||||
(with-tag oport BODY ()
|
||||
(newline oport)
|
||||
(do/timeout
|
||||
10
|
||||
(receive vals
|
||||
;; Do the computation.
|
||||
(begin (emit-header oport 2 "Output from execution")
|
||||
(newline oport)
|
||||
(with-tag oport PRE ()
|
||||
(newline oport)
|
||||
(force-output oport); In case we're gunned down.
|
||||
(eval-safely sexp)))
|
||||
|
||||
;; Pretty-print the returned value(s).
|
||||
(emit-header oport 2 "Return value(s)")
|
||||
(with-tag oport PRE ()
|
||||
(for-each (lambda (val) (p val oport))
|
||||
vals))))))))))
|
||||
|
||||
|
||||
;;; Read an HTTP request entity body from stdin. The Content-length:
|
||||
;;; element of request REQ's header tells how many bytes to this entity
|
||||
;;; is. The entity should be a URI-encoded form body. Pull out the
|
||||
;;; program=<stuff>
|
||||
;;; string, extract <stuff>, uri-decode it, parse that into an s-expression,
|
||||
;;; and return it.
|
||||
|
||||
(define (read-request-sexp req iport)
|
||||
(cond
|
||||
((get-header (request-headers req) 'content-length) =>
|
||||
(lambda (cl-str) ; Take the first Content-length: header,
|
||||
(let* ((cl-start (string-skip cl-str char-set:whitespace)) ; skip whitespace,
|
||||
(cl (if cl-start ; & convert to
|
||||
(string->number (substring cl-str ; a number.
|
||||
cl-start
|
||||
(string-length cl-str)))
|
||||
0)) ; All whitespace?? -- WTF.
|
||||
(qs (read-string cl iport)) ; Read in CL chars,
|
||||
(q (parse-html-form-query qs)) ; and parse them up.
|
||||
(s (cond ((assoc "program" q) => cdr)
|
||||
(else (error "No program in entity body.")))))
|
||||
(http-syslog (syslog-level debug)
|
||||
"Seval sexp: ~s" s)
|
||||
(read (make-string-input-port s)))))
|
||||
(else (error "No `Content-length:' field in POST request."))))
|
|
@ -1,51 +0,0 @@
|
|||
;;; NCSA's WWW Common Gateway Interface -- script-side code -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; See http://hoohoo.ncsa.uiuc.edu/cgi/interface.html for a sort of "spec".
|
||||
|
||||
;;; This file provides routines to help you write programs in Scheme
|
||||
;;; that can interface to HTTP servers using the CGI program interface
|
||||
;;; to carry out HTTP transactions.
|
||||
|
||||
;;; ISINDEX queries:
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; (Likewise for ISINDEX URL queries from browsers.)
|
||||
;;; Browser url-form encodes the query (see above), which then becomes the
|
||||
;;; ?<search> part of the URI. (Hence the CGI script will split the individual
|
||||
;;; fields into argv[].)
|
||||
|
||||
|
||||
;;; CGI interface:
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; - The URL's <search> part is assigned to env var $QUERY_STRING, undecoded.
|
||||
;;; - If it contains no raw "=" chars, it is split at "+" chars. The
|
||||
;;; substrings are URI decoded, and become the elts of argv[]. You aren't
|
||||
;;; supposed to rely on this unless you are replying to ISINDEX queries.
|
||||
;;; - The CGI script is run with stdin hooked up to the socket. If it's going
|
||||
;;; to read the entity, it should read $CONTENT_LENGTH bytes worth.
|
||||
;;; - A bunch of env vars are set with useful values.
|
||||
;;; - Entity block is passed to script on stdin;
|
||||
;;; script writes reply to stdout.
|
||||
;;; - If the script begins with "nph-" its output is the entire reply.
|
||||
;;; Otherwise, when it replies to the server, it sends back a special
|
||||
;;; little header that tells the server how to construct the real header
|
||||
;;; for the reply.
|
||||
;;; See the "spec" for further details. (URL above)
|
||||
|
||||
(define (cgi-form-query)
|
||||
(let ((request-method (getenv "REQUEST_METHOD")))
|
||||
(cond
|
||||
|
||||
((string=? request-method "GET")
|
||||
(parse-html-form-query (getenv "QUERY_STRING")))
|
||||
|
||||
((string=? request-method "POST")
|
||||
(let ((nchars (string->number (getenv "CONTENT_LENGTH"))))
|
||||
(parse-html-form-query (read-string nchars))))
|
||||
|
||||
(else (error "Method not handled."))))) ; Don't be calling me.
|
|
@ -1,50 +0,0 @@
|
|||
;;; Read cr/lf and lf terminated lines. -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers. <shivers@lcs.mit.edu>
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; (read-crlf-line [fd/port retain-crlf?]) -> string or EOF object
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Read a line terminated by either line-feed or EOF. If RETAIN-CRLF? is #f
|
||||
;;; (the default), a terminating cr/lf or lf sequence is trimmed from the
|
||||
;;; returned string.
|
||||
;;;
|
||||
;;; This is simple and inefficient. It would be save one copy if we didn't
|
||||
;;; use READ-LINE, but replicated its implementation instead.
|
||||
|
||||
(define (read-crlf-line . args)
|
||||
(let-optionals args ((fd/port (current-input-port))
|
||||
(retain-crlf? #f))
|
||||
(let ((ln (read-line fd/port retain-crlf?)))
|
||||
(if (or retain-crlf? (eof-object? ln))
|
||||
ln
|
||||
(let ((slen (string-length ln))) ; Trim a trailing cr, if any.
|
||||
(if (or (zero? slen)
|
||||
(not (char=? (string-ref ln (- slen 1)) cr)))
|
||||
ln
|
||||
(substring ln 0 (- slen 1))))))))
|
||||
|
||||
(define cr (ascii->char 13))
|
||||
|
||||
(define (write-crlf port)
|
||||
(write-string "\r\n" port)
|
||||
(force-output port))
|
||||
|
||||
(define (read-crlf-line-timeout . args)
|
||||
(let-optionals args ((fd/port (current-input-port))
|
||||
(retain-crlf? #f)
|
||||
(timeout 8000)
|
||||
(max-interval 500))
|
||||
(let loop ((waited 0) (interval 100))
|
||||
(cond ((> waited timeout)
|
||||
'timeout)
|
||||
((char-ready? fd/port)
|
||||
(read-crlf-line fd/port retain-crlf?))
|
||||
(else (sleep interval)
|
||||
(loop (+ waited interval) (min (* interval 2)
|
||||
max-interval)))))))
|
||||
|
||||
|
1567
scheme/lib/dns.scm
1567
scheme/lib/dns.scm
File diff suppressed because it is too large
Load Diff
|
@ -1,39 +0,0 @@
|
|||
;; Pretty-printing of IPv4 Internet addresses
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1998 by Mike Sperber.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;; ADDRESS address to pretty-print
|
||||
;; SEPERATOR optional, defaults to ".", seperator between address-parts
|
||||
;; Example:
|
||||
;; (format-internet-host-address #x0a00ffff)
|
||||
;; ==> "10.0.255.255"
|
||||
;; (format-internet-host-address #x0a00ffff ":")
|
||||
;; ==> "10:0:255:255"
|
||||
|
||||
(define (format-internet-host-address address . maybe-separator)
|
||||
|
||||
(let ((extract (lambda (shift)
|
||||
(number->string
|
||||
(bitwise-and (arithmetic-shift address (- shift))
|
||||
255)))))
|
||||
|
||||
(let-optionals maybe-separator ((separator "."))
|
||||
(string-append
|
||||
(extract 24) separator (extract 16) separator
|
||||
(extract 8) separator (extract 0)))))
|
||||
|
||||
;; does pretty-print of ports
|
||||
;; Example:
|
||||
;; (format-port #x0aff)
|
||||
;; => "10,255"
|
||||
|
||||
(define (format-port port)
|
||||
(string-append
|
||||
(number->string (bitwise-and (arithmetic-shift port -8) 255))
|
||||
","
|
||||
(number->string (bitwise-and port 255))))
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
; Utility library for FTP clients and servers
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1998-2002 by Mike Sperber <sperber@informatik.uni-tuebingen.de>
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
(define *window-size* 4096)
|
||||
|
||||
(define (copy-port->port-binary input-port output-port)
|
||||
(let ((buffer (make-string *window-size*)))
|
||||
(let loop ()
|
||||
(cond
|
||||
((read-string! buffer input-port)
|
||||
=> (lambda (length)
|
||||
(write-string buffer output-port 0 length)
|
||||
(loop))))))
|
||||
(force-output output-port))
|
||||
|
||||
(define (copy-port->port-ascii input-port output-port)
|
||||
(let loop ()
|
||||
(let ((line (read-line input-port 'concat)))
|
||||
(if (not (eof-object? line))
|
||||
(let ((length (string-length line)))
|
||||
(cond
|
||||
((zero? length)
|
||||
'fick-dich-ins-knie)
|
||||
((char=? #\newline (string-ref line (- length 1)))
|
||||
(write-string line output-port 0 (- length 1))
|
||||
(write-crlf output-port))
|
||||
(else
|
||||
(write-string line output-port)))
|
||||
(loop)))))
|
||||
(force-output output-port))
|
||||
|
||||
(define (copy-ascii-port->port input-port output-port)
|
||||
(let loop ()
|
||||
(let* ((line (read-crlf-line input-port
|
||||
#f))
|
||||
(length (string-length line)))
|
||||
(if (not (eof-object? line))
|
||||
(begin
|
||||
(write-string line output-port 0 length)
|
||||
(newline output-port)
|
||||
(loop)))))
|
||||
(force-output output-port))
|
||||
|
||||
(define *port-arg-regexp*
|
||||
(make-regexp "^([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+)$"))
|
||||
|
||||
(define (parse-port-arg string)
|
||||
(cond
|
||||
((regexp-exec *port-arg-regexp* string)
|
||||
=> (lambda (match)
|
||||
(let ((components
|
||||
(map (lambda (match-index)
|
||||
(string->number
|
||||
(match:substring match match-index)))
|
||||
'(1 2 3 4 5 6))))
|
||||
(if (any (lambda (component)
|
||||
(> component 255))
|
||||
components)
|
||||
(call-error "invalid PORT argument" parse-port-arg))
|
||||
(apply
|
||||
(lambda (a1 a2 a3 a4 p1 p2)
|
||||
(let ((address (+ (arithmetic-shift a1 24)
|
||||
(arithmetic-shift a2 16)
|
||||
(arithmetic-shift a3 8)
|
||||
a4))
|
||||
(port (+ (arithmetic-shift p1 8) p2)))
|
||||
(values address port)))
|
||||
components))))
|
||||
(else
|
||||
(call-error "invalid PORT argument" parse-port-arg))))
|
||||
|
|
@ -1,436 +0,0 @@
|
|||
;;; ftp.scm -- an FTP client library for the Scheme Shell
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1998 by Eric Marsden.
|
||||
;;; Copyright (c) 2003 by Mike Sperber <sperber@informatik.uni-tuebingen.de>
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;; The following rfc959 commands are not implemented:
|
||||
;;
|
||||
;; * ACCT (account; this is ignored by most servers)
|
||||
;; * SMNT (structure mount, for mounting another filesystem)
|
||||
;; * REIN (reinitialize connection)
|
||||
;; * LOGOUT (quit without interrupting ongoing transfers)
|
||||
;; * STRU (file structure)
|
||||
;; * ALLO (allocate space on server)
|
||||
|
||||
|
||||
;;; Related work ======================================================
|
||||
;;
|
||||
;; * rfc959 describes the FTP protocol; see
|
||||
;; http://www.ietf.org/rfc/rfc959.txt
|
||||
;;
|
||||
;; * /anonymous@sunsite.unc.edu:/pub/Linux/libs/ftplib.tar.gz is a
|
||||
;; library similar to this one, written in C, by Thomas Pfau
|
||||
;;
|
||||
;; * FTP.pm is a Perl module with similar functionality (available
|
||||
;; from http://www.perl.com/CPAN)
|
||||
;;
|
||||
;; * XEmacs gets transparent remote file access from EFS.
|
||||
;; However, it cheats by using /usr/bin/ftp.
|
||||
;;
|
||||
;; * Siod (a small-footprint Scheme implementation by George Carette)
|
||||
;; comes with a file ftp.scm with a small subset of these functions
|
||||
;; defined
|
||||
|
||||
|
||||
;;; TODO ============================================================
|
||||
;;
|
||||
;; * Unix-specific commands such as SITE UMASK, SITE CHMOD
|
||||
;; * improved error handling
|
||||
|
||||
;; Communication is initiated by the client. The server responds to
|
||||
;; each request with a three digit status code and an explanatory
|
||||
;; message, and occasionally with data (which is sent via a separate,
|
||||
;; one-off channel). The client starts by opening a command connection
|
||||
;; to a well known port on the server machine. Messages send to the
|
||||
;; server are of the form
|
||||
;;
|
||||
;; CMD [ <space> arg ] <CR> <LF>
|
||||
;;
|
||||
;; Replies from the server are of the form
|
||||
;;
|
||||
;; xyz <space> Informative message <CR> <LF>
|
||||
;;
|
||||
;; where xyz is a three digit code which indicates whether the
|
||||
;; operation succeeded or not, whether the server is waiting for more
|
||||
;; data, etc. The server may also send multiline messages of the form
|
||||
;;
|
||||
;; xyz- <space> Start of multiline message <CR> <LF>
|
||||
;; [ <space>+ More information ]* <CR> <LF>
|
||||
;; xyz <space> End of multiline message <CR> <LF>
|
||||
;;
|
||||
;; Some of the procedures in this module extract useful information
|
||||
;; from the server's reply, such as the size of a file, or the name of
|
||||
;; the directory we have moved to. These procedures return either the
|
||||
;; extracted information, or #f to indicate failure. Other procedures
|
||||
;; return a "status", which is either the server's reply as a string,
|
||||
;; or #f to signify failure.
|
||||
|
||||
;; beware, the log file contains password information!
|
||||
|
||||
(define (ftp-connect host login password passive? . args)
|
||||
(let-optionals* args ((log #f))
|
||||
(let* ((hst-info (host-info host))
|
||||
(hostname (host-info:name hst-info))
|
||||
(srvc-info (service-info "ftp" "tcp"))
|
||||
(sock (socket-connect protocol-family/internet
|
||||
socket-type/stream
|
||||
hostname
|
||||
(service-info:port srvc-info)))
|
||||
(connection (make-ftp-connection hostname
|
||||
sock
|
||||
passive?
|
||||
log)))
|
||||
(ftp-log connection
|
||||
(string-append "-- "
|
||||
(date->string (date))
|
||||
": opened ftp connection to "
|
||||
hostname))
|
||||
(ftp-read-reply connection (exactly-code "220")) ; the initial welcome banner
|
||||
(ftp-login connection login password)
|
||||
connection)))
|
||||
|
||||
;; Send user information to the remote host. Args are login
|
||||
;; and password. If they are not provided, the Netrc module is used to
|
||||
;; try to determine a login and password for the server.
|
||||
|
||||
(define (ftp-login connection login password)
|
||||
(let* ((netrc-record #f)
|
||||
(get-netrc-record
|
||||
(lambda ()
|
||||
(cond
|
||||
(netrc-record)
|
||||
(else
|
||||
(set! netrc-record
|
||||
(netrc-machine-entry (ftp-connection-host-name connection) #t))
|
||||
netrc-record)))))
|
||||
(let ((login (or login
|
||||
(netrc-entry-login (get-netrc-record)))))
|
||||
(let ((reply
|
||||
(ftp-send-command connection (build-command "USER" login)
|
||||
(lambda (code)
|
||||
(or (string=? code "331") ; "User name okay, need password."
|
||||
(string=? code "230")))))) ; "User logged in, proceed."
|
||||
|
||||
(if (string-prefix? "331" reply) ; "User name okay, need password."
|
||||
(ftp-send-command connection
|
||||
(build-command
|
||||
"PASS"
|
||||
(or password
|
||||
(netrc-entry-password (get-netrc-record))))
|
||||
(exactly-code "230")))))))
|
||||
|
||||
(define-enumerated-type ftp-type :ftp-type
|
||||
ftp-type?
|
||||
ftp-types
|
||||
ftp-type-name
|
||||
ftp-type-index
|
||||
(binary ascii))
|
||||
|
||||
(define (ftp-set-type! connection type)
|
||||
(let ((ttype (cond
|
||||
((eq? type (ftp-type binary)) "I")
|
||||
((eq? type (ftp-type ascii)) "A"))))
|
||||
(ftp-send-command connection (build-command "TYPE" ttype))
|
||||
(values)))
|
||||
|
||||
(define (ftp-rename connection oldname newname)
|
||||
(ftp-send-command connection (build-command "RNFR " oldname)
|
||||
(code-with-prefix "35"))
|
||||
(ftp-send-command connection (build-command "RNTO" newname)
|
||||
(code-with-prefix "25"))
|
||||
(values))
|
||||
|
||||
(define (ftp-delete connection file)
|
||||
(ftp-send-command connection (build-command "DELE" file)
|
||||
(code-with-prefix "25"))
|
||||
(values))
|
||||
|
||||
;;: connection x string -> status
|
||||
(define (ftp-cd connection dir)
|
||||
(ftp-send-command connection (build-command "CWD" dir))
|
||||
(values))
|
||||
|
||||
;;: connection -> status
|
||||
(define (ftp-cdup connection)
|
||||
(ftp-send-command connection "CDUP" (exactly-code "250"))
|
||||
(values))
|
||||
|
||||
;;: on success return the new directory as a string
|
||||
(define (ftp-pwd connection)
|
||||
(let ((reply (ftp-send-command connection "PWD" (exactly-code "257"))))
|
||||
(cond
|
||||
((regexp-search (rx (seq bos (= 3 digit) #\space
|
||||
(* (~ #\")) #\" (submatch (* (~ #\"))) #\"))
|
||||
reply)
|
||||
=> (lambda (match)
|
||||
(match:substring match 1))))))
|
||||
|
||||
(define (ftp-rmdir connection dir)
|
||||
(ftp-send-command connection (build-command "RMD " dir))
|
||||
(values))
|
||||
|
||||
(define (ftp-mkdir connection dir)
|
||||
(ftp-send-command connection (build-command "MKD ~a" dir))
|
||||
(values))
|
||||
|
||||
;; On success return a Scsh date record. This message is not part of
|
||||
;; rfc959 but seems to be supported by many ftp servers (it's useful
|
||||
;; for mirroring)
|
||||
|
||||
(define (ftp-modification-time connection file)
|
||||
(let* ((reply (ftp-send-command connection
|
||||
(build-command "MDTM" file)))
|
||||
(timestr (substring reply 4 (string-length reply))))
|
||||
(let ((year (substring timestr 0 4))
|
||||
(month (substring timestr 4 6))
|
||||
(mday (substring timestr 6 8))
|
||||
(hour (substring timestr 8 10))
|
||||
(min (substring timestr 10 12))
|
||||
(sec (substring timestr 12 14)))
|
||||
(make-date (string->number sec)
|
||||
(string->number min)
|
||||
(string->number hour)
|
||||
(string->number mday)
|
||||
(string->number month)
|
||||
(- (string->number year) 1900)))))
|
||||
|
||||
;; On success return the size of the file in bytes.
|
||||
;;: connection x string -> integer
|
||||
(define (ftp-size connection file)
|
||||
(let* ((reply (ftp-send-command connection
|
||||
(build-command "SIZE" file))))
|
||||
(string->number (substring reply
|
||||
4 (string-length reply)))))
|
||||
|
||||
;; Abort the current data transfer. Maybe we should close the data
|
||||
;; socket?
|
||||
|
||||
(define (ftp-abort connection)
|
||||
(ftp-send-command connection "ABOR")
|
||||
(values))
|
||||
|
||||
(define (ftp-quit connection)
|
||||
(ftp-send-command connection "QUIT" (exactly-code "221"))
|
||||
(close-socket (ftp-connection-command-socket connection)))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; The following commands require the use of a data connection as well
|
||||
;; as the command connection. The command and the server's reply are
|
||||
;; transmitted via the command connection, while the data is
|
||||
;; transmitted via the data connection (you could have guessed that,
|
||||
;; right?).
|
||||
;;
|
||||
;; The data socket is created by the client, who sends a PORT command
|
||||
;; to the server to indicate on which port it is ready to accept a
|
||||
;; connection. The port command specifies an IP number and a port
|
||||
;; number, in the form of 4+2 comma-separated bytes. The server then
|
||||
;; initiates the data transfer. A fresh data connection is created for
|
||||
;; each data transfer (unlike the command connection which stays open
|
||||
;; during the entire conversation with the server).
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define (ftp-ls connection . maybe-dir)
|
||||
(with-data-connection
|
||||
connection
|
||||
(lambda ()
|
||||
(ftp-send-command connection
|
||||
(apply build-command "NLST" maybe-dir)
|
||||
(code-with-prefix "1")))
|
||||
(lambda (data-socket)
|
||||
(port->lines (socket:inport data-socket)))))
|
||||
|
||||
(define (find-port-arg string)
|
||||
(cond
|
||||
((regexp-search (rx (: (+ digit) (= 5 (: #\, (+ digit))))) string)
|
||||
=> (lambda (match)
|
||||
(match:substring match 0)))))
|
||||
|
||||
(define (ftp-dir connection . maybe-dir)
|
||||
(with-data-connection
|
||||
connection
|
||||
(lambda ()
|
||||
(ftp-send-command connection
|
||||
(apply build-command "LIST" maybe-dir)
|
||||
(code-with-prefix "1")))
|
||||
(lambda (data-socket)
|
||||
(port->lines (socket:inport data-socket)))))
|
||||
|
||||
(define (port->lines port)
|
||||
(let loop ((reverse-lines '()))
|
||||
(let ((line (read-crlf-line port)))
|
||||
(if (eof-object? line)
|
||||
(reverse reverse-lines)
|
||||
(loop (cons line reverse-lines))))))
|
||||
|
||||
(define (ftp-get connection remote-file act)
|
||||
(with-data-connection
|
||||
connection
|
||||
(lambda ()
|
||||
(ftp-send-command connection
|
||||
(build-command "RETR" remote-file)
|
||||
(exactly-code "150")))
|
||||
(lambda (data-socket)
|
||||
(act (socket:inport data-socket)))))
|
||||
|
||||
;; FIXME: should have an optional argument :rename which defaults to
|
||||
;; false, which would make us upload to a temporary name and rename at
|
||||
;; the end of the upload. This atomicity is important for ftp or http
|
||||
;; servers which are serving a load, and to avoid problems with "no
|
||||
;; space on device".
|
||||
|
||||
(define (ftp-put connection remote-file act)
|
||||
(with-data-connection
|
||||
connection
|
||||
(lambda ()
|
||||
(ftp-send-command connection (build-command "STOR" remote-file)
|
||||
(exactly-code "150")))
|
||||
(lambda (data-socket)
|
||||
(act (socket:outport data-socket)))))
|
||||
|
||||
(define (ftp-append connection remote-file act)
|
||||
(with-data-connection
|
||||
connection
|
||||
(lambda ()
|
||||
(ftp-send-command connection (build-command "APPE" remote-file)
|
||||
(exactly-code "150"))
|
||||
(lambda (data-socket)
|
||||
(act (socket:outport data-socket))))))
|
||||
|
||||
;; send a command verbatim to the remote server and wait for a
|
||||
;; reply.
|
||||
|
||||
(define (ftp-quot connection cmd)
|
||||
(ftp-send-command connection cmd))
|
||||
|
||||
;; ------------------------------------------------------------------------
|
||||
;; no exported procedures below
|
||||
|
||||
(define (with-data-connection connection command-thunk proc)
|
||||
(if (ftp-connection-passive-mode? connection)
|
||||
(let* ((pasv-reply (ftp-send-command connection "PASV" (exactly-code "227")))
|
||||
(port-arg (find-port-arg pasv-reply)))
|
||||
(call-with-values
|
||||
(lambda () (parse-port-arg port-arg))
|
||||
(lambda (address port)
|
||||
(let ((data-socket (create-socket protocol-family/internet
|
||||
socket-type/stream)))
|
||||
(set-socket-option data-socket level/socket socket/reuse-address #t)
|
||||
(connect-socket data-socket
|
||||
(internet-address->socket-address
|
||||
address port))
|
||||
(command-thunk)
|
||||
(let ((retval (proc data-socket)))
|
||||
(close-socket data-socket)
|
||||
(ftp-read-reply connection)
|
||||
retval)))))
|
||||
|
||||
(let* ((sock (create-socket protocol-family/internet
|
||||
socket-type/stream))
|
||||
(sockaddr (internet-address->socket-address
|
||||
internet-address/any
|
||||
0))) ; 0 to accept any port
|
||||
(set-socket-option sock level/socket socket/reuse-address #t)
|
||||
(set-socket-option sock level/socket socket/linger 120)
|
||||
(bind-socket sock sockaddr)
|
||||
(listen-socket sock 0)
|
||||
(ftp-send-command connection ; send PORT command
|
||||
(ftp-build-PORT-string (socket-local-address sock)))
|
||||
(command-thunk)
|
||||
(receive (data-socket data-socket-address)
|
||||
(accept-connection sock)
|
||||
(let ((retval (proc data-socket)))
|
||||
(close-socket data-socket)
|
||||
(close-socket sock)
|
||||
(ftp-read-reply connection)
|
||||
retval)))))
|
||||
|
||||
;; TODO: Unix-specific commands
|
||||
;; SITE UMASK 002
|
||||
;; SITE IDLE 60
|
||||
;; SITE CHMOD 755 filename
|
||||
;; SITE HELP
|
||||
|
||||
|
||||
|
||||
;; We cache the login and password to be able to relogin automatically
|
||||
;; if we lose the connection (a la ange-ftp). Not implemented.
|
||||
(define-record-type ftp-connection :ftp-connection
|
||||
(make-ftp-connection host-name command-socket passive-mode? logfd)
|
||||
ftp-connection?
|
||||
(host-name ftp-connection-host-name)
|
||||
(command-socket ftp-connection-command-socket)
|
||||
(passive-mode? ftp-connection-passive-mode?)
|
||||
(logfd ftp-connection-logfd))
|
||||
|
||||
(define-condition-type 'ftp-error '(error))
|
||||
(define ftp-error? (condition-predicate 'ftp-error))
|
||||
|
||||
|
||||
(define (ftp-build-PORT-string sockaddr)
|
||||
(let* ((hst-info (host-info (system-name)))
|
||||
(ip-address (car (host-info:addresses hst-info))))
|
||||
(receive (hst-address srvc-port)
|
||||
(socket-address->internet-address sockaddr)
|
||||
(string-append "PORT "
|
||||
(format-internet-host-address ip-address ",")
|
||||
","
|
||||
(format-port srvc-port)))))
|
||||
|
||||
(define (ftp-send-command connection command . maybe-expected)
|
||||
(let* ((sock (ftp-connection-command-socket connection))
|
||||
(out (socket:outport sock)))
|
||||
(write-string command out)
|
||||
(write-crlf out)
|
||||
(ftp-log connection (string-append "<- " command))
|
||||
(apply ftp-read-reply connection maybe-expected)))
|
||||
|
||||
(define any-code (lambda (code) #t))
|
||||
(define (code-with-prefix prefix)
|
||||
(lambda (code)
|
||||
(string-prefix? prefix code)))
|
||||
(define (exactly-code the-code)
|
||||
(lambda (code)
|
||||
(string=? code the-code)))
|
||||
|
||||
;; This is where we check that the server's 3 digit status code
|
||||
;; corresponds to what we expected.
|
||||
|
||||
;; EXPECTED? is a predicate on reply codes. If the server's reply
|
||||
;; doesn't satisfy EXPECTED?, we raise an FTP-ERROR.
|
||||
|
||||
(define (ftp-read-reply connection . maybe-expected)
|
||||
(let-optionals* maybe-expected ((expected? (code-with-prefix "2")))
|
||||
(let* ((sock (ftp-connection-command-socket connection))
|
||||
(in (socket:inport sock))
|
||||
(reply (read-crlf-line in))
|
||||
(code (substring reply 0 3)))
|
||||
(ftp-log connection (string-append "-> " reply))
|
||||
(if (not (expected? code))
|
||||
(signal 'ftp-error reply))
|
||||
;; handle multi-line replies
|
||||
(if (char=? (string-ref reply 3) #\-)
|
||||
(let ((end-prefix (string-append code " ")))
|
||||
(let loop ()
|
||||
(let* ((line (read-crlf-line in))
|
||||
(reply (string-join (list reply line "\n"))))
|
||||
(ftp-log connection (string-append "-> " line))
|
||||
(if (string-prefix? end-prefix line)
|
||||
reply
|
||||
(loop)))))
|
||||
reply))))
|
||||
|
||||
(define (build-command str . opt-args)
|
||||
(string-join (cons str opt-args)))
|
||||
|
||||
(define (ftp-log connection line)
|
||||
(cond
|
||||
((ftp-connection-logfd connection)
|
||||
=> (lambda (log)
|
||||
(write-string line log)
|
||||
(write-string "\n" log)
|
||||
(force-output log)))))
|
|
@ -1,97 +0,0 @@
|
|||
;;; Handle fatal errors in a sensible way. -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; (with-fatal-error-handler* handler thunk)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Call THUNK, and return whatever it returns. If THUNK signals a condition,
|
||||
;;; and that condition is an error condition (or a subtype of error), then
|
||||
;;; HANDLER gets a chance to handle it.
|
||||
;;; The HANDLER proc is applied to two values:
|
||||
;;; (HANDLER condition decline)
|
||||
;;; HANDLER's continuation is WITH-FATAL-ERROR-HANDLER*'s; whatever HANDLER
|
||||
;;; returns is returned from WITH-FATAL-ERROR-HANDLER. HANDLER declines to
|
||||
;;; handle the error by throwing to DECLINE, a nullary continuation.
|
||||
;;;
|
||||
;;; Why is it called with-FATAL-error-handler*? Because returning to the
|
||||
;;; guy that signalled the error is not an option.
|
||||
;;;
|
||||
;;; Why the nested outer pair of CALL/CC's? Well, what happens if the user's
|
||||
;;; error handler *itself* raises an error? This could potentially give
|
||||
;;; rise to an infinite loop, because WITH-HANDLER runs its handler in
|
||||
;;; the original condition-signaller's context, so you'd search back for a
|
||||
;;; handler, and find yourself again. For example, here is an infinite loop:
|
||||
;;;
|
||||
;;; (with-handler (lambda (condition more)
|
||||
;;; (display "Loop!")
|
||||
;;; (error "ouch")) ; Get back, Loretta.
|
||||
;;; (lambda () (error "start me up")))
|
||||
;;;
|
||||
;;; I could require W-F-E-H* users to code carefully, but instead I make sure
|
||||
;;; the user's fatal-error handler runs in w-f-e-h*'s handler context, so
|
||||
;;; if it signals a condition, we'll start the search from there. That's the
|
||||
;;; point of continuation K. When the original thunk completes successfully,
|
||||
;;; we dodge the K hackery by using ACCEPT to make a normal return.
|
||||
|
||||
(define (with-fatal-error-handler* handler thunk)
|
||||
(call-with-current-continuation
|
||||
(lambda (accept)
|
||||
((call-with-current-continuation
|
||||
(lambda (k)
|
||||
(with-handler (lambda (condition more)
|
||||
(if (error? condition)
|
||||
(call-with-current-continuation
|
||||
(lambda (decline)
|
||||
(k (lambda () (handler condition decline))))))
|
||||
(more)) ; Keep looking for a handler.
|
||||
(lambda () (call-with-values thunk accept)))))))))
|
||||
|
||||
(define-syntax with-fatal-error-handler
|
||||
(syntax-rules ()
|
||||
((with-fatal-error-handler handler body ...)
|
||||
(with-fatal-error-handler* handler
|
||||
(lambda () body ...)))))
|
||||
|
||||
;This one ran HANDLER in the signaller's condition-handler context.
|
||||
;It was therefore susceptible to infinite loops if you didn't code
|
||||
;your handler's carefully.
|
||||
;
|
||||
;(define (with-fatal-error-handler* handler thunk)
|
||||
; (call-with-current-continuation
|
||||
; (lambda (accept)
|
||||
; (with-handler (lambda (condition more)
|
||||
; (if (error? condition)
|
||||
; (call-with-current-continuation
|
||||
; (lambda (decline)
|
||||
; (accept (handler condition decline)))))
|
||||
; (more)) ; Keep looking for a handler.
|
||||
; thunk))))
|
||||
|
||||
;;; (%error-handler-cond kont eh-clauses cond-clauses)
|
||||
;;; Transform error-handler clauses into COND clauses by wrapping continuation
|
||||
;;; KONT around the body of each e-h clause, so that if it fires, the result
|
||||
;;; is thrown to KONT, but if no clause fires, the cond returns to the default
|
||||
;;; continuation.
|
||||
|
||||
;(define-syntax %error-handler-cond
|
||||
; (syntax-rules (=> else)
|
||||
;
|
||||
; ((%error-handler-cond kont ((test => proc) clause ...) (ans ...))
|
||||
; (%error-handler-cond kont
|
||||
; (clause ...)
|
||||
; ((test => (lambda (v) (kont (proc v)))) ans ...)))
|
||||
;
|
||||
; ((%error-handler-cond kont ((test body ...) clause ...) (ans ...))
|
||||
; (%error-handler-cond kont
|
||||
; (clause ...)
|
||||
; ((test (kont (begin body ...))) ans ...)))
|
||||
;
|
||||
; ((%error-handler-cond kont ((else body ...)) (ans-clause ...))
|
||||
; (cond (else body ...) ans-clause ...))
|
||||
;
|
||||
; ((%error-handler-cond kont () (ans-clause ...))
|
||||
; (cond ans-clause ...))))
|
|
@ -1,193 +0,0 @@
|
|||
;;; Simple code for doing structured html output. -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; Copyright (c) 1996 by Mike Sperber.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; - An attribute-quoter, that will map an attribute value to its
|
||||
;;; HTML text representation -- surrounding it with single or double quotes,
|
||||
;;; as appropriate, etc.
|
||||
|
||||
;;; Printing HTML tags.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; All the emit-foo procedures have the same basic calling conventions:
|
||||
;;; (emit-foo out <required values> ... [<extra attributes> ...])
|
||||
;;; - OUT is either a port or #t for the current input port.
|
||||
;;; - Each attribute is either a (name . value) pair, which is printed as
|
||||
;;; name="value"
|
||||
;;; or a single symbol or string, which is simply printed as-is
|
||||
;;; (this is useful for attributes that don't have values, such as the
|
||||
;;; ISMAP attribute in <img> tags).
|
||||
|
||||
|
||||
|
||||
;;; <tag name1="val1" name2="val2" ...>
|
||||
|
||||
(define (emit-tag out tag . attrs)
|
||||
(let ((out (fmt->port out)))
|
||||
(display "<" out)
|
||||
(display tag out)
|
||||
(for-each (lambda (attr)
|
||||
(display #\space out)
|
||||
(cond ((pair? attr) ; name="val"
|
||||
(display (car attr) out)
|
||||
(display "=\"" out) ; Should check for
|
||||
(display (cdr attr) out) ; internal double-quote
|
||||
(display #\" out)) ; etc.
|
||||
(else
|
||||
(display attr out)))) ; name
|
||||
attrs)
|
||||
(display #\> out)))
|
||||
|
||||
|
||||
;;; </tag>
|
||||
|
||||
(define (emit-close-tag out tag)
|
||||
(format out "</~a>" tag))
|
||||
|
||||
|
||||
;;; <P>
|
||||
|
||||
(define (emit-p . args) ; (emit-p [out attr1 ...])
|
||||
(receive (out attrs) (if (pair? args)
|
||||
(let* ((out (car args)))
|
||||
(values (if (eq? out #t) (current-output-port) out)
|
||||
(cdr args)))
|
||||
(values (current-output-port) args))
|
||||
|
||||
(apply emit-tag out 'p attrs)))
|
||||
|
||||
|
||||
;;; <TITLE> Make Money Fast!!! </TITLE>
|
||||
|
||||
(define (emit-title out title) ; Takes no attributes.
|
||||
(format out "<title>~a~%</title>~%" title))
|
||||
|
||||
(define (emit-header out level text . attribs)
|
||||
(apply with-tag* out (string-append "H" (number->string level))
|
||||
(lambda () (display text (fmt->port out)))
|
||||
attribs))
|
||||
|
||||
;;; ...and so forth. Could stand to define a bunch of little emitters for the
|
||||
;;; various tags. (define-tag-emitter ...)
|
||||
|
||||
|
||||
;;; Printing out balanced <tag> ... </tag> pairs.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;; (with-tag out tag (attr-elt ...) body ...)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Execute the body forms between a <tag attrs> ... </tag> pair.
|
||||
;;; The (ATTR-ELT ...) list specifies the attributes for the <tag>.
|
||||
;;; It is rather like a LET-list, having the form
|
||||
;;; ((name val) ...)
|
||||
;;; Each NAME must be a symbol, and each VAL must be a Scheme expression
|
||||
;;; whose value is the string to use as attribute NAME's value. Attributes
|
||||
;;; that have no value (e.g., ISMAP) can be specified as attr-elt NAME,
|
||||
;;; instead of (NAME VALUE).
|
||||
;;;
|
||||
;;; For example,
|
||||
;;; (let ((hp "http://clark.lcs.mit.edu/~shivers")) ; My home page.
|
||||
;;; (with-tag port A ((href hp-url) (name "hp"))
|
||||
;;; (display "home page" port)))
|
||||
;;; outputs
|
||||
;;; <A href="http://clark.lcs.mit.edu/~shivers" name="hp">home page</A>
|
||||
|
||||
(define-syntax with-tag
|
||||
(syntax-rules ()
|
||||
((with-tag out tag (attr-elt ...) body ...)
|
||||
(with-tag* out 'tag (lambda () body ...)
|
||||
(%hack-attr-elt attr-elt)
|
||||
...))))
|
||||
|
||||
;;; Why does this have to be top-level?
|
||||
;;; Why can't this be a LET-SYNTAX inside of WITH-TAG?
|
||||
|
||||
(define-syntax %hack-attr-elt
|
||||
(syntax-rules () ; Build attribute-list element:
|
||||
((%hack-attr-elt (name val)) ; (name elt) => (cons 'name elt)
|
||||
(cons 'name val))
|
||||
((%hack-attr-elt name) 'name))) ; name => 'name
|
||||
|
||||
|
||||
;;; Execute THUNK between a <tag attrs> ... </tag> pair.
|
||||
|
||||
(define (with-tag* out tag thunk . attrs)
|
||||
(apply emit-tag out tag attrs)
|
||||
(let ((out (fmt->port out)))
|
||||
(call-with-values thunk
|
||||
(lambda results
|
||||
(emit-close-tag out tag)
|
||||
(apply values results)))))
|
||||
|
||||
|
||||
(define (fmt->port x)
|
||||
(if (eq? x #t) (current-output-port) x))
|
||||
|
||||
;;; Translate text to HTML, mapping special chars such as <, >, &, and
|
||||
;;; double-quote to their HTML escape sequences.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Note iso8859-1 above 127 is perfectly OK
|
||||
|
||||
(define *html-entity-alist*
|
||||
(list
|
||||
(cons (ascii->char 60) "<")
|
||||
(cons (ascii->char 62) ">")
|
||||
(cons (ascii->char 38) "&")
|
||||
(cons (ascii->char 34) """)))
|
||||
|
||||
(define *html-entities*
|
||||
(list->char-set (map car *html-entity-alist*)))
|
||||
|
||||
(define *html-entity-table*
|
||||
(let ((v (make-vector 256 #f)))
|
||||
(for-each (lambda (entry)
|
||||
(vector-set! v
|
||||
(char->ascii (car entry))
|
||||
(cdr entry)))
|
||||
*html-entity-alist*)
|
||||
v))
|
||||
|
||||
(define (string-set-substring! t start s)
|
||||
(let* ((l (string-length s))
|
||||
(end (+ l start)))
|
||||
(do ((i start (+ 1 i)))
|
||||
((= i end) t)
|
||||
(string-set! t i (string-ref s (- i start))))))
|
||||
|
||||
(define (escape-html s)
|
||||
(let ((target-length
|
||||
(string-fold (lambda (c i)
|
||||
(+ i
|
||||
(if (char-set-contains? *html-entities* c)
|
||||
(string-length
|
||||
(vector-ref *html-entity-table*
|
||||
(char->ascii c)))
|
||||
1)))
|
||||
0
|
||||
s)))
|
||||
(if (= target-length (string-length s))
|
||||
s
|
||||
(let ((target (make-string target-length)))
|
||||
(string-fold
|
||||
(lambda (c i)
|
||||
(+ i
|
||||
(if (char-set-contains? *html-entities* c)
|
||||
(let ((entity (vector-ref *html-entity-table* (char->ascii c))))
|
||||
(string-set-substring! target i entity)
|
||||
(string-length entity))
|
||||
(begin
|
||||
(string-set! target i c)
|
||||
1))))
|
||||
0
|
||||
s)
|
||||
target))))
|
||||
|
||||
(define (emit-text s . maybe-port)
|
||||
(if (null? maybe-port)
|
||||
(write-string (escape-html s))
|
||||
(write-string (escape-html s) (fmt->port (car maybe-port)))))
|
|
@ -1,335 +0,0 @@
|
|||
; ls clone in scsh
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1998 Michael Sperber.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
; This currently does a whole bunch of stats on every file in some
|
||||
; cases. In a decent OS implementation, this stuff is cached, so
|
||||
; there isn't any problem, at least not in theory :-)
|
||||
|
||||
; FLAGS is a list of symbols from:
|
||||
;
|
||||
; all - include stuff starting with "."
|
||||
; recursive - guess what
|
||||
; long - output interesting information per file
|
||||
; directory - display only the information for the directory named
|
||||
; flag - flag files as per their types
|
||||
; columns - sorts output vertically in a multicolumn format
|
||||
|
||||
(define ls-crlf? (make-fluid #f))
|
||||
|
||||
(define (ls flags paths . maybe-port)
|
||||
(let* ((port (optional maybe-port (current-output-port)))
|
||||
(paths (if (null? paths)
|
||||
(list (cwd))
|
||||
paths))
|
||||
(only-one? (null? (cdr paths))))
|
||||
(call-with-values
|
||||
(lambda () (parse-flags flags))
|
||||
(lambda (all? recursive? long? directory? flag? columns?)
|
||||
(real-ls paths
|
||||
(if only-one? #f "")
|
||||
all? recursive? long? directory? flag? columns?
|
||||
port)))))
|
||||
|
||||
(define (parse-flags flags)
|
||||
(let ((all? (memq 'all flags))
|
||||
(recursive? (memq 'recursive flags))
|
||||
(long? (memq 'long flags))
|
||||
(directory? (memq 'directory flags))
|
||||
(flag? (memq 'flag flags))
|
||||
(columns? (memq 'columns flags)))
|
||||
(values all? recursive? long? directory? flag? columns?)))
|
||||
|
||||
(define (real-ls paths prefix
|
||||
all? recursive? long? directory? flag? columns?
|
||||
port)
|
||||
(let ((first #t))
|
||||
(for-each
|
||||
(lambda (path)
|
||||
(if first
|
||||
(set! first #f)
|
||||
(ls-newline port))
|
||||
(if prefix
|
||||
(format port "~A~A:~%" prefix path))
|
||||
(ls-path path all? recursive? long? directory? flag? columns? port))
|
||||
paths)))
|
||||
|
||||
(define (ls-path path all? recursive? long? directory? flag? columns? port)
|
||||
(cond
|
||||
((and (not directory?) ;; go into directories
|
||||
(or (and (file-name-directory? path) ;; path specifies directory
|
||||
(file-directory? path #t)) ;; either as a symlink (if the names end with a slash)
|
||||
(file-directory? path #f))) ;; or not
|
||||
(ls-directory path all? recursive? long? directory? flag? columns? port))
|
||||
(else
|
||||
(if (or long? flag?) ;; see LS-DIRECTORY for details
|
||||
(ls-file (cons path (file-info path #f)) long? flag? port)
|
||||
(ls-file (cons path #f) long? flag? port)))))
|
||||
|
||||
(define (ls-directory directory all? recursive? long? directory? flag? columns? port)
|
||||
; terminology: a FILE-NAME is the name of a file
|
||||
; a FILE is a pair whose car is a file-name and whose cdr is
|
||||
; either its file-info-object or #f (if not needed)
|
||||
; a INFO is a file-info-object
|
||||
(let* ((directory (file-name-as-directory directory))
|
||||
(substantial-directory (string-append directory "."))
|
||||
(file-names (directory-files substantial-directory all?)))
|
||||
(with-cwd*
|
||||
substantial-directory
|
||||
(lambda ()
|
||||
(let ((files (if (or recursive? long? flag?) ; these are the flags for which we need the file-info
|
||||
(map (lambda (file-name)
|
||||
(cons file-name (file-info file-name #f)))
|
||||
file-names)
|
||||
(map (lambda (file-name) (cons file-name #f))
|
||||
file-names))))
|
||||
|
||||
(if (and (not long?)
|
||||
columns?)
|
||||
(ls-files-columns files flag? port)
|
||||
(ls-files-column files long? flag? port))
|
||||
|
||||
(if recursive?
|
||||
(let ((directories
|
||||
(map (lambda (file) (car file))
|
||||
(filter (lambda (file)
|
||||
(eq? (file-info:type (cdr file)) 'directory))
|
||||
files))))
|
||||
(if (not (null? directories))
|
||||
(begin
|
||||
(ls-newline port)
|
||||
(real-ls directories directory
|
||||
all? recursive? long? directory? flag? columns?
|
||||
port))))))))))
|
||||
|
||||
(define *width* 79)
|
||||
|
||||
(define (ls-files-columns files flag? port)
|
||||
(let* ((max-file-name-width
|
||||
(if (null? files)
|
||||
0
|
||||
(apply max (map (lambda (file) (string-length (car file))) files))))
|
||||
(max-file-name-width
|
||||
(if flag?
|
||||
(+ 1 max-file-name-width)
|
||||
max-file-name-width))
|
||||
|
||||
(column-width (+ 2 max-file-name-width))
|
||||
|
||||
(columns (quotient *width*
|
||||
column-width))
|
||||
(columns (if (zero? columns)
|
||||
1
|
||||
columns))
|
||||
|
||||
(number-of-files (length files))
|
||||
(rows (quotient (+ number-of-files (- columns 1))
|
||||
columns))
|
||||
|
||||
(tails
|
||||
(do ((column 0 (+ 1 column))
|
||||
(tails (make-vector columns)))
|
||||
((= column columns)
|
||||
tails)
|
||||
(vector-set! tails column
|
||||
(list-tail-or-null files (* rows column))))))
|
||||
|
||||
(do ((row 0 (+ 1 row)))
|
||||
((= row rows))
|
||||
(do ((column 0 (+ 1 column)))
|
||||
((= column columns))
|
||||
(let ((tail (vector-ref tails column)))
|
||||
(if (not (null? tail))
|
||||
(let* ((file (car tail))
|
||||
(width (display-file file flag? port)))
|
||||
(display-spaces (- column-width width) port)
|
||||
(vector-set! tails column (cdr tail))))))
|
||||
(ls-newline port))))
|
||||
|
||||
(define (list-tail-or-null list index)
|
||||
(let loop ((list list) (index index))
|
||||
(cond
|
||||
((null? list) list)
|
||||
((zero? index) list)
|
||||
(else (loop (cdr list) (- index 1))))))
|
||||
|
||||
(define (ls-files-column files long? flag? port)
|
||||
(for-each
|
||||
(lambda (file)
|
||||
(ls-file file long? flag? port))
|
||||
files))
|
||||
|
||||
(define (ls-file file long? flag? port)
|
||||
(if long?
|
||||
(ls-file-long file flag? port)
|
||||
(ls-file-short file flag? port)))
|
||||
|
||||
(define (ls-file-short file flag? port)
|
||||
(display-file file flag? port)
|
||||
(ls-newline port))
|
||||
|
||||
(define (ls-file-long file flag? port)
|
||||
(let ((info (cdr file)))
|
||||
(display-permissions info port)
|
||||
(display-decimal-justified (file-info:nlinks info) 4 port)
|
||||
(write-char #\space port)
|
||||
(let* ((uid (file-info:uid info))
|
||||
(user-name
|
||||
(call-with-current-continuation
|
||||
(lambda (escape)
|
||||
(with-handler
|
||||
(lambda (condition more)
|
||||
(escape (number->string uid)))
|
||||
(lambda ()
|
||||
(user-info:name (user-info uid))))))))
|
||||
(display-padded user-name 9 port))
|
||||
(let* ((gid (file-info:gid info))
|
||||
(group-name
|
||||
(call-with-current-continuation
|
||||
(lambda (escape)
|
||||
(with-handler
|
||||
(lambda (condition more)
|
||||
(escape (number->string gid)))
|
||||
(lambda ()
|
||||
(group-info:name (group-info gid))))))))
|
||||
(display-padded group-name 9 port))
|
||||
(display-decimal-justified (file-info:size info) 7 port)
|
||||
(write-char #\space port)
|
||||
(display-time (file-info:mtime info) port)
|
||||
(write-char #\space port)
|
||||
(display-file file flag? port)
|
||||
(if (eq? (file-info:type info) 'symlink)
|
||||
(begin
|
||||
(display " -> " port)
|
||||
(display (read-symlink (car file)) port)))
|
||||
(ls-newline port)))
|
||||
|
||||
(define *year-seconds* (* 365 24 60 60))
|
||||
|
||||
(define (display-time the-time port)
|
||||
(let ((time-difference (abs (- (time) the-time)))
|
||||
(date (date the-time 0)))
|
||||
(if (< time-difference *year-seconds*)
|
||||
(display (format-date "~b ~d ~H:~M" date) port)
|
||||
(display (format-date "~b ~d ~Y " date) port))))
|
||||
|
||||
(define (display-file file flag? port)
|
||||
(let ((file-name (car file)))
|
||||
(display file-name port)
|
||||
(if (maybe-display-flag (cdr file) flag? port)
|
||||
(+ 1 (string-length file-name))
|
||||
(string-length file-name))))
|
||||
|
||||
(define (maybe-display-flag info flag? port)
|
||||
(and flag?
|
||||
(begin
|
||||
(cond
|
||||
((eq? (file-info:type info) 'directory)
|
||||
(write-char #\/ port))
|
||||
((eq? (file-info:type info) 'symlink)
|
||||
(write-char #\@ port))
|
||||
; 'executable: bits 0, 3 or 6 are set:
|
||||
; that means, 'AND' with 1+8+64=73 results in a nonzero-value
|
||||
; note: there is no distinction between user's, group's and other's permissions
|
||||
; (as the real GNU-ls does not)
|
||||
((not (zero? (bitwise-and (file-info:mode info) 73)))
|
||||
(write-char #\* port))
|
||||
((eq? (file-info:type info) 'socket)
|
||||
(write-char #\= port))
|
||||
((eq? (file-info:type info) 'fifo)
|
||||
(write-char #\| port)))
|
||||
#t)))
|
||||
|
||||
(define (display-permissions info port)
|
||||
(case (file-info:type info)
|
||||
((directory)
|
||||
(write-char #\d port))
|
||||
((symlink)
|
||||
(write-char #\l port))
|
||||
((fifo)
|
||||
(write-char #\p port))
|
||||
(else
|
||||
(write-char #\- port)))
|
||||
(let ((mode (file-info:mode info))
|
||||
(bit 8))
|
||||
(for-each
|
||||
(lambda (id)
|
||||
(if (not (zero? (bitwise-and (arithmetic-shift 1 bit)
|
||||
mode)))
|
||||
(write-char id port)
|
||||
(write-char #\- port))
|
||||
(set! bit (- bit 1)))
|
||||
'(#\r #\w #\x #\r #\w #\x #\r #\w #\x))))
|
||||
|
||||
(define (display-decimal-justified number width port)
|
||||
(display-justified (number->string number) width port))
|
||||
|
||||
(define (display-justified string width port)
|
||||
(let ((length (string-length string)))
|
||||
(if (< length width)
|
||||
(display-spaces (- width length) port))
|
||||
(display string port)))
|
||||
|
||||
(define (display-padded string width port)
|
||||
(let ((length (string-length string)))
|
||||
(display string port)
|
||||
(if (< length width)
|
||||
(display-spaces (- width length) port))))
|
||||
|
||||
(define (display-spaces number port)
|
||||
(do ((i 0 (+ 1 i)))
|
||||
((= i number))
|
||||
(write-char #\space port)))
|
||||
|
||||
;; Convert Unix-style arguments to flags suitable for LS.
|
||||
|
||||
(define (arguments->ls-flags args)
|
||||
(let loop ((args args) (flags '()))
|
||||
(if (null? args)
|
||||
flags
|
||||
(cond
|
||||
((argument->ls-flags (car args))
|
||||
=> (lambda (new-flags)
|
||||
(loop (cdr args) (append new-flags flags))))
|
||||
(else #f)))))
|
||||
|
||||
(define (argument->ls-flags arg)
|
||||
(let ((arg (if (symbol? arg)
|
||||
(symbol->string arg)
|
||||
arg)))
|
||||
(if (or (string=? "" arg)
|
||||
(not (char=? #\- (string-ref arg 0))))
|
||||
#f
|
||||
(let loop ((chars (cdr (string->list arg))) (flags '()))
|
||||
(cond
|
||||
((null? chars)
|
||||
flags)
|
||||
((char->flag (car chars))
|
||||
=> (lambda (flag)
|
||||
(loop (cdr chars) (cons flag flags))))
|
||||
(else #f))))))
|
||||
|
||||
(define (char->flag char)
|
||||
(case char
|
||||
((#\a) 'all)
|
||||
((#\R) 'recursive)
|
||||
((#\l) 'long)
|
||||
((#\d) 'directory)
|
||||
((#\F) 'flag)
|
||||
((#\C) 'columns)
|
||||
(else #f)))
|
||||
|
||||
(define (optional maybe-arg default-exp)
|
||||
(cond
|
||||
((null? maybe-arg) default-exp)
|
||||
((null? (cdr maybe-arg)) (car maybe-arg))
|
||||
(else (error "too many optional arguments" maybe-arg))))
|
||||
|
||||
(define (ls-newline port)
|
||||
(if (fluid ls-crlf?)
|
||||
(write-crlf port)
|
||||
(newline port)))
|
|
@ -1,155 +0,0 @@
|
|||
;;; netrc.scm -- parse authentication information contained in ~/.netrc
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 2003 by Mike Sperber <sperber@informatik.uni-tuebingen.de>
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
(define (check-permissions file-name)
|
||||
(if (not (zero? (bitwise-and #b000111111 (file-mode file-name))))
|
||||
(error "Not parsing netrc file; dangerous permissions."
|
||||
file-name)))
|
||||
|
||||
(define (netrc-file-name)
|
||||
(string-append (file-name-as-directory (home-dir))
|
||||
".netrc"))
|
||||
|
||||
(define (skip-whitespace port)
|
||||
(let loop ()
|
||||
(let ((char (peek-char port)))
|
||||
(cond
|
||||
((eof-object? char)
|
||||
(values))
|
||||
((char-set-contains? char-set:whitespace char)
|
||||
(read-char port)
|
||||
(loop))
|
||||
(else (values))))))
|
||||
|
||||
(define (skip-until-eol port)
|
||||
(let loop ()
|
||||
(let ((char (peek-char port)))
|
||||
(cond
|
||||
((eof-object? char)
|
||||
(values))
|
||||
((char=? #\newline char)
|
||||
(read-char port))
|
||||
(else
|
||||
(read-char port)
|
||||
(loop))))))
|
||||
|
||||
(define (read-lines-until-double-eol port)
|
||||
(let loop ((reverse-lines '()))
|
||||
(let ((line (read-line port)))
|
||||
(if (or (eof-object? line)
|
||||
(string=? "" line))
|
||||
(reverse reverse-lines)
|
||||
(loop (cons line reverse-lines))))))
|
||||
|
||||
(define (next-token port)
|
||||
(skip-whitespace port)
|
||||
(let loop ((reverse-chars '()))
|
||||
|
||||
(define (token)
|
||||
(if (null? reverse-chars)
|
||||
#f
|
||||
(list->string (reverse reverse-chars))))
|
||||
|
||||
(let ((char (peek-char port)))
|
||||
(cond
|
||||
((eof-object? char) (token))
|
||||
((char-set-contains? char-set:whitespace char) (token))
|
||||
(else
|
||||
(loop (cons (read-char port) reverse-chars)))))))
|
||||
|
||||
(define (next-field port)
|
||||
(let ((token (next-token port)))
|
||||
(cond
|
||||
((not token)
|
||||
(values #f #f))
|
||||
((string=? "default" token)
|
||||
(values token #f))
|
||||
((string=? "macdef" token)
|
||||
(let ((name (next-token port)))
|
||||
(skip-until-eol port)
|
||||
(values token
|
||||
(cons name (read-lines-until-double-eol port)))))
|
||||
(else
|
||||
(values token (next-token port))))))
|
||||
|
||||
(define (skip-until-machine port machine accept-default?)
|
||||
(let loop ()
|
||||
(call-with-values
|
||||
(lambda () (next-field port))
|
||||
(lambda (tag value)
|
||||
(cond
|
||||
((not tag) #f)
|
||||
((and accept-default? (string=? "default" tag))
|
||||
#t)
|
||||
((and (string=? tag "machine")
|
||||
(string-ci=? machine value))
|
||||
#t)
|
||||
(else
|
||||
(loop)))))))
|
||||
|
||||
(define (next-macro-definition port)
|
||||
(let loop ()
|
||||
(call-with-values
|
||||
(lambda () (next-field port))
|
||||
(lambda (tag value)
|
||||
(cond
|
||||
((not tag) #f)
|
||||
((string=? "macdef" tag) value)
|
||||
(else (loop)))))))
|
||||
|
||||
(define-record-type netrc-entry :netrc-entry
|
||||
(make-netrc-entry machine login password account)
|
||||
netrc-entry?
|
||||
(machine netrc-entry-machine set-netrc-entry-machine!)
|
||||
(login netrc-entry-login set-netrc-entry-login!)
|
||||
(password netrc-entry-password set-netrc-entry-password!)
|
||||
(account netrc-entry-account set-netrc-entry-account!))
|
||||
|
||||
(define (netrc-machine-entry machine accept-default? . maybe-file-name)
|
||||
(let ((file-name (if (pair? maybe-file-name)
|
||||
(car maybe-file-name)
|
||||
(netrc-file-name)))
|
||||
(entry (make-netrc-entry machine #f #f #f)))
|
||||
(check-permissions file-name)
|
||||
(call-with-input-file file-name
|
||||
(lambda (port)
|
||||
(if (not (skip-until-machine port machine accept-default?))
|
||||
#f
|
||||
(let loop ()
|
||||
(call-with-values
|
||||
(lambda () (next-field port))
|
||||
(lambda (tag value)
|
||||
(cond
|
||||
((not tag) entry)
|
||||
((or (string=? "default" tag)
|
||||
(string=? "machine" tag))
|
||||
entry)
|
||||
((string=? "login" tag)
|
||||
(set-netrc-entry-login! entry value)
|
||||
(loop))
|
||||
((string=? "password" tag)
|
||||
(set-netrc-entry-password! entry value)
|
||||
(loop))
|
||||
((string=? "account" tag)
|
||||
(set-netrc-entry-account! entry value)
|
||||
(loop))
|
||||
(else (loop)))))))))))
|
||||
|
||||
(define (netrc-macro-definitions . maybe-file-name)
|
||||
(let ((file-name (if (pair? maybe-file-name)
|
||||
(car maybe-file-name)
|
||||
(netrc-file-name))))
|
||||
(check-permissions file-name)
|
||||
(call-with-input-file file-name
|
||||
(lambda (port)
|
||||
(let loop ((reverse-alist '()))
|
||||
(cond
|
||||
((next-macro-definition port)
|
||||
=> (lambda (pair)
|
||||
(loop (cons pair reverse-alist))))
|
||||
(else (reverse reverse-alist))))))))
|
|
@ -1,111 +0,0 @@
|
|||
;;; nettime.scm -- obtain the time on remote machines
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1998 by Eric Marsden
|
||||
;;; Copyright (c) 2003 by Mike Sperber <sperber@informatik.uni-tuebingen.de>
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; Related work ======================================================
|
||||
;;
|
||||
;; * Time.pm is a Perl module by Graham Barr
|
||||
;; * rfc868 describes the Time protocol
|
||||
;; http://www.ietf.org/rfc/rfc868.txt
|
||||
;; * rfc867 describes the Daytime protocol in all its glory
|
||||
;; http://www.ietf.org/rfc/rfc867.txt
|
||||
;; * for a genuinely useful protocol look at the Network Time Protocol
|
||||
;; defined in rfc1305, which allows for the synchronization of clocks
|
||||
;; on networked computers.
|
||||
|
||||
;; args host protocol, where host may be an IP number or a fqdn. we
|
||||
;; subtract 70 years' worth of seconds at the end, since the time
|
||||
;; protocol returns the number of seconds since 1900, whereas Unix
|
||||
;; time is since 1970.
|
||||
|
||||
(define (rfc868-time/tcp host)
|
||||
(let* ((hst-info (host-info host))
|
||||
(srvc-info (service-info "time" "tcp"))
|
||||
(sock (socket-connect protocol-family/internet
|
||||
socket-type/stream
|
||||
(host-info:name hst-info)
|
||||
(service-info:port srvc-info)))
|
||||
(result (read-integer (socket:inport sock))))
|
||||
(close-socket sock)
|
||||
(- result 2208988800)))
|
||||
|
||||
(define (rfc868-time/udp host . maybe-timeout)
|
||||
(let* ((hst-info (host-info host))
|
||||
(srvc-info (service-info "time" "udp"))
|
||||
(timeout (if (pair? maybe-timeout)
|
||||
(car maybe-timeout)
|
||||
#f))
|
||||
(socket (create-socket protocol-family/internet socket-type/datagram)))
|
||||
(connect-socket socket
|
||||
(internet-address->socket-address
|
||||
(car (host-info:addresses hst-info))
|
||||
(service-info:port srvc-info)))
|
||||
(send-message socket "")
|
||||
(if (null? (select-ports timeout (socket:inport socket)))
|
||||
(begin
|
||||
(close-socket socket)
|
||||
#f)
|
||||
(with-fatal-error-handler*
|
||||
(lambda (result punt)
|
||||
;; we may see a "connection refused" error here
|
||||
#f)
|
||||
(lambda ()
|
||||
(let ((result (read-integer (socket:inport socket))))
|
||||
(close-socket socket)
|
||||
(- result 2208988800)))))))
|
||||
|
||||
(define (rfc867-daytime/tcp host)
|
||||
(let* ((hst-info (host-info host))
|
||||
(srvc-info (service-info "daytime" "tcp"))
|
||||
(sock (socket-connect protocol-family/internet
|
||||
socket-type/stream
|
||||
(host-info:name hst-info)
|
||||
(service-info:port srvc-info)))
|
||||
(result (read-string 20 (socket:inport sock))))
|
||||
(close-socket sock)
|
||||
result))
|
||||
|
||||
(define (rfc867-daytime/udp host . maybe-timeout)
|
||||
(let* ((hst-info (host-info host))
|
||||
(srvc-info (service-info "daytime" "udp"))
|
||||
(timeout (if (pair? maybe-timeout)
|
||||
(car maybe-timeout)
|
||||
#f))
|
||||
(socket (create-socket protocol-family/internet socket-type/datagram)))
|
||||
(connect-socket socket
|
||||
(internet-address->socket-address
|
||||
(car (host-info:addresses hst-info))
|
||||
(service-info:port srvc-info)))
|
||||
(send-message socket "")
|
||||
(if (null? (select-ports timeout (socket:inport socket)))
|
||||
(begin
|
||||
(close-socket socket)
|
||||
#f)
|
||||
(with-fatal-error-handler*
|
||||
(lambda (result punt)
|
||||
;; we may see a "connection refused" error here
|
||||
#f)
|
||||
(lambda ()
|
||||
(call-with-values
|
||||
(lambda () (receive-message socket 20))
|
||||
(lambda (result socket-address)
|
||||
(close-socket socket)
|
||||
result)))))))
|
||||
|
||||
;; read 4 bytes from fd and build an integer from them
|
||||
(define (read-integer fd)
|
||||
(let loop ((accum 0)
|
||||
(remaining 4))
|
||||
(if (zero? remaining)
|
||||
accum
|
||||
(loop (+ (arithmetic-shift accum 8) (read-byte fd))
|
||||
(- remaining 1)))))
|
||||
|
||||
;; what about EOF??
|
||||
(define (read-byte fd)
|
||||
(char->ascii (read-char fd)))
|
|
@ -1,57 +0,0 @@
|
|||
;;; Code to parse information submitted from HTML forms. -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; See http://www.w3.org/hypertext/WWW/MarkUp/html-spec/html-spec_toc.html
|
||||
|
||||
;;; About HTML forms
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; The form's field data are turned into a single string, of the form
|
||||
;;; name=val&name=val
|
||||
;;; where the <name> and <val> parts are URI encoded to hide their
|
||||
;;; &, =, and + chars, among other things. After URI encoding, the
|
||||
;;; space chars are converted to + chars, just for fun. It is important
|
||||
;;; to encode the spaces this way, because the perfectly general %xx escape
|
||||
;;; mechanism might be insufficiently confusing. This variant encoding is
|
||||
;;; called "form-url encoding."
|
||||
;;;
|
||||
;;; If the form's method is POST,
|
||||
;;; Browser sends the form's field data in the entity block, e.g.,
|
||||
;;; "button=on&ans=yes". The request's Content-type: is application/
|
||||
;;; x-www-form-urlencoded, and the request's Content-length: is the
|
||||
;;; number of bytes in the form data.
|
||||
;;;
|
||||
;;; If the form's method is GET,
|
||||
;;; Browser sends the form's field data in the URL's <search> part.
|
||||
;;; (So the server will pass to the CGI script as $QUERY_STRING,
|
||||
;;; and perhaps also on in argv[]).
|
||||
;;;
|
||||
;;; In either case, the data is "form-url encoded" (as described above).
|
||||
|
||||
(define (parse-html-form-query q)
|
||||
(let ((qlen (string-length q)))
|
||||
(let recur ((i 0))
|
||||
(cond
|
||||
((>= i qlen) '())
|
||||
((string-index q #\= i) =>
|
||||
(lambda (j)
|
||||
(let ((k (or (string-index q #\& j) qlen)))
|
||||
(cons (cons (unescape-uri+ q i j)
|
||||
(unescape-uri+ q (+ j 1) k))
|
||||
(recur (+ k 1))))))
|
||||
(else '()))))) ; BOGUS STRING -- Issue a warning.
|
||||
|
||||
|
||||
;;; Map plus characters to spaces, then do URI decoding.
|
||||
(define (unescape-uri+ s . maybe-start/end)
|
||||
(let-optionals maybe-start/end ((start 0)
|
||||
(end (string-length s)))
|
||||
(unescape-uri (string-map (lambda (c) (if (char=? c #\+) #\space c))
|
||||
(if (and (zero? start)
|
||||
(= end (string-length s)))
|
||||
s ; Gratuitous optimisation.
|
||||
(substring s start end))))))
|
|
@ -1,290 +0,0 @@
|
|||
;;; pop3.scm --- implement the POP3 maildrop protocol in the Scheme Shell
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1998 by Eric Marsden
|
||||
;;; Copyright (c) 2003 by Mike Sperber <sperber@informatik.uni-tuebingen.de>
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; Related work =====================================================
|
||||
;;
|
||||
;; * Emacs is distributed with a C program called movemail which can
|
||||
;; be compiled with support for the POP protocol. There is also an
|
||||
;; Emacs Lisp library called pop3.el by Richard Pieri which includes
|
||||
;; APOP support.
|
||||
;;
|
||||
;; * Shriram Krishnamurthi has written a POP3 library for MzScheme (as
|
||||
;; well as support for the NNTP protocol, for SMTP, ...).
|
||||
;;
|
||||
;; * Siod (a small-footprint Scheme implementation by George Carette)
|
||||
;; includes support for the POP3 protocol.
|
||||
;;
|
||||
;; * rfc1939 describes the POP3 protocol.
|
||||
;; http://www.ietf.org/rfc/rfc1939.txt
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Communication is initiated by the client. The server responds to
|
||||
;; each request with a status indicator and an explanatory message.
|
||||
;; The client starts off by opening a connection to a well known port
|
||||
;; on the server machine (typically TCP 110, or 109 on some broken
|
||||
;; systems). Messages sent to the server are of the form
|
||||
;;
|
||||
;; CMD [ <space> arg ] <CR> <LF>
|
||||
;;
|
||||
;; Replies from the server are of the form
|
||||
;;
|
||||
;; status [ <space> Informative message ] <CR> <LF>
|
||||
;;
|
||||
;; where status is either "+OK" or "-ERR". If the server is sending
|
||||
;; data (the contents of a message for example), it marks the end of
|
||||
;; the data by a line consisting only of a decimal point (thus the
|
||||
;; bytes to look out for are <CR><LF>.<CR><LF>. Any lines in the data
|
||||
;; starting with a . have an additional . added to the beginning, to
|
||||
;; avoid the client thinking that the line marks the end of the
|
||||
;; message. The client should therefore replace double decimal points
|
||||
;; at the beginning of a line by a single decimal point.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define (pop3-connect . args)
|
||||
(let-optionals args ((host-arg #f)
|
||||
(login #f)
|
||||
(password #f)
|
||||
(log #f))
|
||||
(let* ((host (or host-arg
|
||||
(getenv "MAILHOST")))
|
||||
(hst-info (host-info host))
|
||||
(hostname (host-info:name hst-info))
|
||||
(srvc-info (service-info "pop3" "tcp"))
|
||||
(sock (socket-connect protocol-family/internet
|
||||
socket-type/stream
|
||||
hostname
|
||||
(service-info:port srvc-info)))
|
||||
(connection (make-pop3-connection hostname
|
||||
sock
|
||||
log "" "" #f #f)))
|
||||
(pop3-log connection
|
||||
(string-append "-- "
|
||||
(date->string (date))
|
||||
": opened POP3 connection to "
|
||||
hostname))
|
||||
|
||||
;; read the challenge the server sends in its welcome banner
|
||||
(let* ((banner (read-response connection))
|
||||
(match (regexp-search (rx (: "+OK " (* (~ #\<))
|
||||
#\< (submatch (+ (~ #\>))) #\>))
|
||||
banner))
|
||||
(challenge (and match (match:substring match 1))))
|
||||
(set-pop3-connection-challenge! connection challenge))
|
||||
|
||||
(pop3-login connection login password)
|
||||
|
||||
connection)))
|
||||
|
||||
;; first try standard USER/PASS authentication, and switch to APOP
|
||||
;; authentication if the server prefers.
|
||||
|
||||
(define (pop3-login connection login password)
|
||||
(let* ((netrc-record #f)
|
||||
(get-netrc-record
|
||||
(lambda ()
|
||||
(cond
|
||||
(netrc-record)
|
||||
(else
|
||||
(set! netrc-record
|
||||
(netrc-machine-entry (pop3-connection-host-name connection) #f))
|
||||
netrc-record)))))
|
||||
(let ((login (or login
|
||||
(begin
|
||||
(if (or (not (get-netrc-record))
|
||||
(not (netrc-entry-login (get-netrc-record))))
|
||||
(signal 'pop3-error
|
||||
"no login record specified and no netrc entry"))
|
||||
(netrc-entry-login (get-netrc-record)))))
|
||||
(password (or password
|
||||
(begin
|
||||
(if (not (netrc-entry-password (get-netrc-record)))
|
||||
(signal 'pop3-error
|
||||
"no password record specified and no netrc entry"))
|
||||
(netrc-entry-password (get-netrc-record))))))
|
||||
(with-fatal-error-handler*
|
||||
(lambda (result punt)
|
||||
(cond
|
||||
((not (pop3-error? result)) (punt))
|
||||
((pop3-connection-challenge connection)
|
||||
(pop3-apop-login connection login password))))
|
||||
(lambda ()
|
||||
(send-command connection (build-command "USER" login))
|
||||
(send-command connection (build-command "PASS" password))
|
||||
(set-pop3-connection-login! connection login)
|
||||
(set-pop3-connection-password! connection password)
|
||||
(set-pop3-connection-state! connection 'connected))))))
|
||||
|
||||
;; Login to the server using APOP authentication (no cleartext
|
||||
;; passwords are sent over the network). The server appends a token to
|
||||
;; its welcome message, which is built from the server's fully
|
||||
;; qualified domain name and a unique serial number. The client
|
||||
;; concatenates this token and the pass phrase and applies the MD5
|
||||
;; digest algorithm (a one-way hash) to produce a digest. The user
|
||||
;; name and the digest are sent to the server to authenticate the
|
||||
;; user. The following example comes from the RFC:
|
||||
;;
|
||||
;; S: +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>
|
||||
;; C: APOP mrose c4c9334bac560ecc979e58001b3e22fb
|
||||
;; S: +OK maildrop has 1 message (369 octets)
|
||||
;;
|
||||
;; In this example, the shared secret is the string `tan-
|
||||
;; staaf'. Hence, the MD5 algorithm is applied to the string
|
||||
;;
|
||||
;; <1896.697170952@dbc.mtview.ca.us>tanstaaf
|
||||
;;
|
||||
;; which produces a digest value of
|
||||
;;
|
||||
;; c4c9334bac560ecc979e58001b3e22fb
|
||||
;;
|
||||
|
||||
(define (pop3-apop-login connection login password)
|
||||
(let* ((key (string-append (pop3-connection-challenge connection)
|
||||
password))
|
||||
(digest (number->string
|
||||
(md5-digest->number (md5-digest-for-string key))
|
||||
16))
|
||||
(status (send-command connection
|
||||
(build-command "APOP" login digest))))
|
||||
(set-pop3-connection-login! connection login)
|
||||
(set-pop3-connection-password! connection password)
|
||||
(set-pop3-connection-state! connection 'connected)
|
||||
status))
|
||||
|
||||
;; return number of messages and number of bytes waiting at the maildrop
|
||||
|
||||
(define (pop3-stat connection)
|
||||
(check-transaction-state connection pop3-stat)
|
||||
(let* ((response (send-command connection "STAT"))
|
||||
(match (regexp-search (rx (posix-string "([0-9]+) ([0-9]+)")) response)))
|
||||
(values (string->number (match:substring match 1))
|
||||
(string->number (match:substring match 2)))))
|
||||
|
||||
(define (pop3-retrieve-message connection msgid)
|
||||
(check-transaction-state connection pop3-retrieve-message)
|
||||
(let* ((status (send-command connection
|
||||
(build-command "RETR" (number->string msgid))))
|
||||
(port (socket:inport (pop3-connection-command-socket connection)))
|
||||
(headers (read-rfc822-headers port read-crlf-line))
|
||||
(body (multiline-response->lines port)))
|
||||
(values headers body)))
|
||||
|
||||
(define (pop3-retrieve-headers connection msgid)
|
||||
(check-transaction-state connection pop3-retrieve-headers)
|
||||
(let* ((status (send-command connection
|
||||
(build-command "TOP" (number->string msgid) "0")))
|
||||
(port (socket:inport (pop3-connection-command-socket connection)))
|
||||
(headers (read-rfc822-headers port read-crlf-line)))
|
||||
(exhaust-multiline-response port)
|
||||
headers))
|
||||
|
||||
;; Return highest accessed message-id number for the session. This
|
||||
;; ain't in the RFC, but seems to be supported by several servers.
|
||||
|
||||
(define (pop3-last connection)
|
||||
(check-transaction-state connection pop3-last)
|
||||
(let ((response (send-command connection "LAST")))
|
||||
(string->number (car ((infix-splitter) response)))))
|
||||
|
||||
;; mark the message number MSGID for deletion. Note that the messages
|
||||
;; are not truly deleted until the QUIT command is sent, and messages
|
||||
;; can be undeleted using the RSET command.
|
||||
|
||||
(define (pop3-delete connection msgid)
|
||||
(check-transaction-state connection pop3-delete)
|
||||
(send-command connection (build-command "DELE" (number->string msgid)))
|
||||
(values))
|
||||
|
||||
|
||||
;; any messages which have been marked for deletion are unmarked
|
||||
|
||||
(define (pop3-reset connection)
|
||||
(check-transaction-state connection pop3-reset)
|
||||
(send-command connection "RSET")
|
||||
(values))
|
||||
|
||||
(define (pop3-quit connection)
|
||||
(check-transaction-state connection pop3-quit)
|
||||
(let ((status (send-command connection "QUIT")))
|
||||
(close-socket (pop3-connection-command-socket connection))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Nothing exported below.
|
||||
|
||||
(define-record-type pop3-connection :pop3-connection
|
||||
(make-pop3-connection host-name command-socket log-port login password challenge state)
|
||||
pop3-connection?
|
||||
(host-name pop3-connection-host-name)
|
||||
(command-socket pop3-connection-command-socket)
|
||||
(log-port pop3-connection-log-port)
|
||||
(login pop3-connection-login set-pop3-connection-login!)
|
||||
(password pop3-connection-password set-pop3-connection-password!)
|
||||
(challenge pop3-connection-challenge set-pop3-connection-challenge!)
|
||||
(state pop3-connection-state set-pop3-connection-state!))
|
||||
|
||||
(define-condition-type 'pop3-error '(error))
|
||||
(define pop3-error? (condition-predicate 'pop3-error))
|
||||
|
||||
(define (check-transaction-state connection caller)
|
||||
(if (not (eq? (pop3-connection-state connection) 'connected))
|
||||
(error "not in transaction state" caller)))
|
||||
|
||||
(define (read-response connection)
|
||||
(let* ((sock (pop3-connection-command-socket connection))
|
||||
(in (socket:inport sock))
|
||||
(line (read-crlf-line in)))
|
||||
(pop3-log connection (string-append "-> " line))
|
||||
line))
|
||||
|
||||
;; this could perhaps be improved
|
||||
(define (handle-response response command)
|
||||
(let ((match (regexp-search (rx (posix-string "^\\+OK(.*)")) response)))
|
||||
(if match
|
||||
(match:substring match 1)
|
||||
(let ((match2 (regexp-search (rx (posix-string "^-ERR(.*)")) response)))
|
||||
(if match2
|
||||
(signal 'pop3-error (match:substring match2 1) command)
|
||||
(signal 'pop3-error response command))))))
|
||||
|
||||
|
||||
(define (pop3-log connection line)
|
||||
(let ((log (pop3-connection-log-port connection)))
|
||||
(if log
|
||||
(begin
|
||||
(write-string line log)
|
||||
(newline log)
|
||||
(force-output log)))))
|
||||
|
||||
(define (send-command connection command)
|
||||
(let* ((sock (pop3-connection-command-socket connection))
|
||||
(out (socket:outport sock)))
|
||||
(write-string command out)
|
||||
(write-crlf out)
|
||||
(pop3-log connection (string-append "<- " command))
|
||||
(handle-response (read-response connection) command)))
|
||||
|
||||
(define (multiline-response->lines port)
|
||||
(let loop ((reverse-lines '()))
|
||||
(let ((line (read-crlf-line port)))
|
||||
(if (and (not (eof-object? line))
|
||||
(not (string=? line ".")))
|
||||
(let ((line (if (string-prefix? ".." line)
|
||||
(substring line 1 (string-length line))
|
||||
line)))
|
||||
(loop (cons line reverse-lines)))
|
||||
(reverse reverse-lines)))))
|
||||
|
||||
(define (exhaust-multiline-response port)
|
||||
(let loop ()
|
||||
(let ((line (read-crlf-line port)))
|
||||
(if (and (not (eof-object? line))
|
||||
(not (string=? line ".")))
|
||||
(loop)))))
|
||||
|
||||
(define (build-command str . opt-args)
|
||||
(string-join (cons str opt-args)))
|
|
@ -1,63 +0,0 @@
|
|||
;;; Rate limiting -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 2002 by Mike Sperber.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
(define-record-type rate-limiter :rate-limiter
|
||||
(really-make-rate-limiter simultaneous-requests
|
||||
access-lock
|
||||
block-lock
|
||||
current-requests)
|
||||
rate-limiter?
|
||||
(simultaneous-requests rate-limiter-simultaneous-requests)
|
||||
(access-lock rate-limiter-access-lock)
|
||||
(block-lock rate-limiter-block-lock)
|
||||
(current-requests rate-limiter-current-requests-unsafe
|
||||
set-rate-limiter-current-requests!))
|
||||
|
||||
(define (make-rate-limiter simultaneous-requests)
|
||||
(really-make-rate-limiter simultaneous-requests
|
||||
(make-lock)
|
||||
(make-lock)
|
||||
0))
|
||||
|
||||
(define (rate-limit-block rate-limiter)
|
||||
(obtain-lock (rate-limiter-block-lock rate-limiter)))
|
||||
|
||||
(define (rate-limit-open rate-limiter)
|
||||
(obtain-lock (rate-limiter-access-lock rate-limiter))
|
||||
(let ((current-requests
|
||||
(+ 1 (rate-limiter-current-requests-unsafe rate-limiter))))
|
||||
(set-rate-limiter-current-requests! rate-limiter
|
||||
current-requests)
|
||||
(if (>= current-requests
|
||||
(rate-limiter-simultaneous-requests rate-limiter))
|
||||
(maybe-obtain-lock (rate-limiter-block-lock rate-limiter))
|
||||
(release-lock (rate-limiter-block-lock rate-limiter))))
|
||||
(release-lock (rate-limiter-access-lock rate-limiter)))
|
||||
|
||||
(define (rate-limit-close rate-limiter)
|
||||
(obtain-lock (rate-limiter-access-lock rate-limiter))
|
||||
(let ((current-requests
|
||||
(- (rate-limiter-current-requests-unsafe rate-limiter) 1)))
|
||||
(if (negative? current-requests)
|
||||
(error "rate-limiter: too many close operations"
|
||||
rate-limiter))
|
||||
(set-rate-limiter-current-requests! rate-limiter
|
||||
current-requests)
|
||||
(if (= current-requests
|
||||
(- (rate-limiter-simultaneous-requests rate-limiter)
|
||||
1))
|
||||
;; we just came back into range
|
||||
(release-lock (rate-limiter-block-lock rate-limiter))))
|
||||
(release-lock (rate-limiter-access-lock rate-limiter)))
|
||||
|
||||
(define (rate-limiter-current-requests rate-limiter)
|
||||
(obtain-lock (rate-limiter-access-lock rate-limiter))
|
||||
(let ((current-requests
|
||||
(rate-limiter-current-requests-unsafe rate-limiter)))
|
||||
(release-lock (rate-limiter-access-lock rate-limiter))
|
||||
current-requests))
|
|
@ -1,113 +0,0 @@
|
|||
;;; RFC 822 field-parsing code
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers <shivers@lcs.mit.edu>
|
||||
;;; Copyright (c) 2003 by Mike Sperber <sperber@informatik.uni-tuebingen.de>
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; RFC 822 is the "Standard for the format of ARPA Internet text messages"
|
||||
;;; -- the document that essentially tells how the fields in email headers
|
||||
;;; (e.g., the Subject: and To: fields) are formatted. This code is for
|
||||
;;; parsing these headers.
|
||||
|
||||
;;; Here is a pointer to the document:
|
||||
;;; http://www.ietf.org/rfc/rfc0822.txt
|
||||
|
||||
;;; RFC 822 parsing is useful in other contexts as well -- the HTTP protocol
|
||||
;;; uses it, and it tends to pop up here and there.
|
||||
;;;
|
||||
;;; RFC 822 header syntax has two levels: the general syntax for headers,
|
||||
;;; and the syntax for specific headers. For example, once you have figured
|
||||
;;; out which chunk of text is the To: line, there are more rules telling
|
||||
;;; how to split the To: line up into a list of addresses. Another example:
|
||||
;;; lines with dates, e.g., the Date: header, have a specific syntax for
|
||||
;;; the time and date.
|
||||
;;;
|
||||
;;; This code currently *only* provides routines for parsing the gross
|
||||
;;; structure -- splitting the message header into its distinct fields.
|
||||
;;; It would be nice to provide the finer-detail parsers, too. You do it.
|
||||
;;; -Olin
|
||||
|
||||
;;; A note on line-terminators:
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Line-terminating sequences are always a drag, because there's no agreement
|
||||
;;; on them -- the Net protocols and DOS use cr/lf; Unix uses lf; the Mac
|
||||
;;; uses cr. One one hand, you'd like to use the code for all of the above,
|
||||
;;; on the other, you'd also like to use the code for strict applications
|
||||
;;; that need definitely not to recognise bare cr's or lf's as terminators.
|
||||
;;;
|
||||
;;; RFC 822 requires a cr/lf (carriage-return/line-feed) pair to terminate
|
||||
;;; lines of text. On the other hand, careful perusal of the text shows up
|
||||
;;; some ambiguities (there are maybe three or four of these, and I'm too
|
||||
;;; lazy to write them all down). Furthermore, it is an unfortunate fact
|
||||
;;; that many Unix apps separate lines of RFC 822 text with simple linefeeds
|
||||
;;; (e.g., messages kept in /usr/spool/mail). As a result, this code takes a
|
||||
;;; broad-minded view of line-terminators: lines can be terminated by either
|
||||
;;; cr/lf or just lf, and either terminating sequence is trimmed.
|
||||
;;;
|
||||
;;; If you need stricter parsing, you can pass a read-line procedure
|
||||
;;; as an extra parameter. This means that you can pass in a procedure
|
||||
;;; that recognizes only cr/lf's, or only cr's (for a Mac app,
|
||||
;;; perhaps), and you can determine whether or not the terminators get
|
||||
;;; trimmed. However, your read-line procedure must indicate the
|
||||
;;; header-terminating empty line by returning *either* the empty
|
||||
;;; string or the two-char string cr/lf (or the EOF object).
|
||||
|
||||
(define htab (ascii->char 9))
|
||||
|
||||
;;; Convert to a symbol using the Scheme implementation's preferred case,
|
||||
;;; so we can compare these things against quoted constants.
|
||||
(define string->symbol-pref
|
||||
(if (char=? #\a (string-ref (symbol->string 'a) 0)) ; Is it #\a or #\A?
|
||||
(lambda (s) (string->symbol (string-map char-downcase s)))
|
||||
(lambda (s) (string->symbol (string-map char-upcase s)))))
|
||||
|
||||
(define (read-rfc822-field . args)
|
||||
(receive (field body)
|
||||
(apply read-rfc822-field-with-line-breaks args)
|
||||
(values field
|
||||
(string-concatenate body))))
|
||||
|
||||
(define (read-rfc822-field-with-line-breaks . args)
|
||||
(let-optionals args ((port (current-input-port))
|
||||
(read-line read-crlf-line))
|
||||
(let ((line1 (read-line port)))
|
||||
(if (or (eof-object? line1)
|
||||
(zero? (string-length line1))
|
||||
(string=? line1 "\r\n")) ; In case read-line doesn't trim.
|
||||
(values #f #f)
|
||||
(cond
|
||||
((string-index line1 #\:) =>
|
||||
(lambda (colon)
|
||||
(let ((name (string->symbol-pref (substring line1 0 colon))))
|
||||
;; Read in continuation lines.
|
||||
(let lp ((lines (list (substring line1
|
||||
(+ colon 1)
|
||||
(string-length line1)))))
|
||||
(let ((c (peek-char port)))
|
||||
;; RFC822: continuous lines has to start with a space or a htab
|
||||
(if (or (eqv? c #\space) (eqv? c htab))
|
||||
(lp (cons (read-line port) lines))
|
||||
(values name (reverse lines))))))))
|
||||
(else (error "Illegal RFC 822 field syntax." line1))))))) ; No :
|
||||
|
||||
(define (make-read-rfc822-headers read-field)
|
||||
(lambda args
|
||||
(let-optionals args ((port (current-input-port))
|
||||
(read-line read-crlf-line))
|
||||
(let lp ((alist '()))
|
||||
(receive (field val)
|
||||
(read-rfc822-field port read-line)
|
||||
(if field
|
||||
(lp (cons (cons field val) alist))
|
||||
(reverse alist)))))))
|
||||
|
||||
(define read-rfc822-headers
|
||||
(make-read-rfc822-headers read-rfc822-field))
|
||||
(define read-rfc822-headers-with-line-breaks
|
||||
(make-read-rfc822-headers read-rfc822-field-with-line-breaks))
|
||||
|
||||
(define (rfc822-time->string time)
|
||||
(format-date " ~a, ~d ~b ~Y ~H:~M:~S GMT" (date time 0)))
|
|
@ -1,493 +0,0 @@
|
|||
;;; SMTP client code -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Brian D. Carlstrom and Olin Shivers.
|
||||
;;; Copyright (c) 2002-2003 by Mike Sperber <sperber@informatik.uni-tuebingen.de>
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; See rfc821: http://www.ietf.org/rfc/rfc0821.txt
|
||||
|
||||
;;; SMTP protocol procedures tend to return two values:
|
||||
;;; - CODE The integer SMTP reply code returned by server for the transaction.
|
||||
;;; - TEXT A list of strings -- the text messages tagged by the code.
|
||||
;;; The text strings have the initial code numerals and the terminating
|
||||
;;; cr/lf's stripped. Codes in the range [1,399] are sucess codes; codes
|
||||
;;; in the range [400,599] are error codes; codes >= 600 are not part
|
||||
;;; of the official SMTP spec. This module uses codes >= 600 to indicate
|
||||
;;; extra-protocol errors. There are two of these:
|
||||
;;; - 600 Server reply could not be parsed.
|
||||
;;; The server sent back some sort of incomprehensible garbage reply.
|
||||
;;; - 621 Premature EOF while reading server reply.
|
||||
;;; The server shut down in the middle of a reply.
|
||||
;;; A list of the official protocol return codes is appended at the end of
|
||||
;;; this file.
|
||||
|
||||
;;; These little cover functions are trivial packagings of the protocol.
|
||||
;;; You could write your own to handle, e.g., mailing a message to a list
|
||||
;;; of addresses.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define-condition-type 'smtp-error '(error))
|
||||
(define smtp-error? (condition-predicate 'smtp-error))
|
||||
|
||||
(define-condition-type 'smtp-recipients-rejected-error '(smtp-error))
|
||||
(define smtp-recipients-rejected-error?
|
||||
(condition-predicate 'smtp-recipients-rejected-error?))
|
||||
|
||||
(define (smtp-send-mail from to-list headers body . maybe-host)
|
||||
(let* ((host (:optional maybe-host "localhost"))
|
||||
(local (if (string=? host "localhost")
|
||||
(system-name) ; we don't need any DNS for that
|
||||
(system-fqdn)))
|
||||
(connection (smtp-connect host)))
|
||||
(receive (code text)
|
||||
(smtp-transactions/no-close connection ; Do prologue.
|
||||
(smtp-helo local)
|
||||
(smtp-mail from))
|
||||
(if (>= code 400)
|
||||
(begin
|
||||
(smtp-quit (smtp-connection-socket connection))
|
||||
(signal 'smtp-error code text))
|
||||
;; Send over recipients and collect the losers.
|
||||
(let ((losers (filter-map
|
||||
(lambda (to)
|
||||
(receive (code text)
|
||||
((smtp-rcpt to) (smtp-connection-socket connection))
|
||||
(and (>= code 400) ; Error
|
||||
(cond ((>= code 600)
|
||||
(smtp-quit
|
||||
(smtp-connection-socket connection))
|
||||
(signal 'smtp-error code text))
|
||||
(else `(,to ,code ,@text))))))
|
||||
to-list)))
|
||||
|
||||
;; Send the message body and wrap things up.
|
||||
(receive (code text)
|
||||
(smtp-transactions connection
|
||||
(smtp-data (normalize-headers headers) body))
|
||||
(if (or (>= code 400)
|
||||
(not (null? losers)))
|
||||
(signal 'smtp-recipients-rejected-error 700 losers))))))))
|
||||
|
||||
(define (normalize-headers headers)
|
||||
(if (assq 'date headers)
|
||||
headers
|
||||
(cons (cons 'date
|
||||
(rfc822-time->string (time)))
|
||||
headers)))
|
||||
|
||||
(define (smtp-query socket query arg)
|
||||
(receive (code text)
|
||||
(smtp-transactions socket
|
||||
(smtp-helo (system-name))
|
||||
(query arg))
|
||||
(values code text)))
|
||||
|
||||
(define (smtp-expand name host)
|
||||
(smtp-query (smtp-connect host) smtp-expn name))
|
||||
|
||||
(define (smtp-verify name host)
|
||||
(smtp-query (smtp-connect host) smtp-vrfy name))
|
||||
|
||||
(define (smtp-get-help host . details)
|
||||
(smtp-query (smtp-connect host) smtp-help (apply string-append (cons " " details))))
|
||||
|
||||
(define (smtp-transactions connection . transactions)
|
||||
(let ((socket (smtp-connection-socket connection)))
|
||||
(receive (code text) (apply smtp-transactions/no-close connection transactions)
|
||||
(cond
|
||||
((or (= code 221)
|
||||
(= code 421))
|
||||
(values))
|
||||
(else
|
||||
(smtp-quit socket)))
|
||||
(values code text))))
|
||||
|
||||
(define (smtp-transactions/no-close connection . transactions)
|
||||
(let loop ((transactions transactions))
|
||||
(receive (code text) ((car transactions) (smtp-connection-socket connection))
|
||||
(if (or (null? (cdr transactions))
|
||||
(= code 221)
|
||||
(= code 421) ; Redundant, I know.
|
||||
(<= 400 code))
|
||||
(values code text)
|
||||
(loop (cdr transactions))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; The basics of the protocol
|
||||
|
||||
(define (nullary-smtp-command command)
|
||||
(lambda (socket)
|
||||
(let ((port (socket:outport socket)))
|
||||
(write-string command port)
|
||||
(write-crlf port))
|
||||
(handle-smtp-reply socket)))
|
||||
|
||||
(define (unary-smtp-command command)
|
||||
(lambda (data)
|
||||
(lambda (socket)
|
||||
(let ((port (socket:outport socket)))
|
||||
(write-string command port)
|
||||
(display #\space port)
|
||||
(write-string data port)
|
||||
(write-crlf port))
|
||||
(handle-smtp-reply socket))))
|
||||
|
||||
(define-record-type smtp-connection :smtp-connection
|
||||
(make-smtp-connection socket)
|
||||
smtp-connection?
|
||||
(socket smtp-connection-socket))
|
||||
|
||||
(define (smtp-connect host . maybe-port)
|
||||
(let ((sock (socket-connect protocol-family/internet socket-type/stream host
|
||||
(:optional maybe-port "smtp"))))
|
||||
(receive (code text) (handle-smtp-reply sock)
|
||||
(if (< code 400)
|
||||
(make-smtp-connection sock)
|
||||
(error "SMTP socket-open server-reply error" sock code text)))))
|
||||
|
||||
;; HELLO <local-hostname>
|
||||
(define smtp-helo (unary-smtp-command "HELO"))
|
||||
|
||||
;; MAIL FROM: <sender-address>
|
||||
(define smtp-mail (unary-smtp-command "MAIL FROM:"))
|
||||
|
||||
;; RECIPIENT TO: <destination-address>
|
||||
(define smtp-rcpt (unary-smtp-command "RCPT TO:"))
|
||||
|
||||
;; DATA
|
||||
(define smtp-data
|
||||
(let ((send-DATA-msg (nullary-smtp-command "DATA")))
|
||||
(lambda (headers message) ; MESSAGE is a list of strings or an input port.
|
||||
(lambda (socket)
|
||||
(receive (code text) (send-DATA-msg socket)
|
||||
(if (>= code 400)
|
||||
(values code text) ; Error.
|
||||
|
||||
;; We got a positive acknowledgement for the DATA msg,
|
||||
;; now send the message body.
|
||||
(let ((p (socket:outport socket)))
|
||||
(for-each (lambda (pair)
|
||||
(display (symbol->field-name (car pair)) p)
|
||||
(write-char #\: p)
|
||||
(display (cdr pair) p)
|
||||
(write-crlf p))
|
||||
headers)
|
||||
(write-crlf p)
|
||||
|
||||
(cond ((or (null? message) (pair? message))
|
||||
(for-each (lambda (line)
|
||||
(write-data-line line p))
|
||||
message))
|
||||
|
||||
((input-port? message)
|
||||
(let lp ()
|
||||
(let ((stuff (read-line message)))
|
||||
(if (not (eof-object? stuff))
|
||||
(begin
|
||||
(write-data-line stuff p)
|
||||
(newline))))))
|
||||
|
||||
(else (error "Message must be string or input-port.")))
|
||||
|
||||
(write-crlf p)
|
||||
(write-char #\. p)
|
||||
(write-crlf p)
|
||||
(force-output p)
|
||||
(handle-smtp-reply socket))))))))
|
||||
|
||||
(define component-charset (char-set-complement (char-set #\-)))
|
||||
|
||||
(define (symbol->field-name symbol)
|
||||
(let ((components (string-tokenize (symbol->string symbol) component-charset)))
|
||||
(string-join (map upcase-string components) "-")))
|
||||
|
||||
(define (upcase-string strng)
|
||||
(if (string=? "" strng)
|
||||
""
|
||||
(string-append (string (char-upcase (string-ref strng 0)))
|
||||
(substring strng 1 (string-length strng)))))
|
||||
|
||||
(define (write-data-line line port)
|
||||
(display (if (string=? line ".")
|
||||
".."
|
||||
line)
|
||||
port)
|
||||
(write-crlf port))
|
||||
|
||||
;; SEND FROM: <sender-address>
|
||||
(define smtp-send (unary-smtp-command "SEND FROM:"))
|
||||
|
||||
;; SEND OR MAIL <sender-address>
|
||||
(define smtp-soml (unary-smtp-command "SOML FROM:"))
|
||||
|
||||
;; SEND AND MAIL <sender-address>
|
||||
(define smtp-saml (unary-smtp-command "SOML SAML:"))
|
||||
|
||||
;; RESET
|
||||
(define smtp-rset (nullary-smtp-command "RSET"))
|
||||
|
||||
;; VERIFY <user>
|
||||
(define smtp-vrfy (unary-smtp-command "VRFY"))
|
||||
|
||||
;; EXPAND <user>
|
||||
(define smtp-expn (unary-smtp-command "EXPN"))
|
||||
|
||||
;; HELP <details>
|
||||
(define smtp-help
|
||||
(let ((send-help (unary-smtp-command "HELP")))
|
||||
(lambda details
|
||||
(send-help (apply string-append details)))))
|
||||
|
||||
;; NOOP
|
||||
(define smtp-noop (nullary-smtp-command "NOOP"))
|
||||
|
||||
;; QUIT
|
||||
(define smtp-quit
|
||||
(let ((quit (nullary-smtp-command "QUIT")))
|
||||
(lambda (socket)
|
||||
(receive (code text) (quit socket) ; Quit & close socket gracefully.
|
||||
(case code
|
||||
((221 421))
|
||||
(else (close-socket socket))) ; But close in any event.
|
||||
(values code text)))))
|
||||
|
||||
;; TURN
|
||||
(define smtp-turn (nullary-smtp-command "TURN"))
|
||||
|
||||
;;; Read and handle the reply. Return an integer (the reply code),
|
||||
;;; and a list of the text lines that came tagged by the reply code.
|
||||
;;; The text lines have the reply-code prefix (first 4 chars) and the
|
||||
;;; terminating cr/lf's stripped.
|
||||
;;;
|
||||
;;; In bdc's analog of this proc, he would read another reply if the code was
|
||||
;;; in the one-hundred range (1xx). These codes aren't even used in smtp,
|
||||
;;; according to the RFC. So why?
|
||||
|
||||
(define (handle-smtp-reply socket)
|
||||
(receive (code text) (read-smtp-reply (socket:inport socket))
|
||||
(case code
|
||||
((221 421) (close-socket socket))) ; All done.
|
||||
(values code text)))
|
||||
|
||||
;;; Read a reply from the SMTP server. Returns two values:
|
||||
;;; - CODE Integer. The reply code.
|
||||
;;; - TEXT String list. A list of the text lines comprising the reply.
|
||||
;;; Each line of text is stripped of the initial reply-code
|
||||
;;; numerals (e.g., the first four chars of the reply), and
|
||||
;;; the trailing cr/lf. We are in fact generous about what
|
||||
;;; we take to be a line -- the protocol requires cr/lf
|
||||
;;; terminators, but we'll accept just lf. This appears to
|
||||
;;; true to the spirit of the "be strict in what you send,
|
||||
;;; and generous in what you accept" Internet protocol philosphy.
|
||||
|
||||
(define (read-smtp-reply port)
|
||||
(let lp ((replies '()))
|
||||
(let ((ln (read-crlf-line port)))
|
||||
(if (eof-object? ln)
|
||||
(values 621 (cons "Premature EOF during smtp reply."
|
||||
(reverse replies)))
|
||||
(receive (code line more?) (parse-smtp-reply ln)
|
||||
(let ((replies (cons line replies)))
|
||||
(if more?
|
||||
(lp replies)
|
||||
(values code (reverse replies)))))))))
|
||||
|
||||
;;; Parse a line of SMTP reply. Return three values:
|
||||
;;; CODE integer - the reply code that prefixes the string.
|
||||
;;; REST string - the rest of the line.
|
||||
;;; MORE? boolean - is there more reply to read (i.e., was the numeric
|
||||
;;; reply code terminated by a "-" character?)
|
||||
|
||||
(define (parse-smtp-reply line)
|
||||
(if (and (string? line) ; This is all checking
|
||||
(> (string-length line) 3) ; to see if the line
|
||||
(char-numeric? (string-ref line 0)) ; is properly formatted.
|
||||
(char-numeric? (string-ref line 1))
|
||||
(char-numeric? (string-ref line 2))
|
||||
(let ((c (string-ref line 3)))
|
||||
(or (char=? c #\space) (char=? c #\-))))
|
||||
|
||||
(values (string->number (substring line 0 3)) ; It is.
|
||||
(substring line 4 (string-length line))
|
||||
(char=? (string-ref line 3) #\-))
|
||||
|
||||
(values 600 ; It isn't.
|
||||
(string-append "Improperly-formatted smtp reply: " line)
|
||||
#f)))
|
||||
|
||||
;;; Reply codes
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; This material taken from the RFC.
|
||||
;;;
|
||||
;;; 1yz Positive Preliminary reply
|
||||
;;;
|
||||
;;; The command has been accepted, but the requested action
|
||||
;;; is being held in abeyance, pending confirmation of the
|
||||
;;; information in this reply. The sender-SMTP should send
|
||||
;;; another command specifying whether to continue or abort
|
||||
;;; the action.
|
||||
;;;
|
||||
;;; [Note: SMTP does not have any commands that allow this
|
||||
;;; type of reply, and so does not have the continue or
|
||||
;;; abort commands.]
|
||||
;;;
|
||||
;;; 2yz Positive Completion reply
|
||||
;;;
|
||||
;;; The requested action has been successfully completed. A
|
||||
;;; new request may be initiated.
|
||||
;;;
|
||||
;;; 3yz Positive Intermediate reply
|
||||
;;;
|
||||
;;; The command has been accepted, but the requested action
|
||||
;;; is being held in abeyance, pending receipt of further
|
||||
;;; information. The sender-SMTP should send another command
|
||||
;;; specifying this information. This reply is used in
|
||||
;;; command sequence groups.
|
||||
;;;
|
||||
;;; 4yz Transient Negative Completion reply
|
||||
;;;
|
||||
;;; The command was not accepted and the requested action did
|
||||
;;; not occur. However, the error condition is temporary and
|
||||
;;; the action may be requested again. The sender should
|
||||
;;; return to the beginning of the command sequence (if any).
|
||||
;;; It is difficult to assign a meaning to "transient" when
|
||||
;;; two different sites (receiver- and sender- SMTPs) must
|
||||
;;; agree on the interpretation. Each reply in this category
|
||||
;;; might have a different time value, but the sender-SMTP is
|
||||
;;; encouraged to try again. A rule of thumb to determine if
|
||||
;;; a reply fits into the 4yz or the 5yz category (see below)
|
||||
;;; is that replies are 4yz if they can be repeated without
|
||||
;;; any change in command form or in properties of the sender
|
||||
;;; or receiver. (E.g., the command is repeated identically
|
||||
;;; and the receiver does not put up a new implementation.)
|
||||
;;;
|
||||
;;; 5yz Permanent Negative Completion reply
|
||||
;;;
|
||||
;;; The command was not accepted and the requested action did
|
||||
;;; not occur. The sender-SMTP is discouraged from repeating
|
||||
;;; the exact request (in the same sequence). Even some
|
||||
;;; "permanent" error conditions can be corrected, so the
|
||||
;;; human user may want to direct the sender-SMTP to
|
||||
;;; reinitiate the command sequence by direct action at some
|
||||
;;; point in the future (e.g., after the spelling has been
|
||||
;;; changed, or the user has altered the account status).
|
||||
;;;
|
||||
;;;The second digit encodes responses in specific categories:
|
||||
;;;
|
||||
;;; x0z Syntax -- These replies refer to syntax errors,
|
||||
;;; syntactically correct commands that don't fit any
|
||||
;;; functional category, and unimplemented or superfluous
|
||||
;;; commands.
|
||||
;;;
|
||||
;;; x1z Information -- These are replies to requests for
|
||||
;;; information, such as status or help.
|
||||
;;;
|
||||
;;; x2z Connections -- These are replies referring to the
|
||||
;;; transmission channel.
|
||||
;;;
|
||||
;;; x3z Unspecified as yet.
|
||||
;;;
|
||||
;;; x4z Unspecified as yet.
|
||||
;;;
|
||||
;;; x5z Mail system -- These replies indicate the status of
|
||||
;;; the receiver mail system vis-a-vis the requested
|
||||
;;; transfer or other mail system action.
|
||||
|
||||
;;; Complete list (grouped by function)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; 500 Syntax error, command unrecognized
|
||||
;;; [This may include errors such as command line too long]
|
||||
;;; 501 Syntax error in parameters or arguments
|
||||
;;; 502 Command not implemented
|
||||
;;; 503 Bad sequence of commands
|
||||
;;; 504 Command parameter not implemented
|
||||
;;;
|
||||
;;; 211 System status, or system help reply
|
||||
;;; 214 Help message
|
||||
;;; [Information on how to use the receiver or the meaning of a
|
||||
;;; particular non-standard command; this reply is useful only
|
||||
;;; to the human user]
|
||||
;;;
|
||||
;;; 220 <domain> Service ready
|
||||
;;; 221 <domain> Service closing transmission channel
|
||||
;;; 421 <domain> Service not available,
|
||||
;;; closing transmission channel
|
||||
;;; [This may be a reply to any command if the service knows it
|
||||
;;; must shut down]
|
||||
;;;
|
||||
;;; 250 Requested mail action okay, completed
|
||||
;;; 251 User not local; will forward to <forward-path>
|
||||
;;; 450 Requested mail action not taken: mailbox unavailable
|
||||
;;; [E.g., mailbox busy]
|
||||
;;; 550 Requested action not taken: mailbox unavailable
|
||||
;;; [E.g., mailbox not found, no access]
|
||||
;;; 451 Requested action aborted: error in processing
|
||||
;;; 551 User not local; please try <forward-path>
|
||||
;;; 452 Requested action not taken: insufficient system storage
|
||||
;;; 552 Requested mail action aborted: exceeded storage allocation
|
||||
;;; 553 Requested action not taken: mailbox name not allowed
|
||||
;;; [E.g., mailbox syntax incorrect]
|
||||
;;; 354 Start mail input; end with <CRLF>.<CRLF>
|
||||
;;; 554 Transaction failed
|
||||
;;;
|
||||
|
||||
;;; State diagram
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; CONNECTION ESTABLISHMENT
|
||||
;;; S: 220
|
||||
;;; F: 421
|
||||
;;; HELO
|
||||
;;; S: 250
|
||||
;;; E: 500, 501, 504, 421
|
||||
;;; MAIL
|
||||
;;; S: 250
|
||||
;;; F: 552, 451, 452
|
||||
;;; E: 500, 501, 421
|
||||
;;; RCPT
|
||||
;;; S: 250, 251
|
||||
;;; F: 550, 551, 552, 553, 450, 451, 452
|
||||
;;; E: 500, 501, 503, 421
|
||||
;;; DATA
|
||||
;;; I: 354 -> data -> S: 250
|
||||
;;; F: 552, 554, 451, 452
|
||||
;;; F: 451, 554
|
||||
;;; E: 500, 501, 503, 421
|
||||
;;; RSET
|
||||
;;; S: 250
|
||||
;;; E: 500, 501, 504, 421
|
||||
;;; SEND
|
||||
;;; S: 250
|
||||
;;; F: 552, 451, 452
|
||||
;;; E: 500, 501, 502, 421
|
||||
;;; SOML
|
||||
;;; S: 250
|
||||
;;; F: 552, 451, 452
|
||||
;;; E: 500, 501, 502, 421
|
||||
;;; SAML
|
||||
;;; S: 250
|
||||
;;; F: 552, 451, 452
|
||||
;;; E: 500, 501, 502, 421
|
||||
;;; VRFY
|
||||
;;; S: 250, 251
|
||||
;;; F: 550, 551, 553
|
||||
;;; E: 500, 501, 502, 504, 421
|
||||
;;; EXPN
|
||||
;;; S: 250
|
||||
;;; F: 550
|
||||
;;; E: 500, 501, 502, 504, 421
|
||||
;;; HELP
|
||||
;;; S: 211, 214
|
||||
;;; E: 500, 501, 502, 504, 421
|
||||
;;; NOOP
|
||||
;;; S: 250
|
||||
;;; E: 500, 421
|
||||
;;; QUIT
|
||||
;;; S: 221
|
||||
;;; E: 500
|
||||
;;; TURN
|
||||
;;; S: 250
|
||||
;;; F: 502
|
||||
;;; E: 500, 503
|
|
@ -1,77 +0,0 @@
|
|||
; some useful utilities
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 2002 by Andreas Bernauer.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
(define (host-name-or-ip addr)
|
||||
(with-fatal-error-handler
|
||||
(lambda (condition more)
|
||||
(call-with-values
|
||||
(lambda () (socket-address->internet-address addr))
|
||||
(lambda (ip port)
|
||||
(format-internet-host-address ip))))
|
||||
(host-info:name (host-info addr))))
|
||||
|
||||
(define (on-interrupt interrupt thunk)
|
||||
(let lp ((event (most-recent-sigevent)))
|
||||
(let ((next (next-sigevent event interrupt)))
|
||||
(thunk)
|
||||
(lp next))))
|
||||
|
||||
(define (socket-address->string socket-address . with-port?)
|
||||
(let ((with-port? (:optional with-port? #t)))
|
||||
(receive (host-address service-port)
|
||||
(socket-address->internet-address socket-address)
|
||||
(if with-port?
|
||||
(format #f "~A:~A"
|
||||
(format-internet-host-address host-address)
|
||||
(format-port service-port))
|
||||
(format #f "~A"
|
||||
(format-internet-host-address host-address))))))
|
||||
|
||||
|
||||
(define (system-fqdn)
|
||||
(let ((host (host-info (system-name))))
|
||||
(let loop ((addresses (host-info:addresses host)))
|
||||
(if (null? addresses)
|
||||
#f
|
||||
(or (dns-lookup-ip (car addresses))
|
||||
(loop (cdr addresses)))))))
|
||||
|
||||
;;; Assemble a filename from ROOT and the elts of PATH-LIST.
|
||||
;;; If the assembled filename contains a .. subdirectory, return #f,
|
||||
;;; otw return the filename.
|
||||
|
||||
(define dotdot-check
|
||||
(let ((dotdot-re (make-regexp "(^|/)\\.\\.($|/)"))) ; Matches a .. subdir.
|
||||
(lambda (root path-list)
|
||||
(let ((fname (if (null? path-list) root ; Bogus hack.
|
||||
(string-append (file-name-as-directory root)
|
||||
(string-join path-list "/")))))
|
||||
(and (not (regexp-exec dotdot-re fname)) ; Check for .. subdir.
|
||||
fname)))))
|
||||
|
||||
;;; Timeout on network writes?
|
||||
|
||||
(define (copy-inport->outport in out . maybe-buffer-size)
|
||||
(let* ((buffer-size (:optional maybe-buffer-size 1024))
|
||||
(buf (make-string buffer-size)))
|
||||
(let loop ()
|
||||
(cond ((read-string! buf in) => (lambda (nchars)
|
||||
(write-string buf out 0 nchars)
|
||||
(loop)))))
|
||||
(force-output out)))
|
||||
|
||||
(define (dump fd)
|
||||
(copy-inport->outport fd (current-output-port)))
|
||||
|
||||
(define (with-lock lock thunk)
|
||||
(dynamic-wind
|
||||
(lambda ()
|
||||
(release-lock lock))
|
||||
thunk
|
||||
(lambda ()
|
||||
(release-lock lock))))
|
|
@ -1,198 +0,0 @@
|
|||
;;; -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; URI syntax -- [scheme] : path [? search ] [# fragmentid]
|
||||
|
||||
;;; References:
|
||||
;;; - http://www.w3.org/Addressing/rfc1630.txt
|
||||
;;; Original RFC
|
||||
;;; - http://www.w3.org/hypertext/WWW/Addressing/URL/URI_Overview.html
|
||||
;;; General Web page of URI pointers.
|
||||
|
||||
(define uri-reserved (string->char-set ";/#?: ="))
|
||||
|
||||
(define uri-reserved-sans-= (char-set-delete uri-reserved #\=))
|
||||
|
||||
(define (parse-uri s)
|
||||
(let* ((slen (string-length s))
|
||||
;; Search forwards for colon (or intervening reserved char).
|
||||
(rs1 (string-index s uri-reserved)) ; 1st reserved char
|
||||
(colon (and rs1 (char=? (string-ref s rs1) #\:) rs1))
|
||||
(path-start (if colon (+ colon 1) 0))
|
||||
|
||||
;; Search backwards for # (or intervening reserved char).
|
||||
(rs-last (string-index-right s uri-reserved))
|
||||
(sharp (and rs-last (char=? (string-ref s rs-last) #\#) rs-last))
|
||||
|
||||
;; Search backwards for ? (or intervening reserved char).
|
||||
;; (NB: #\= may be after #\? and before #\#)
|
||||
(rs-penult (string-index-right s
|
||||
uri-reserved-sans-=
|
||||
path-start
|
||||
(or sharp slen)))
|
||||
(ques (and rs-penult (char=? (string-ref s rs-penult) #\?) rs-penult))
|
||||
|
||||
(path-end (or ques sharp slen)))
|
||||
(values (and colon (substring s 0 colon))
|
||||
(split-uri s path-start path-end)
|
||||
(and ques (substring s (+ ques 1) (or sharp slen)))
|
||||
(and sharp (substring s (+ sharp 1) slen)))))
|
||||
|
||||
;;; Caution:
|
||||
;;; Don't use this proc until *after* you've parsed the URL -- unescaping
|
||||
;;; might introduce reserved chars (like slashes and colons) that could
|
||||
;;; blow your parse.
|
||||
|
||||
(define (unescape-uri s . maybe-start/end)
|
||||
(let-optionals maybe-start/end ((start 0)
|
||||
(end (string-length s)))
|
||||
(let* ((esc-seq? (lambda (i) (and (< (+ i 2) end)
|
||||
(char=? (string-ref s i) #\%)
|
||||
(hex-digit? (string-ref s (+ i 1)))
|
||||
(hex-digit? (string-ref s (+ i 2))))))
|
||||
(hits (let lp ((i start) (hits 0)) ; count # of esc seqs.
|
||||
(if (< i end)
|
||||
(if (esc-seq? i)
|
||||
(lp (+ i 3) (+ hits 1))
|
||||
(lp (+ i 1) hits))
|
||||
hits))))
|
||||
|
||||
(if (and (zero? hits) (zero? start) (= end (string-length s)))
|
||||
s
|
||||
(let* ((nlen (- (- end start) (* hits 2))) ; the new length
|
||||
; of the
|
||||
; unescaped
|
||||
; string stores
|
||||
; the result
|
||||
(ns (make-string nlen)))
|
||||
|
||||
(let lp ((i start) (j 0)) ; sweep over the string
|
||||
(if (< j nlen)
|
||||
(lp (cond
|
||||
((esc-seq? i) ; unescape
|
||||
; escape-sequence
|
||||
(string-set! ns j
|
||||
(let ((d1 (string-ref s (+ i 1)))
|
||||
(d2 (string-ref s (+ i 2))))
|
||||
(ascii->char (+ (* 16 (hexchar->int d1))
|
||||
(hexchar->int d2)))))
|
||||
(+ i 3))
|
||||
(else (string-set! ns j (string-ref s i))
|
||||
(+ i 1)))
|
||||
(+ j 1))))
|
||||
ns)))))
|
||||
|
||||
(define hex-digit?
|
||||
(let ((hex-digits (string->char-set "0123456789abcdefABCDEF")))
|
||||
(lambda (c) (char-set-contains? hex-digits c))))
|
||||
|
||||
; make use of the fact that numbers and characters are in order in the ascii table
|
||||
(define (hexchar->int c)
|
||||
(- (char->ascii c)
|
||||
(if (char-numeric? c)
|
||||
(char->ascii #\0)
|
||||
(- (if (char-upper-case? c)
|
||||
(char->ascii #\A)
|
||||
(char->ascii #\a))
|
||||
10))))
|
||||
|
||||
(define int->hexchar
|
||||
(let ((table '#(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9
|
||||
#\A #\B #\C #\D #\E #\F)))
|
||||
(lambda (i) (vector-ref table i))))
|
||||
|
||||
|
||||
;;; Caution:
|
||||
;;; All reserved chars (e.g., slash, sharp, colon) get escaped: "=;/#?: "
|
||||
;;; So don't apply this proc to chunks of text with syntactically meaningful
|
||||
;;; reserved chars (e.g., paths with URI slashes or colons) -- they'll be
|
||||
;;; escaped, and lose their special meaning. E.g. it would be a mistake
|
||||
;;; to apply ESCAPE-URI to "//lcs.mit.edu:8001/foo/bar.html" because the
|
||||
;;; slashes and colons would be escaped.
|
||||
|
||||
(define uri-escaped-chars
|
||||
(char-set-complement (char-set-union char-set:letter+digit
|
||||
(string->char-set "$-_@.&!*\"'(),+"))))
|
||||
|
||||
;;; Takes a set of chars to escape. This is because we sometimes need to
|
||||
;;; escape larger sets of chars for different parts of a URI.
|
||||
|
||||
(define (escape-uri s . maybe-escaped-chars)
|
||||
(let-optionals maybe-escaped-chars ((escaped-chars uri-escaped-chars))
|
||||
(let ((nlen (string-fold
|
||||
(lambda (c i)
|
||||
(+ i
|
||||
(if (char-set-contains? escaped-chars c)
|
||||
3
|
||||
1)))
|
||||
0
|
||||
s))) ; new length of escaped string
|
||||
(if (= nlen (string-length s))
|
||||
s
|
||||
(let ((ns (make-string nlen)))
|
||||
(string-fold
|
||||
(lambda (c i) ; replace each occurance of an
|
||||
; character to escape with %ff where ff
|
||||
; is the ascii-code in hexadecimal
|
||||
; notation
|
||||
(+ i (cond
|
||||
((char-set-contains? escaped-chars c)
|
||||
(string-set! ns i #\%)
|
||||
(let* ((d (char->ascii c))
|
||||
(dhi (bitwise-and (arithmetic-shift d -4) #xF))
|
||||
(dlo (bitwise-and d #xF)))
|
||||
(string-set! ns (+ i 1)
|
||||
(int->hexchar dhi))
|
||||
(string-set! ns (+ i 2)
|
||||
(int->hexchar dlo)))
|
||||
3)
|
||||
(else (string-set! ns i c)
|
||||
1))))
|
||||
0
|
||||
s)
|
||||
ns)))))
|
||||
|
||||
;;; Cribbed from scsh's fname.scm
|
||||
|
||||
(define (split-uri uri start end) ; Split at /'s (infix grammar).
|
||||
(let split ((i start)) ; "" -> ("")
|
||||
(cond
|
||||
((>= i end) '(""))
|
||||
((string-index uri #\/ i) =>
|
||||
(lambda (slash)
|
||||
(cons (substring uri i slash)
|
||||
(split (+ slash 1)))))
|
||||
(else (list (substring uri i end))))))
|
||||
|
||||
|
||||
;;; The elements of PLIST must be escaped in case they contain slashes.
|
||||
;;; This procedure doesn't escape them for you; you must do that yourself:
|
||||
;;; (uri-path->uri (map escape-uri pathlist))
|
||||
|
||||
(define (uri-path->uri plist)
|
||||
(string-join plist "/")) ; Insert slashes between elts of PLIST.
|
||||
|
||||
(define (simplify-uri-path p)
|
||||
(if (null? p)
|
||||
#f ; P must be non-null
|
||||
(let lp ((path-list (cdr p))
|
||||
(stack (list (car p))))
|
||||
(if (null? path-list) ; we're done
|
||||
(reverse stack)
|
||||
(cond
|
||||
((string=? (car path-list) "..") ; back up
|
||||
; neither the empty path nor root
|
||||
(if (not (or (null? stack) (string=? (car stack) "")))
|
||||
(lp (cdr path-list) (cdr stack))
|
||||
#f))
|
||||
((string=? (car path-list) ".") ; leave this
|
||||
(lp (cdr path-list) stack))
|
||||
((string=? (car path-list) "") ; back to root
|
||||
(lp (cdr path-list) '("")))
|
||||
(else ; usual segment
|
||||
(lp (cdr path-list) (cons (car path-list) stack))))))))
|
|
@ -1,163 +0,0 @@
|
|||
;;; URL parsing and unparsing -*- Scheme -*-
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1995 by Olin Shivers.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;;; I'm only implementing HTTP URL's right now.
|
||||
|
||||
;;; References:
|
||||
;;; - http://www.w3.org/Addressing/rfc1738.txt
|
||||
;;; Original RFC
|
||||
;;; - http://www.w3.org/hypertext/WWW/Addressing/URL/Overview.html
|
||||
;;; General Web page of URI pointers.
|
||||
|
||||
|
||||
;;; Unresolved issues:
|
||||
;;; - The server parser shouldn't substitute default values --
|
||||
;;; that should happen in a separate step.
|
||||
|
||||
;;; The steps in hacking a URL are:
|
||||
;;; - Take the UID, parse it, and resolve it with the context UID, if any.
|
||||
;;; - Consult the UID's <scheme>. Pick the appropriate URL parser and parse.
|
||||
|
||||
|
||||
;;; Server strings: //<user>:<password>@<host>:<port>/
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; A SERVER record describes path-prefixes of the form
|
||||
;;; //<user>:<password>@<host>:<port>/
|
||||
;;; These are frequently used as the initial prefix of URL's describing
|
||||
;;; Internet resources.
|
||||
|
||||
(define-record-type server :server ; Each slot is a decoded string or #f.
|
||||
(make-server user password host port)
|
||||
server?
|
||||
(user server-user)
|
||||
(password server-password)
|
||||
(host server-host)
|
||||
(port server-port))
|
||||
|
||||
;;; Parse a URI path (a list representing a path, not a string!) into
|
||||
;;; a server record. Default values are taken from the server
|
||||
;;; record DEFAULT except for the host. Returns a server record if
|
||||
;;; it wins. CADDR drops the server portion of the path. In fact,
|
||||
;;; fatal-syntax-error is called, if the path doesn't start with '//'.
|
||||
|
||||
;
|
||||
(define (parse-server path default)
|
||||
(if (and (pair? path) ; The thing better begin
|
||||
(string=? (car path) "") ; with // (i.e., have two
|
||||
(pair? (cdr path)) ; initial "" elements).
|
||||
(string=? (cadr path) ""))
|
||||
|
||||
(let* ((uhs (caddr path)) ; Server string.
|
||||
(uhs-len (string-length uhs))
|
||||
(at (string-index uhs #\@)) ; Usr:passwd at-sign, if any.
|
||||
|
||||
(colon1 (and at (string-index uhs #\:))) ; Usr:passwd colon,
|
||||
(colon1 (and colon1 (< colon1 at) colon1)) ; if any.
|
||||
|
||||
(colon2 (string-index uhs #\: (or at 0)))) ; Host:port colon, if any.
|
||||
(make-server (if at
|
||||
(unescape-uri uhs 0 (or colon1 at))
|
||||
(server-user default))
|
||||
(if colon1
|
||||
(unescape-uri uhs (+ colon1 1) at)
|
||||
(server-password default))
|
||||
(unescape-uri uhs (if at (+ at 1) 0)
|
||||
(or colon2 uhs-len))
|
||||
(if colon2
|
||||
(unescape-uri uhs (+ colon2 1) uhs-len)
|
||||
(server-port default))))
|
||||
|
||||
(fatal-syntax-error "URL must begin with //..." path)))
|
||||
|
||||
;;; Unparser
|
||||
|
||||
(define server-escaped-chars
|
||||
(char-set-union uri-escaped-chars ; @ and : are also special
|
||||
(string->char-set "@:"))) ; in UH strings.
|
||||
|
||||
(define (server->string uh)
|
||||
(let* ((us (server-user uh))
|
||||
(pw (server-password uh))
|
||||
(ho (server-host uh))
|
||||
(po (server-port uh))
|
||||
|
||||
;; Encode before assembly in case pieces contain colons or at-signs.
|
||||
(e (lambda (s) (escape-uri s server-escaped-chars)))
|
||||
|
||||
(user/passwd (if us
|
||||
`(,(e us) . ,(if pw `(":" ,(e pw) "@") '("@")))
|
||||
'()))
|
||||
(host/port (if ho
|
||||
`(,(e ho) . ,(if po `(":" ,(e po)) '()))
|
||||
'())))
|
||||
|
||||
(apply string-append (append user/passwd host/port))))
|
||||
|
||||
|
||||
;;; HTTP URL parsing
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;; The PATH slot of this record is the URL's path split at slashes,
|
||||
;;; e.g., "foo/bar//baz/" => ("foo" "bar" "" "baz" "")
|
||||
;;; These elements are in raw, unescaped format. To convert back to
|
||||
;;; a string, use (uri-path->uri (map escape-uri pathlist)).
|
||||
|
||||
(define-record-type http-url :http-url
|
||||
(make-http-url server path search fragment-identifier)
|
||||
http-url?
|
||||
(server http-url-server) ; Initial //anonymous@clark.lcs.mit.edu:80/
|
||||
(path http-url-path) ; Rest of path, split at slashes & decoded.
|
||||
(search http-url-search)
|
||||
(fragment-identifier http-url-fragment-identifier))
|
||||
|
||||
;;; The URI parser (parse-uri in uri.scm) maps a string to four parts:
|
||||
;;; <scheme> : <path> ? <search> # <frag-id> <scheme>, <search>, and
|
||||
;;; <frag-id> are strings; <path> is a non-empty string list -- the
|
||||
;;; URI's path split at slashes. Optional parts of the URI, when
|
||||
;;; missing, are specified as #f. If <scheme> is "http", then the
|
||||
;;; other three parts can be passed to PARSE-HTTP-URL, which parses
|
||||
;;; them into a HTTP-URL record. All strings come back from the URI
|
||||
;;; parser encoded. SEARCH and FRAG-ID are left that way; this parser
|
||||
;;; decodes the path elements.
|
||||
;;;
|
||||
;;; Returns a HTTP-URL record, if possible. Otherwise
|
||||
;;; FATAL-SYNTAX-ERROR is called.
|
||||
|
||||
(define (parse-http-url path search frag-id)
|
||||
(let ((uh (parse-server path default-http-server)))
|
||||
(if (or (server-user uh) (server-password uh))
|
||||
(fatal-syntax-error
|
||||
"HTTP URL's may not specify a user or password field" path))
|
||||
|
||||
(make-http-url uh (map unescape-uri (cdddr path)) search frag-id)))
|
||||
|
||||
(define (parse-http-url-string string)
|
||||
(call-with-values
|
||||
(lambda () (parse-uri string))
|
||||
(lambda (scheme path search frag-id)
|
||||
(if (string=? scheme "http")
|
||||
(parse-http-url path search frag-id)
|
||||
(fatal-syntax-error "not an HTTP URL" path)))))
|
||||
|
||||
;;; Default http port is 80.
|
||||
(define default-http-server (make-server #f #f #f "80"))
|
||||
|
||||
|
||||
;;; Unparse.
|
||||
|
||||
(define (http-url->string url)
|
||||
(string-append "http://"
|
||||
(server->string (http-url-server url))
|
||||
"/"
|
||||
(uri-path->uri (map escape-uri (http-url-path url)))
|
||||
(cond ((http-url-search url) =>
|
||||
(lambda (s) (string-append "?" s)))
|
||||
(else ""))
|
||||
(cond ((http-url-fragment-identifier url) =>
|
||||
(lambda (fi) (string-append "#" fi)))
|
||||
(else ""))))
|
|
@ -1,791 +0,0 @@
|
|||
;; Scheme 48 package definitions for the
|
||||
;; Scheme Untergrund Networking Suite
|
||||
|
||||
;;; This file is part of the Scheme Untergrund Networking package.
|
||||
|
||||
;;; Copyright (c) 1994 by Brian D. Carlstrom and Olin Shivers.
|
||||
;;; Copyright (c) 1996-2002 by Mike Sperber.
|
||||
;;; Copyright (c) 2000-2002 by Martin Gasbichler.
|
||||
;;; Copyright (c) 1998-2001 by Eric Marsden.
|
||||
;;; For copyright information, see the file COPYING which comes with
|
||||
;;; the distribution.
|
||||
|
||||
;; Interfaces
|
||||
|
||||
;; Net protocols and formats
|
||||
|
||||
(define-interface parse-html-forms-interface
|
||||
(export parse-html-form-query unescape-uri+))
|
||||
|
||||
(define-interface htmlout-interface
|
||||
(export emit-tag
|
||||
emit-close-tag
|
||||
|
||||
emit-p
|
||||
emit-title
|
||||
emit-header ; And so forth...
|
||||
|
||||
with-tag
|
||||
with-tag*
|
||||
|
||||
escape-html
|
||||
emit-text))
|
||||
|
||||
(define-interface smtp-interface
|
||||
(export smtp-send-mail
|
||||
smtp-expand smtp-verify smtp-help
|
||||
smtp-transactions
|
||||
smtp-transactions/no-close
|
||||
smtp-connect
|
||||
smtp-helo smtp-mail smtp-rcpt smtp-data
|
||||
smtp-send smtp-soml smtp-saml smtp-rset smtp-expn
|
||||
smtp-help smtp-noop smtp-quit smtp-turn))
|
||||
|
||||
(define-interface rfc822-interface
|
||||
(export read-rfc822-headers
|
||||
read-rfc822-headers-with-line-breaks
|
||||
read-rfc822-field
|
||||
read-rfc822-field-with-line-breaks
|
||||
rfc822-time->string))
|
||||
|
||||
(define-interface uri-interface
|
||||
(export parse-uri
|
||||
uri-escaped-chars
|
||||
unescape-uri
|
||||
escape-uri
|
||||
split-uri
|
||||
uri-path->uri
|
||||
simplify-uri-path))
|
||||
|
||||
(define-interface url-interface
|
||||
(export server?
|
||||
make-server
|
||||
|
||||
server-user
|
||||
server-password
|
||||
server-host
|
||||
server-port
|
||||
|
||||
parse-server
|
||||
server->string
|
||||
|
||||
http-url?
|
||||
make-http-url
|
||||
|
||||
http-url-server
|
||||
http-url-path
|
||||
http-url-search
|
||||
http-url-fragment-identifier
|
||||
|
||||
parse-http-url
|
||||
parse-http-url-string
|
||||
http-url->string))
|
||||
|
||||
(define-interface ftp-library-interface
|
||||
(export copy-port->port-binary
|
||||
copy-port->port-ascii
|
||||
copy-ascii-port->port
|
||||
parse-port-arg))
|
||||
|
||||
(define-interface ftp-interface
|
||||
(export ftp-connect
|
||||
(ftp-type :syntax)
|
||||
ftp-set-type!
|
||||
ftp-rename
|
||||
ftp-delete
|
||||
ftp-cd
|
||||
ftp-cdup
|
||||
ftp-pwd
|
||||
ftp-rmdir
|
||||
ftp-mkdir
|
||||
ftp-modification-time
|
||||
ftp-size
|
||||
ftp-abort
|
||||
ftp-quit
|
||||
ftp-ls
|
||||
ftp-dir
|
||||
ftp-get
|
||||
ftp-put
|
||||
ftp-append
|
||||
ftp-quot
|
||||
ftp-error?
|
||||
|
||||
copy-port->port-binary
|
||||
copy-port->port-ascii
|
||||
copy-ascii-port->port))
|
||||
|
||||
(define-interface netrc-interface
|
||||
(export netrc-machine-entry
|
||||
netrc-entry?
|
||||
netrc-entry-machine
|
||||
netrc-entry-login
|
||||
netrc-entry-password
|
||||
netrc-entry-account
|
||||
netrc-macro-definitions))
|
||||
|
||||
(define-interface pop3-interface
|
||||
(export pop3-connect
|
||||
pop3-stat
|
||||
pop3-retrieve-message
|
||||
pop3-retrieve-headers
|
||||
pop3-last
|
||||
pop3-delete
|
||||
pop3-reset
|
||||
pop3-quit
|
||||
pop3-error?))
|
||||
|
||||
(define-interface rfc868-interface
|
||||
(export rfc868-time/tcp rfc868-time/udp))
|
||||
|
||||
(define-interface rfc867-interface
|
||||
(export rfc867-daytime/tcp rfc867-daytime/udp))
|
||||
|
||||
(define-interface dns-interface
|
||||
(export dns-clear-cache! ; clears the cache
|
||||
dns-lookup ; complex lookup function
|
||||
dns-lookup-name ; simple lookup function
|
||||
dns-inverse-lookup ; obsolete, use dns-lookup-ip
|
||||
dns-lookup-ip ; simple lookup function
|
||||
dns-lookup-nameserver ; simple lookup function
|
||||
dns-lookup-mail-exchanger ; simple lookpu function
|
||||
pretty-print-dns-message ; prints a human readable dns-msg
|
||||
force-ip ; reruns a lookup until a ip is resolved
|
||||
force-ip-list ; reruns a lookup until a list of ips is resolved
|
||||
address32->ip-string ; converts a address32 in an ip-string
|
||||
ip-string->address32 ; converts a ip-string in an address32
|
||||
dns-find-nameserver ; returns a nameserver
|
||||
dns-find-nameserver-list ; returns a list of nameservers
|
||||
socket-address->fqdn
|
||||
internet-address->fqdn
|
||||
host-fqdn
|
||||
system-fqdn))
|
||||
|
||||
(define-interface cgi-scripts-interface
|
||||
(export cgi-form-query))
|
||||
|
||||
;; Utility libraries
|
||||
|
||||
(define-interface rate-limit-interface
|
||||
(export make-rate-limiter
|
||||
rate-limit-block
|
||||
rate-limit-open
|
||||
rate-limit-close
|
||||
rate-limiter-current-requests))
|
||||
|
||||
(define-interface crlf-io-interface
|
||||
(export read-crlf-line
|
||||
read-crlf-line-timeout
|
||||
write-crlf))
|
||||
|
||||
(define-interface ls-interface
|
||||
(export ls-crlf?
|
||||
ls
|
||||
arguments->ls-flags))
|
||||
|
||||
(define-interface format-net-interface
|
||||
(export format-internet-host-address
|
||||
format-port))
|
||||
|
||||
(define-interface sunet-utilities-interface
|
||||
(export host-name-or-ip
|
||||
on-interrupt
|
||||
socket-address->string
|
||||
dump
|
||||
system-fqdn
|
||||
copy-inport->outport
|
||||
dotdot-check
|
||||
with-lock))
|
||||
|
||||
(define-interface handle-fatal-error-interface
|
||||
(export with-fatal-error-handler*
|
||||
(with-fatal-error-handler :syntax)))
|
||||
|
||||
;; FTP server
|
||||
|
||||
(define-interface ftpd-interface
|
||||
(export with-port with-anonymous-home with-banner with-logfile with-dns-lookup?
|
||||
make-ftpd-options
|
||||
ftpd
|
||||
ftpd-inetd))
|
||||
|
||||
;; Web server
|
||||
|
||||
(define-interface httpd-core-interface
|
||||
(export httpd))
|
||||
|
||||
(define-interface httpd-make-options-interface
|
||||
(export make-httpd-options
|
||||
with-port
|
||||
with-root-directory
|
||||
with-icon-name
|
||||
with-fqdn
|
||||
with-reported-port
|
||||
with-request-handler
|
||||
with-server-admin
|
||||
with-simultaneous-requests
|
||||
with-logfile
|
||||
with-syslog?
|
||||
with-resolve-ips?))
|
||||
|
||||
(define-interface httpd-read-options-interface
|
||||
(export httpd-options-port
|
||||
httpd-options-root-directory
|
||||
httpd-options-icon-name
|
||||
httpd-options-fqdn
|
||||
httpd-options-reported-port
|
||||
httpd-options-request-handler
|
||||
httpd-options-server-admin
|
||||
httpd-options-simultaneous-requests
|
||||
httpd-options-logfile
|
||||
httpd-options-syslog?
|
||||
httpd-options-resolve-ips?))
|
||||
|
||||
(define-interface httpd-access-control-interface
|
||||
(export access-denier
|
||||
access-allower
|
||||
access-controller
|
||||
access-controlled-handler))
|
||||
|
||||
(define-interface httpd-errors-interface
|
||||
(export http-error?
|
||||
http-error
|
||||
fatal-syntax-error?
|
||||
fatal-syntax-error))
|
||||
|
||||
(define-interface httpd-logging-interface
|
||||
(export init-http-log!
|
||||
http-syslog?
|
||||
http-syslog
|
||||
http-log
|
||||
logging
|
||||
make-logging))
|
||||
|
||||
(define-interface httpd-requests-interface
|
||||
(export make-request ; HTTP request
|
||||
request? ; record type.
|
||||
request-method
|
||||
request-uri
|
||||
request-url
|
||||
request-version
|
||||
request-headers
|
||||
request-socket
|
||||
|
||||
version< version<=
|
||||
v0.9-request?
|
||||
version->string))
|
||||
|
||||
(define-interface httpd-responses-interface
|
||||
(export make-response response?
|
||||
response-code
|
||||
response-message
|
||||
response-seconds
|
||||
response-mime
|
||||
response-extras
|
||||
response-body
|
||||
|
||||
make-nph-response nph-response?
|
||||
nph-response-body
|
||||
|
||||
make-writer-body writer-body?
|
||||
make-reader-writer-body reader-writer-body?
|
||||
make-redirect-body redirect-body? redirect-body-location
|
||||
display-http-body
|
||||
|
||||
status-code?
|
||||
status-code-number
|
||||
status-code-message
|
||||
(status-code :syntax)
|
||||
name->status-code
|
||||
number->status-code
|
||||
|
||||
make-error-response
|
||||
make-redirect-response))
|
||||
|
||||
(define-interface httpd-basic-handlers-interface
|
||||
(export make-predicate-handler
|
||||
make-path-predicate-handler
|
||||
make-host-name-handler
|
||||
make-path-prefix-handler
|
||||
alist-path-dispatcher
|
||||
null-request-handler))
|
||||
|
||||
(define-interface httpd-file-directory-handlers-interface
|
||||
(export home-dir-handler
|
||||
tilde-home-dir-handler
|
||||
rooted-file-handler
|
||||
rooted-file-or-directory-handler))
|
||||
|
||||
(define-interface httpd-seval-handlers-interface
|
||||
(export seval-handler))
|
||||
|
||||
(define-interface httpd-info-gateway-interface
|
||||
(export info-handler
|
||||
find-info-file
|
||||
info-gateway-error))
|
||||
|
||||
(define-interface httpd-rman-gateway-interface
|
||||
(export rman-handler
|
||||
man
|
||||
parse-man-entry
|
||||
cat-man-page
|
||||
find-man-file
|
||||
file->man-directory
|
||||
cat-n-decode
|
||||
nroff-n-decode))
|
||||
|
||||
(define-interface httpd-cgi-handlers-interface
|
||||
(export cgi-default-bin-path
|
||||
cgi-handler))
|
||||
|
||||
(define-interface loser-interface (export loser))
|
||||
|
||||
(define-interface toothless-interface (interface-of scheme))
|
||||
|
||||
(define-interface toothless-eval-interface (export eval-safely))
|
||||
|
||||
;; Structures
|
||||
|
||||
(define-structure sunet-version (export sunet-version-identifier)
|
||||
(open scheme)
|
||||
(begin
|
||||
(define sunet-version-identifier "2.0")))
|
||||
|
||||
;; Net protocols and formats
|
||||
|
||||
(define-structure parse-html-forms parse-html-forms-interface
|
||||
(open scheme-with-scsh
|
||||
let-opt
|
||||
(subset srfi-13 (string-index string-map))
|
||||
receiving
|
||||
uri)
|
||||
(files (lib parse-forms)))
|
||||
|
||||
(define-structure htmlout htmlout-interface
|
||||
(open scheme-with-scsh
|
||||
(subset srfi-13 (string-fold))
|
||||
formats
|
||||
ascii
|
||||
receiving)
|
||||
(files (lib htmlout)))
|
||||
|
||||
(define-structure smtp smtp-interface
|
||||
(open scheme-with-scsh
|
||||
signals conditions
|
||||
define-record-types
|
||||
(subset srfi-1 (filter-map))
|
||||
(subset srfi-13 (string-tokenize string-join))
|
||||
crlf-io ; read-crlf-line write-crlf
|
||||
receiving ; values receive
|
||||
dns ; SYSTEM-FQDN
|
||||
let-opt
|
||||
(subset rfc822 (rfc822-time->string)))
|
||||
(files (lib smtp)))
|
||||
|
||||
(define-structure rfc822 rfc822-interface
|
||||
(open scheme-with-scsh
|
||||
receiving
|
||||
(subset srfi-13 (string-map string-index string-concatenate))
|
||||
let-opt
|
||||
crlf-io
|
||||
ascii)
|
||||
(files (lib rfc822)))
|
||||
|
||||
(define-structure uri uri-interface
|
||||
(open scheme-with-scsh
|
||||
(subset srfi-13 (string-index string-index-right string-fold string-join))
|
||||
let-opt
|
||||
receiving
|
||||
ascii
|
||||
bitwise
|
||||
field-reader-package)
|
||||
(files (lib uri)))
|
||||
|
||||
(define-structure url url-interface
|
||||
(open scheme-with-scsh
|
||||
define-record-types
|
||||
receiving
|
||||
(subset srfi-13 (string-index))
|
||||
uri
|
||||
httpd-errors)
|
||||
(files (lib url)))
|
||||
|
||||
(define-structure ftp-library ftp-library-interface
|
||||
(open scheme-with-scsh
|
||||
(subset signals (call-error))
|
||||
(subset srfi-1 (any))
|
||||
crlf-io)
|
||||
(files (lib ftp-library)))
|
||||
|
||||
(define-structure ftp ftp-interface
|
||||
(open scheme-with-scsh
|
||||
netrc
|
||||
define-record-types
|
||||
finite-types
|
||||
receiving
|
||||
handle
|
||||
conditions
|
||||
signals
|
||||
(subset srfi-13 (string-join string-prefix?))
|
||||
let-opt
|
||||
sunet-utilities
|
||||
format-net
|
||||
crlf-io
|
||||
ftp-library)
|
||||
(files (lib ftp)))
|
||||
|
||||
(define-structure netrc netrc-interface
|
||||
(open scheme-with-scsh
|
||||
define-record-types
|
||||
srfi-14)
|
||||
(files (lib netrc)))
|
||||
|
||||
(define-structure pop3 pop3-interface
|
||||
(open scheme-with-scsh
|
||||
netrc rfc822
|
||||
define-record-types
|
||||
handle
|
||||
conditions handle-fatal-error
|
||||
signals
|
||||
(subset srfi-13 (string-index string-prefix? string-join))
|
||||
let-opt
|
||||
crlf-io)
|
||||
(files (lib pop3)))
|
||||
|
||||
(define-structures ((rfc867 rfc867-interface)
|
||||
(rfc868 rfc868-interface))
|
||||
(open scheme-with-scsh
|
||||
handle-fatal-error)
|
||||
(files (lib nettime)))
|
||||
|
||||
(define-structure dns dns-interface
|
||||
(open scheme-with-scsh
|
||||
(subset srfi-1 (filter reverse! delete lset-difference lset-union))
|
||||
tables
|
||||
ascii
|
||||
formats
|
||||
signals
|
||||
finite-types
|
||||
define-record-types
|
||||
random
|
||||
queues
|
||||
conditions
|
||||
handle
|
||||
sort
|
||||
threads
|
||||
locks)
|
||||
(files (lib dns)))
|
||||
|
||||
(define-structure cgi-scripts cgi-scripts-interface
|
||||
(open scheme-with-scsh
|
||||
parse-html-forms)
|
||||
(files (lib cgi-script)))
|
||||
|
||||
;; Utility libraries
|
||||
|
||||
(define-structure rate-limit rate-limit-interface
|
||||
(open scheme
|
||||
define-record-types
|
||||
locks
|
||||
signals)
|
||||
(files (lib rate-limit)))
|
||||
|
||||
(define-structure crlf-io crlf-io-interface
|
||||
(open scheme-with-scsh
|
||||
ascii ; ascii->char
|
||||
receiving ; MV return (RECEIVE and VALUES)
|
||||
let-opt ; let-optionals
|
||||
threads ; sleep
|
||||
)
|
||||
(files (lib crlf-io)))
|
||||
|
||||
(define-structure ls ls-interface
|
||||
(open scheme-with-scsh
|
||||
handle
|
||||
(subset srfi-1 (filter))
|
||||
bitwise
|
||||
fluids
|
||||
crlf-io)
|
||||
(files (lib ls)))
|
||||
|
||||
(define-structure format-net format-net-interface
|
||||
(open scheme-with-scsh
|
||||
let-opt)
|
||||
(files (lib format-net)))
|
||||
|
||||
(define-structure sunet-utilities sunet-utilities-interface
|
||||
(open scheme-with-scsh
|
||||
format-net
|
||||
sigevents
|
||||
let-opt
|
||||
(subset srfi-13 (string-join))
|
||||
dns
|
||||
let-opt ; :optional
|
||||
locks
|
||||
handle-fatal-error)
|
||||
(files (lib sunet-utilities)))
|
||||
|
||||
(define-structure handle-fatal-error handle-fatal-error-interface
|
||||
(open scheme conditions handle)
|
||||
(files (lib handle-fatal-error)))
|
||||
|
||||
;; FTP server
|
||||
|
||||
(define-structure ftpd ftpd-interface
|
||||
(open scheme-with-scsh
|
||||
conditions handle signals
|
||||
define-record-types
|
||||
handle-fatal-error
|
||||
threads threads-internal ; last one to get CURRENT-THREAD
|
||||
fluids thread-fluids
|
||||
locks
|
||||
(subset srfi-13 (string-map string-trim-both string-index))
|
||||
(subset srfi-1 (partition))
|
||||
crlf-io
|
||||
ls
|
||||
ftp-library
|
||||
dns
|
||||
sunet-version
|
||||
sunet-utilities
|
||||
receiving
|
||||
format-net)
|
||||
(files (ftpd ftpd)))
|
||||
|
||||
;; Web server
|
||||
|
||||
(define-structure httpd-core httpd-core-interface
|
||||
(open scheme-with-scsh
|
||||
thread-fluids ; fork-thread
|
||||
receiving
|
||||
crlf-io ; write-crlf, read-crlf-line
|
||||
rfc822
|
||||
handle ; ignore-errors
|
||||
conditions ; condition-stuff
|
||||
uri
|
||||
url
|
||||
format-net
|
||||
rate-limit ; rate-limiting stuff
|
||||
(subset srfi-13 (string-index))
|
||||
dns ; dns-lookup-ip
|
||||
sunet-utilities ; socket-address->string
|
||||
locks ; make-lock et al.
|
||||
fluids ; let-fluid
|
||||
enumerated ; enum
|
||||
architecture ; os-error
|
||||
|
||||
handle-fatal-error
|
||||
httpd-read-options
|
||||
httpd-errors
|
||||
httpd-logging
|
||||
httpd-requests
|
||||
httpd-responses
|
||||
|
||||
sunet-version
|
||||
)
|
||||
(files (httpd core)))
|
||||
|
||||
(define-structures ((httpd-make-options httpd-make-options-interface)
|
||||
(httpd-read-options httpd-read-options-interface))
|
||||
(open scheme
|
||||
define-record-types)
|
||||
(files (httpd options)))
|
||||
|
||||
(define-structure httpd (compound-interface httpd-core-interface
|
||||
httpd-make-options-interface)
|
||||
(open httpd-core
|
||||
httpd-make-options))
|
||||
|
||||
(define-structure httpd-access-control httpd-access-control-interface
|
||||
(open scheme-with-scsh
|
||||
(subset srfi-1 (any every))
|
||||
httpd-responses
|
||||
httpd-requests
|
||||
httpd-errors
|
||||
(subset srfi-13 (string-map))
|
||||
)
|
||||
(files (httpd access-control)))
|
||||
|
||||
(define-structure httpd-errors httpd-errors-interface
|
||||
(open conditions signals handle scheme)
|
||||
(files (httpd error)))
|
||||
|
||||
(define-structure httpd-logging httpd-logging-interface
|
||||
(open scheme-with-scsh
|
||||
httpd-read-options
|
||||
i/o ; make-null-output-port
|
||||
locks
|
||||
receiving
|
||||
uri ; uri-path->uri
|
||||
url ; http-url-path
|
||||
httpd-requests ; request record
|
||||
httpd-responses
|
||||
formats
|
||||
format-net ; format-internet-host-address
|
||||
(subset srfi-13 (string-join string-trim))
|
||||
rfc822 ; get-header
|
||||
sunet-utilities ; on-interrupt
|
||||
threads ; spawn
|
||||
dns ; dns-lookup-ip
|
||||
define-record-types
|
||||
thread-fluids ; make-preserved-fluid et al.
|
||||
handle-fatal-error
|
||||
)
|
||||
(files (httpd logging)))
|
||||
|
||||
(define-structure httpd-requests httpd-requests-interface
|
||||
(open scheme
|
||||
define-record-types)
|
||||
(files (httpd request)))
|
||||
|
||||
(define-structure httpd-responses httpd-responses-interface
|
||||
(open scheme
|
||||
(subset scsh (format-date write-string time date))
|
||||
syslog
|
||||
define-record-types
|
||||
finite-types
|
||||
formats
|
||||
(subset signals (call-error))
|
||||
httpd-requests
|
||||
httpd-read-options)
|
||||
(files (httpd response)))
|
||||
|
||||
(define-structure httpd-basic-handlers httpd-basic-handlers-interface
|
||||
(open scheme-with-scsh
|
||||
rfc822
|
||||
httpd-requests ; REQUEST record type, v0.9-request
|
||||
(subset srfi-1 (fold-right))
|
||||
(subset srfi-13 (string-trim))
|
||||
httpd-responses
|
||||
httpd-errors
|
||||
)
|
||||
(files (httpd handlers)))
|
||||
|
||||
(define-structure httpd-file-directory-handlers httpd-file-directory-handlers-interface
|
||||
(open scheme-with-scsh
|
||||
httpd-core
|
||||
httpd-requests
|
||||
httpd-responses
|
||||
httpd-errors
|
||||
httpd-basic-handlers
|
||||
httpd-read-options
|
||||
url
|
||||
htmlout
|
||||
crlf-io
|
||||
(subset srfi-13 (string-join))
|
||||
sunet-utilities ; dotdot-check, copy-inport->outport
|
||||
conditions
|
||||
handle-fatal-error
|
||||
)
|
||||
(files (httpd file-dir-handler)))
|
||||
|
||||
(define-structure httpd-seval-handlers httpd-seval-handlers-interface
|
||||
(open scheme-with-scsh ; syscalls & INDEX
|
||||
httpd-errors
|
||||
httpd-requests ; v0.9-request
|
||||
httpd-responses
|
||||
httpd-logging ; http-log
|
||||
uri ; UNESCAPE-URI
|
||||
htmlout ; Formatted HTML output
|
||||
pp
|
||||
(subset srfi-13 (string-skip))
|
||||
rfc822
|
||||
toothless-eval ; EVAL-SAFELY
|
||||
handle ; IGNORE-ERROR
|
||||
parse-html-forms ; PARSE-HTML-FORM-QUERY
|
||||
threads ; SLEEP
|
||||
)
|
||||
(files (httpd seval)))
|
||||
|
||||
(define-structure httpd-info-gateway httpd-info-gateway-interface
|
||||
(open scheme-with-scsh
|
||||
(subset srfi-1 (find))
|
||||
(subset srfi-13 (string-map string-skip string-index))
|
||||
conditions signals handle
|
||||
htmlout
|
||||
httpd-requests
|
||||
httpd-responses
|
||||
httpd-errors
|
||||
url
|
||||
uri
|
||||
handle-fatal-error)
|
||||
(files (httpd info-gateway)))
|
||||
|
||||
(define-structure httpd-rman-gateway httpd-rman-gateway-interface
|
||||
(open scheme-with-scsh
|
||||
httpd-responses
|
||||
httpd-requests
|
||||
httpd-errors
|
||||
conditions
|
||||
url
|
||||
uri
|
||||
htmlout
|
||||
httpd-basic-handlers
|
||||
handle-fatal-error
|
||||
let-opt
|
||||
sunet-utilities
|
||||
(subset srfi-13 (string-join))
|
||||
)
|
||||
(files (httpd rman-gateway)))
|
||||
|
||||
(define-structure httpd-cgi-handlers httpd-cgi-handlers-interface
|
||||
(open scheme-with-scsh
|
||||
(subset srfi-1 (alist-delete))
|
||||
(subset srfi-13 (string-prefix? string-index string-trim substring/shared))
|
||||
rfc822
|
||||
crlf-io ; WRITE-CRLF
|
||||
uri
|
||||
url ; HTTP-URL record type
|
||||
httpd-logging
|
||||
httpd-requests
|
||||
httpd-responses
|
||||
httpd-basic-handlers ; HTTP-HOMEDIR, SERVE-ROOTED-FILE-PATH
|
||||
httpd-errors ; HTTP-ERROR
|
||||
httpd-file-directory-handlers ; dot-dot-check, copy-inport->outport
|
||||
sunet-version
|
||||
formats
|
||||
format-net
|
||||
sunet-utilities ; host-name-or-empty
|
||||
let-opt ; let-optionals
|
||||
handle-fatal-error
|
||||
)
|
||||
(files (httpd cgi-server)))
|
||||
|
||||
(define-structure loser (export loser)
|
||||
(open scheme signals)
|
||||
(begin (define (loser name)
|
||||
(lambda x (error "Illegal call" name)))))
|
||||
|
||||
(define-structure toothless toothless-interface
|
||||
(open scheme loser)
|
||||
(begin
|
||||
(define call-with-input-file (loser "call-with-input-file"))
|
||||
(define call-with-output-file (loser "call-with-output-file"))
|
||||
(define load (loser "load"))
|
||||
(define open-input-file (loser "open-input-file"))
|
||||
(define open-output-file (loser "open-output-file"))
|
||||
(define transcript-on (loser "transcript-on"))
|
||||
(define with-input-from-file (loser "with-input-from-file"))
|
||||
(define with-input-to-file (loser "with-input-to-file"))
|
||||
(define eval (loser "eval"))
|
||||
(define interaction-environment (loser "interaction-environment"))
|
||||
(define scheme-report-environment (loser "scheme-report-environment"))))
|
||||
|
||||
(define-structure toothless-eval toothless-eval-interface
|
||||
(open scheme
|
||||
package-commands-internal ; config-package, get-reflective-tower
|
||||
packages ; structure-package, make-simple-package
|
||||
environments ; environment-ref
|
||||
handle ; ignore-errors
|
||||
)
|
||||
(access toothless) ; Force it to be loaded.
|
||||
(begin
|
||||
|
||||
(define toothless-struct (environment-ref (config-package) 'toothless))
|
||||
(define toothless-package (structure-package toothless-struct))
|
||||
|
||||
(define (new-safe-package)
|
||||
(make-simple-package (list toothless-struct) #t
|
||||
(get-reflective-tower toothless-package) ; ???
|
||||
'safe-env))
|
||||
|
||||
(define (eval-safely exp)
|
||||
(ignore-errors (lambda () (eval exp (new-safe-package)))))))
|
|
@ -1,283 +0,0 @@
|
|||
_XML_ Library
|
||||
=============
|
||||
|
||||
Files: xml.ss xmlr.ss xmls.ss
|
||||
Signature: xml^
|
||||
|
||||
Basic XML Data Types
|
||||
====================
|
||||
|
||||
Document:
|
||||
This structure represents an XML document. The only useful part is
|
||||
the document-element, which contains all the content. The rest of
|
||||
of the structure contains DTD information, which isn't supported,
|
||||
and processing-instructions.
|
||||
|
||||
Element:
|
||||
Each pair of start/end tags and everything in between is an element.
|
||||
It has the following pieces:
|
||||
a name
|
||||
attributes
|
||||
contents including sub-elements
|
||||
Xexpr:
|
||||
S-expression representations of XML data.
|
||||
|
||||
The end of this document has more details.
|
||||
|
||||
Functions
|
||||
=========
|
||||
|
||||
> read-xml : [Input-port] -> Document
|
||||
reads in an XML document from the given or current input port
|
||||
XML documents contain exactly one element. It throws an xml-read:error
|
||||
if there isn't any element or if there are more than one element.
|
||||
|
||||
Malformed xml is reported with source locations in
|
||||
the form `l.c/o', where l is the line number, c is
|
||||
the column number and o is the number of characters
|
||||
from the beginning of the file.
|
||||
|
||||
> write-xml : Document [Output-port] -> Void
|
||||
writes a document to the given or current output port, currently
|
||||
ignoring everything except the document's root element.
|
||||
|
||||
> write-xml/content : Content [Output-port] -> Void
|
||||
writes a document's contents to the given or current output port
|
||||
|
||||
> display-xml : Document [Output-port] -> Void
|
||||
just like write-xml, but newlines and indentation make the output more
|
||||
readable, though less technically correct when white space is
|
||||
significant.
|
||||
|
||||
> display-xml/content : Content [Output-port] -> Void
|
||||
just like write-xml/content, but with indentation and newlines
|
||||
|
||||
> xml->xexpr : Content -> Xexpr
|
||||
converts the interesting part of an XML document into an Xexpression
|
||||
|
||||
> xexpr->xml : Xexpr -> Content
|
||||
converts an Xexpression into the interesting part of an XML document
|
||||
|
||||
> xexpr->string : Xexpression -> String
|
||||
converts an Xexpression into a string representation
|
||||
|
||||
> eliminate-whitespace : (listof Symbol) (Bool -> Bool) -> Element -> Element
|
||||
Some elements should not contain any text, only other tags, except they
|
||||
often contain whitespace for formating purposes. Given a list of tag names
|
||||
and the identity function, eliminate-whitespace produces a function that
|
||||
filters out pcdata consisting solely of whitespace from those elements and
|
||||
raises and error if any non-whitespace text appears. Passing in the function
|
||||
called "not" instead of the identity function filters all elements which are not
|
||||
named in the list. Using void filters all elements regardless of the list.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
> empty-tag-shorthand : 'always | 'never | (listof Symbol)
|
||||
Default: 'always
|
||||
This determines if the output functions should use the <empty/> tag
|
||||
notation instead of writing <empty></empty>. The first form is the
|
||||
preferred XML notation. However, most browsers designed for HTML
|
||||
will only properly render XHTML if the document uses a mixture of the
|
||||
two formats. _html-empty-tags_ contains the W3 consortium's
|
||||
recommended list of XHTML tags that should use the shorthand.
|
||||
|
||||
> collapse-whitespace : Bool
|
||||
Default: #f
|
||||
All consecutive whitespace is replaced by a single space.
|
||||
CDATA sections are not affected.
|
||||
|
||||
> trim-whitespace : Bool
|
||||
This parameter no longer exists. Consider using collapse-whitespace
|
||||
and eliminate-whitespace instead.
|
||||
|
||||
> read-comments : Bool
|
||||
Default: #f
|
||||
Comments, by definition, should be ignored by programs. However,
|
||||
interoperating with ad hoc extentions to other languages sometimes
|
||||
requires processing comments anyway.
|
||||
|
||||
> xexpr-drop-empty-attributes : Bool
|
||||
Default: #f
|
||||
It's easier to write functions processing Xexpressions, if they always
|
||||
have a list of attributes. On the other hand, it's less cumbersome to
|
||||
write Xexpresssions by hand without empty lists of attributes
|
||||
everywhere. Normally xml->xexpr leaves in empty attribute lists.
|
||||
Setting this parameter to #t drops them, so further editing the
|
||||
Xexpression by hand is less annoying.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Reading an Xexpression:
|
||||
(xml->xexpr (document-element (read-xml input-port)))
|
||||
|
||||
Writing an Xexpression:
|
||||
(empty-tag-shorthand html-empty-tags)
|
||||
(write-xml/content (xexpr->xml `(html (head (title ,banner))
|
||||
(body ((bgcolor "white"))
|
||||
,text)))
|
||||
output-port)
|
||||
|
||||
What this Library Doesn't Provide
|
||||
=================================
|
||||
|
||||
Document Type Declaration (DTD) processing
|
||||
Validation
|
||||
Expanding user-defined entites
|
||||
Reading user-defined entites in attributes
|
||||
Unicode support
|
||||
|
||||
XML Datatype Details
|
||||
====================
|
||||
|
||||
Note: Users of the XML collection don't need to know most of these definitions.
|
||||
|
||||
Note: Xexpr is the only important one to understand. Even then,
|
||||
Processing-instructions may be ignored.
|
||||
|
||||
> Xexpr = String
|
||||
| (list* Symbol (listof (list Symbol String)) (list Xexpr))
|
||||
| (cons Symbol (listof Xexpr)) ;; an element with no attributes
|
||||
| Symbol ;; symbolic entities such as
|
||||
| Number ;; numeric entities like 
|
||||
| Misc
|
||||
|
||||
> Document = (make-document Prolog Element (listof Processing-instruction))
|
||||
(define-struct document (prolog element misc))
|
||||
|
||||
> Prolog = (make-prolog (listof Misc) Document-type [Misc ...])
|
||||
(define-struct prolog (misc dtd misc2))
|
||||
The last field is a (listof Misc), but the maker accepts optional
|
||||
arguments instead for backwards compatibility.
|
||||
|
||||
> Document-type = #f | (make-document-type Symbol External-dtd #f)
|
||||
(define-struct document-type (name external inlined))
|
||||
|
||||
> External-dtd = (make-external-dtd/public str str)
|
||||
| (make-external-dtd/system str)
|
||||
| #f
|
||||
(define-struct external-dtd (system))
|
||||
(define-struct (external-dtd/public external-dtd) (public))
|
||||
(define-struct (external-dtd/system external-dtd) ())
|
||||
|
||||
> Element = (make-element Location Location
|
||||
Symbol
|
||||
(listof Attribute)
|
||||
(listof Content))
|
||||
(define-struct (element struct:source) (name attributes content))
|
||||
|
||||
> Attribute = (make-attribute Location Location Symbol String)
|
||||
(define-struct (attribute struct:source) (name value))
|
||||
|
||||
> Content = Pcdata
|
||||
| Element
|
||||
| Entity
|
||||
| Misc
|
||||
|
||||
Misc = Comment
|
||||
| Processing-instruction
|
||||
|
||||
> Pcdata = (make-pcdata Location Location String)
|
||||
(define-struct (pcdata struct:source) (string))
|
||||
|
||||
> Entity = (make-entity (U Nat Symbol))
|
||||
(define-struct entity (text))
|
||||
|
||||
> Processing-instruction = (make-pi Location Location String (list String))
|
||||
(define-struct (pi struct:source) (target-name instruction))
|
||||
|
||||
> Comment = (make-comment String)
|
||||
(define-struct comment (text))
|
||||
|
||||
Source = (make-source Location Location)
|
||||
(define-struct source (start stop))
|
||||
|
||||
Location = Nat
|
||||
| Symbol
|
||||
|
||||
|
||||
The PList Library
|
||||
=================
|
||||
|
||||
Files: plist.ss
|
||||
|
||||
The PList library provides the ability to read and write xml documents which
|
||||
conform to the "plist" DTD, used to store 'dictionaries' of string - value
|
||||
associations.
|
||||
|
||||
To Load
|
||||
=======
|
||||
|
||||
(require (lib "plist.ss" "xml"))
|
||||
|
||||
Functions
|
||||
=========
|
||||
|
||||
> read-plist : Port -> PLDict
|
||||
reads a plist from a port, and produces a 'dict' x-expression
|
||||
|
||||
> write-plist : PLDict Port -> Void
|
||||
writes a plist to the given port. May raise the exn:application:type
|
||||
exception if the plist is badly formed.
|
||||
|
||||
Datatypes
|
||||
=========
|
||||
|
||||
NB: all of these are subtypes of x-expression:
|
||||
|
||||
> PLDict = (list 'dict Assoc-pair ...)
|
||||
|
||||
> PLAssoc-pair = (list 'assoc-pair String PLValue)
|
||||
|
||||
> PLValue = String
|
||||
|
||||
| (list 'true)
|
||||
| (list 'false)
|
||||
| (list 'integer Integer)
|
||||
| (list 'real Real)
|
||||
| PLDict
|
||||
| PLArray
|
||||
|
||||
> PLArray = (list 'array PLValue ...)
|
||||
|
||||
In fact, the PList DTD also defines Data and Date types, but we're ignoring
|
||||
these for the moment.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Here's a sample PLDict:
|
||||
|
||||
(define my-dict
|
||||
`(dict (assoc-pair "first-key"
|
||||
"just a string
|
||||
with some whitespace in it")
|
||||
(assoc-pair "second-key"
|
||||
(false))
|
||||
(assoc-pair "third-key"
|
||||
(dict ))
|
||||
(assoc-pair "fourth-key"
|
||||
(dict (assoc-pair "inner-key"
|
||||
(real 3.432))))
|
||||
(assoc-pair "fifth-key"
|
||||
(array (integer 14)
|
||||
"another string"
|
||||
(true)))
|
||||
(assoc-pair "sixth-key"
|
||||
(array))))
|
||||
|
||||
Let's write it to disk:
|
||||
|
||||
(call-with-output-file "/Users/clements/tmp.plist"
|
||||
(lambda (port)
|
||||
(write-plist my-dict port))
|
||||
'truncate)
|
||||
|
||||
Let's read it back from the disk:
|
||||
|
||||
(define new-dict
|
||||
(call-with-input-file "/Users/clements/tmp.plist"
|
||||
(lambda (port)
|
||||
(read-plist port))))
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
; Taken directly from the SRFI document.
|
||||
|
||||
(define-syntax let-values
|
||||
(syntax-rules ()
|
||||
((let-values (?binding ...) ?body0 ?body1 ...)
|
||||
(let-values "bind" (?binding ...) () (begin ?body0 ?body1 ...)))
|
||||
|
||||
((let-values "bind" () ?tmps ?body)
|
||||
(let ?tmps ?body))
|
||||
|
||||
((let-values "bind" ((?b0 ?e0) ?binding ...) ?tmps ?body)
|
||||
(let-values "mktmp" ?b0 ?e0 () (?binding ...) ?tmps ?body))
|
||||
|
||||
((let-values "mktmp" () ?e0 ?args ?bindings ?tmps ?body)
|
||||
(call-with-values
|
||||
(lambda () ?e0)
|
||||
(lambda ?args
|
||||
(let-values "bind" ?bindings ?tmps ?body))))
|
||||
|
||||
((let-values "mktmp" (?a . ?b) ?e0 (?arg ...) ?bindings (?tmp ...) ?body)
|
||||
(let-values "mktmp" ?b ?e0 (?arg ... x) ?bindings (?tmp ... (?a x)) ?body))
|
||||
|
||||
((let-values "mktmp" ?a ?e0 (?arg ...) ?bindings (?tmp ...) ?body)
|
||||
(call-with-values
|
||||
(lambda () ?e0)
|
||||
(lambda (?arg ... . x)
|
||||
(let-values "bind" ?bindings (?tmp ... (?a x)) ?body))))))
|
||||
|
||||
(define-syntax let*-values
|
||||
(syntax-rules ()
|
||||
((let*-values () ?body0 ?body1 ...)
|
||||
(begin ?body0 ?body1 ...))
|
||||
|
||||
((let*-values (?binding0 ?binding1 ...) ?body0 ?body1 ...)
|
||||
(let-values (?binding0)
|
||||
(let*-values (?binding1 ...) ?body0 ?body1 ...)))))
|
||||
|
||||
(define (add1 x) (+ x 1))
|
||||
(define (sub1 x) (- x 1))
|
||||
|
||||
(define-syntax when
|
||||
(syntax-rules
|
||||
()
|
||||
((when test expr ...)
|
||||
(if test (begin expr ...)))))
|
||||
|
||||
(define-syntax unless
|
||||
(syntax-rules
|
||||
()
|
||||
((unless test expr ...)
|
||||
(if (not test) (begin expr ...)))))
|
||||
|
||||
(define (void . a)
|
||||
(if #f #f))
|
||||
|
||||
(define-syntax begin0
|
||||
(syntax-rules
|
||||
()
|
||||
((begin0 expr1 expr ...)
|
||||
(let ((r expr1))
|
||||
(begin expr ...)
|
||||
r))))
|
||||
|
||||
(define andmap
|
||||
(lambda (f list0 . lists)
|
||||
(if (null? list0)
|
||||
(and)
|
||||
(let loop ((lists (cons list0 lists)))
|
||||
(if (null? (cdr (car lists)))
|
||||
(apply f (map car lists))
|
||||
(and (apply f (map car lists))
|
||||
(loop (map cdr lists))))))))
|
||||
(define null '())
|
||||
|
||||
; stolen from mzlib/functior.ss
|
||||
(define (quicksort l less-than)
|
||||
(let* ((v (list->vector l))
|
||||
(count (vector-length v)))
|
||||
(let loop ((min 0)(max count))
|
||||
(if (< min (sub1 max))
|
||||
(let ((pval (vector-ref v min)))
|
||||
(let pivot-loop ((pivot min)
|
||||
(pos (add1 min)))
|
||||
(if (< pos max)
|
||||
(let ((cval (vector-ref v pos)))
|
||||
(if (less-than cval pval)
|
||||
(begin
|
||||
(vector-set! v pos (vector-ref v pivot))
|
||||
(vector-set! v pivot cval)
|
||||
(pivot-loop (add1 pivot) (add1 pos)))
|
||||
(pivot-loop pivot (add1 pos))))
|
||||
(if (= min pivot)
|
||||
(loop (add1 pivot) max)
|
||||
(begin
|
||||
(loop min pivot)
|
||||
(loop pivot max))))))))
|
||||
(vector->list v)))
|
||||
|
||||
;;; HACK!
|
||||
(define call/ec call-with-current-continuation)
|
||||
(define-syntax let/ec
|
||||
(syntax-rules
|
||||
()
|
||||
((let/ec k expr ...)
|
||||
(call-with-current-continuation (lambda (k) expr ...)))))
|
||||
|
||||
|
||||
;;; HACK!
|
||||
(define (make-parameter val . maybe-guard)
|
||||
(if (null? maybe-guard)
|
||||
(lambda ()
|
||||
val)
|
||||
(lambda ()
|
||||
((car maybe-guard) val))))
|
||||
|
||||
(define (list* . args)
|
||||
(if (null? (cdr args))
|
||||
(car args)
|
||||
(cons (car args) (apply list* (cdr args)))))
|
||||
|
||||
(define (format str . args)
|
||||
(apply (structure-ref big-scheme format) #f str args))
|
||||
|
||||
(define fprintf (structure-ref big-scheme format))
|
||||
|
||||
(define foldr (structure-ref list-lib fold-right))
|
||||
|
||||
(define regexp posix-string->regexp)
|
||||
|
||||
;;; convert "\\1y \\2" to '(1 "y " 2)
|
||||
(define (convert-string str)
|
||||
(let ((e.s
|
||||
(regexp-fold (rx (: "\\" numeric))
|
||||
(lambda (s m nil)
|
||||
(cons (match:end m)
|
||||
(append (cdr nil)
|
||||
(list (substring str (car nil) (match:start m))
|
||||
(string->number
|
||||
(string-drop (match:substring m) 2))))))
|
||||
(cons 0 '()) str)))
|
||||
(append (cdr e.s) (list (substring str (car e.s) (string-length str))))))
|
||||
|
||||
;;; does not handle &
|
||||
(define (regexp-replace* pattern string insert-string)
|
||||
(apply regexp-substitute/global #f pattern string
|
||||
(append (cons 'pre (convert-string insert-string)) (list 'post))))
|
||||
|
||||
(define (compose f g)
|
||||
(lambda (x)
|
||||
(call-with-values (lambda () (g x)) f)))
|
||||
|
||||
(define open-output-string make-string-output-port)
|
||||
(define get-output-string string-output-port-output)
|
|
@ -1,378 +0,0 @@
|
|||
;; Token ::= Contents | Start-tag | End-tag | Eof
|
||||
|
||||
(define read-comments (make-parameter #f))
|
||||
(define collapse-whitespace (make-parameter #f))
|
||||
|
||||
;; read-xml : [Input-port] -> Document
|
||||
(define (read-xml . maybe-port)
|
||||
(read-from-port (if (null? maybe-port) (current-input-port) (car maybe-port))))
|
||||
|
||||
|
||||
;; read-from-port : Input-port -> Document
|
||||
(define (read-from-port in)
|
||||
(let*-values (((in pos) (positionify in))
|
||||
((misc0 start) (read-misc in pos)))
|
||||
(make-document (make-prolog misc0 #f)
|
||||
(cond
|
||||
((start-tag? start) (read-element start in pos))
|
||||
((element? start) start)
|
||||
(else (error 'read-xml "expected root element - received ~a" start)))
|
||||
(let-values (((misc1 end-of-file) (read-misc in pos)))
|
||||
(unless (eof-object? end-of-file)
|
||||
(error 'read-xml "extra stuff at end of document ~a" end-of-file))
|
||||
misc1))))
|
||||
|
||||
;; read-misc : Input-port (-> Location) -> (listof Misc) Token
|
||||
(define (read-misc in pos)
|
||||
(let read-more ()
|
||||
(let ((x (lex in pos)))
|
||||
(cond
|
||||
((or (pi? x) (comment? x))
|
||||
(let-values (((lst next) (read-more)))
|
||||
(values (cons x lst) next)))
|
||||
((and (pcdata? x) (andmap char-whitespace? (string->list (pcdata-string x))))
|
||||
(read-more))
|
||||
(else (values null x))))))
|
||||
|
||||
;; read-element : Start-tag Input-port (-> Location) -> Element
|
||||
(define (read-element start in pos)
|
||||
(let ((name (start-tag-name start))
|
||||
(a (source-start start))
|
||||
(b (source-stop start)))
|
||||
(make-element
|
||||
a b name (start-tag-attrs start)
|
||||
(let read-content ()
|
||||
(let ((x (lex in pos)))
|
||||
(cond
|
||||
((eof-object? x)
|
||||
(error 'read-xml "unclosed ~a tag at [~a ~a]" name
|
||||
(format-source a)
|
||||
(format-source b)))
|
||||
((start-tag? x) (cons (read-element x in pos) (read-content)))
|
||||
((end-tag? x)
|
||||
(unless (eq? name (end-tag-name x))
|
||||
(error 'read-xml "start tag ~a at [~a ~a] doesn't match end tag ~a at [~a ~a]"
|
||||
name
|
||||
(format-source a)
|
||||
(format-source b)
|
||||
(end-tag-name x)
|
||||
(format-source (source-start x))
|
||||
(format-source (source-stop x))))
|
||||
null)
|
||||
((entity? x) (cons (expand-entity x) (read-content)))
|
||||
((comment? x) (if (read-comments)
|
||||
(cons x (read-content))
|
||||
(read-content)))
|
||||
(else (cons x (read-content)))))))))
|
||||
|
||||
;; expand-entity : Entity -> (U Entity Pcdata)
|
||||
;; more here - allow expansion of user defined entities
|
||||
(define (expand-entity x)
|
||||
(let ((expanded (default-entity-table (entity-text x))))
|
||||
(if expanded
|
||||
(make-pcdata (source-start x) (source-stop x) expanded)
|
||||
x)))
|
||||
|
||||
;; default-entity-table : Symbol -> (U #f String)
|
||||
(define (default-entity-table name)
|
||||
(case name
|
||||
((amp) "&")
|
||||
((lt) "<")
|
||||
((gt) ">")
|
||||
((quot) "\"")
|
||||
((apos) "'")
|
||||
(else #f)))
|
||||
|
||||
;; lex : Input-port (-> Location) -> Token
|
||||
(define (lex in pos)
|
||||
(let ((c (peek-char in)))
|
||||
(cond
|
||||
((eof-object? c) c)
|
||||
((eq? c #\&) (lex-entity in pos))
|
||||
((eq? c #\<) (lex-tag-cdata-pi-comment in pos))
|
||||
(else (lex-pcdata in pos)))))
|
||||
|
||||
;; lex-entity : Input-port (-> Location) -> Entity
|
||||
(define (lex-entity in pos)
|
||||
(let ((start (pos)))
|
||||
(read-char in)
|
||||
(let ((data (case (peek-char in)
|
||||
((#\#)
|
||||
(read-char in)
|
||||
(let ((n (case (peek-char in)
|
||||
((#\x) (read-char in)
|
||||
(string->number (read-until #\; in pos) 16))
|
||||
(else (string->number (read-until #\; in pos))))))
|
||||
(unless (number? n)
|
||||
(lex-error in pos "malformed numeric entity"))
|
||||
n))
|
||||
(else
|
||||
(begin0
|
||||
(lex-name in pos)
|
||||
(unless (eq? (read-char in) #\;)
|
||||
(lex-error in pos "expected ; at the end of an entity")))))))
|
||||
(make-entity start (pos) data))))
|
||||
|
||||
;; lex-tag-cdata-pi-comment : Input-port (-> Location) -> Start-tag | Element | End-tag | Pcdata | Pi | Comment
|
||||
(define (lex-tag-cdata-pi-comment in pos)
|
||||
(let ((start (pos)))
|
||||
(read-char in)
|
||||
(case (non-eof peek-char in pos)
|
||||
((#\!)
|
||||
(read-char in)
|
||||
(case (non-eof peek-char in pos)
|
||||
((#\-) (read-char in)
|
||||
(unless (eq? (read-char in) #\-)
|
||||
(lex-error in pos "expected second - after <!-"))
|
||||
(let ((data (lex-comment-contents in pos)))
|
||||
(unless (eq? (read-char in) #\>)
|
||||
(lex-error in pos "expected > to end comment (\"--\" can't appear in comments)"))
|
||||
;(make-comment start (pos) data)
|
||||
(make-comment data)))
|
||||
((#\[) (read-char in)
|
||||
(unless (string=? (read-string 6 in) "CDATA[")
|
||||
(lex-error in pos "expected CDATA following <["))
|
||||
(let ((data (lex-cdata-contents in pos)))
|
||||
(make-pcdata start (pos) data)))
|
||||
(else (skip-dtd in pos)
|
||||
(skip-space in)
|
||||
(unless (eq? (peek-char in) #\<)
|
||||
(lex-error in pos "expected pi, comment, or element after doctype"))
|
||||
(lex-tag-cdata-pi-comment in pos))))
|
||||
((#\?) (read-char in)
|
||||
(let ((name (lex-name in pos)))
|
||||
(skip-space in)
|
||||
(let ((data (lex-pi-data in pos)))
|
||||
(make-pi start (pos) name data))))
|
||||
((#\/) (read-char in)
|
||||
(let ((name (lex-name in pos)))
|
||||
(skip-space in)
|
||||
(unless (eq? (read-char in) #\>)
|
||||
(lex-error in pos "expected > to close ~a's end tag" name))
|
||||
(make-end-tag start (pos) name)))
|
||||
(else
|
||||
(let ((name (lex-name in pos))
|
||||
(attrs (lex-attributes in pos)))
|
||||
(skip-space in)
|
||||
(case (read-char in)
|
||||
((#\/)
|
||||
(unless (eq? (read-char in) #\>)
|
||||
(lex-error in pos "expected > to close empty element ~a" name))
|
||||
(make-element start (pos) name attrs null))
|
||||
((#\>) (make-start-tag start (pos) name attrs))
|
||||
(else (lex-error in pos "expected / or > to close tag `~a'" name))))))))
|
||||
|
||||
;; lex-attributes : Input-port (-> Location) -> (listof Attribute)
|
||||
(define (lex-attributes in pos)
|
||||
(quicksort (let loop ()
|
||||
(skip-space in)
|
||||
(cond
|
||||
((name-start? (peek-char in))
|
||||
(cons (lex-attribute in pos) (loop)))
|
||||
(else null)))
|
||||
(lambda (a b)
|
||||
(let ((na (attribute-name a))
|
||||
(nb (attribute-name b)))
|
||||
(cond
|
||||
((eq? na nb) (lex-error in pos "duplicated attribute name ~a" na))
|
||||
(else (string<? (symbol->string na) (symbol->string nb))))))))
|
||||
|
||||
;; lex-attribute : Input-port (-> Location) -> Attribute
|
||||
(define (lex-attribute in pos)
|
||||
(let ((start (pos))
|
||||
(name (lex-name in pos)))
|
||||
(skip-space in)
|
||||
(unless (eq? (read-char in) #\=)
|
||||
(lex-error in pos "expected = in attribute ~a" name))
|
||||
(skip-space in)
|
||||
;; more here - handle entites and disallow "<"
|
||||
(let* ((delimiter (read-char in))
|
||||
(value (case delimiter
|
||||
((#\' #\")
|
||||
(list->string
|
||||
(let read-more ()
|
||||
(let ((c (non-eof peek-char in pos)))
|
||||
(cond
|
||||
((eq? c delimiter) (read-char in) null)
|
||||
((eq? c #\&)
|
||||
(let ((entity (expand-entity (lex-entity in pos))))
|
||||
(if (pcdata? entity)
|
||||
(append (string->list (pcdata-string entity)) (read-more))
|
||||
;; more here - do something with user defined entites
|
||||
(read-more))))
|
||||
(else (read-char in) (cons c (read-more))))))))
|
||||
(else (lex-error in pos "attribute values must be in ''s or in \"\"s")))))
|
||||
(make-attribute start (pos) name value))))
|
||||
|
||||
;; skip-space : Input-port -> Void
|
||||
;; deviation - should sometimes insist on at least one space
|
||||
(define (skip-space in)
|
||||
(let loop ()
|
||||
(let ((c (peek-char in)))
|
||||
(when (and (not (eof-object? c)) (char-whitespace? c))
|
||||
(read-char in)
|
||||
(loop)))))
|
||||
|
||||
;; lex-pcdata : Input-port (-> Location) -> Pcdata
|
||||
;; deviation - disallow ]]> "for compatability" with SGML, sec 2.4 XML spec
|
||||
(define (lex-pcdata in pos)
|
||||
(let ((start (pos))
|
||||
(data (let loop ()
|
||||
(let ((next (peek-char in)))
|
||||
(cond
|
||||
((or (eof-object? next) (eq? next #\&) (eq? next #\<))
|
||||
null)
|
||||
((and (char-whitespace? next) (collapse-whitespace))
|
||||
(skip-space in)
|
||||
(cons #\space (loop)))
|
||||
(else (cons (read-char in) (loop))))))))
|
||||
(make-pcdata start
|
||||
(pos)
|
||||
(list->string data))))
|
||||
|
||||
;; lex-name : Input-port (-> Location) -> Symbol
|
||||
(define (lex-name in pos)
|
||||
(let ((c (read-char in)))
|
||||
(unless (name-start? c)
|
||||
(lex-error in pos "expected name, received ~a" c))
|
||||
(string->symbol
|
||||
(list->string
|
||||
(cons c (let lex-rest ()
|
||||
(cond
|
||||
((name-char? (peek-char in))
|
||||
(cons (read-char in) (lex-rest)))
|
||||
(else null))))))))
|
||||
|
||||
;; skip-dtd : Input-port (-> Location) -> Void
|
||||
(define (skip-dtd in pos)
|
||||
(let skip ()
|
||||
(case (non-eof read-char in pos)
|
||||
((#\') (read-until #\' in pos) (skip))
|
||||
((#\") (read-until #\" in pos) (skip))
|
||||
((#\<)
|
||||
(case (non-eof read-char in pos)
|
||||
((#\!) (case (non-eof read-char in pos)
|
||||
((#\-) (read-char in) (lex-comment-contents in pos) (read-char in) (skip))
|
||||
(else (skip) (skip))))
|
||||
((#\?) (lex-pi-data in pos) (skip))
|
||||
(else (skip) (skip))))
|
||||
((#\>) (void))
|
||||
(else (skip)))))
|
||||
|
||||
;; name-start? : Char -> Bool
|
||||
(define (name-start? ch)
|
||||
(or (char-alphabetic? ch)
|
||||
(eq? ch #\_)
|
||||
(eq? ch #\:)))
|
||||
|
||||
;; name-char? : Char -> Bool
|
||||
(define (name-char? ch)
|
||||
(or (name-start? ch)
|
||||
(char-numeric? ch)
|
||||
(eq? ch #\.)
|
||||
(eq? ch #\-)))
|
||||
|
||||
;; read-until : Char Input-port (-> Location) -> String
|
||||
;; discards the stop character, too
|
||||
(define (read-until char in pos)
|
||||
(list->string
|
||||
(let read-more ()
|
||||
(let ((c (non-eof read-char in pos)))
|
||||
(cond
|
||||
((eq? c char) null)
|
||||
(else (cons c (read-more))))))))
|
||||
|
||||
;; non-eof : (Input-port -> (U Char Eof)) Input-port (-> Location) -> Char
|
||||
(define (non-eof f in pos)
|
||||
(let ((c (f in)))
|
||||
(cond
|
||||
((eof-object? c) (lex-error in pos "unexpected eof"))
|
||||
(else c))))
|
||||
|
||||
;; gen-read-until-string : String -> Input-port (-> Location) -> String
|
||||
;; uses Knuth-Morris-Pratt from
|
||||
;; Introduction to Algorithms, Cormen, Leiserson, and Rivest, pages 869-876
|
||||
;; discards stop from input
|
||||
(define (gen-read-until-string stop)
|
||||
(let* ((len (string-length stop))
|
||||
(prefix (make-vector len 0))
|
||||
(fall-back
|
||||
(lambda (k c)
|
||||
(let ((k (let loop ((k k))
|
||||
(cond
|
||||
((and (> k 0) (not (eq? (string-ref stop k) c)))
|
||||
(loop (vector-ref prefix (sub1 k))))
|
||||
(else k)))))
|
||||
(if (eq? (string-ref stop k) c)
|
||||
(add1 k)
|
||||
k)))))
|
||||
(let init ((k 0) (q 1))
|
||||
(when (< q len)
|
||||
(let ((k (fall-back k (string-ref stop q))))
|
||||
(vector-set! prefix q k)
|
||||
(init k (add1 q)))))
|
||||
;; (vector-ref prefix x) = the longest suffix that matches a prefix of stop
|
||||
(lambda (in pos)
|
||||
(list->string
|
||||
(let/ec out
|
||||
(let loop ((matched 0) (out out))
|
||||
(let* ((c (non-eof read-char in pos))
|
||||
(matched (fall-back matched c)))
|
||||
(cond
|
||||
((= matched len) (out null))
|
||||
((zero? matched) (cons c (let/ec out (loop matched out))))
|
||||
(else (cons c (loop matched out)))))))))))
|
||||
|
||||
;; "-->" makes more sense, but "--" follows the spec.
|
||||
(define lex-comment-contents (gen-read-until-string "--"))
|
||||
(define lex-pi-data (gen-read-until-string "?>"))
|
||||
(define lex-cdata-contents (gen-read-until-string "]]>"))
|
||||
|
||||
;; positionify : Input-port -> Input-port (-> Location)
|
||||
|
||||
;; Well, this really depends on scsh-0.6 and should be replace by
|
||||
;; big-scheme's more-port
|
||||
;; For S48 you probably need to do something completely different
|
||||
|
||||
(define (positionify in)
|
||||
(let ((line 1)
|
||||
(char 0)
|
||||
(offset 0)
|
||||
(old-handler (port-handler in)))
|
||||
(set-port-buffering in bufpol/block 1)
|
||||
(let ((handler (make-buffered-input-port-handler
|
||||
(port-handler-discloser old-handler)
|
||||
(port-handler-close old-handler)
|
||||
(lambda (data buffer start needed)
|
||||
(let ((res
|
||||
((port-handler-buffer-proc old-handler)
|
||||
data buffer start needed)))
|
||||
(if (number? res)
|
||||
(begin
|
||||
(set! char (add1 char))
|
||||
(set! offset (add1 offset))
|
||||
(let ((c (byte-vector-ref buffer 0)))
|
||||
(when (= c 10)
|
||||
(set! line (+ line 1))
|
||||
(set! char -1)))))
|
||||
res))
|
||||
(port-handler-ready? old-handler)
|
||||
(port-handler-steal old-handler))))
|
||||
(set-port-handler! in handler)
|
||||
(values in
|
||||
(lambda ()
|
||||
(make-location line char offset))))))
|
||||
; (- n (- (port-limit in) (port-index in))))))))
|
||||
|
||||
;; lex-error : Input-port String (-> Location) TST* -> alpha
|
||||
(define (lex-error in pos str . rest)
|
||||
(error 'lex-error " at position:" (format-source (pos)) str rest))
|
||||
|
||||
;; format-source : Location -> string
|
||||
;; to format the source location for an error message
|
||||
(define (format-source loc)
|
||||
(if (location? loc)
|
||||
(format #f "~a.~a/~a" (location-line loc) (location-char loc) (location-offset loc))
|
||||
(format #f "~a" loc)))
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
;; eliminate-whitespace : (listof Symbol) (Bool -> Bool) -> Element -> Element
|
||||
(define (eliminate-whitespace special eliminate-special?)
|
||||
(letrec ((blank-it
|
||||
(lambda (el)
|
||||
(let ((name (element-name el))
|
||||
(content (map (lambda (x)
|
||||
(if (element? x) (blank-it x) x))
|
||||
(element-content el))))
|
||||
(make-element
|
||||
(source-start el)
|
||||
(source-stop el)
|
||||
name
|
||||
(element-attributes el)
|
||||
(cond
|
||||
((eliminate-special? (memq (element-name el) special))
|
||||
(filter (lambda (s)
|
||||
(not (and (pcdata? s)
|
||||
(or (all-blank (pcdata-string s))
|
||||
(error 'eliminate-blanks "Element <~a> is not allowed to contain text ~s" name (pcdata-string s))))))
|
||||
content))
|
||||
(else content)))))))
|
||||
blank-it))
|
||||
|
||||
;; all-blank : String -> Bool
|
||||
(define (all-blank s)
|
||||
(andmap char-whitespace? (string->list s)))
|
|
@ -1,194 +0,0 @@
|
|||
; Location = (make-location Nat Nat) | Symbol
|
||||
(define-record-type location :location
|
||||
(make-location line char offset)
|
||||
location?
|
||||
(line location-line)
|
||||
(char location-char)
|
||||
(offset location-offset))
|
||||
|
||||
;; Source ::= (make-source Location Location)
|
||||
(define-record-type source :source
|
||||
(make-source start stop)
|
||||
really-source?
|
||||
(start really-source-start)
|
||||
(stop really-source-stop))
|
||||
|
||||
(define (source-start obj)
|
||||
(cond ((element? obj) (element-start obj))
|
||||
((attribute? obj) (attribute-start obj))
|
||||
((pcdata? obj) (pcdata-start obj))
|
||||
((entity? obj) (entity-start obj))
|
||||
((pi? obj) (pi-start obj))
|
||||
((start-tag? obj) (start-tag-start obj))
|
||||
((end-tag? obj) (end-tag-start obj))
|
||||
(else (really-source-start obj))))
|
||||
|
||||
(define (source-stop obj)
|
||||
(cond ((element? obj) (element-stop obj))
|
||||
((attribute? obj) (attribute-stop obj))
|
||||
((pcdata? obj) (pcdata-stop obj))
|
||||
((entity? obj) (entity-stop obj))
|
||||
((pi? obj) (pi-stop obj))
|
||||
((start-tag? obj) (start-tag-stop obj))
|
||||
((end-tag? obj) (end-tag-stop obj))
|
||||
(else (really-source-stop obj))))
|
||||
|
||||
(define (does-any-satisfy? preds obj)
|
||||
(if (null? preds)
|
||||
#f
|
||||
(or ((car preds) obj) (does-any-satisfy? (cdr preds) obj))))
|
||||
|
||||
(define (source? obj)
|
||||
(does-any-satisfy? (list really-source? element? attribute? pcdata?
|
||||
entity? pi? start-tag? end-tag?)
|
||||
obj))
|
||||
|
||||
;; Document ::= (make-document Prolog Element (listof Misc))
|
||||
(define-record-type document :document
|
||||
(make-document prolog element misc)
|
||||
document?
|
||||
(prolog document-prolog)
|
||||
(element document-element)
|
||||
(misc document-misc))
|
||||
|
||||
; Prolog = (make-prolog (listof Misc) Document-type [Misc ...])
|
||||
; The Misc items after the Document-type are optional arguments to maintain
|
||||
; backward compatability with older versions of the XML library.
|
||||
;(define-struct prolog (misc dtd misc2))
|
||||
|
||||
(define-record-type prolog :prolog
|
||||
(really-make-prolog misc dtd misc2)
|
||||
prolog?
|
||||
(misc prolog-misc)
|
||||
(dtd prolog-dtd)
|
||||
(misc2 prolog-misc2))
|
||||
|
||||
(define (make-prolog misc dtd . misc2)
|
||||
(really-make-prolog misc dtd misc2))
|
||||
|
||||
;;; Document-type = (make-document-type sym External-dtd #f)
|
||||
;;; | #f
|
||||
|
||||
(define-record-type document-type :document-type
|
||||
(make-document-type name external inlined)
|
||||
really-document-type?
|
||||
(name document-type-name)
|
||||
(external document-type-external)
|
||||
(inlined document-type-inlined))
|
||||
|
||||
;;; External-dtd = (make-external-dtd/public str str)
|
||||
;;; | (make-external-dtd/system str)
|
||||
;;; | #f
|
||||
(define-record-type external-dtd :external-dtd
|
||||
(make-external-dtd system)
|
||||
really-external-dtd?
|
||||
(system really-external-dtd-system))
|
||||
|
||||
(define (external-dtd-system external-dtd)
|
||||
(cond ((really-external-dtd? external-dtd)
|
||||
(really-external-dtd-system external-dtd))
|
||||
((external-dtd/public? external-dtd)
|
||||
(external-dtd/public-system external-dtd))
|
||||
((external-dtd/system? external-dtd)
|
||||
(external-dtd/system-system external-dtd))
|
||||
(else (error "bottom of external-dtd-system" external-dtd))))
|
||||
|
||||
(define (external-dtd? obj)
|
||||
(does-any-satisfy? (list really-external-dtd? external-dtd/public?
|
||||
external-dtd/system?)
|
||||
obj))
|
||||
|
||||
(define-record-type external-dtd/public :external-dtd/public
|
||||
(make-external-dtd/public system public)
|
||||
external-dtd/public?
|
||||
(system external-dtd/public-system)
|
||||
(public external-dtd/public-public))
|
||||
|
||||
(define-record-type external-dtd/system :external-dtd/system
|
||||
(make-external-dtd/system system)
|
||||
external-dtd/system?
|
||||
(system external-dtd/system-system))
|
||||
|
||||
|
||||
;; Element ::= (make-element Location Location Symbol (listof Attribute) (listof Content))
|
||||
(define-record-type element :element
|
||||
(make-element start stop name attributes content)
|
||||
element?
|
||||
(start element-start)
|
||||
(stop element-stop)
|
||||
(name element-name)
|
||||
(attributes element-attributes)
|
||||
(content element-content))
|
||||
|
||||
;; Attribute ::= (make-attribute Location Location Symbol String)
|
||||
(define-record-type attribute :attribute
|
||||
(make-attribute start stop name value)
|
||||
attribute?
|
||||
(start attribute-start)
|
||||
(stop attribute-stop)
|
||||
(name attribute-name)
|
||||
(value attribute-value))
|
||||
|
||||
;; Pcdata ::= (make-pcdata Location Location String)
|
||||
(define-record-type pcdata :pcdata
|
||||
(make-pcdata start stop string)
|
||||
pcdata?
|
||||
(start pcdata-start)
|
||||
(stop pcdata-stop)
|
||||
(string pcdata-string))
|
||||
|
||||
;; Content ::= Pcdata
|
||||
;; | Element
|
||||
;; | Entity
|
||||
;; | Misc
|
||||
|
||||
;; Misc ::= Comment
|
||||
;; | Processing-instruction
|
||||
|
||||
;; Entity ::= (make-entity Location Location (U Nat Symbol))
|
||||
(define-record-type entity :entity
|
||||
(make-entity start stop text)
|
||||
entity?
|
||||
(start entity-start)
|
||||
(stop entity-stop)
|
||||
(text entity-text))
|
||||
|
||||
;; Processing-instruction ::= (make-pi Location Location String (list String))
|
||||
;; also represents XMLDecl
|
||||
(define-record-type pi :pi
|
||||
(make-pi start stop target-name instruction)
|
||||
pi?
|
||||
(start pi-start)
|
||||
(stop pi-stop)
|
||||
(target-name pi-target-name)
|
||||
(instruction pi-instruction))
|
||||
|
||||
;; Comment ::= (make-comment String)
|
||||
(define-record-type comment :comment
|
||||
(make-comment text)
|
||||
comment?
|
||||
(text comment-text))
|
||||
|
||||
;; content? : TST -> Bool
|
||||
(define (content? x)
|
||||
(or (pcdata? x) (element? x) (entity? x) (comment? x) (pi? x)))
|
||||
|
||||
|
||||
;;; moved here from reader as it inherits from source
|
||||
;; Start-tag ::= (make-start-tag Location Location Symbol (listof Attribute))
|
||||
(define-record-type start-tag :start-tag
|
||||
(make-start-tag start stop name attrs)
|
||||
start-tag?
|
||||
(start start-tag-start)
|
||||
(stop start-tag-stop)
|
||||
(name start-tag-name)
|
||||
(attrs start-tag-attrs))
|
||||
|
||||
|
||||
;; End-tag ::= (make-end-tag Location Location Symbol)
|
||||
(define-record-type end-tag :end-tag
|
||||
(make-end-tag start stop name)
|
||||
end-tag?
|
||||
(start end-tag-start)
|
||||
(stop end-tag-stop)
|
||||
(name end-tag-name))
|
|
@ -1,155 +0,0 @@
|
|||
|
||||
;;(define empty-tag-shorthand (make-parameter #t))
|
||||
;;(define empty-tag-shorthand (make-parameter void))
|
||||
|
||||
;; (empty-tag-shorthand) : (U 'always 'never (listof Symbol))
|
||||
(define empty-tag-shorthand
|
||||
(make-parameter 'always
|
||||
(lambda (x)
|
||||
(if (or (eq? x 'always) (eq? x 'never) (and (list? x) (andmap symbol? x)))
|
||||
x
|
||||
(error 'empty-tag-shorthand "expected 'always, 'never, or a list of symbols: received ~a" x)))))
|
||||
|
||||
(define html-empty-tags '(param meta link isindex input img hr frame col br basefont base area))
|
||||
|
||||
;; var-argify : (a Output-port -> b) -> (a [Output-port] -> b)
|
||||
(define (var-argify f)
|
||||
(lambda (x . maybe-port)
|
||||
(f x (if (null? maybe-port)
|
||||
(current-output-port)
|
||||
(car maybe-port)))))
|
||||
|
||||
;; gen-write/display-xml/content : (Nat Output-port -> Void) -> Content [Output-Port]-> Void
|
||||
(define (gen-write/display-xml/content dent)
|
||||
(var-argify (lambda (c out)
|
||||
(write-xml-content c 0 dent out))))
|
||||
|
||||
;; indent : Nat Output-port -> Void
|
||||
(define (indent n out)
|
||||
(newline out)
|
||||
(let loop ((n n))
|
||||
(unless (zero? n)
|
||||
(display #\space out)
|
||||
(loop (sub1 n)))))
|
||||
|
||||
;; write-xml/content : Content [Output-port] -> Void
|
||||
(define write-xml/content (gen-write/display-xml/content void))
|
||||
|
||||
;; display-xml/content : Content [Output-port] -> Void
|
||||
(define display-xml/content (gen-write/display-xml/content indent))
|
||||
|
||||
;; gen-write/display-xml : (Content [Output-port] -> Void) -> Document [Output-port] -> Void
|
||||
(define (gen-write/display-xml output-content)
|
||||
(var-argify (lambda (doc out)
|
||||
(let ((prolog (document-prolog doc)))
|
||||
(display-outside-misc (prolog-misc prolog) out)
|
||||
(display-dtd (prolog-dtd prolog) out)
|
||||
(display-outside-misc (prolog-misc2 prolog) out))
|
||||
(output-content (document-element doc) out)
|
||||
(display-outside-misc (document-misc doc) out))))
|
||||
|
||||
; display-dtd : document-type oport -> void
|
||||
(define (display-dtd dtd out)
|
||||
(when dtd
|
||||
(fprintf out "<!DOCTYPE ~a" (document-type-name dtd))
|
||||
(let ((external (document-type-external dtd)))
|
||||
(cond
|
||||
((external-dtd/public? external)
|
||||
(fprintf out " PUBLIC \"~a\" \"~a\""
|
||||
(external-dtd/public-public external)
|
||||
(external-dtd-system external)))
|
||||
((external-dtd/system? external)
|
||||
(fprintf out " SYSTEM \"~a\"" (external-dtd-system external)))
|
||||
((not external) (void))))
|
||||
(display ">" out)
|
||||
(newline out)))
|
||||
|
||||
;; write-xml : Document [Output-port] -> Void
|
||||
(define write-xml (gen-write/display-xml write-xml/content))
|
||||
|
||||
;; display-xml : Document [Output-port] -> Void
|
||||
(define display-xml (gen-write/display-xml display-xml/content))
|
||||
|
||||
;; display-outside-misc : (listof Misc) Output-port -> Void
|
||||
(define (display-outside-misc misc out)
|
||||
(for-each (lambda (x)
|
||||
((cond
|
||||
((comment? x) write-xml-comment)
|
||||
((pi? x) write-xml-pi)
|
||||
(else (error "bottom " x))) x 0 void out)
|
||||
(newline out))
|
||||
misc))
|
||||
|
||||
;; write-xml-content : Content Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
||||
(define (write-xml-content el over dent out)
|
||||
((cond
|
||||
((element? el) write-xml-element)
|
||||
((pcdata? el) write-xml-pcdata)
|
||||
((entity? el) write-xml-entity)
|
||||
((comment? el) write-xml-comment)
|
||||
((pi? el) write-xml-pi)
|
||||
(else (error 'write-xml-content "received ~a" el)))
|
||||
el over dent out))
|
||||
|
||||
;; write-xml-element : Element Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
||||
(define (write-xml-element el over dent out)
|
||||
(let* ((name (element-name el))
|
||||
(start (lambda (f) (write-xml-base (format f name) over dent out)))
|
||||
(content (element-content el)))
|
||||
(start "<~a")
|
||||
(for-each (lambda (att)
|
||||
(fprintf out " ~s=~s" (attribute-name att)
|
||||
(escape (attribute-value att) escape-attribute-table)))
|
||||
(element-attributes el))
|
||||
(if (and (null? content)
|
||||
(let ((short (empty-tag-shorthand)))
|
||||
(case short
|
||||
((always) #t)
|
||||
((never) #f)
|
||||
(else (memq name short)))))
|
||||
(fprintf out " />")
|
||||
(begin
|
||||
(fprintf out ">")
|
||||
(for-each (lambda (c) (write-xml-content c (incr over) dent out)) content)
|
||||
(start "</~a")
|
||||
(fprintf out ">")))))
|
||||
|
||||
;; write-xml-base : (U String Char Symbol) Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
||||
(define (write-xml-base el over dent out)
|
||||
(dent over out)
|
||||
(display el out))
|
||||
|
||||
;; write-xml-pcdata : Pcdata Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
||||
(define (write-xml-pcdata str over dent out)
|
||||
(write-xml-base (escape (pcdata-string str) escape-table) over dent out))
|
||||
|
||||
;; write-xml-pi : Processing-instruction Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
||||
(define (write-xml-pi pi over dent out)
|
||||
(write-xml-base (format "<?~a ~a?>" (pi-target-name pi) (pi-instruction pi)) over dent out))
|
||||
|
||||
;; write-xml-comment : Comment Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
||||
(define (write-xml-comment comment over dent out)
|
||||
(write-xml-base (format "<!--~a-->" (comment-text comment)) over dent out))
|
||||
|
||||
;; write-xml-entity : Entity Nat (Nat Output-stream -> Void) Output-stream -> Void
|
||||
(define (write-xml-entity entity over dent out)
|
||||
(let ((n (entity-text entity)))
|
||||
(fprintf out (if (number? n) "&#~a;" "&~a;") n)))
|
||||
|
||||
(define escape-table
|
||||
(map (lambda (x y) (cons (regexp (symbol->string x)) y))
|
||||
'(< > &)
|
||||
'("<" ">" "&")))
|
||||
|
||||
(define escape-attribute-table
|
||||
(list* (cons (regexp "'") "'") (cons (regexp "\"") """) escape-table))
|
||||
|
||||
;; escape : String -> String
|
||||
;; more here - this could be much more efficient
|
||||
(define (escape x table)
|
||||
(foldr (lambda (esc str) (regexp-replace* (car esc) str (cdr esc)))
|
||||
x
|
||||
table))
|
||||
|
||||
;; incr : Nat -> Nat
|
||||
(define (incr n) (+ n 2))
|
|
@ -1,81 +0,0 @@
|
|||
; (import xml-structs^ writer^ mzlib:function^)
|
||||
;; Xexpr ::= String
|
||||
;; | (list* Symbol (listof Attribute-srep) (listof Xexpr))
|
||||
;; | (cons Symbol (listof Xexpr))
|
||||
;; | Symbol
|
||||
;; | Nat
|
||||
;; | Comment
|
||||
;; | Processing-instruction
|
||||
;; Attribute-srep ::= (list Symbol String)
|
||||
|
||||
;; sorting is no longer necessary, since xt3d uses xml->zxexpr, which sorts.
|
||||
|
||||
;; assoc-sort : (listof (list Symbol a)) -> (listof (list Symbol a))
|
||||
(define (assoc-sort to-sort)
|
||||
(quicksort to-sort (bcompose string<? (compose symbol->string car))))
|
||||
|
||||
(define xexpr-drop-empty-attributes (make-parameter #f))
|
||||
|
||||
;; xml->xexpr : Content -> Xexpr
|
||||
;; The contract is loosely enforced.
|
||||
(define (xml->xexpr x)
|
||||
(let* ((non-dropping-combine
|
||||
(lambda (atts body)
|
||||
(cons (assoc-sort (map attribute->srep atts))
|
||||
body)))
|
||||
(combine (if (xexpr-drop-empty-attributes)
|
||||
(lambda (atts body)
|
||||
(if (null? atts)
|
||||
body
|
||||
(non-dropping-combine atts body)))
|
||||
non-dropping-combine)))
|
||||
(let loop ((x x))
|
||||
(cond
|
||||
((element? x)
|
||||
(let ((body (map loop (element-content x)))
|
||||
(atts (element-attributes x)))
|
||||
(cons (element-name x) (combine atts body))))
|
||||
((pcdata? x) (pcdata-string x))
|
||||
((entity? x) (entity-text x))
|
||||
((or (comment? x) (pi? x)) x)
|
||||
((document? x) (error 'xml->xexpr "Expected content, given ~a~nUse document-element to extract the content." x))
|
||||
(else (error 'xml->xexpr "Expected content, given ~a" x))))))
|
||||
|
||||
;; attribute->srep : Attribute -> Attribute-srep
|
||||
(define (attribute->srep a)
|
||||
(list (attribute-name a) (attribute-value a)))
|
||||
|
||||
;; srep->attribute : Attribute-srep -> Attribute
|
||||
(define (srep->attribute a)
|
||||
(unless (and (pair? a) (pair? (cdr a)) (null? (cddr a)) (symbol? (car a)) (string? (cadr a)))
|
||||
(error 'srep->attribute "expected (cons Symbol String) given ~a" a))
|
||||
(make-attribute 'scheme 'scheme (car a) (cadr a)))
|
||||
|
||||
;; xexpr->xml : Xexpr -> Content
|
||||
;; The contract is enforced.
|
||||
(define (xexpr->xml x)
|
||||
(cond
|
||||
((pair? x)
|
||||
(let ((f (lambda (atts body)
|
||||
(unless (list? body)
|
||||
(error 'xexpr->xml "expected a list of xexprs a the body in ~a" x))
|
||||
(make-element 'scheme 'scheme (car x)
|
||||
atts
|
||||
(map xexpr->xml body)))))
|
||||
(if (and (pair? (cdr x)) (or (null? (cadr x)) (and (pair? (cadr x)) (pair? (caadr x)))))
|
||||
(f (map srep->attribute (cadr x)) (cddr x))
|
||||
(f null (cdr x)))))
|
||||
((string? x) (make-pcdata 'scheme 'scheme x))
|
||||
((or (symbol? x) (and (integer? x) (>= x 0))) (make-entity 'scheme 'scheme x))
|
||||
((or (comment? x) (pi? x)) x)
|
||||
(else (error 'xexpr->xml "malformed xexpr ~s" x))))
|
||||
|
||||
;; xexpr->string : Xexpression -> String
|
||||
(define (xexpr->string xexpr)
|
||||
(let ((port (open-output-string)))
|
||||
(write-xml/content (xexpr->xml xexpr) port)
|
||||
(get-output-string port)))
|
||||
|
||||
;; bcompose : (a a -> c) (b -> a) -> (b b -> c)
|
||||
(define (bcompose f g)
|
||||
(lambda (x y) (f (g x) (g y))))
|
|
@ -1,128 +0,0 @@
|
|||
(define-interface xml-structures-interface
|
||||
(export source-start
|
||||
source-stop
|
||||
make-location location? location-line location-char location-offset
|
||||
make-document document? document-prolog document-element document-misc
|
||||
make-prolog prolog? prolog-misc prolog-dtd prolog-misc2
|
||||
make-document-type document-type-name document-type-external
|
||||
external-dtd-system external-dtd/system?
|
||||
make-external-dtd/public external-dtd/public? external-dtd/public-public
|
||||
make-external-dtd/system
|
||||
make-element element? element-name element-attributes element-content
|
||||
make-attribute attribute? attribute-name attribute-value
|
||||
make-pcdata pcdata? pcdata-string
|
||||
make-entity entity? entity-text
|
||||
make-pi pi? pi-target-name pi-instruction
|
||||
make-comment comment? comment-text
|
||||
content?
|
||||
make-start-tag start-tag? start-tag-name start-tag-attrs
|
||||
make-end-tag end-tag? end-tag-name))
|
||||
|
||||
(define-structure xml-structures xml-structures-interface
|
||||
(open scheme
|
||||
signals
|
||||
extended-ports
|
||||
define-record-types)
|
||||
(files structures))
|
||||
|
||||
(define-interface plt-compat-interface
|
||||
(export let-values
|
||||
let*-values
|
||||
add1 sub1
|
||||
when unless
|
||||
begin0
|
||||
void
|
||||
andmap
|
||||
quicksort
|
||||
make-parameter
|
||||
let/ec call/ec
|
||||
list* null
|
||||
format
|
||||
fprintf
|
||||
regexp regexp-replace*
|
||||
foldr
|
||||
compose
|
||||
open-output-string get-output-string
|
||||
))
|
||||
|
||||
(define-structure plt-compat plt-compat-interface
|
||||
(open scsh
|
||||
scheme
|
||||
string-lib
|
||||
structure-refs)
|
||||
(access big-scheme ;; format
|
||||
list-lib) ;; fold
|
||||
(files plt))
|
||||
|
||||
(define-interface reader-interface
|
||||
(export read-xml
|
||||
read-comments
|
||||
collapse-whitespace))
|
||||
|
||||
(define-structure reader reader-interface
|
||||
(open scsh ;read-string
|
||||
scheme
|
||||
xml-structures
|
||||
i/o
|
||||
i/o-internal
|
||||
ports
|
||||
plt-compat
|
||||
byte-vectors
|
||||
signals)
|
||||
(files reader))
|
||||
|
||||
(define-interface writer-interface
|
||||
(export write-xml
|
||||
display-xml
|
||||
write-xml/content
|
||||
display-xml/content
|
||||
empty-tag-shorthand
|
||||
html-empty-tags))
|
||||
|
||||
(define-structure writer writer-interface
|
||||
(open scheme
|
||||
xml-structures
|
||||
signals
|
||||
plt-compat)
|
||||
(files writer))
|
||||
|
||||
|
||||
(define-interface space-interface
|
||||
(export eliminate-whitespace))
|
||||
|
||||
(define-structure space space-interface
|
||||
(open scheme
|
||||
plt-compat
|
||||
signals
|
||||
list-lib
|
||||
xml-structures)
|
||||
(files space))
|
||||
|
||||
(define-interface xexpr-interface
|
||||
(export xml->xexpr
|
||||
xexpr->xml
|
||||
xexpr->string
|
||||
xexpr-drop-empty-attributes))
|
||||
|
||||
(define-structure xexpr xexpr-interface
|
||||
(open scheme
|
||||
plt-compat
|
||||
writer
|
||||
signals
|
||||
xml-structures)
|
||||
(files xexpr))
|
||||
|
||||
(define-structure xml (compound-interface xml-structures-interface
|
||||
reader-interface
|
||||
writer-interface
|
||||
xexpr-interface
|
||||
space-interface)
|
||||
(open scheme
|
||||
plt-compat
|
||||
xml-structures
|
||||
reader
|
||||
writer
|
||||
xexpr
|
||||
space))
|
||||
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
#!/bin/sh
|
||||
echo "Loading..."
|
||||
exec scsh -lm packages.scm -dm -o http-test -e main -s "$0" "$@"
|
||||
!#
|
||||
|
||||
(define-structure http-test
|
||||
(export main)
|
||||
(open httpd-core
|
||||
httpd-make-options
|
||||
httpd-basic-handlers
|
||||
httpd-file-directory-handlers
|
||||
cgi-server
|
||||
seval-handler
|
||||
rman-gateway
|
||||
info-gateway
|
||||
let-opt
|
||||
scsh
|
||||
scheme)
|
||||
|
||||
(begin
|
||||
|
||||
(define (usage)
|
||||
(format #f
|
||||
"Usage: start-web-server [-h htdocs-dir] [-c cgi-bin-dir] [-p port]
|
||||
[-l log-file-name] [-r requests] [--help]
|
||||
|
||||
with
|
||||
htdocs-dir directory of html files (default: web-server/root/htdocs)
|
||||
cgi-bin-dir directory of cgi files (default: web-server/root/cgi-bin)
|
||||
port port server is listening to (default: 8080)
|
||||
log-file-name directory where to store the logfile in CLF
|
||||
(default: web-server/httpd.log)
|
||||
requests maximal amount of simultaneous requests (default 5)
|
||||
--help show this help
|
||||
"
|
||||
))
|
||||
|
||||
(define htdocs-dir #f)
|
||||
(define cgi-bin-dir #f)
|
||||
(define port #f)
|
||||
(define log-file-name #f)
|
||||
(define root #f)
|
||||
(define simultaneous-requests #f)
|
||||
|
||||
(define (init)
|
||||
(set! htdocs-dir "web-server/root/htdocs")
|
||||
(set! cgi-bin-dir "web-server/root/cgi-bin")
|
||||
(set! port "8080")
|
||||
(set! log-file-name "web-server/httpd.log")
|
||||
(set! root "web-server/root")
|
||||
(set! simultaneous-requests "5"))
|
||||
|
||||
(define get-options
|
||||
(let* ((unknown-option-error
|
||||
(lambda (option)
|
||||
(format (error-output-port)
|
||||
"unknown option `~A'~%try `start-web-server --help'~%"
|
||||
option)
|
||||
(exit 1)))
|
||||
(missing-argument-error
|
||||
(lambda (option)
|
||||
(format (error-output-port)
|
||||
"option `~A' requires an argument~%try `start-web-server --help'~%"
|
||||
option)
|
||||
(exit 1))))
|
||||
(lambda (options)
|
||||
(let loop ((options options))
|
||||
(if (null? options)
|
||||
(begin
|
||||
(set! htdocs-dir (absolute-file-name htdocs-dir))
|
||||
(set! log-file-name (absolute-file-name log-file-name))
|
||||
(set! cgi-bin-dir (absolute-file-name cgi-bin-dir))
|
||||
(set! port (string->number port))
|
||||
(set! simultaneous-requests (string->number simultaneous-requests)))
|
||||
(cond
|
||||
((string=? (car options) "-h")
|
||||
(if (null? (cdr options))
|
||||
(missing-argument-error (car options))
|
||||
(set! htdocs-dir (cadr options)))
|
||||
(loop (cddr options)))
|
||||
((string=? (car options) "-c")
|
||||
(if (null? (cdr options))
|
||||
(missing-argument-error (car options))
|
||||
(set! cgi-bin-dir (cadr options)))
|
||||
(loop (cddr options)))
|
||||
((string=? (car options) "-p")
|
||||
(if (null? (cdr options))
|
||||
(missing-argument-error (car options))
|
||||
(set! port (cadr options)))
|
||||
(loop (cddr options)))
|
||||
((string=? (car options) "-l")
|
||||
(if (null? (cdr options))
|
||||
(missing-argument-error (car options))
|
||||
(set! log-file-name (cadr options)))
|
||||
(loop (cddr options)))
|
||||
((string=? (car options) "-r")
|
||||
(if (null? (cdr options))
|
||||
(missing-argument-error (car options))
|
||||
(set! simultaneous-requests (cadr options)))
|
||||
(loop (cddr options)))
|
||||
((string=? (car options) "--help")
|
||||
(display (usage))
|
||||
(exit 0))
|
||||
((string=? (car options) "--dump")
|
||||
(let ((image-name (if (null? (cdr options))
|
||||
"server"
|
||||
(cadr options))))
|
||||
(dump-scsh-program main image-name))
|
||||
(exit 0))
|
||||
(else
|
||||
(unknown-option-error (car options)))))))))
|
||||
|
||||
|
||||
(define (main args)
|
||||
(init)
|
||||
(format #t "reading options: ~s~%" (cdr args))
|
||||
(get-options (cdr args))
|
||||
(cond ((zero? (user-uid))
|
||||
(set-gid (->gid "nobody"))
|
||||
(set-uid (->uid "nobody"))))
|
||||
|
||||
(format #t "Going to run Webserver with:
|
||||
htdocs-dir: ~a
|
||||
cgi-bin-dir: ~a
|
||||
port: ~a
|
||||
log-file-name: ~a
|
||||
a maximum of ~a simultaneous requests, syslogging activated,
|
||||
and home-dir-handler (public_html) activated.
|
||||
"
|
||||
htdocs-dir
|
||||
cgi-bin-dir
|
||||
port
|
||||
log-file-name
|
||||
simultaneous-requests)
|
||||
|
||||
(httpd (with-port port
|
||||
(with-root-directory (cwd)
|
||||
(with-simultaneous-requests simultaneous-requests
|
||||
(with-syslog? #t
|
||||
(with-logfile log-file-name
|
||||
(with-request-handler
|
||||
(alist-path-dispatcher
|
||||
(list (cons "h" (home-dir-handler "public_html"))
|
||||
(cons "seval" seval-handler)
|
||||
(cons "man" (rman-handler #f "man?%s(%s)"
|
||||
"Generated by rman-gateway"))
|
||||
(cons "info" (info-handler #f #f #f
|
||||
"Generated by info-gateway"))
|
||||
(cons "cgi-bin" (cgi-handler cgi-bin-dir)))
|
||||
(rooted-file-or-directory-handler htdocs-dir))))))))))
|
||||
))
|
||||
|
||||
;; EOF
|
||||
|
||||
;;; Local Variables:
|
||||
;;; mode:scheme
|
||||
;;; End:
|
134
start-web-server
134
start-web-server
|
@ -1,134 +0,0 @@
|
|||
#!/bin/sh
|
||||
echo "Loading..."
|
||||
exec scsh -lm packages.scm -dm -o http-test -e main -s "$0" "$@"
|
||||
!#
|
||||
|
||||
(define-structure http-test
|
||||
(export main)
|
||||
(open httpd-core
|
||||
httpd-make-options
|
||||
httpd-basic-handlers
|
||||
httpd-file-directory-handlers
|
||||
httpd-cgi-handlers
|
||||
scheme-with-scsh)
|
||||
|
||||
(begin
|
||||
|
||||
(define (usage)
|
||||
(format #f
|
||||
"Usage: start-web-server [-h htdocs-dir] [-c cgi-bin-dir] [-p port]
|
||||
[-l log-file-name] [--help]
|
||||
|
||||
with
|
||||
htdocs-dir directory of html files (default: web-server/root/htdocs)
|
||||
cgi-bin-dir directory of cgi files (default: web-server/root/cgi-bin)
|
||||
port port server is listening to (default: 8080)
|
||||
log-file-name directory where to store the logfile in CLF
|
||||
(default: web-server/httpd.log)
|
||||
--help show this help
|
||||
"
|
||||
))
|
||||
|
||||
(define htdocs-dir #f)
|
||||
(define cgi-bin-dir #f)
|
||||
(define port #f)
|
||||
(define log-file-name #f)
|
||||
(define root #f)
|
||||
|
||||
(define (init)
|
||||
(set! htdocs-dir "web-server/root/htdocs")
|
||||
(set! cgi-bin-dir "web-server/root/cgi-bin")
|
||||
(set! port "8080")
|
||||
(set! log-file-name "web-server/httpd.log")
|
||||
(set! root "web-server/root"))
|
||||
|
||||
(define get-options
|
||||
(let* ((unknown-option-error
|
||||
(lambda (option)
|
||||
(format (error-output-port)
|
||||
"unknown option `~A'~%try `start-web-server --help'~%"
|
||||
option)
|
||||
(exit 1)))
|
||||
(missing-argument-error
|
||||
(lambda (option)
|
||||
(format (error-output-port)
|
||||
"option `~A' requires an argument~%try `start-web-server --help'~%"
|
||||
option)
|
||||
(exit 1))))
|
||||
(lambda (options)
|
||||
(let loop ((options options))
|
||||
(if (null? options)
|
||||
(begin
|
||||
(set! htdocs-dir (absolute-file-name htdocs-dir))
|
||||
(set! log-file-name (absolute-file-name log-file-name))
|
||||
(set! cgi-bin-dir (absolute-file-name cgi-bin-dir))
|
||||
(set! port (string->number port)))
|
||||
(cond
|
||||
((string=? (car options) "-h")
|
||||
(if (null? (cdr options))
|
||||
(missing-argument-error (car options))
|
||||
(set! htdocs-dir (cadr options)))
|
||||
(loop (cddr options)))
|
||||
((string=? (car options) "-c")
|
||||
(if (null? (cdr options))
|
||||
(missing-argument-error (car options))
|
||||
(set! cgi-bin-dir (cadr options)))
|
||||
(loop (cddr options)))
|
||||
((string=? (car options) "-p")
|
||||
(if (null? (cdr options))
|
||||
(missing-argument-error (car options))
|
||||
(set! port (cadr options)))
|
||||
(loop (cddr options)))
|
||||
((string=? (car options) "-l")
|
||||
(if (null? (cdr options))
|
||||
(missing-argument-error (car options))
|
||||
(set! log-file-name (cadr options)))
|
||||
(loop (cddr options)))
|
||||
((string=? (car options) "--help")
|
||||
(display (usage))
|
||||
(exit 0))
|
||||
((string=? (car options) "--dump")
|
||||
(let ((image-name (if (null? (cdr options))
|
||||
"web-server"
|
||||
(cadr options))))
|
||||
(dump-scsh-program main image-name))
|
||||
(exit 0))
|
||||
(else
|
||||
(unknown-option-error (car options)))))))))
|
||||
|
||||
|
||||
(define (main args)
|
||||
(init)
|
||||
(format #t "reading options: ~s~%" (cdr args))
|
||||
(get-options (cdr args))
|
||||
(cond ((zero? (user-uid))
|
||||
(set-gid (->gid "nobody"))
|
||||
(set-uid (->uid "nobody"))))
|
||||
|
||||
(format #t "Going to run Webserver with:
|
||||
htdocs-dir: ~a
|
||||
cgi-bin-dir: ~a
|
||||
port: ~a
|
||||
log-file-name: ~a
|
||||
syslogging activated.
|
||||
"
|
||||
htdocs-dir
|
||||
cgi-bin-dir
|
||||
port
|
||||
log-file-name)
|
||||
|
||||
(httpd (with-port port
|
||||
(with-root-directory (cwd)
|
||||
(with-syslog? #t
|
||||
(with-logfile log-file-name
|
||||
(with-request-handler
|
||||
(tilde-home-dir-handler "public_html"
|
||||
(alist-path-dispatcher
|
||||
(list (cons "cgi-bin" (cgi-handler cgi-bin-dir)))
|
||||
(rooted-file-or-directory-handler htdocs-dir))))))))))
|
||||
))
|
||||
;; EOF
|
||||
|
||||
;;; Local Variables:
|
||||
;;; mode:scheme
|
||||
;;; End:
|
|
@ -1 +0,0 @@
|
|||
httpd.log
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/sh
|
||||
# An example CGI program outputing the current date
|
||||
echo Content-Type: text/html
|
||||
echo Status: 200 OK
|
||||
echo
|
||||
echo "<h2> This is the cgi script. </h2>"
|
||||
echo "<br> Current date: "
|
||||
echo `date`
|
||||
echo
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Example for server redirection
|
||||
echo Location:http://www.scsh.net/resources/sunet.html
|
|
@ -1 +0,0 @@
|
|||
This is a text file.
|
Binary file not shown.
|
@ -1,8 +0,0 @@
|
|||
<html>
|
||||
<head><title>Home</title></head>
|
||||
<body>
|
||||
<p>
|
||||
Hello world! <a href=index2.html>(more...)</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,39 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head><title>Scheme Unterground</title></head>
|
||||
<body>
|
||||
<p>
|
||||
<h1>Hello Unterground!</h1>
|
||||
|
||||
Following files are available:
|
||||
<ul>
|
||||
<li><a href=/manual/man.html>SUnet release manual</a></li>
|
||||
<li><a href=../cgi-bin/comments.sh>A small CGI script</a></li>
|
||||
<li><a href=seval.html>Computing Scheme Forms
|
||||
Interactively</a></li>
|
||||
<li><a href=man?man>Get a man page</a><br>
|
||||
(needs a matching man page installation;<br>
|
||||
see httpd/rman-gateway.scm for details)</li>
|
||||
<li><a href=info?(dir)Top>Get the dir info page</a><br>
|
||||
(needs a matching info page installation;<br>
|
||||
among others, we need non-gzipped info pages)</li>
|
||||
<li><a href=files/text.txt>Text file</a></li>
|
||||
<li><a href=files>Directory</a></li>
|
||||
<li><a href=files/zipped.gz>Compressed File</a></li>
|
||||
<li><a href=index.html>Start</a></li>
|
||||
<li><a href=index2.html>This file</a></li>
|
||||
</ul>
|
||||
<br>
|
||||
And nothing else...
|
||||
|
||||
<hr>
|
||||
<!-- Created: Thu Aug 22 16:44:16 CEST 2002 -->
|
||||
<!-- hhmts start -->
|
||||
Last modified: Wed Jan 15 16:16:58 MET 2003
|
||||
<!-- hhmts end -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,25 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>Evaluating Scheme Expressions Interactively</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Evaluating Scheme Expressions Interactively</h1>
|
||||
|
||||
<form action="seval" method="post">
|
||||
Type in your scheme expression as you would at any REPL:
|
||||
<br>
|
||||
<input type="text" name="program">
|
||||
<input type="submit">
|
||||
<input type="reset">
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
<address><a href="mailto:andreas.bernauer@gmx.de">Andreas Bernauer</a></address>
|
||||
<!-- Created: Wed Aug 28 16:35:32 CEST 2002 -->
|
||||
<!-- hhmts start -->
|
||||
Last modified: Wed Aug 28 16:40:54 CEST 2002
|
||||
<!-- hhmts end -->
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue