diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 5cbdc5e..0000000 --- a/.gitignore +++ /dev/null @@ -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 diff --git a/COPYING b/COPYING deleted file mode 100644 index 88c93a6..0000000 --- a/COPYING +++ /dev/null @@ -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. diff --git a/Makefile b/Makefile deleted file mode 100644 index e241f6c..0000000 --- a/Makefile +++ /dev/null @@ -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 - - diff --git a/Readme b/Readme deleted file mode 100644 index 3087b91..0000000 --- a/Readme +++ /dev/null @@ -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 -. - -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. \ No newline at end of file diff --git a/doc/html/index.html b/doc/html/index.html deleted file mode 100644 index 0b8e359..0000000 --- a/doc/html/index.html +++ /dev/null @@ -1,85 +0,0 @@ - - -The Scheme Underground Network Package - - - -

The Scheme Underground Network Package

-I have written a set of libraries 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. - -
The SU Web server -
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: -
    -
  • 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. -
- The server has three main design goals: -
-
Extensibility -
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. - -

- 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. - -

Mobile code -
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.) - - -
Clarity -
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. -
- -

- 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. - -

- Some simple documentation on the server - is available. - -

- -

Obtaining the system

-The network code is available by -ftp. -To run the server, you need our 0.4 release of -scsh -which has just been released. - -Beyond actually running the server, -the separate parser libraries and other utilites may be of use as separate -modules. - -
Olin Shivers - / shivers@ai.mit.edu
- - - - - diff --git a/doc/html/su-httpd.html b/doc/html/su-httpd.html deleted file mode 100644 index 356aa37..0000000 --- a/doc/html/su-httpd.html +++ /dev/null @@ -1,482 +0,0 @@ - - - -The Scheme Underground Web system - - - -

The Scheme Underground Web System

- -
Olin Shivers - / shivers@ai.mit.edu -
-July 1995 - -
-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 -ASCII version of this note, instead. -
- - - - -

Introduction

- -The -Scheme underground -Web system is a package of -Scheme -code that provides -utilities for interacting with the -World-Wide Web. -This includes: - - -

-The code can be obtained via - -anonymous ftp -and is implemented in -Scheme 48, -using the system calls and support procedures of -scsh, -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. - -

-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. - -

The Scheme Underground Web Server

- -The server was designed with three principle goals in mind: -
-
Extensibility -
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." - - -
Mobile code -
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. - - -
Clarity of implementation -
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. -
- - -

Basic server structure

- -The Web server is started by calling the httpd procedure, -which takes one required and two optional arguments: -
-    (httpd path-handler [port working-directory])
-
- -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 -
-    /usr/local/etc/httpd
-
- - -

-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 httpd). -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. - -

-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. - -

-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. - - - -

Path handlers

- -A path handler is a procedure taking two arguments: -
-    (path-handler path req)
-
- - -The req argument is a request record giving all the details of the -client's request; it has the following structure: -
-    (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.
-
- -The path argument is the URL's path, -parsed and split at slashes into a string list. -For example, if the Web client dereferences URL -
-    http://clark.lcs.mit.edu:8001/h/shivers/code/web.tar.gz
-
-then the server would pass the following path to the top-level handler: -
-    ("h" "shivers" "code" "web.tar.gz")
-
- -

-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. - -

-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 -not 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. - - -

Basic path handlers

- -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: - -
- -
-(alist-path-dispatcher ph-alist default-ph) -> path-handler - -
- 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 - ("foo" "bar" "baz"), - it uses the first element of the path -- "foo" -- 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, ("bar" "baz"). - 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, ("foo" "bar" "baz"). - -

- 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. - -

- 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. - -

- Example:
- A typical top-level path handler is - -

-  (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")))
-
- - This means: -
    -
  • If the path looks like ("h" "shivers" "code" "web.tar.gz"), - pass the path ("shivers" "code" "web.tar.gz") to a - home-directory path handler. - - -
  • If the path looks like ("cgi-bin" "calendar"), - pass ("calendar") off to the CGI path handler. - - -
  • If the path looks like ("seval" ...), - the tail of the path is passed off to the code-uploading seval - path handler. - -
  • Otherwise, the whole path is passed to a rooted file handler, who - will convert it into a filename, rooted at - /usr/local/etc/httpd/htdocs, and serve that file. -
- - -
(home-dir-handler subdir) -> - path-handler -
- 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 (user . file-path), - then it serves the file -
-    user's-home-directory/subdir/file-path
-
- The path handler only handles GET requests; the filename is not - allowed to contain .. elements. - - -
-(tilde-home-dir-handler subdir default-path-handler) - -> path-handler - -
- This path handler examines the car of the path. If it is a string - beginning with a tilde, e.g., "~ziggy", - then the string is taken - to mean a home directory, and the request is served similarly to a - home-dir-handler path handler. - Otherwise, the request is passed off - in its entirety to the default path handler. - -

- This procedure is useful for implementing servers that provide the - semantics of the NCSA httpd server. - - -

-(cgi-handler cgi-directory) -> path-handler - -
- 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 ..'s. - If the path is -
-    ("my-prog" "foo" "bar")
-
- then the program executed is -
-    cgi-directory/my-prog
-
-

- When the CGI path handler builds the process environment for the - CGI script, several elements - (e.g., $PATH and $SERVER_SOFTWARE) - are request-invariant, and can be computed at server start-up time. - This can be done by calling -

-    (initialise-request-invariant-cgi-env)
-
- when the server starts up. This is not necessary, - but will make CGI requests a little faster. - - -
-(rooted-file-handler root-dir) -> path-handler - -
- 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 root-dir. - The file name is checked for .. components, - and the transaction is aborted if it does. Otherwise, the file is - served to the client. - -
-(null-path-handler path req) -
- This path handler is useful as a default handler. It handles no requests, - always returning a "404 Not found" reply to the client. - -
- - -

HTTP errors

- -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 http-error 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. - -
- -
-(http-error reply-code req [extra ...]) -
- This raises an http error condition. The reply code is one of the - numeric HTTP error reply codes, which are bound to the variables - http-reply/ok, http-reply/not-found, - http-reply/bad-request, and so - forth. The req argument is the request record that caused - the error. - Any following extra 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 extra values as the - URI: and Location: - fields in the reply header, respectively. See the clauses of the - send-http-error-reply procedure for details. - - -
-(send-http-error-reply reply-code request - [extra ...]) - -
- 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. -
- - -

Simple directory generation

- -Most path-handlers that serve files to clients eventually call an internal -procedure named file-serve, -which implements a simple directory-generation service using the -following rules: - - - - -

Support procs

- -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. - - - -

Losing

- -Be aware of two Unix problems, which may require workarounds: -
    - -
  1. - NeXTSTEP's Posix implementation of the getpwnam() routine - will silently tell you that every user has uid 0. This means - that if your server, running as root, does a -
    -    (set-uid (user->uid "nobody"))
    -
    - it will essentially do a -
    -    (set-uid 0)
    -
    - and you will thus still be running as root. - -

    - 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: -

    -    (set-uid -2)
    -
    - This problem is NeXTSTEP specific. If you are using not using NeXTSTEP, - no problem. - - -
  2. - On NeXTSTEP, the ip-address->host-name translation routine - (in C, gethostbyaddr(); in scsh, - (host-info addr)) 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. - -

    - This problem may occur on other OS's; - I cannot determine if gethostbyaddr() - 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.) - -

    - If your system doesn't give you a complete Internet address when - you say -

    -    (host-info:name (host-info (system-name)))
    -
    - then you have this problem. - -

    - The server has a workaround. There is a procedure exported from - the httpd-core package: -

    -    (set-my-fqdn name)
    -
    - Call this to crow-bar the server's idea of its own Internet host name - before running the server, and all will be well. -
- - - diff --git a/doc/latex/.gitignore b/doc/latex/.gitignore deleted file mode 100644 index 0454526..0000000 --- a/doc/latex/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -*.aux -*.toc -*.dvi -*.ps -*.pdf -*.log - diff --git a/doc/latex/.tex2page.hdir b/doc/latex/.tex2page.hdir deleted file mode 100644 index aaa8dd6..0000000 --- a/doc/latex/.tex2page.hdir +++ /dev/null @@ -1 +0,0 @@ -../../web-server/root/htdocs/sunet-manual diff --git a/doc/latex/cgi-script.tex b/doc/latex/cgi-script.tex deleted file mode 100644 index e846a7d..0000000 --- a/doc/latex/cgi-script.tex +++ /dev/null @@ -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: diff --git a/doc/latex/code.sty b/doc/latex/code.sty deleted file mode 100644 index 2786d61..0000000 --- a/doc/latex/code.sty +++ /dev/null @@ -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] 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. diff --git a/doc/latex/css.t2p b/doc/latex/css.t2p deleted file mode 100644 index 7c1fcee..0000000 --- a/doc/latex/css.t2p +++ /dev/null @@ -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 \ No newline at end of file diff --git a/doc/latex/ct.sty b/doc/latex/ct.sty deleted file mode 100644 index 1edfbc0..0000000 --- a/doc/latex/ct.sty +++ /dev/null @@ -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} diff --git a/doc/latex/decls.tex b/doc/latex/decls.tex deleted file mode 100644 index 873fcac..0000000 --- a/doc/latex/decls.tex +++ /dev/null @@ -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{\}} -\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 ) -> 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 diff --git a/doc/latex/dns.tex b/doc/latex/dns.tex deleted file mode 100644 index 7c56859..0000000 --- a/doc/latex/dns.tex +++ /dev/null @@ -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: diff --git a/doc/latex/ftp.tex b/doc/latex/ftp.tex deleted file mode 100644 index af7e657..0000000 --- a/doc/latex/ftp.tex +++ /dev/null @@ -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: - diff --git a/doc/latex/ftpd.tex b/doc/latex/ftpd.tex deleted file mode 100644 index 7c42e25..0000000 --- a/doc/latex/ftpd.tex +++ /dev/null @@ -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 <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: diff --git a/doc/latex/intro.tex b/doc/latex/intro.tex deleted file mode 100644 index 0aa445e..0000000 --- a/doc/latex/intro.tex +++ /dev/null @@ -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: diff --git a/doc/latex/man.t2p b/doc/latex/man.t2p deleted file mode 100644 index e6a4e80..0000000 --- a/doc/latex/man.t2p +++ /dev/null @@ -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}} -} diff --git a/doc/latex/man.tex b/doc/latex/man.tex deleted file mode 100644 index c05625f..0000000 --- a/doc/latex/man.tex +++ /dev/null @@ -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} diff --git a/doc/latex/mantitle.sty b/doc/latex/mantitle.sty deleted file mode 100644 index b17f5b5..0000000 --- a/doc/latex/mantitle.sty +++ /dev/null @@ -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{} diff --git a/doc/latex/matter.sty b/doc/latex/matter.sty deleted file mode 100644 index f0c4fda..0000000 --- a/doc/latex/matter.sty +++ /dev/null @@ -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} diff --git a/doc/latex/mysize10.sty b/doc/latex/mysize10.sty deleted file mode 100644 index 94c52c0..0000000 --- a/doc/latex/mysize10.sty +++ /dev/null @@ -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 diff --git a/doc/latex/netrc.tex b/doc/latex/netrc.tex deleted file mode 100644 index 76e8b18..0000000 --- a/doc/latex/netrc.tex +++ /dev/null @@ -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: diff --git a/doc/latex/nettime.tex b/doc/latex/nettime.tex deleted file mode 100644 index 6b57ede..0000000 --- a/doc/latex/nettime.tex +++ /dev/null @@ -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: diff --git a/doc/latex/pdfcond.tex b/doc/latex/pdfcond.tex deleted file mode 100644 index 34d7cf2..0000000 --- a/doc/latex/pdfcond.tex +++ /dev/null @@ -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 diff --git a/doc/latex/pop3.tex b/doc/latex/pop3.tex deleted file mode 100644 index 0458d8d..0000000 --- a/doc/latex/pop3.tex +++ /dev/null @@ -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: diff --git a/doc/latex/rfc822.tex b/doc/latex/rfc822.tex deleted file mode 100644 index d5be6c9..0000000 --- a/doc/latex/rfc822.tex +++ /dev/null @@ -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: diff --git a/doc/latex/skeleton.tex b/doc/latex/skeleton.tex deleted file mode 100644 index c802198..0000000 --- a/doc/latex/skeleton.tex +++ /dev/null @@ -1,8 +0,0 @@ -\section{Section-Title} -% -\begin{description} -\item[Used files:] -\item[Name of the package:] -\end{description} -% -Not implemented yet. diff --git a/doc/latex/smtp.tex b/doc/latex/smtp.tex deleted file mode 100644 index 63fbd13..0000000 --- a/doc/latex/smtp.tex +++ /dev/null @@ -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: diff --git a/doc/latex/uri.tex b/doc/latex/uri.tex deleted file mode 100644 index 5516f17..0000000 --- a/doc/latex/uri.tex +++ /dev/null @@ -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: diff --git a/doc/latex/url.tex b/doc/latex/url.tex deleted file mode 100644 index 687fb1e..0000000 --- a/doc/latex/url.tex +++ /dev/null @@ -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: diff --git a/scheme/ftpd/ftpd.scm b/scheme/ftpd/ftpd.scm deleted file mode 100644 index 4f55961..0000000 --- a/scheme/ftpd/ftpd.scm +++ /dev/null @@ -1,1324 +0,0 @@ -; RFC 959 ftp daemon - -;;; This file is part of the Scheme Untergrund Networking package. - -;;; Copyright (c) 1998-2002 by Mike Sperber -;;; For copyright information, see the file COPYING which comes with -;;; the distribution. - -; It doesn't support the following desirable things: -; -; - Login by user -; - RESTART support -; - Banners from files on CWD -; - Lots of fancy stuff like ProFTPD, http://www.proftpd.org/ - - -; following things should be improved: -; -; - GET/RETR-command: ftpd reports "Can't open FILENAME for reading" if -; file actually doesn't exist. This is confusing. Reporting -; "FILENAME does not exist" is much better. -; - default value for ftpd should be looked up as in ftp.scm - -(define-record-type ftpd-options :ftpd-options - (really-make-ftpd-options port anonymous-home banner - logfile dns-lookup?) - ftpd-options? - (port ftpd-options-port set-ftpd-options-port!) - (anonymous-home ftpd-options-anonymous-home set-ftpd-options-anonymous-home!) - (banner ftpd-options-banner set-ftpd-options-banner!) - (logfile ftpd-options-logfile set-ftpd-options-logfile!) - (dns-lookup? ftpd-options-dns-lookup? set-ftpd-options-dns-lookup?!)) - -(define (make-default-ftpd-options) - (really-make-ftpd-options 21 - "~ftp" - (string-append "Scheme Untergrund ftp server (version " - sunet-version-identifier - ") ready.") - #f - #f)) - -(define (copy-ftpd-options options) - (really-make-ftpd-options (ftpd-options-port options) - (ftpd-options-anonymous-home options) - (ftpd-options-banner options) - (ftpd-options-logfile options) - (ftpd-options-dns-lookup? options))) - -(define (make-ftpd-options-transformer set-option!) - (lambda (new-value . stuff) - (let ((new-options (if (not (null? stuff)) - (copy-ftpd-options (car stuff)) - (make-default-ftpd-options)))) - (set-option! new-options new-value) - new-options))) - -(define with-port - (make-ftpd-options-transformer set-ftpd-options-port!)) -(define with-anonymous-home - (make-ftpd-options-transformer set-ftpd-options-anonymous-home!)) -(define with-banner - (make-ftpd-options-transformer set-ftpd-options-banner!)) -(define with-logfile - (make-ftpd-options-transformer set-ftpd-options-logfile!)) -(define with-dns-lookup? - (make-ftpd-options-transformer set-ftpd-options-dns-lookup?!)) - -(define (make-ftpd-options . stuff) - (let loop ((options (make-default-ftpd-options)) - (stuff stuff)) - (if (null? stuff) - options - (let* ((transformer (car stuff)) - (value (cadr stuff))) - (loop (transformer value options) - (cddr stuff)))))) - -(define-record-type session :session - (really-make-session control-input-port - control-output-port - logfile-lock - logged-in? - authenticated? - anonymous? - root-directory - current-directory - to-be-renamed - reverse-replies - reply-code - type - data-socket - passive-socket - maybe-log-port) - session? - (control-input-port session-control-input-port - set-session-control-input-port!) - (control-output-port session-control-output-port - set-session-control-output-port!) - (logfile-lock session-logfile-lock) - (logged-in? session-logged-in? - set-session-logged-in?!) - (authenticated? session-authenticated? - set-session-authenticated?!) - (anonymous? session-anonymous? - set-session-anonymous?!) - (root-directory session-root-directory - set-session-root-directory!) - (current-directory session-current-directory - set-session-current-directory!) - (to-be-renamed session-to-be-renamed - set-session-to-be-renamed!) - (reverse-replies session-reverse-replies - set-session-reverse-replies!) - (reply-code session-reply-code - set-session-reply-code!) - (type session-type - set-session-type!) - (data-socket session-data-socket - set-session-data-socket!) - (passive-socket session-passive-socket - set-session-passive-socket!) - (maybe-log-port session-maybe-log-port - set-session-maybe-log-port!)) - -(define (make-session input-port output-port maybe-log-port) - (really-make-session input-port output-port - (make-lock) - #f ; logged-in? - #f ; autenticated? - #f ; anonymous? - #f ; root-directory - "" ; current-directory - #f ; to-be-renamed - '() ; reverse-replies - #f ; reply-code - 'ascii ; type - #f ; data-socket - #f ; passive-socket - maybe-log-port - )) - -(define session (make-fluid #f)) -(define options (make-fluid #f)) - -(define (make-session-selector selector) - (lambda () - (selector (fluid session)))) - -(define (make-session-modifier setter) - (lambda (value) - (setter (fluid session) value))) - -(define the-session-control-input-port - (make-session-selector session-control-input-port)) -(define the-session-control-output-port - (make-session-selector session-control-output-port)) -(define the-session-logfile-lock - (make-session-selector session-logfile-lock)) - -(define the-session-logged-in? (make-session-selector session-logged-in?)) -(define the-session-authenticated? (make-session-selector session-authenticated?)) -(define the-session-anonymous? (make-session-selector session-anonymous?)) -(define the-session-root-directory (make-session-selector session-root-directory)) -(define the-session-current-directory (make-session-selector session-current-directory)) -(define the-session-to-be-renamed (make-session-selector session-to-be-renamed)) -(define the-session-reverse-replies (make-session-selector session-reverse-replies)) -(define the-session-reply-code (make-session-selector session-reply-code)) -(define the-session-type (make-session-selector session-type)) -(define the-session-data-socket (make-session-selector session-data-socket)) -(define the-session-passive-socket (make-session-selector session-passive-socket)) -(define the-session-maybe-log-port (make-session-selector session-maybe-log-port)) - -(define set-the-session-control-input-port! - (make-session-modifier set-session-control-input-port!)) -(define set-the-session-control-output-port! - (make-session-modifier set-session-control-output-port!)) -(define set-the-session-logged-in?! - (make-session-modifier set-session-logged-in?!)) -(define set-the-session-authenticated?! - (make-session-modifier set-session-authenticated?!)) -(define set-the-session-anonymous?! - (make-session-modifier set-session-anonymous?!)) -(define set-the-session-root-directory! - (make-session-modifier set-session-root-directory!)) -(define set-the-session-current-directory! - (make-session-modifier set-session-current-directory!)) -(define set-the-session-to-be-renamed! - (make-session-modifier set-session-to-be-renamed!)) -(define set-the-session-reverse-replies! - (make-session-modifier set-session-reverse-replies!)) -(define set-the-session-reply-code! - (make-session-modifier set-session-reply-code!)) -(define set-the-session-type! - (make-session-modifier set-session-type!)) -(define set-the-session-data-socket! - (make-session-modifier set-session-data-socket!)) -(define set-the-session-passive-socket! - (make-session-modifier set-session-passive-socket!)) -(define set-the-session-maybe-log-port! - (make-session-modifier set-session-maybe-log-port!)) - -(define (make-ftpd-options-selector selector) - (lambda () - (selector (fluid options)))) - -(define the-ftpd-options-port - (make-ftpd-options-selector ftpd-options-port)) -(define the-ftpd-options-anonymous-home - (make-ftpd-options-selector ftpd-options-anonymous-home)) -(define the-ftpd-options-banner - (make-ftpd-options-selector ftpd-options-banner)) -(define the-ftpd-options-logfile - (make-ftpd-options-selector ftpd-options-logfile)) -(define the-ftpd-options-dns-lookup? - (make-ftpd-options-selector ftpd-options-dns-lookup?)) - -;;; LOG ------------------------------------------------------- -(define (log level format-message . args) - (syslog level - (apply format #f (string-append "(thread ~D) " format-message) - (thread-uid (current-thread)) args))) - -(define (log-command level command-name . argument) - (if (null? argument) - (log level "handling ~A command" command-name) - (if (not (null? (cdr argument))) - (log level "handling ~A command with argument ~S" - command-name argument) - (log level "handling ~A command with argument ~S" ; does this ever happen? - command-name (car argument))))) - -;; Extended logging like wu.ftpd: -;; Each file up/download is protocolled - -; Mon Dec 3 18:52:41 1990 1 wuarchive.wustl.edu 568881 /files.lst.Z a _ o a chris@wugate.wustl.edu ftp 0 * -; -; %.24s %d %s %d %s %c %s %c %c %s %s %d %s -; 1 2 3 4 5 6 7 8 9 10 11 12 13 -; -; 1 current time in the form DDD MMM dd hh:mm:ss YYYY -; 2 transfer time in seconds -; 3 remote host name -; 4 file size in bytes -; 5 name of file -; 6 transfer type (a>scii, b>inary) -; 7 special action flags (concatenated as needed): -; C file was compressed -; U file was uncompressed -; T file was tar'ed -; _ no action taken -; 8 file was sent to user (o>utgoing) or received from -; user (i>ncoming) -; 9 accessed anonymously (r>eal, a>nonymous, g>uest) -- mostly for FTP -; 10 local username or, if guest, ID string given -; (anonymous FTP password) -; 11 service name ('ftp', other) -; 12 authentication method (bitmask) -; 0 none -; 1 RFC931 Authentication -; 13 authenticated user id (if available, '*' otherwise) -; -(define file-log - (let ((maybe-dns-lookup (lambda (ip) - (if (the-ftpd-options-dns-lookup?) - (or (dns-lookup-ip ip) - ip)) - ip))) - (lambda (start-transfer-seconds info full-path direction) - (if (the-session-maybe-log-port) - (begin - (obtain-lock (the-session-logfile-lock)) - (format (the-session-maybe-log-port) - "~A ~A ~A ~A ~A ~A _ ~A a nop@ssword ftp 0 *~%" - (format-date "~a ~b ~d ~H:~M:~S ~Y" (date)) ; current date and time - (- (current-seconds) start-transfer-seconds) ; transfer time in secs - (maybe-dns-lookup - (socket-address->string - (socket-remote-address (the-session-data-socket)) #f)) ; remote host ip - (file-info:size info) ; file size in bytes - (string-map (lambda (c) - (if (eq? c #\space) #\_ c)) - full-path) ; name of file (spaces replaced by "_") - (case (the-session-type) - ((ascii) "a") - ((image) "b") - (else "?")) ; transfer type - direction ; incoming / outgoing file - ; anonymous access - ; password (no password given) - ; service name - ; authentication mode - ; authenticated user id' - ) - (force-output (the-session-maybe-log-port)) - (release-lock (the-session-logfile-lock))))))) - -(define (maybe-open-logfile maybe-logfile) - (with-errno-handler - ((errno packet) - (else - (format (current-error-port) - "[ftpd] Warning: Unable to write logs to ~S. Logging is now made to (current-error-port).~%[ftpd] (To disable logging at all, either leave the logfile argument or give #f as logfile)~%" - maybe-logfile) - (current-error-port))) - (and maybe-logfile - (open-output-file maybe-logfile - (bitwise-ior open/create open/append))))) - -;;; CONVERTERS ------------------------------------------------ -(define (protocol-family->string protocol-family) - (cond ((= protocol-family protocol-family/unspecified) - "unspecified") - ((= protocol-family protocol-family/internet) - "internet") - ((= protocol-family protocol-family/unix) - "unix") - (else "unknown"))) - -(define (socket->string socket) - (format #f - "family: ~A, ~&local address: ~A, ~&remote address: ~A, ~&input-port ~A, ~&output-port ~A" - (protocol-family->string (socket:family socket)) - (socket-address->string (socket-local-address socket)) - (socket-address->string (socket-remote-address socket)) - (socket:inport socket) - (socket:outport socket))) - - -;;; ftpd ------------------------------------------------------- - -(define (ftpd ftpd-options) - (display ">>>ftpd ") (write (list (ftpd-options-port ftpd-options))) (newline) - (with-syslog-destination - "ftpd" - #f - #f - #f - (lambda () - (log (syslog-level notice) - "starting daemon on port ~D with ~S as anonymous home and logfile ~S" - (ftpd-options-port ftpd-options) - (expand-file-name (ftpd-options-anonymous-home ftpd-options) - (cwd)) - (ftpd-options-logfile ftpd-options)) - (let ((maybe-log-port (maybe-open-logfile (ftpd-options-logfile ftpd-options)))) - (bind-listen-accept-loop - protocol-family/internet - (lambda (socket address) - (let ((remote-address (socket-address->string address))) - (set-ftp-socket-options! socket) - (fork-thread - (lambda () - (handle-connection-encapsulated ftpd-options - socket - address - remote-address - maybe-log-port))))) - (ftpd-options-port ftpd-options)))))) - -(define (handle-connection-encapsulated ftpd-options socket address remote-address maybe-log-port) - (call-with-current-continuation - (lambda (exit) - (with-errno-handler* - (lambda (errno packet) - (log (syslog-level notice) - "error with connection to ~A (~A)" - remote-address (car packet)) - (exit 'fick-dich-ins-knie)) - (lambda () - (let ((socket-string (socket->string socket))) - - (log (syslog-level notice) - "new connection to ~S" - remote-address) - - (log (syslog-level debug) "socket: ~S" socket-string) - - (dynamic-wind - (lambda () 'fick-dich-ins-knie) - (lambda () - (handle-connection ftpd-options - (socket:inport socket) - (socket:outport socket) - maybe-log-port)) - (lambda () - (log (syslog-level debug) - "shutting down socket ~S" - socket-string) - (call-with-current-continuation - (lambda (exit) - (with-errno-handler* - (lambda (errno packet) - (log (syslog-level notice) - "error shutting down socket to ~A (~A)" - remote-address (car packet)) - (exit 'fick-dich-ins-knie)) - (lambda () - (shutdown-socket socket shutdown/sends+receives))))) - (log (syslog-level notice) - "closing connection to ~A and finishing thread" remote-address) - (log (syslog-level debug) - "closing socket ~S" socket-string) - (close-socket socket))))))))) - -(define (ftpd-inetd ftpd-options) - (with-syslog-destination - "ftpd" - #f - #f - #f - (lambda () - (log (syslog-level notice) - "starting ftpd from inetd" - (expand-file-name (ftpd-options-anonymous-home ftpd-options) - (cwd))) - (handle-connection ftpd-options - (current-input-port) - (current-output-port) - (maybe-open-logfile (ftpd-options-logfile ftpd-options)))))) - -(define (set-ftp-socket-options! socket) - ;; If the client closes the connection, we won't lose when we try to - ;; close the socket by trying to flush the output buffer. - ;; ... only it somehow exposes a bug in Windows Internet Explorer - ;; so we leave it disabled. - ;; (set-port-buffering (socket:outport socket) bufpol/none) - - (set-socket-option socket level/socket tcp/no-delay #t) - - (set-socket-option socket level/socket socket/oob-inline #t)) - - -(define (handle-connection ftpd-options input-port output-port maybe-log-port) - (log (syslog-level debug) - "handling connection with input port ~A, output port ~A" - input-port - output-port) - (call-with-current-continuation - (lambda (escape) - (with-handler - (lambda (condition more) - (log (syslog-level notice) - "hit error condition ~A (~S) -- exiting" - (condition-type condition) - (condition-stuff condition)) - (escape 'fick-dich-ins-knie)) - (lambda () - (let-fluids - session (make-session input-port output-port maybe-log-port) - options ftpd-options - (lambda () - (display-banner) - (handle-commands)))))))) - -(define (display-banner) - (log (syslog-level debug) - "displaying banner (220)") - (register-reply! 220 - (the-ftpd-options-banner))) - -(define-condition-type 'ftpd-quit '()) -(define ftpd-quit? (condition-predicate 'ftpd-quit)) - -(define-condition-type 'ftpd-irregular-quit '()) -(define ftpd-irregular-quit? (condition-predicate 'ftpd-irregular-quit)) - -(define-condition-type 'ftpd-error '()) -(define ftpd-error? (condition-predicate 'ftpd-error)) - - -(define (handle-commands) - (log (syslog-level debug) "handling commands") - (call-with-current-continuation - (lambda (exit) - (with-handler - (lambda (condition more) - (if (ftpd-quit? condition) - (begin - (log (syslog-level debug) "quitting (write-accept-loop)") - (with-handler - (lambda (condition ignore) - (more)) - (lambda () - (write-replies) - (exit 'fick-dich-ins-knie)))) - (more))) - (lambda () - (log (syslog-level debug) - "starting write-accept-loop") - (let loop () - (write-replies) - (accept-command) - (loop))))))) - -(define (accept-command) - (let* ((timeout-seconds 90) - (command-line (read-crlf-line-timeout (the-session-control-input-port) - #f - (* 1000 timeout-seconds);timeout - 500))) ; max interval - (log (syslog-level debug) - "Command line: ~A" - command-line) - (cond ((eq? command-line 'timeout) - (log (syslog-level notice) "hit timelimit of ~D seconds (421)" - timeout-seconds) - (log (syslog-level debug) - "so closing control connection and quitting") - (register-reply! - 421 - (format #f "Timeout (~D seconds): closing control connection." - timeout-seconds) - (signal 'ftpd-quit))) - (else - (call-with-values - (lambda () (parse-command-line command-line)) - (lambda (command arg) - (handle-command command arg))))))) - -(define (handle-command command arg) -; (log (syslog-level debug) -; "handling command ~S with argument ~S" -; command arg) - (call-with-current-continuation - (lambda (escape) - (with-handler - (lambda (condition more) - (cond - ((error? condition) - (let ((reason (condition-stuff condition))) - (log (syslog-level notice) - "internal error occured: ~S (maybe reason: ~S) -- replying and escaping (451)" - condition reason) - (register-reply! 451 - (format #f "Internal error: ~S" reason)) - (escape 'fick-dich-ins-knie))) - ((ftpd-error? condition) - ; debug level because nearly every unsuccessful command ends - ; here (no args, can't change dir, etc.) - (log (syslog-level debug) - "ftpd error occured (maybe reason: ~S)-- escaping" (condition-stuff condition)) - (escape 'fick-dich-ins-knie)) - (else - (more)))) - (lambda () - (with-errno-handler* - (lambda (errno packet) - (let ((unix-error (car packet))) - (log (syslog-level notice) - "unix error occured: ~S -- replying (451) and escaping" - unix-error) - (register-reply! 451 - (format #f "Unix error: ~A." unix-error)) - (escape 'fick-dich-ins-knie))) - (lambda () - (dispatch-command command arg)))))))) - -(define (dispatch-command command arg) -; (log (syslog-level debug) -; "dispatching command ~S with argument ~S" -; command arg) - (cond - ((assoc command *command-alist*) - => (lambda (pair) - (log (syslog-level debug) - "command ~S was found in command-list and is executed with argument ~S" - (car pair) arg) - ((cdr pair) arg))) - (else - (log (syslog-level debug) "rejecting unknown command ~S (500) (argument: ~S)" - command arg) - (register-reply! 500 - (string-append - (format #f "Unknown command: \"~A\"" command) - (if (string=? "" arg) - "." - (format #f " (argument(s) \"~A\")." arg))))))) - - -(define (handle-user name) - (log-command (syslog-level info) "USER" name) - (cond - ((the-session-logged-in?) - (log (syslog-level info) "user ~S is already logged in (230)" - name) - (register-reply! 230 - "You are already logged in.")) - ((or (string=? "anonymous" name) - (string=? "ftp" name)) - (handle-user-anonymous)) - (else - (log (syslog-level info) "rejecting non-anonymous login (530)") - (register-reply! 530 - "Only anonymous logins allowed.")))) - -(define (handle-user-anonymous) - (log (syslog-level info) "anonymous user login (230)") - (set-the-session-logged-in?! #t) - (set-the-session-authenticated?! #t) - (set-the-session-anonymous?! #t) - (set-the-session-root-directory! - (file-name-as-directory (the-ftpd-options-anonymous-home))) - (set-the-session-current-directory! "") - - (register-reply! 230 "Anonymous user logged in.")) - -(define (handle-pass password) - (log-command (syslog-level info) "PASS" password) - (cond - ((not (the-session-logged-in?)) - (log (syslog-level info) "Rejecting password; user has not logged in yet. (530)") - (register-reply! 530 "You have not logged in yet.")) - ((the-session-anonymous?) - (log (syslog-level info) "Accepting password; user is logged in (200)") - (register-reply! 200 "Thank you.")) - (else - (log (syslog-level notice) "Reached unreachable case-branch while handling password (502)") - (register-reply! 502 "This can't happen.")))) - -(define (handle-quit foo) - (log-command (syslog-level info) "QUIT") - (log (syslog-level debug) "quitting (221)") - (register-reply! 221 "Goodbye! Au revoir! Auf Wiedersehen!") - (signal 'ftpd-quit)) - -(define (handle-syst foo) - (log-command (syslog-level info) "SYST") - (log (syslog-level debug) "telling system type (215)") - (register-reply! 215 "UNIX Type: L8")) - -(define (handle-cwd path) - (log-command (syslog-level info) "CWD" path) - (ensure-authenticated-login) - (let ((current-directory (assemble-path (the-session-current-directory) - path))) - (with-errno-handler* - (lambda (errno packet) - (let ((error-reason (car packet))) - (log (syslog-level info) - "can't change to directory \"~A\": ~A (550)" - path error-reason) - (signal-error! 550 - (format #f "Can't change directory to \"~A\": ~A." - path - error-reason)))) - (lambda () - (with-cwd* - (file-name-as-directory - (string-append (the-session-root-directory) current-directory)) - (lambda () ; I hate gratuitous syntax - (log (syslog-level debug) - "changing current directory to \"/~A\" (250)" - current-directory) - (set-the-session-current-directory! current-directory) - (register-reply! 250 - (format #f "Current directory changed to \"/~A\"." - current-directory)))))))) - -(define (handle-cdup foo) - (log-command (syslog-level info) "CDUP") - (handle-cwd "..")) - -(define (handle-pwd foo) - (log-command (syslog-level info) "PWD") - (ensure-authenticated-login) - (let ((current-directory (the-session-current-directory))) - (log (syslog-level info) "replying \"/~A\" as current directory (257)" - current-directory) - (register-reply! 257 - (format #f "Current directory is \"/~A\"." - current-directory)))) - - -(define (make-file-action-handler error-format-string action) - (lambda (path) - (ensure-authenticated-login) - (if (string=? "" path) - (begin - (log (syslog-level info) - "finishing processing command because of missing arguments (500)") - (signal-error! 500 "No argument."))) - (let ((full-path (string-append (the-session-root-directory) - (assemble-path (the-session-current-directory) - path)))) - (with-errno-handler* - (lambda (errno packet) - (let ((error-reason (car packet))) - (log (syslog-level info) - (string-append error-format-string " (550)") path error-reason) - (signal-error! 550 - (format #f error-format-string - path error-reason)))) - (lambda () - (action path full-path)))))) - -(define handle-dele - (make-file-action-handler - "Could not delete \"~A\": ~A." - (lambda (path full-path) - (log-command (syslog-level info) "DELE" path) - (delete-file full-path) - (log (syslog-level debug) "deleted ~S (250)" full-path) - (log (syslog-level debug) "reporting about ~S" path) - (register-reply! 250 (format #f "Deleted \"~A\"." path))))) - -(define handle-mdtm - (make-file-action-handler - "Could not get info on \"~A\": ~A." - (lambda (path full-path) - (log-command (syslog-level info) "MDTM" path) - (let* ((info (file-info full-path)) - (the-date (date (file-info:mtime info) 0)) - (formatted-date (format-date "~Y~m~d~H~M~S" the-date))) - (log (syslog-level debug) "reporting modification time of ~S: ~A (213)" - full-path - formatted-date) - (register-reply! 213 - formatted-date))))) - -(define handle-mkd - (make-file-action-handler - "Could not make directory \"~A\": ~A." - (lambda (path full-path) - (log-command (syslog-level info) "MKD" path) - (create-directory full-path #o755) - (log (syslog-level debug) "created directory ~S (257)" full-path) - (log (syslog-level debug) "reporting about ~S" path) - (register-reply! 257 - (format #f "Created directory \"~A\"." path))))) - -(define handle-rmd - (make-file-action-handler - "Could not remove directory \"~A\": ~A." - (lambda (path full-path) - (log-command (syslog-level info) "RMD" path) - (delete-directory full-path) - (log (syslog-level debug) "deleted directory ~S (250)" full-path) - (log (syslog-level debug) "reporting about ~S" path) - (register-reply! 250 - (format #f "Deleted directory \"~A\"." path))))) - - -(define handle-rnfr - (make-file-action-handler - "Could not get info on file \"~A\": ~A." - (lambda (path full-path) - (log-command (syslog-level info) "RNFR" path) - (file-info full-path) - (log (syslog-level debug) - "RNFR-command accepted, waiting for RNTO-command (350)") - (register-reply! 350 "RNFR accepted. Gimme a RNTO next.") - (set-the-session-to-be-renamed! full-path)))) - -(define (handle-rnto path) - (log-command (syslog-level info) "RNTO" path) - (ensure-authenticated-login) - (if (not (the-session-to-be-renamed)) - (begin - (log (syslog-level info) - "RNTO-command rejected: need RNFR-command before (503)") - (signal-error! 503 "Need RNFR before RNTO."))) - (if (string=? "" path) - (begin - (log (syslog-level info) - "No argument -- still waiting for (correct) RNTO-command (500)") - (signal-error! 500 "No argument."))) - (let ((full-path (string-append (the-session-root-directory) - (assemble-path (the-session-current-directory) - path)))) - - (if (file-exists? full-path) - (begin - (log (syslog-level info) "rename of ~S failed (already exists) (550)" - full-path) - (log (syslog-level debug) "reporting about ~S" - path) - (signal-error! - 550 - (format #f "Rename failed---\"~A\" already exists or is protected." - path)))) - - (with-errno-handler* - (lambda (errno packet) - (log (syslog-level info) - "failed to rename ~A (550)" path) - (signal-error! 550 - (format #f "Could not rename: ~A." path))) - (lambda () - (let ((old-name (the-session-to-be-renamed))) - (rename-file old-name full-path) - (log (syslog-level debug) - "~S renamed to ~S - no more waiting for RNTO-command (250)" - old-name full-path) - (register-reply! 250 "File renamed.") - (set-the-session-to-be-renamed! #f)))))) - -(define handle-size - (make-file-action-handler - "Could not get info on file \"~A\": ~A." - (lambda (path full-path) - (log-command (syslog-level info) "SIZE" path) - (let ((info (file-info full-path))) - (if (not (eq? 'regular (file-info:type info))) - (begin - (log (syslog-level info) - "rejecting SIZE-command as ~S is not a regular file (550)" - full-path) - (log (syslog-level debug) "reporting about ~S" path) - (signal-error! 550 - (format #f "\"~A\" is not a regular file." - path)))) - (let ((file-size (file-info:size info))) - (log (syslog-level debug) - "reporting ~D as size of ~S (213)" - file-size full-path) - (register-reply! 213 (number->string file-size))))))) - - -(define (handle-type arg) - (log-command (syslog-level info) "TYPE" arg) - (cond - ((string-ci=? "A" arg) - (log (syslog-level debug) "changed type to ascii (200)") - (set-the-session-type! 'ascii)) - ((string-ci=? "I" arg) - (log (syslog-level debug) "changed type to image (8-bit binary) (200)") - (set-the-session-type! 'image)) - ((string-ci=? "L8" arg) - (log (syslog-level debug) "changed type to image (8-bit binary) (200)") - (set-the-session-type! 'image)) - (else - (log (syslog-level info) - "rejecting TYPE-command: unknown type (504)") - (signal-error! 504 - (format #f "Unknown TYPE: ~S." arg)))) - - (log (syslog-level debug) "reporting new type (see above)") - (register-reply! 200 - (format #f "TYPE is now ~A." - (case (the-session-type) - ((ascii) "ASCII") - ((image) "8-bit binary") - (else "somethin' weird, man"))))) - -(define (handle-mode arg) - (log-command (syslog-level info) "MODE" arg) - (cond - ((string=? "" arg) - (log (syslog-level info) "rejecting MODE-command: no arguments (500)") - (register-reply! 500 - "No arguments. Not to worry---I'd ignore them anyway.")) - ((string-ci=? "S" arg) - (log (syslog-level info) - "stream mode is (still) used for file-transfer (200)") - (register-reply! 200 "Using stream mode to transfer files.")) - (else - (log (syslog-level info) "mode ~S is not supported (504)" arg) - (register-reply! 504 (format #f "Mode \"~A\" is not supported." - arg))))) - -(define (handle-stru arg) - (log-command (syslog-level info) "STRU" arg) - (cond - ((string=? "" arg) - (log (syslog-level info) "rejecting STRU-command: no arguments (500)") - (register-reply! 500 - "No arguments. Not to worry---I'd ignore them anyway.")) - ((string-ci=? "F" arg) - (log (syslog-level debug) "(still) using file structure to transfer files (200)") - (register-reply! 200 "Using file structure to transfer files.")) - (else - (log (syslog-level info) "file structure ~S is not supported (504)" arg) - (register-reply! 504 - (format #f "File structure \"~A\" is not supported." - arg))))) - -(define (handle-noop arg) - (log-command (syslog-level info) "NOOP") - (log (syslog-level debug) "successfully done nothing (200)") - (register-reply! 200 "Done nothing, but successfully.")) - -(define (ftpd-parse-port-arg stuff) - (with-fatal-error-handler* - (lambda (condition more) - (log (syslog-level debug) "reporting syntax error in argument (500)") - (signal-error! 500 - "Syntax error in argument to PORT.")) - (lambda () - (parse-port-arg stuff)))) - -(define (handle-port stuff) - (log-command (syslog-level info) "PORT" stuff) - (ensure-authenticated-login) - (maybe-close-data-connection) - (call-with-values - (lambda () (ftpd-parse-port-arg stuff)) - (lambda (address port) - (let ((socket (create-socket protocol-family/internet - socket-type/stream))) - (log (syslog-level debug) - "created new socket (internet, stream, reusing address)") - (set-socket-option socket level/socket socket/reuse-address #t) - - (connect-socket socket - (internet-address->socket-address - address port)) - - (set-the-session-data-socket! socket) - - (let ((formatted-internet-host-address - (format-internet-host-address address))) - (log (syslog-level debug) - "connected to ~A, port ~A (200)" - formatted-internet-host-address port) - - (register-reply! 200 - (format #f "Connected to ~A, port ~A." - formatted-internet-host-address - port))))))) - - -(define (handle-pasv stuff) - (log-command (syslog-level info) "PASV") - (ensure-authenticated-login) - (maybe-close-data-connection) - (let ((socket (create-socket protocol-family/internet - socket-type/stream))) - - (set-socket-option socket level/socket socket/reuse-address #t) - - (bind-socket socket - (internet-address->socket-address (this-host-address) - 0)) - (listen-socket socket 1) - - (let ((address (socket-local-address socket))) - - (call-with-values - (lambda () (socket-address->internet-address address)) - (lambda (host-address port) - - (set-the-session-passive-socket! socket) - - - (let ((formatted-this-host-address - (format-internet-host-address (this-host-address) ",")) - (formatted-port (format-port port))) - (log (syslog-level debug) "accepting passive mode (on ~A,~A) (227)" - formatted-this-host-address formatted-port) - (register-reply! 227 - (format #f "Passive mode OK (~A,~A)" - formatted-this-host-address - formatted-port)))))))) - -(define (this-host-address) - (let ((socket (port->socket (the-session-control-input-port) - protocol-family/internet))) - (call-with-values - (lambda () - (socket-address->internet-address - (socket-local-address socket))) - (lambda (host-address control-port) - (log (syslog-level debug) "Closing ~A ~A" - (socket:inport socket) (socket:outport socket)) - (close-socket socket) - host-address)))) - -(define (handle-nlst arg) - (log-command (syslog-level info) "NLST" arg) - (handle-listing arg '())) - -(define (handle-list arg) - (log-command (syslog-level info) "LIST" arg) - (handle-listing arg '(long))) - -(define (handle-listing arg preset-flags) - (ensure-authenticated-login) - (with-data-connection - (lambda () - (let ((args (split-arguments arg))) - (call-with-values - (lambda () - (partition - (lambda (arg) - (and (not (string=? "" arg)) - (char=? #\- (string-ref arg 0)))) - args)) - (lambda (flag-args rest-args) - - (if (and (not (null? rest-args)) - (not (null? (cdr rest-args)))) - (begin - (log (syslog-level info) "got more than one path argument - rejection (501)") - (signal-error! 501 "More than one path argument."))) - - (let ((path (if (null? rest-args) - "" - (car rest-args))) - (flags (arguments->ls-flags flag-args))) - - (if (not flags) - (begin - (log (syslog-level info) "got invalid flags (501)") - (signal-error! 501 "Invalid flag(s)."))) - (let ((all-flags (append preset-flags flags))) - (log (syslog-level debug) - "sending file-listing for path ~S with flags ~A" - path all-flags) - - (generate-listing path all-flags))))))))) - -; Note this doesn't call ENSURE-AUTHENTICATED-LOGIN or -; ENSURE-DATA-CONNECTION. - -(define (generate-listing path flags) - (let ((full-path (string-append (the-session-root-directory) - (assemble-path (the-session-current-directory) - path)))) - (with-errno-handler* - (lambda (errno packet) - (let ((error-reason (car packet))) - (log (syslog-level info) - "can't access directory at ~A: ~A (451)" - path error-reason) - (signal-error! 451 - (format #f "Can't access directory at ~A: ~A." - path - error-reason)))) - (lambda () - (with-cwd* - (file-name-directory full-path) - (lambda () - (let ((nondir (file-name-nondirectory full-path))) - (let-fluid - ls-crlf? #t - (lambda () - (ls flags - (list - ;; work around OLIN BUG - (if (string=? nondir "") - "." - nondir)) - (socket:outport (the-session-data-socket)))))))))))) - -(define (handle-abor foo) - (log-command (syslog-level info) "ABOR") - (maybe-close-data-connection) - (log (syslog-level debug) "closing data connection (226)") - (register-reply! 226 "Closing data connection.")) - -(define (handle-retr path) - (log-command (syslog-level info) "RETR" path) - (ensure-authenticated-login) - (let ((full-path (string-append (the-session-root-directory) - (assemble-path (the-session-current-directory) - path)))) - (with-fatal-error-handler* ; CALL-WITH-INPUT-FILE doesn't go through ERRNO - (lambda (condition more) - (let ((reason (condition-stuff condition))) - (log (syslog-level info) "failed to open ~S for reading (maybe reason: ~S) (550)" full-path reason) - (log (syslog-level debug) "replying error for file ~S (maybe reason: ~S)" path reason) - (signal-error! 550 - (format #f "Can't open \"~A\" for reading." - path)))) - (lambda () - (let ((info (file-info full-path)) - (start-transfer-seconds (current-seconds))) - (if (not (eq? 'regular (file-info:type info))) - (begin - (log (syslog-level info) "rejecting RETR-command as ~S is not a regular file (450)" - full-path) - (log (syslog-level debug) "reporting about ~S" path) - (signal-error! 450 - (format #f "\"~A\" is not a regular file." - path)))) - (call-with-input-file full-path - (lambda (file-port) - (with-data-connection - (lambda () - (case (the-session-type) - ((image) - (log (syslog-level debug) - "sending file ~S (binary mode)" - full-path) - (log (syslog-level debug) "sending is from port ~S" file-port) - (copy-port->port-binary - file-port - (socket:outport (the-session-data-socket)))) - ((ascii) - (log (syslog-level debug) "sending file ~S (ascii mode)" - full-path) - (log (syslog-level debug) "sending is from port ~S" file-port) - (copy-port->port-ascii - file-port - (socket:outport (the-session-data-socket))))) - (file-log start-transfer-seconds info full-path "o")))))))))) - -(define (current-seconds) - (receive (time ticks) (time+ticks) time)) - -(define (handle-stor path) - (log-command (syslog-level info) "STOR" path) - (ensure-authenticated-login) - (let ((full-path (string-append (the-session-root-directory) - (assemble-path (the-session-current-directory) - path)))) - (with-fatal-error-handler* - (lambda (condition more) - (let ((reason (condition-stuff condition))) - (log (syslog-level info) "can't open ~S for writing (maybe reason: ~S) (550)" full-path reason) - (log (syslog-level debug) "replying error for file ~S (maybe reason: ~S)" path reason) - (signal-error! 550 (format #f "Can't open \"~A\" for writing." path)))) - (lambda () - (let ((start-transfer-seconds (current-seconds))) - (call-with-output-file full-path - (lambda (file-port) - (with-data-connection - (lambda () - (let ((inport (socket:inport (the-session-data-socket)))) - (case (the-session-type) - ((image) - (log (syslog-level notice) - "storing data to ~S (binary mode)" - full-path) - (log (syslog-level debug) - "storing comes from socket-inport ~S (binary-mode)" - inport) - (copy-port->port-binary - (socket:inport (the-session-data-socket)) - file-port)) - ((ascii) - (log (syslog-level notice) - "storing data to ~S (ascii-mode)" - full-path) - (log (syslog-level debug) - "storing comes from socket-inport ~S (ascii-mode)" - inport) - (copy-ascii-port->port - (socket:inport (the-session-data-socket)) - file-port))) - (file-log start-transfer-seconds (file-info full-path) full-path "i") - )))))))))) - -(define (assemble-path current-directory path) - (log (syslog-level debug) "assembling path ~S" - path) - (let* ((interim-path - (if (not (file-name-rooted? path)) - (string-append (file-name-as-directory current-directory) - path) - path)) - (complete-path (if (file-name-rooted? interim-path) - (file-name-sans-rooted interim-path) - interim-path))) - (log (syslog-level debug) "name ~S assembled to ~S" - path complete-path) - (cond - ((normalize-path complete-path) - => (lambda (assembled-path) assembled-path)) - (else - (log (syslog-level debug) - "invalid pathname -- tried to pass root directory (501)") - (signal-error! 501 "Invalid pathname"))))) - -(define (ensure-authenticated-login) - (if (or (not (the-session-logged-in?)) - (not (the-session-authenticated?))) - (begin - (log (syslog-level debug) - "login authentication failed - user is not logged in (530)") - (signal-error! 530 "You're not logged in yet.")) - (log (syslog-level debug) "authenticated login ensured"))) - -(define (with-data-connection thunk) - (dynamic-wind ensure-data-connection - thunk - maybe-close-data-connection)) - -(define *window-size* 4096) - -(define (ensure-data-connection) - (if (and (not (the-session-data-socket)) - (not (the-session-passive-socket))) - (begin - (log (syslog-level debug) "no data connection (425)") - (signal-error! 425 "No data connection."))) - - (if (the-session-passive-socket) - (call-with-values - (lambda () (accept-connection (the-session-passive-socket))) - (lambda (socket socket-address) - (set-the-session-data-socket! socket)))) - - (log (syslog-level debug) "opening data connection (150)") - (register-reply! 150 "Opening data connection.") - (write-replies) - - (set-socket-option (the-session-data-socket) level/socket - socket/send-buffer *window-size*) - (set-socket-option (the-session-data-socket) level/socket - socket/receive-buffer *window-size*)) - -(define (maybe-close-data-connection) - (if (or (the-session-data-socket) (the-session-passive-socket)) - (close-data-connection))) - -(define (close-data-connection) - (if (the-session-data-socket) - (close-socket (the-session-data-socket))) - (if (the-session-passive-socket) - (close-socket (the-session-passive-socket))) - (log (syslog-level debug) "closing data connection (226)") - (register-reply! 226 "Closing data connection.") - (set-the-session-data-socket! #f) - (set-the-session-passive-socket! #f)) - -(define *command-alist* - (list - (cons "NOOP" handle-noop) - (cons "USER" handle-user) - (cons "PASS" handle-pass) - (cons "QUIT" handle-quit) - (cons "SYST" handle-syst) - (cons "CWD" handle-cwd) - (cons "PWD" handle-pwd) - (cons "CDUP" handle-cdup) - (cons "DELE" handle-dele) - (cons "MDTM" handle-mdtm) - (cons "MKD" handle-mkd) - (cons "RMD" handle-rmd) - (cons "RNFR" handle-rnfr) - (cons "RNTO" handle-rnto) - (cons "SIZE" handle-size) - (cons "TYPE" handle-type) - (cons "MODE" handle-mode) - (cons "STRU" handle-stru) - (cons "PORT" handle-port) - (cons "PASV" handle-pasv) - (cons "NLST" handle-nlst) - (cons "LIST" handle-list) - (cons "RETR" handle-retr) - (cons "STOR" handle-stor) - (cons "ABOR" handle-abor))) - -(define (parse-command-line line) - (if (eof-object? line) ; Netscape does this - (signal 'ftpd-irregular-quit) - (let* ((line (string-trim-both line char-set:whitespace)) - (split-position (string-index line #\space))) - (if split-position - (values (string-map char-upcase (substring line 0 split-position)) - (string-trim-both (substring line - (+ 1 split-position) - (string-length line)) - char-set:whitespace)) - (values (string-map char-upcase line) ""))))) - -; Path names - -; This removes all internal ..'s from a path. -; NORMALIZE-PATH returns #f if PATH points to a parent directory. - -(define (normalize-path path) - (let loop ((components (split-file-name (simplify-file-name path))) - (reverse-result '())) - (cond - ((null? components) - (path-list->file-name (reverse reverse-result))) - ((string=? ".." (car components)) - (if (null? reverse-result) - #f - (loop (cdr components) (cdr reverse-result)))) - (else - (loop (cdr components) (cons (car components) reverse-result)))))) - -(define (file-name-rooted? file-name) - (and (not (string=? "" file-name)) - (char=? #\/ (string-ref file-name 0)))) - -(define (file-name-sans-rooted file-name) - (substring file-name 1 (string-length file-name))) - -(define split-arguments - (infix-splitter (make-regexp " +"))) - -; Reply handling - -; Replies must be synchronous with requests and actions. Therefore, -; they are queued on generation via REGISTER-REPLY!. The messages are -; printed via WRITE-REPLIES. For the nature of the replies, see RFC -; 959. - - -(define (write-replies) - (if (not (null? (the-session-reverse-replies))) - (let loop ((messages (reverse (the-session-reverse-replies)))) - (if (null? (cdr messages)) - (write-final-reply (car messages)) - (begin - (write-nonfinal-reply (car messages)) - (loop (cdr messages)))))) - (set-the-session-reverse-replies! '())) - -(define (write-final-reply line) - (format (the-session-control-output-port) "~D ~A" (the-session-reply-code) line) - (log (syslog-level debug) "Reply: ~D ~A~%" (the-session-reply-code) line) - (write-crlf (the-session-control-output-port)) - (force-output (the-session-control-output-port))) - -(define (write-nonfinal-reply line) - (format (the-session-control-output-port) "~D-~A" (the-session-reply-code) line) - (log (syslog-level debug) "Reply: ~D-~A~%" (the-session-reply-code) line) - (write-crlf (the-session-control-output-port))) - -(define (signal-error! code message) - (register-reply! code message) - (signal 'ftpd-error)) - -(define (register-reply! code message) - (set-the-session-reverse-replies! - (cons message (the-session-reverse-replies))) - (set-the-session-reply-code! code)) - diff --git a/scheme/httpd/access-control.scm b/scheme/httpd/access-control.scm deleted file mode 100644 index 9e83fa5..0000000 --- a/scheme/httpd/access-control.scm +++ /dev/null @@ -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. -;;; 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))))) \ No newline at end of file diff --git a/scheme/httpd/cgi-server.scm b/scheme/httpd/cgi-server.scm deleted file mode 100644 index 37516b0..0000000 --- a/scheme/httpd/cgi-server.scm +++ /dev/null @@ -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 and 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 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 -;;; ? part of the URI. (Hence the CGI script will split the individual -;;; fields into argv[].) - - -;;; CGI interface: -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; - The URL's 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 diff --git a/scheme/httpd/core.scm b/scheme/httpd/core.scm deleted file mode 100644 index d942b35..0000000 --- a/scheme/httpd/core.scm +++ /dev/null @@ -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 # 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)) - diff --git a/scheme/httpd/error.scm b/scheme/httpd/error.scm deleted file mode 100644 index fcb6dde..0000000 --- a/scheme/httpd/error.scm +++ /dev/null @@ -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)) - diff --git a/scheme/httpd/file-dir-handler.scm b/scheme/httpd/file-dir-handler.scm deleted file mode 100644 index 4e6f22e..0000000 --- a/scheme/httpd/file-dir-handler.scm +++ /dev/null @@ -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 ( . ), -;;; serving -;;; ~// - -(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 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 ""))) - (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)))) - diff --git a/scheme/httpd/handlers.scm b/scheme/httpd/handlers.scm deleted file mode 100644 index 81bdeaa..0000000 --- a/scheme/httpd/handlers.scm +++ /dev/null @@ -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)) diff --git a/scheme/httpd/http-top.scm b/scheme/httpd/http-top.scm deleted file mode 100644 index 908f064..0000000 --- a/scheme/httpd/http-top.scm +++ /dev/null @@ -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// => serve 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/ passes control to script -;;; /usr/local/etc/httpd/cgi-bin/ -;;; - 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"))) - diff --git a/scheme/httpd/info-gateway.scm b/scheme/httpd/info-gateway.scm deleted file mode 100644 index 18a49ff..0000000 --- a/scheme/httpd/info-gateway.scm +++ /dev/null @@ -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 (), 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?. -;;; -;;; 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))))))))) diff --git a/scheme/httpd/logging.scm b/scheme/httpd/logging.scm deleted file mode 100644 index ea6a48f..0000000 --- a/scheme/httpd/logging.scm +++ /dev/null @@ -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)) \ No newline at end of file diff --git a/scheme/httpd/options.scm b/scheme/httpd/options.scm deleted file mode 100644 index 24ec38a..0000000 --- a/scheme/httpd/options.scm +++ /dev/null @@ -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)))))) diff --git a/scheme/httpd/request.scm b/scheme/httpd/request.scm deleted file mode 100644 index 303724f..0000000 --- a/scheme/httpd/request.scm +++ /dev/null @@ -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)))) - diff --git a/scheme/httpd/response.scm b/scheme/httpd/response.scm deleted file mode 100644 index 79cd524..0000000 --- a/scheme/httpd/response.scm +++ /dev/null @@ -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 "
~%Further Information: ~A
~%" message)))) - (close-html (lambda (port) - (for-each (lambda (x) (format port "
~s~%" x)) extras) - (write-string "\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 new location.~%" - (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 "

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 "

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 - "

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. -

-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 "~%~%~A~%~%~%~%" message) - (format out "~%

~A

~%" 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))) diff --git a/scheme/httpd/rman-gateway.scm b/scheme/httpd/rman-gateway.scm deleted file mode 100644 index b652846..0000000 --- a/scheme/httpd/rman-gateway.scm +++ /dev/null @@ -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"))) diff --git a/scheme/httpd/server.scm b/scheme/httpd/server.scm deleted file mode 100755 index 7364f99..0000000 --- a/scheme/httpd/server.scm +++ /dev/null @@ -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"))))))) diff --git a/scheme/httpd/seval.scm b/scheme/httpd/seval.scm deleted file mode 100644 index a3ae9b0..0000000 --- a/scheme/httpd/seval.scm +++ /dev/null @@ -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= -;;; string, extract , 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.")))) diff --git a/scheme/lib/cgi-script.scm b/scheme/lib/cgi-script.scm deleted file mode 100644 index f9ed15c..0000000 --- a/scheme/lib/cgi-script.scm +++ /dev/null @@ -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 -;;; ? part of the URI. (Hence the CGI script will split the individual -;;; fields into argv[].) - - -;;; CGI interface: -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; - The URL's 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. diff --git a/scheme/lib/crlf-io.scm b/scheme/lib/crlf-io.scm deleted file mode 100644 index 8dd2ced..0000000 --- a/scheme/lib/crlf-io.scm +++ /dev/null @@ -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. -;;; 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))))))) - - diff --git a/scheme/lib/dns.scm b/scheme/lib/dns.scm deleted file mode 100644 index 6a4da50..0000000 --- a/scheme/lib/dns.scm +++ /dev/null @@ -1,1567 +0,0 @@ -; -; dns.scm -; -; Implementation of the RFC1035 - -;;; This file is part of the Scheme Untergrund Networking package. - -;;; Copyright (c) 2002 by Marcus Crestani. -;;; Copyright (c) 2002-2003 by Martin Gasbichler -;;; For copyright information, see the file COPYING which comes with -;;; the distribution. - -; domain names - implementation and specification -; based on the PLT-implementation. -; -; -; TODO: -; - test, test, test -; - types from newer RFCs (41, unknown) -; - more documentation -; -; --- -; sample usage & documentation: -; -; is a 32bit integer internet->address, shortly address32. -; is a string in standard dot notation "xxx.xxx.xxx.xxx". -; is a string -; -; can either be a domainname, an ip-string or an ip-address32. -; if it is a domainname, its ip is looked up on a nameserver listed in -; /etc/resolv.conf. -; -; (dns-find-nameserver) --> -; this parses the /etc/resolv.conf file and returns the first found -; nameserver in address32 format. -; -; -; -; (dns-lookup-name [nameserver]) --> -; (dns-lookup-ip [nameserver]) --> -; (dns-lookup-nameserver [nameserver]) -; --> -; (dns-lookup-mail-exchanger [nameserver]) -; --> -; -; dns-lookup-name, dns-lookup-ip, dns-lookup-nameserver and -; dns-lookup-mail-exchanger are "simple lookup functions", -; they return the wanted information or #f. -; dns-lookup-ip can either be given an ip-string or an ip-address32. -; -; concurrent dns lookup: -; if a list of nameservers is given to the optional argument, -; a concurrent lookup to all nameservers in this list is started. -; The nameservers in this list could either be ip-strings or ip-address32s. -; example: (dns-lookup-name "www.uni-tuebingen.de" (dns-find-nameserver-list)) -; starts an concurrent lookup which contacts all nameservers in -; /etc/resolv.conf. -; -; -; (dns-lookup [nameserver]) -; --> -; (show-dns-message the whole message, human readable -; -; a is a record, with several entries, which holds the whole -; query/response dialog. the simplest way to get detailed information about -; the record structure is to view the result of show-dns-message. -; -; dns-lookup returns much more information than the simple lookup functions, -; only useful in very special cases. -; -; -; some lookups return a hostname (e.g. mx). -; many applications need instead of a hostname a ip address. -; force-ip and force-ip-list guarantee that a ip address is -; returned. -; -; (force-ip ) --> -; (force-ip-list ) --> -; -; -; useful converters: -; -; (address32->ip-string ) -> -; (ip-string->address32 ) -> - - - -;; --- error conditions - -;; supertype of all errors signaled by this library -(define-condition-type 'dns-error '(error)) -(define dns-error? (condition-predicate 'dns-error)) - -(define-condition-type 'parse-error '(dns-error)) -(define parse-error? (condition-predicate 'parse)) - -(define-condition-type 'unexpected-eof-from-server '(dns-error)) -(define unexpected-eof-from-server? (condition-predicate 'unexpected-eof-from-server)) - -(define-condition-type 'bad-address '(dns-error)) -(define bad-address? (condition-predicate 'bad-address)) - -(define-condition-type 'no-nameservers '(dns-error)) -(define no-nameservers? (condition-predicate 'no-nameservers)) - -(define-condition-type 'bad-nameserver '(dns-error)) -(define bad-nameserver? (condition-predicate 'bad-nameserver)) - -(define-condition-type 'not-a-hostname '(dns-error)) -(define not-a-hostname? (condition-predicate 'not-a-hostname)) - -(define-condition-type 'not-a-ip '(dns-error)) -(define not-a-ip? (condition-predicate 'not-a-ip)) - -;; supertype of all errors signaled if the dns server returned a non-sero -;; reply code -(define-condition-type 'dns-server-error '(dns-error)) -(define dns-server-error? (condition-predicate 'dns-server-error)) - -(define-condition-type 'dns-format-error '(dns-server-error)) -(define dns-format-error? (condition-predicate 'dns-format-error)) - -(define-condition-type 'dns-server-failure '(dns-server-error)) -(define dns-server-failure? (condition-predicate 'dns-server-failure)) - -(define-condition-type 'dns-name-error '(dns-server-error)) -(define dns-name-error? (condition-predicate 'dns-name-error)) - -(define-condition-type 'dns-not-implemented '(dns-server-error)) -(define dns-not-implemented? (condition-predicate 'dns-not-implemented)) - -(define-condition-type 'dns-refused '(dns-server-error)) -(define dns-refused? (condition-predicate 'dns-refused)) - -(define (dns-error condition . stuff) - (apply signal condition (dns-error->string condition) stuff)) - -(define (dns-error->string condition) - (string-append - "dns-error: " - (case condition - ((parse-error) - "parse: error parsing server message") - ((unexpected-eof-from-server) - "send-receive-message: unexpected EOF from server") - ((bad-address) - "dns-get-information: bad address (in combination with query type)") - ((no-nameservers) - "dns-find-nameserver: no nameservers found in /etc/resolv.conf") - ((bad-nameserver) - "send-receive-message: nameserver refused connection") - ((not-a-hostname) - "no hostname given") - ((not-a-ip) - "no ip given") - ((dns-format-error) - "error from server: (1) format error") - ((dns-server-failure) - "error from server: (2) server failure") - ((dns-name-error) - "error from server: (3) name error") - ((dns-not-implemented) - "error from server: (4) not implemented") - ((dns-refused) - "error from server: (5) refused") - (else (error "Unknown dns-error" condition))))) - - -;;; -- globals and types -;; off -(define *nul* (ascii->char 0)) - -;; on -(define *on* (ascii->char 1)) - -;; message types -(define-enumerated-type message-type :message-type - message-type? - the-message-types - message-type-name - message-type-number - (unknown ; types, which are not yet implemented - a ; a host address - ns ; an authoritative name server - md ; (obsolete) - mf ; (obsolete) - cname ; the canonical name for an alias - soa ; marks the start of a zone of authority - mb ; (experimental) - mg ; (experimental) - mr ; (experimental) - null ; (experimental) - wks ; a well known service description - ptr ; a domain name pointer - hinfo ; host information - minfo ; (experimental) - mx ; mail exchange - txt)) ; text strings - -;; message classes -(define-enumerated-type message-class :message-class - message-class? - the-message-classes - message-class-name - message-class-number - (placeholder ; this starts at 0... - in ; the Internet - cs ; (obsolete) - ch ; the CHAOS class - hs)) ; Hesoid - - -;;; -- useful stuff - -;; number: 0<= x < 256 -;; octet-pair: (char char) -;; octet-quad: (char char char char) -;; name: string *{"." string} -;; octets: *{(char *char)} nullchar -;; octet-ip: (char char char char) -;; address32: 0 <= x < 2^32-1 -;; ip-string: "www.xxx.yyy.zzz" -;; ip-string-arpa: "zzz.yyy.xxx.www.in-addr.arpa" - -;; encodes numbers (16bit) to octets -(define (number->octet-pair n) - (list (ascii->char (arithmetic-shift n -8)) - (ascii->char (modulo n 256)))) - -;; decodes octets to numbers (16bit) -(define (octet-pair->number a b) - (+ (arithmetic-shift (char->ascii a) 8) - (char->ascii b))) - -;; encodes numbers (32bit) to octets, needed for ttl -(define (number->octet-quad n) - (list (ascii->char (arithmetic-shift n -24)) - (ascii->char (modulo (arithmetic-shift n -16) 256)) - (ascii->char (modulo (arithmetic-shift n -8) 256)) - (ascii->char (modulo n 256)))) - -;; decodes octets to numbers, needed for 32bit ttl -(define (octet-quad->number a b c d) - (+ (arithmetic-shift (char->ascii a) 24) - (arithmetic-shift (char->ascii b) 16) - (arithmetic-shift (char->ascii c) 8) - (char->ascii d))) - -;; encodes a domain-name string to octets -(define (name->octets s) - (define (encode-portion s) - (cons - (ascii->char (string-length s)) - (string->list s))) - - (let loop ((s s)) - (cond - ((regexp-search (rx (: bos (submatch (* (~ "."))) "." (submatch (* any)))) - s) - => (lambda (match) - (append - (encode-portion (match:substring match 1)) - (loop (match:substring match 2))))) - (else - (if (= 0 (string-length s)) - (list *nul*) - ;;; TODO isn't this case an error? - (append - (encode-portion s) - (list *nul*))))))) - - -;; for tcp: message has to be tagged with its length -(define (add-size-tag m) - (append (number->octet-pair (length m)) m)) - -;; converts an octeted-ip to a 32bit integer internet-address -(define (octet-ip->address32 ip) - (+ (arithmetic-shift (char->ascii (list-ref ip 0)) 24) - (arithmetic-shift (char->ascii (list-ref ip 1)) 16) - (arithmetic-shift (char->ascii (list-ref ip 2)) 8) - (char->ascii (list-ref ip 3)))) - -;; converts a 32 bit integer internet-address to an octeted-ip -(define (address32->octet-ip ip) - (list (arithmetic-shift ip -24) - (modulo (arithmetic-shift ip -16) 256) - (modulo (arithmetic-shift ip -8) 256) - (modulo ip 256))) - -;; converts an ip-string to an 32bit integer internet-address -(define (ip-string->address32 ip) - (octet-ip->address32 (ip-string->octet-ip ip))) - -;; converts an ip-string to an 32bit integer internet-address -(define (address32->ip-string ip) - (format #f - "~a.~a.~a.~a" - (arithmetic-shift ip -24) - (modulo (arithmetic-shift ip -16) 256) - (modulo (arithmetic-shift ip -8) 256) - (modulo ip 256))) - -;; converts an octeted-ip to an human readable ip-string -(define (octet-ip->ip-string s) - (format #f - "~a.~a.~a.~a" - (char->ascii (list-ref s 0)) - (char->ascii (list-ref s 1)) - (char->ascii (list-ref s 2)) - (char->ascii (list-ref s 3)))) - -(define ip-string-regexp (rx (: bos - (submatch (** 1 3 digit)) "." - (submatch (** 1 3 digit)) "." - (submatch (** 1 3 digit)) "." - (submatch (** 1 3 digit)) - eos))) - -;; converts an ip-string to octets -(define (ip-string->octet-ip s) - (cond - ((regexp-search ip-string-regexp s) - => (lambda (match) - (list - (ascii->char (string->number (match:substring match 1))) - (ascii->char (string->number (match:substring match 2))) - (ascii->char (string->number (match:substring match 3))) - (ascii->char (string->number (match:substring match 4)))))) - (else - (error "invalid ip-string" s)))) - -;; calculates a "random" number, needed for message-ids -;; TODO use SRFI-27 -(define random - (let ((crank (make-random (modulo (time) (- (expt 2 27) 1))))) - (lambda (limit) - (quotient (* (modulo (crank) 314159265) - limit) - 314159265)))) - -;; checks if a string is a ip -(define (ip-string? s) - (define (byte-as-string? string) - (let ((number (string->number string))) - (and number - (>= number 0) - (< number 256)))) - (cond - ((regexp-search ip-string-regexp s) - => (lambda (match) - (and (byte-as-string? (match:substring match 1)) - (byte-as-string? (match:substring match 2)) - (byte-as-string? (match:substring match 3)) - (byte-as-string? (match:substring match 4))))) - (else #f))) - - -;; checks if v is a address32 -(define (address32? v) - (and (number? v) - (<= 0 v #xffffffff))) - -;; returns a in-addr.arpa name-string or #f (needed to resolve hostname by ip) -(define (ip-string->in-addr-arpa s) - (cond - ((regexp-search ip-string-regexp s) - => (lambda (match) - (string-append - (match:substring match 4) "." - (match:substring match 3) "." - (match:substring match 2) "." - (match:substring match 1) "." - "in-addr.arpa"))) - (else #f))) - -;; filters types in a list of rrs -(define (filter-type list type) - (filter (lambda (rr) - (eq? (rr-type rr) type)) - list)) - -;; sorts a mx-rr-list by preference. needed for dns-lookup-mail-exchanger. -(define (sort-by-preference mx-list) - (sort-list mx-list - (lambda (a b) - (< (rr-data-mx-preference (rr-data a)) (rr-data-mx-preference (rr-data b)))))) - - -;; returns a IP if available (additonal type-a processing) -(define (force-ip name) - (let loop ((result (dns-lookup-name name))) - (if (ip-string? result) - result - (loop (dns-lookup-name result))))) - -;; returns a list of IPs (additional type-a processing) -(define (force-ip-list names) - (map (lambda (elem) (force-ip elem)) names)) - - -;; a standard query header, usefull for most queries -(define (make-std-query-header id question-count) - (let* ((qr 'query) ; querytype: query 0, response 1 - (opcode 0) ; opcode: query 0, iquery 1 (OBSOLETE), status 2 - (aa #f) ; authorative answer (in answers only) - (tc #f) ; truncation (size matters only with UDP) - (rd #t) ; recursion desired: nameserver pursues the query recursivly (optional) - (ra #f) ; recursion available (in answers only) - (zero 0) ; future use - (response-code 0) ; response code: error conditions (in answers only) - (question-count question-count) - (answer-count 0) ; answer count (in answers only) - (nameserver-count 0) ; name server resources (in answers only) - (additional-count 0)) ; additional records (in answers only) - - (make-header - id - (make-flags qr opcode aa tc rd ra zero response-code) - question-count answer-count nameserver-count additional-count))) - - -;; makes a query-message (header and question only) -;; TODO does this really work for several questions as well? -(define (make-query-dns-message header . questions) - (make-message header questions '() '() '() - (apply - append - (header->octets header) - (map question->octets questions)))) - -(define (make-simple-query-dns-message id name type class) - (make-query-dns-message (make-std-query-header id 1) - (make-question name type class))) - -;; makes a resource record for ans, nss, ars (name, type, class, ttl, data) -(define (make-octet-rr name type class ttl rdata) - (let* ((name (name->octets name)) - (type (number->octet-pair (message-type-number type))) - (class (number->octet-pair (message-class-number class))) - (ttl (number->octet-quad ttl)) - (rdlength (number->octet-pair (length rdata))) - (rdata rdata)) - (append name type class ttl rdlength rdata))) - - - -;;; -- parsed message records - -;;; -- dns-message: complete data-structure of an dns-lookup -(define-record-type dns-message :dns-message - (make-dns-message query reply cache? protocol tried-nameservers) - dns-message? - (query dns-message-query) - (reply dns-message-reply) - (cache? dns-message-cache?) - (protocol dns-message-protocol) - (tried-nameservers dns-message-tried-nameservers)) - -;; message -(define-record-type message :message - (make-message header questions answers nameservers additionals source) - message? - (header message-header) - (questions message-questions) - (answers message-answers) - (nameservers message-nameservers) - (additionals message-additionals) - (source message-source)) - -;; header -(define-record-type header :header - (make-header id flags question-count answer-count nameserver-count - additional-count) - header? - (id header-id) - (flags header-flags) - (question-count header-question-count) - (answer-count header-answer-count) - (nameserver-count header-nameserver-count) - (additional-count header-additional-count)) - -;;; -- message constructors: encode to octet-messages - -;; makes an message header -(define (header->octets header) - (let* ((header-id (number->octet-pair (header-id header))) - (header-question-count (number->octet-pair (header-question-count header))) - (header-answer-count (number->octet-pair (header-answer-count header))) - (header-nameserver-count (number->octet-pair - (header-nameserver-count header))) - (header-additional-count (number->octet-pair - (header-additional-count header)))) - (append header-id - (flags->octets (header-flags header)) - header-question-count - header-answer-count - header-nameserver-count - header-additional-count))) - -;; flags -(define-record-type flags :flags - (make-flags query-type opcode authoritative? truncated? recursion-desired? - recursion-available? zero response-code) - flags? - (query-type flags-query-type) - (opcode flags-opcode) - (authoritative? flags-authoritative?) - (truncated? flags-truncated?) - (recursion-desired? flags-recursion-desired?) - (recursion-available? flags-recursion-available?) - (zero flags-zero) - (response-code flags-response-code)) - -(define (make-flags-from-numbers - querytype opcode authoritative? truncated? recursion-desired? recursion-available? - zero response-code) - (make-flags - (if (zero? querytype) 'query 'response) - opcode - (not (zero? authoritative?)) - (not (zero? truncated?)) - (not (zero? recursion-desired?)) - (not (zero? recursion-available?)) - zero - (case response-code - ((0) 'dns-no-error) - ((1) 'dns-format-error) - ((2) 'dns-server-failure) - ((3) 'dns-name-error) - ((4) 'dns-not-implemented) - ((5) 'dns-refused)))) - -(define (flags->octets flags) - (define (boolean->0/1 bool) - (if bool 1 0)) - (list - (ascii->char (+ (arithmetic-shift - (if (eq? (flags-query-type flags) 'query) 0 1) 7) - (arithmetic-shift (flags-opcode flags) 3) - (arithmetic-shift - (boolean->0/1 (flags-authoritative? flags)) 2) - (arithmetic-shift - (boolean->0/1 (flags-truncated? flags)) 1) - (boolean->0/1 (flags-recursion-desired? flags)))) - (ascii->char (+ (arithmetic-shift - (boolean->0/1 (flags-recursion-available? flags)) 7) - (arithmetic-shift (flags-zero flags) 4) - (flags-response-code flags))))) - - -;; question -(define-record-type question :question - (make-question name type class) - question? - (name question-name) - (type question-type) - (class question-class)) - -;; makes a question (name, type, class) -(define (question->octets q) - (let* ((qname (name->octets (question-name q))) - (qtype (number->octet-pair - (message-type-number (question-type q)))) - (qclass (number->octet-pair - (message-class-number (question-class q))))) - (append qname qtype qclass))) - -;;type rr -(define-record-type rr :rr - (make-rr name type class ttl data) - rr? - (name rr-name) - (type rr-type) - (class rr-class) - (ttl rr-ttl) - (data rr-data)) - -;; cache -(define-record-type cache :cache - (make-cache answer ttl time) - cache? - (answer cache-answer) - (ttl cache-ttl) - (time cache-time)) - -;;; -- message parser - -;; parses a domain-name in an message. returns the name and the rest of the message. -(define (parse-name start message) - (let ((v (char->ascii (car start)))) - (cond - ((zero? v) - ;; End of name - (values #f (cdr start))) - ((zero? (bitwise-and #xc0 v)) - ;; Normal label - (let loop ((len v) - (start (cdr start)) - (accum '())) - (cond - ((zero? len) - (call-with-values - (lambda () (parse-name start message)) - (lambda (s start) - (let ((s0 (list->string (reverse! accum)))) - (values (if s - (string-append s0 "." s) - s0) - start))))) - (else (loop (- len 1) - (cdr start) - (cons (car start) accum)))))) - (else - ;; Compression offset - (let ((offset (+ (arithmetic-shift (bitwise-and #x3f v) 8) - (char->ascii (cadr start))))) - (call-with-values - (lambda () (parse-name (list-tail message offset) message)) - (lambda (s ignore-start) - (values s (cddr start))))))))) - -;; parses a question in a message. returns the question and the rest of the message. -(define (parse-question start message) - (call-with-values - (lambda () (parse-name start message)) - (lambda (name start) - (let ((type (vector-ref the-message-types - (octet-pair->number (car start) (cadr start)))) - (start (cddr start))) - (let ((class (vector-ref the-message-classes - (octet-pair->number (car start) (cadr start)))) - (start (cddr start))) - (values (make-question name type class) start)))))) - -;; parses a resourcerecord in a message. returns the rr and the rest of the message. -(define (parse-rr start message) - (call-with-values - (lambda () (parse-name start message)) - (lambda (name start) - (let ((type (vector-ref the-message-types - (octet-pair->number (car start) (cadr start)))) - (start (cddr start))) - (let ((class (vector-ref the-message-classes - (octet-pair->number (car start) (cadr start)))) - (start (cddr start))) - (let ((ttl (octet-quad->number (car start) (cadr start) - (caddr start) (cadddr start))) - (start (cddddr start))) - (let ((len (octet-pair->number (car start) (cadr start))) - (start (cddr start))) - ;; Extract next len bytes of data: - (let loop ((len len) - (start start) - (accum '())) - (if (zero? len) - (values (make-rr name type class ttl (parse-rr-data type class (reverse! accum) message)) start) - (loop (- len 1) - (cdr start) - (cons (car start) accum))))))))))) - -;;; -- rr-data-type records - -(define-record-type rr-data-a :rr-data-a - (make-rr-data-a ip) - rr-data-a? - (ip rr-data-a-ip)) - -(define-record-type rr-data-ns :rr-data-ns - (make-rr-data-ns name) - rr-data-ns? - (name rr-data-ns-name)) - -(define-record-type rr-data-cname :rr-data-cname - (make-rr-data-cname name) - rr-data-cname? - (name rr-data-cname-name)) - -;; ### -;; hinfo not correctly implemented, trying to find examples -(define-record-type rr-data-hinfo :rr-data-hinfo - (make-rr-data-hinfo data) - rr-data-hinfo? - (data rr-data-hinfo-data)) - -(define-record-type rr-data-mx :rr-data-mx - (make-rr-data-mx preference exchanger) - rr-data-mx? - (preference rr-data-mx-preference) - (exchanger rr-data-mx-exchanger)) - -(define-record-type rr-data-ptr :rr-data-ptr - (make-rr-data-ptr name) - rr-data-ptr? - (name rr-data-ptr-name)) - -(define-record-type rr-data-soa :rr-data-soa - (make-rr-data-soa mname rname serial refresh retry expire minimum) - rr-data-soa? - (mname rr-data-soa-mname) - (rname rr-data-soa-rname) - (serial rr-data-soa-serial) - (refresh rr-data-soa-refresh) - (retry rr-data-soa-retry) - (expire rr-data-soa-expire) - (minimum rr-data-soa-minimum)) - -;; ### same as hinfo -(define-record-type rr-data-txt :rr-data-txt - (make-rr-data-txt text) - rr-data-txt? - (text rr-data-txt-text)) - -;; ### same as hinfo and txt -(define-record-type rr-data-wks :rr-data-wks - (make-rr-data-wks data) - rr-data-wks? - (data rr-data-wks-data)) - -;; - -(define (parse-rr-data type class data message) - (cond - ((eq? type (message-type a)) - (make-rr-data-a (octet-ip->address32 data))) - - ((eq? type (message-type ns)) - (make-rr-data-ns (call-with-values - (lambda () (parse-name data message)) - (lambda (name rest) name)))) - - ((eq? type (message-type cname)) - (make-rr-data-cname (call-with-values - (lambda () (parse-name data message)) - (lambda (name rest) name)))) - - ((eq? type (message-type mx)) - (make-rr-data-mx (octet-pair->number (car data) (cadr data)) - (call-with-values - (lambda ()(parse-name (cddr data) message)) - (lambda (name rest) name)))) - - ((eq? type (message-type ptr)) - (make-rr-data-ptr (call-with-values - (lambda () (parse-name data message)) - (lambda (name rest) name)))) - - ((eq? type (message-type soa)) - (call-with-values - (lambda () (parse-name data message)) - (lambda (mname rest) - (call-with-values - (lambda () (parse-name rest message)) - (lambda (rname rest) - (let ((serial (octet-quad->number (car rest) (cadr rest) (caddr rest) (cadddr rest))) - (rest (cddddr rest))) - (let ((refresh (octet-quad->number (car rest) (cadr rest) (caddr rest) (cadddr rest))) - (rest (cddddr rest))) - (let ((retry (octet-quad->number (car rest) (cadr rest) (caddr rest) (cadddr rest))) - (rest (cddddr rest))) - (let ((expire (octet-quad->number (car rest) (cadr rest) (caddr rest) (cadddr rest))) - (rest (cddddr rest))) - (let ((minimum (octet-quad->number (car rest) (cadr rest) (caddr rest) (cadddr rest))) - (rest (cddddr rest))) - (make-rr-data-soa mname rname serial refresh retry expire minimum))))))))))) - - ((eq? type (message-type hinfo)) - (make-rr-data-hinfo (list->string data))) - - ((eq? type (message-type txt)) - (make-rr-data-txt (list->string data))) - - ((eq? type (message-type wks)) - (make-rr-data-wks data)) - - (else (list data)))) - -;; parses n-times a message with parse. returns a list of parse-returns. -(define (parse-n parse start message n) - (let loop ((n n) (start start) (accum '())) - (if (zero? n) - (values (reverse! accum) start) - (call-with-values - (lambda () (parse start message)) - (lambda (rr start) - (loop (- n 1) start (cons rr accum))))))) - -;; parses a message-headers flags. returns the flags. -(define (parse-flags message) - (let ((v0 (list-ref message 2)) - (v1 (list-ref message 3))) - ;; Check for error code: - (let ((response-code (bitwise-and #xf (char->ascii v1))) - (zero (arithmetic-shift (bitwise-and 112 (char->ascii v1)) -4)) - (ra (arithmetic-shift (bitwise-and 64 (char->ascii v1)) -7)) - (rd (bitwise-and 1 (char->ascii v0))) - (tc (arithmetic-shift (bitwise-and 2 (char->ascii v0)) -1)) - (aa (arithmetic-shift (bitwise-and 4 (char->ascii v0)) -2)) - (opcode (arithmetic-shift (bitwise-and 120 (char->ascii v0)) -3)) - (qr (arithmetic-shift (bitwise-and 128 (char->ascii v0)) -7))) - (make-flags-from-numbers qr opcode aa tc rd ra zero response-code)))) - - -;; parses a message-header. returns the header. -(define (parse-header message) - (let ((id (octet-pair->number (list-ref message 0) (list-ref message 1))) - (flags (parse-flags message)) - (question-count (octet-pair->number (list-ref message 4) (list-ref message 5))) - (an-count (octet-pair->number (list-ref message 6) (list-ref message 7))) - (ns-count (octet-pair->number (list-ref message 8) (list-ref message 9))) - (ar-count (octet-pair->number (list-ref message 10) (list-ref message 11)))) - (make-header id flags question-count an-count ns-count ar-count))) - - -;; parses a message. returns the parsed message. -(define (parse message) - (let* ((header (parse-header message)) - (start (list-tail message 12))) - (call-with-values - (lambda () (parse-n parse-question start message (header-question-count header))) - (lambda (qds start) - (call-with-values - (lambda () (parse-n parse-rr start message (header-answer-count header))) - (lambda (ans start) - (call-with-values - (lambda () (parse-n parse-rr start message (header-nameserver-count header))) - (lambda (nss start) - (call-with-values - (lambda () (parse-n parse-rr start message (header-additional-count header))) - (lambda (ars start) - (if (not (null? start)) - (dns-error 'parse-error)) - (make-message header qds ans nss ars message))))))))))) - - - -;;; -- send, receive and validate message - -;; checks if the received reply is valid. returns #t or error-msg. -(define (reply-acceptable? reply query) - ;; Check correct id - (if (not (= (header-id (message-header reply)) - (header-id (message-header query)))) - ;; TODO replace error - (error "send-receive-message: bad reply id from server")) - ;; Check for error code: - (let ((response-code (flags-response-code - (header-flags (message-header reply))))) - (if (not (eq? response-code 'dns-no-error)) - (dns-error response-code)))) - -;; connects to nameserver and sends and receives messages. returns the reply. -;; here: via TCP -(define (send-receive-message-tcp nameservers query) - (receive (reply hit-ns other-nss) - (let ((sockets (map (lambda (nameserver) - (let ((sock (create-socket protocol-family/internet - socket-type/stream)) - (addr (internet-address->socket-address - nameserver 53))) - ;; we ignore the return value and select - ;; unconditionally later - (connect-socket-no-wait sock addr) - sock)) - nameservers))) - (let* ((ws (map socket:outport sockets)) - (wport-nameserver-alist (map cons ws nameservers)) - (wport-socket-alist (map cons ws sockets))) - (dynamic-wind - (lambda () #f) - (lambda () - (let* ((ready-ports (apply select-port-channels #f ws)) - (w (car ready-ports)) - (hit-ns (cdr (assoc w wport-nameserver-alist))) - (sock (cdr (assoc w wport-socket-alist)))) - (if (not (connect-socket-successful? sock)) - (dns-error 'bad-nameserver hit-ns)) - (let ((query-string - (add-size-tag (list->string (message-source query)))) - (r (socket:inport sock))) - (display (list->string query-string) w) - (force-output w) - (let ((a (read-char r)) - (b (read-char r))) - (let ((len (octet-pair->number a b))) - (let ((s (read-string len r))) - (if (not (= len (string-length s))) - (dns-error 'unexpected-eof-from-server)) - (values (parse (string->list s)) - hit-ns - (delete hit-ns nameservers)))))))) - (lambda () - (for-each close-socket sockets))))) - (reply-acceptable? reply query) - (values reply - hit-ns - other-nss))) - -;; here: via UDP -(define (send-receive-message-udp nameservers query) - (receive (reply hit-ns other-nss) - (let ((sockets (map (lambda (nameserver) - (let ((sock (create-socket protocol-family/internet - socket-type/datagram)) - (addr (internet-address->socket-address - nameserver 53))) - (connect-socket sock addr) - sock)) - nameservers))) - (let ((rs (map socket:inport sockets)) - (ws (map socket:outport sockets))) - (dynamic-wind - (lambda () - 'nothing-to-be-done-before) - (lambda () - (let ((query-string (list->string (message-source query))) - (rsv (list->vector rs)) - (rport-nameserver-alist (map cons rs nameservers)) - (rport-socket-alist (map cons rs sockets))) - (for-each (lambda (w) (display query-string w)) ws) - (for-each force-output ws) - (let* ((ready (apply select-port-channels #f rs)) - (r (car ready)) - (hit-ns (cdr (assoc r rport-nameserver-alist)))) - (if (not (connect-socket-successful? (cdr (assoc r rport-socket-alist)))) - (dns-error 'bad-nameserver hit-ns)) - ;;; 512 is the maximum udp-message size: - (values (parse (string->list (read-string/partial 512 r))) - hit-ns - (delete hit-ns nameservers))))) - (lambda () - (for-each close-socket sockets))))) - (reply-acceptable? reply query) - (if (flags-truncated? (header-flags (message-header reply))) - (send-receive-message-tcp nameservers query) - (values reply - hit-ns - other-nss)))) - - -;;; -- cache - -;; creates the cache, an empty string-table -(define cache (make-string-table)) - -;; resets the cache -(define (dns-clear-cache!) - (set! cache (make-string-table))) - -;; searches in a dns-msg for the shortest ttl. this is needed for cache-management. -(define (find-shortest-ttl dns-msg) - (letrec ((minimum #f) - (find-shortest-ttl-1 - (lambda (dns-msg) - (cond - ((dns-message? dns-msg) - (find-shortest-ttl-1 (dns-message-reply dns-msg))) - ((message? dns-msg) - (for-each (lambda (x) (find-shortest-ttl-1 x)) (message-answers dns-msg)) - (for-each (lambda (x) (find-shortest-ttl-1 x)) (message-nameservers dns-msg)) - (for-each (lambda (x) (find-shortest-ttl-1 x)) (message-additionals dns-msg)) - minimum) - ((rr? dns-msg) - (cond - ((not minimum) (set! minimum (rr-ttl dns-msg))) - (else - (if (and (not minimum) (> minimum (rr-ttl dns-msg))) - (set! minimum (rr-ttl dns-msg)))))))))) - (find-shortest-ttl-1 dns-msg))) - - -(define (make-key qds nameserver) - (let*;; cache-key relevant data - ((name (question-name (car qds))) - (type (question-type (car qds))) - (class (question-class (car qds)))) - (format #f "~a;~a;~a;~a" - nameserver - name - (message-type-name type) - (message-class-name class)))) - -(define (lookup-cache qds nameserver) - (let* ((key (make-key qds nameserver)) - (found-data (table-ref cache key))) - (cond - ((and found-data - ;; checks if cached-data is still valid - (< (time) (+ (cache-time found-data) (cache-ttl found-data)))) - found-data) - (else #f)))) - -(define (update-cache! key entry) - (table-set! cache key entry)) - -(define (dns-query-no-cache query protocol nameservers tried) - ;; returns new retrieved data - (receive (dns-msg hit-ns nss-with-no-reply) - (send-receive-message nameservers query protocol) - (values - (make-dns-message query dns-msg #f protocol (reverse tried)) - hit-ns - nss-with-no-reply))) - -(define (dns-query-with-cache query protocol nameservers tried) - (let ((qds (message-questions query))) - (let lp ((ns nameservers)) - (if (null? ns) - (receive (reply-msg hit-ns nss-with-no-reply) - (send-receive-message nameservers query protocol) - (update-cache! (make-key qds hit-ns) - (make-cache reply-msg (find-shortest-ttl reply-msg) (time))) - ;; returns new retrieved data and updates cache - (values (make-dns-message query reply-msg #f protocol (reverse tried)) - hit-ns - nss-with-no-reply)) - (cond ((lookup-cache qds (car ns)) - => (lambda (found-data) - ;; returns cached data - (values (make-dns-message query (cache-answer found-data) #t protocol '()) - #f - nameservers))) - (else (lp (cdr ns)))))))) - -(define (send-receive-message nameservers query protocol) - ((cond - ((eq? protocol 'tcp) send-receive-message-tcp) - ((eq? protocol 'udp) send-receive-message-udp)) - nameservers query)) - -;; makes a dns-query. optional cache-check. -;; returns a dns-message with cache-flag and either cache-data or new received data. -(define (dns-query/cache query use-cache? protocol nameservers tried) - (if use-cache? - (dns-query-with-cache query protocol nameservers tried) - (dns-query-no-cache query protocol nameservers tried))) - -;; dns and recursion -;; recursion means, if the demanded information is not available from the -;; nameserver, another nameserver (usualy an authority) has to be contacted. -;; normally the recursion is done for us by the nameserver istself, but -;; this feature is technically optional (RFC 1035). -;; dns-get-information implements the resovler-side recursion. -;; it returns a dns-message -(define (dns-get-information query use-cache? protocol nameservers check-answer) - (let lp ((tried '()) (nss nameservers)) - (if (null? nss) - (dns-error 'bad-address) - (receive (dns-msg hit-ns nss-with-no-reply) - (dns-query/cache query use-cache? protocol nss tried) - (if (check-answer dns-msg) - dns-msg - (let ((auth? (flags-authoritative? (header-flags - (message-header - (dns-message-reply dns-msg)))))) - (if auth? - (dns-error 'bad-address) - ;; other nameservers names are found in the nameserver-part, - ;; but their ip-adresses are found in the additonal-rrs - (let ((other-nameservers - (filter (lambda (elem) (eq? (rr-type elem) (message-type a))) - (message-additionals (dns-message-reply dns-msg))))) - (lp (if (not (member hit-ns tried)) (cons hit-ns tried) tried) - (lset-union equal? - nss-with-no-reply - (lset-difference equal? other-nameservers tried))))))))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Parsing of /etc/resolv.conf - -(define (parse-nameserver rest-of-line) - (let ((match (regexp-search - (rx (: (submatch (** 1 3 digit) "." - (** 1 3 digit) "." - (** 1 3 digit) "." - (** 1 3 digit)) - (* white))); don't complain about tailing white space - rest-of-line))) - (if match - (cons 'nameserver (match:substring match 1)) - (signal 'resolv.conf-parse-error)))) - -; could be more restrictive... -(define domain-name-regexp (rx (+ (| alphanum #\. #\-)))) - -(define (parse-domain rest-of-line) - (let ((match (regexp-search - (rx (: (submatch ,domain-name-regexp) - (* white))); don't complain about tailing white space - rest-of-line))) - (if match - (cons 'domain (match:substring match 1)) - (signal 'resolv.conf-parse-error)))) - -(define (parse-search rest-of-line) - (let ((domains (regexp-fold-right domain-name-regexp - (lambda (match junk accu) - (cons (match:substring match 0) accu)) - '() - rest-of-line))) - (if (null? domains) - (signal 'resolv.conf-parse-error) - (cons 'search domains)))) - -(define (parse-sortlist rest-of-line) - (let ((netmask-pairs (regexp-fold-right (rx (+ (| digit #\. #\/))) - (lambda (match junk accu) - (cons (match:substring match 0) accu)) - '() - rest-of-line))) - (if (null? netmask-pairs) - (signal 'resolv.conf-parse-error) - (cons 'sortlist netmask-pairs)))) - -(define (parse-options rest-of-line) - (regexp-fold-right - (rx (| "debug" "no_tld_query" (: "ndots:" (submatch digit)))) - (lambda (match junk accu) - (let ((str (match:substring match 0))) - (cond ((string=? str "debug") - (cons 'debug accu)) - ((string=? str "no_tld_query") - (cons 'no_tld_query accu)) - (else (cons (cons 'ndots - (string->number (match:substring match 1))) accu))))) - '() - rest-of-line)) - -(define *resolv.conf-cache*) -(define *resolv.conf-cache-date* 0) - -(define (resolv.conf) - (let ((actual-m-time (file-info:mtime (file-info "/etc/resolv.conf")))) - (if (> actual-m-time *resolv.conf-cache-date*) - (parse-resolv.conf!)) - *resolv.conf-cache*)) - -(define (parse-resolv.conf!) - (let ((actual-m-time (file-info:mtime (file-info "/etc/resolv.conf"))) - (contents (really-parse-resolv.conf "/etc/resolv.conf"))) - (set! *resolv.conf-cache* contents) - (set! *resolv.conf-cache-date* actual-m-time))) - -(define (really-parse-resolv.conf file-name) - - ;; accumulate nameserver entries - ;; domain and search are mutual exclusive, take the last - (define (adjust-result rev-result have-search-or-domain? nameservers) - (cond ((null? rev-result) - (if (null? nameservers) - '() - (list (cons 'nameserver nameservers)))) - ((eq? (caar rev-result) 'domain) - (if have-search-or-domain? - (adjust-result (cdr rev-result) have-search-or-domain? nameservers) - (cons (car rev-result) - (adjust-result (cdr rev-result) - #t - nameservers)))) - ((eq? (caar rev-result) 'search) - (if have-search-or-domain? - (adjust-result (cdr rev-result) have-search-or-domain? nameservers) - (cons (car rev-result) - (adjust-result (cdr rev-result) - #t - nameservers)))) - ((eq? (caar rev-result) 'nameserver) - (adjust-result (cdr rev-result) - have-search-or-domain? - (cons (cdar rev-result) - nameservers))) - (else (cons (car rev-result) - (adjust-result (cdr rev-result) - have-search-or-domain? - nameservers))))) - - (with-input-from-file file-name - (lambda () - (let loop ((rev-result '())) - (let ((l (read-line))) - (cond - ((eof-object? l) - (adjust-result rev-result #f '())) - ((regexp-search - (rx (: "nameserver" (+ (| " " "\t") - (submatch (* any)) - eos))) - l) - => (lambda (match) - (loop (cons (parse-nameserver (match:substring match 1)) - rev-result)))) - ((regexp-search - (rx (: "domain" (+ (| " " "\t") - (submatch (* any)) - eos))) - l) - => (lambda (match) - (loop (cons (parse-domain (match:substring match 1)) - rev-result)))) - ((regexp-search - (rx (: "search" (+ (| " " "\t") - (submatch (* any)) - eos))) - l) - => (lambda (match) - (loop (cons (parse-search (match:substring match 1)) - rev-result)))) - - ((regexp-search - (rx (: "sortlist" (+ (| " " "\t") - (submatch (* any)) - eos))) - l) - => (lambda (match) - (parse-sortlist (match:substring match 1)))) - - - ((regexp-search - (rx (: "options" (+ (| " " "\t") - (submatch (* any)) - eos))) - l) - => (lambda (match) - (parse-options (match:substring match 1)))) - (else (signal 'resolv.conf-parse-error)))))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Figure out the default name servers - -(define (dns-find-nameserver-list) - (cond ((assoc 'nameserver (resolv.conf)) - => (lambda (nameserver.list) - (cdr nameserver.list))) - (else '()))) - -;; returns the first found nameserver -(define (dns-find-nameserver) - (let ((ns (dns-find-nameserver-list))) - (if (null? ns) - (dns-error 'no-nameservers) - (car ns)))) - - -;; checks the nameservers argument of the lookup functions. -;; if a nameserver-name is given and not a nameserver-ip -;; (dns-lookup-name nameserver) is called. -(define (check-args args) - (if (null? args) - (map ip-string->address32 (dns-find-nameserver-list)) - (map (lambda (nameserver) - (cond - ((address32? nameserver) nameserver) - ((ip-string? nameserver) (ip-string->address32 nameserver)) - (else (map (dns-lookup-name nameserver (dns-find-nameserver-list)))))) - (car args)))) - -;; dns-lookup with more options than dns-lookup-* -(define (dns-lookup name type . nameservers) - (let* ((maybe-ip-string (if (address32? name) - (ip-string->in-addr-arpa (address32->ip-string name)) - (ip-string->in-addr-arpa name))) - (query (if maybe-ip-string ; if name is a ip-addr, the query is a in-addr.arpa address - (make-simple-query-dns-message - (random 256) maybe-ip-string type (message-class in)) - (make-simple-query-dns-message (random 256) name type (message-class in)))) - (use-cache? #t) - (protocol 'udp) - (nameservers (check-args nameservers)) - (check-answer (lambda (dns-msg) #t)) - (dns-msg (dns-get-information query use-cache? protocol nameservers check-answer)) - (answers (message-answers (dns-message-reply dns-msg)))) - dns-msg)) - - -;; looks up a hostname, returns an ip. -;; (dns-lookup-name nameservers) -(define (dns-lookup-name name . nameservers) - (let* ((maybe-ip-string (if (address32? name) - (ip-string->in-addr-arpa (address32->ip-string name)) - (ip-string->in-addr-arpa name))) - (query (if maybe-ip-string ; if name is a ip-addr, the query is a in-addr.arpa address - (dns-error 'not-a-hostname) - (make-simple-query-dns-message (random 256) name (message-type a) (message-class in)))) - (use-cache? #t) - (protocol 'udp) - (nameservers (check-args nameservers)) - (check-answer (lambda (dns-msg) - (let* ((reply (dns-message-reply dns-msg)) - (answers (message-answers reply))) - (not (null? (filter-type answers (message-type a))))))) - (dns-msg (dns-get-information query use-cache? protocol nameservers check-answer)) - (answers (filter-type (message-answers (dns-message-reply dns-msg)) (message-type a)))) - (rr-data-a-ip (rr-data (car answers))))) - -;; looks up an ip, returns a hostname -;; (dns-inverse-lookup [nameserver]) -(define (dns-lookup-ip ip . nameservers) - (let* ((maybe-ip-string (if (address32? ip) - (ip-string->in-addr-arpa (address32->ip-string ip)) - (ip-string->in-addr-arpa ip))) - (query (if maybe-ip-string ; if name is a ip-addr, the query is a in-addr.arpa address - (make-simple-query-dns-message (random 256) maybe-ip-string (message-type ptr) (message-class in)) - (dns-error 'not-a-ip))) - (use-cache? #t) - (protocol 'udp) - (nameservers (check-args nameservers)) - (check-answer (lambda (dns-msg) - (let* ((reply (dns-message-reply dns-msg)) - (answers (message-answers reply))) - (not (null? (filter-type answers (message-type ptr))))))) - (dns-msg (dns-get-information query use-cache? protocol nameservers check-answer)) - (answers (filter-type (message-answers (dns-message-reply dns-msg)) (message-type ptr)))) - (rr-data-ptr-name (rr-data (car answers))))) - -(define dns-inverse-lookup dns-lookup-ip) - -;; looks up an authoritative nameserver for a hostname -;; returns a list of nameservers -;; (dns-lookup-nameserver [nameserver]) -(define (dns-lookup-nameserver name . nameservers) - (let* ((maybe-ip-string (if (address32? name) - (ip-string->in-addr-arpa (address32->ip-string name)) - (ip-string->in-addr-arpa name))) - (query (if maybe-ip-string ; if name is a ip-addr, the query is a in-addr.arpa address - (dns-error 'not-a-hostname) - (make-simple-query-dns-message - (random 256) name (message-type ns) (message-class in)))) - (use-cache? #t) - (protocol 'udp) - (nameservers (check-args nameservers)) - (check-answer (lambda (dns-msg) - (let* ((reply (dns-message-reply dns-msg)) - (answers (message-answers reply)) - (nameservers (message-nameservers reply))) - (or (not (null? (filter-type nameservers (message-type soa)))) - (not (null? (filter-type answers (message-type ns)))))))) - (dns-msg (dns-get-information query use-cache? protocol nameservers check-answer)) - (reply (dns-message-reply dns-msg)) - (soa (filter-type (message-nameservers reply) (message-type soa))) - (nss (filter-type (message-answers reply) (message-type ns))) - (add (filter-type (message-additionals reply) (message-type a)))) - (if (null? nss) - (list (dns-lookup-name (rr-data-soa-mname (rr-data (car soa))))) - (map (lambda (elem) (rr-data-a-ip (rr-data elem))) add)))) - -;; looks up a mail-exchanger for a hostname. -;; returns a list of mail-exchanger, sorted by their preference -;; if there are no mx-records in the answer-section, -;; implementation based on RFC2821 -;; (dns-lookup-mail-exchanger [nameserver]) -(define (dns-lookup-mail-exchanger name . nameservers) - (let* ((ip-string (if (address32? name) - (ip-string->in-addr-arpa (address32->ip-string name)) - (ip-string->in-addr-arpa name))) - (query (if ip-string ; if name is a ip-addr, the query is a in-addr.arpa address - (dns-error 'not-a-hostname) - (make-simple-query-dns-message - (random 256) name (message-type mx) (message-class in)))) - (use-cache? #t) - (protocol 'tcp) - (nameservers (check-args nameservers)) - (check-answer (lambda (dns-msg) - (let* ((reply (dns-message-reply dns-msg)) - (answers (message-answers reply)) - (nameservers (message-nameservers reply))) - (or (not (null? (filter-type answers (message-type mx)))) - (not (null? (filter-type answers (message-type cname)))) - (not (null? (filter-type answers (message-type a)))))))) - (dns-msg (dns-get-information query use-cache? protocol nameservers check-answer)) - (reply (dns-message-reply dns-msg)) - (mx (filter-type (message-answers reply) (message-type mx))) - (soa (filter-type (message-nameservers reply)(message-type soa))) - (cname (filter-type (message-answers reply) (message-type cname))) - (a (filter-type (message-answers reply) (message-type a)))) - - (cond - ((not (null? a)) - (list (rr-data-a-ip (rr-data (car a))))) - ((not (null? cname)) - (dns-lookup-mail-exchanger (rr-data-cname-name (rr-data (car cname))))) - ((null? mx) - (list (rr-data-soa-rname (rr-data (car soa))))) - (else - (map (lambda (elem) (rr-data-mx-exchanger (rr-data elem))) (sort-by-preference mx)))))) - -;;; pretty-prints a dns-msg -(define (pretty-print-dns-message dns-msg . maybe-port) - (let ((d - (lambda (n s1 s2) - (letrec ((loop (lambda (n) - (if (zero? n) - "" - (string-append " " (loop (- n 1))))))) - (display (loop n)) - (display s1) - (display ": ") - (display s2) - (newline))))) - (with-current-output-port* - (if (null? maybe-port) - (current-output-port) - (car maybe-port)) - (lambda () - (define (show-dns-message dns-msg) - (cond - ((dns-message? dns-msg) - (begin - (d 0 "DNS-MESSAGE" "") - (d 1 "QUERY" "")(show-dns-message (dns-message-query dns-msg))(newline) - (d 1 "REPLY" "")(show-dns-message (dns-message-reply dns-msg))(newline) - (d 1 "CACHE?" (if (dns-message-cache? dns-msg) - "found in cache" - "not found in cache")) - (d 1 "PROTOCOL" (let ((protocol (dns-message-protocol dns-msg))) - (cond - ((eq? protocol 'tcp) "TCP") - ((eq? protocol 'udp) "UDP")))) - (d 1 "TRIED-NAMESERVERS" (if (> (length (dns-message-tried-nameservers dns-msg)) 1) - (begin - (display " had perform recursion: ") - (dns-message-tried-nameservers dns-msg)) - (begin - (display " without recursion: ") - (dns-message-tried-nameservers dns-msg)))))) - ((message? dns-msg) - (begin - (d 2 "MESSAGE" "") - (d 3 "Header " "")(show-dns-message (message-header dns-msg)) - (d 3 "Questions " "")(for-each (lambda (x) (show-dns-message x)(newline)) (message-questions dns-msg)) - (d 3 "Answers " "")(for-each (lambda (x) (show-dns-message x)(newline)) (message-answers dns-msg)) - (d 3 "Nameservers" "")(for-each (lambda (x) (show-dns-message x)(newline)) (message-nameservers dns-msg)) - (d 3 "Additionals" "")(for-each (lambda (x) (show-dns-message x)(newline)) (message-additionals dns-msg)))) - ((header? dns-msg) - (begin - (d 4 "id" (header-id dns-msg)) - (d 4 "Flags" "")(show-dns-message (header-flags dns-msg)) - (d 4 "question-count " (header-question-count dns-msg)) - (d 4 "answer-count " (header-answer-count dns-msg)) - (d 4 "nameserver-count " (header-nameserver-count dns-msg)) - (d 4 "additional-count " (header-additional-count dns-msg)))) - ((flags? dns-msg) - (begin - (d 5 "querytype" (flags-query-type dns-msg)) - (d 5 "opcode" (flags-opcode dns-msg)) - (d 5 "authoritative?" (flags-authoritative? dns-msg)) - (d 5 "truncated?" (flags-truncated? dns-msg)) - (d 5 "recursion-desired?" (flags-recursion-desired? dns-msg)) - (d 5 "recursion-available?" (flags-recursion-available? dns-msg)) - (d 5 "zero" (flags-zero dns-msg)) - (d 5 "response-code" (flags-response-code dns-msg)))) - ((question? dns-msg) - (begin - (d 4 "name " (question-name dns-msg)) - (d 4 "type " (message-type-name (question-type dns-msg))) - (d 4 "class" (message-class-name (question-class dns-msg))))) - ((rr? dns-msg) - (begin - (d 4 "name " (rr-name dns-msg)) - (d 4 "type "(message-type-name (rr-type dns-msg))) - (d 4 "class" (message-class-name (rr-class dns-msg))) - (d 4 "ttl " (rr-ttl dns-msg)) - (d 4 "data " "") (show-dns-message (rr-data dns-msg)))) - ((rr-data-a? dns-msg) - (d 5 "ip " (rr-data-a-ip dns-msg))) - ((rr-data-ns? dns-msg) - (d 5 "name " (rr-data-ns-name dns-msg))) - ((rr-data-cname? dns-msg) - (d 5 "name " (rr-data-cname-name dns-msg))) - ((rr-data-mx? dns-msg) - (begin - (d 5 "preference " (rr-data-mx-preference dns-msg)) - (d 5 "exchanger " (rr-data-mx-exchanger dns-msg)))) - ((rr-data-ptr? dns-msg) - (d 5 "name " (rr-data-ptr-name dns-msg))) - ((rr-data-soa? dns-msg) - (begin - (d 5 "mname " (rr-data-soa-mname dns-msg)) - (d 5 "rname " (rr-data-soa-rname dns-msg)) - (d 5 "serial " (rr-data-soa-serial dns-msg)) - (d 5 "refresh " (rr-data-soa-refresh dns-msg)) - (d 5 "expire " (rr-data-soa-expire dns-msg)) - (d 5 "minimum " (rr-data-soa-expire dns-msg)))) - ;; ### - ((rr-data-hinfo? dns-msg) - (d 5 "data " (rr-data-hinfo-data dns-msg))) - ((rr-data-txt? dns-msg) - (d 5 "text " (rr-data-txt-text dns-msg))) - ((rr-data-wks? dns-msg) - (d 5 "data " (rr-data-wks-data dns-msg))) - )) - (show-dns-message dns-msg))))) - -(define *fqdn-lock* (make-lock)) -(define *fqdn-cache* '()) - -(define (socket-address->fqdn addr cache?) - (receive (ip32 port) - (socket-address->internet-address addr) - (internet-address->fqdn ip32 cache?))) - -(define (internet-address->fqdn ip32 cache?) - (if cache? - (begin - (obtain-lock *fqdn-lock*) - (cond - ((assv ip32 *fqdn-cache*) => - (lambda (pair) - (release-lock *fqdn-lock*) - (cdr pair))) - (else - (release-lock *fqdn-lock*) - (let ((fqdn (dns-lookup-ip ip32))) - (set! *fqdn-cache* - (cons (cons ip32 fqdn) *fqdn-cache*)) - fqdn)))) - (dns-lookup-ip ip32))) - - -(define (is-fqdn? name) - (regexp-search? (rx #\.) name)) - -(define (maybe-dns-lookup-name name) - (call-with-current-continuation - (lambda (k) - (with-handler (lambda (cond more) - (if (dns-error? cond) - (k #f) - (more))) - (lambda () - (dns-lookup-name name)))))) - -(define (maybe-dns-lookup-ip ip-addr) - (call-with-current-continuation - (lambda (k) - (with-handler (lambda (cond more) - (if (dns-error? cond) - (k #f) - (more))) - (lambda () - (dns-lookup-ip ip-addr)))))) - -(define (domains-for-search) - (cond ((assoc 'domain (resolv.conf)) - => (lambda (pair) - (list (cdr pair)))) - ((assoc 'search (resolv.conf)) - => (lambda (pair) - (cdr pair))) - (else '()))) - -(define (host-fqdn name-or-socket-address) - (if (socket-address? name-or-socket-address) - (socket-address->fqdn name-or-socket-address #f) - (let ((name name-or-socket-address)) - (if (is-fqdn? name) - name - (let lp ((domains (domains-for-search))) - (if (null? domains) - #f - (cond ((maybe-dns-lookup-name (string-append name "." (car domains))) - => (lambda (ip) - (dns-lookup-ip ip))) - (else (lp (cdr domains)))))))))) - -(define (system-fqdn) - (host-fqdn (system-name))) - diff --git a/scheme/lib/format-net.scm b/scheme/lib/format-net.scm deleted file mode 100644 index e0fd823..0000000 --- a/scheme/lib/format-net.scm +++ /dev/null @@ -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)))) - diff --git a/scheme/lib/ftp-library.scm b/scheme/lib/ftp-library.scm deleted file mode 100644 index 9419b98..0000000 --- a/scheme/lib/ftp-library.scm +++ /dev/null @@ -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 -;;; 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)))) - diff --git a/scheme/lib/ftp.scm b/scheme/lib/ftp.scm deleted file mode 100644 index 3c45164..0000000 --- a/scheme/lib/ftp.scm +++ /dev/null @@ -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 -;;; 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 [ arg ] -;; -;; Replies from the server are of the form -;; -;; xyz Informative message -;; -;; 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- Start of multiline message -;; [ + More information ]* -;; xyz End of multiline message -;; -;; 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))))) diff --git a/scheme/lib/handle-fatal-error.scm b/scheme/lib/handle-fatal-error.scm deleted file mode 100644 index 63ed459..0000000 --- a/scheme/lib/handle-fatal-error.scm +++ /dev/null @@ -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 ...)))) diff --git a/scheme/lib/htmlout.scm b/scheme/lib/htmlout.scm deleted file mode 100644 index c6472f5..0000000 --- a/scheme/lib/htmlout.scm +++ /dev/null @@ -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 ... [ ...]) -;;; - 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 tags). - - - -;;; - -(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))) - - -;;; - -(define (emit-close-tag out tag) - (format out "" tag)) - - -;;;

- -(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))) - - -;;; Make Money Fast!!! - -(define (emit-title out title) ; Takes no attributes. - (format out "~a~%~%" 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 ... pairs. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;; (with-tag out tag (attr-elt ...) body ...) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Execute the body forms between a ... pair. -;;; The (ATTR-ELT ...) list specifies the attributes for the . -;;; 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 -;;; home page - -(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 ... 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))))) diff --git a/scheme/lib/ls.scm b/scheme/lib/ls.scm deleted file mode 100644 index cabf038..0000000 --- a/scheme/lib/ls.scm +++ /dev/null @@ -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))) \ No newline at end of file diff --git a/scheme/lib/netrc.scm b/scheme/lib/netrc.scm deleted file mode 100644 index d5ed5c3..0000000 --- a/scheme/lib/netrc.scm +++ /dev/null @@ -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 -;;; 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)))))))) diff --git a/scheme/lib/nettime.scm b/scheme/lib/nettime.scm deleted file mode 100644 index aa2228f..0000000 --- a/scheme/lib/nettime.scm +++ /dev/null @@ -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 -;;; 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))) diff --git a/scheme/lib/parse-forms.scm b/scheme/lib/parse-forms.scm deleted file mode 100644 index 6f8a441..0000000 --- a/scheme/lib/parse-forms.scm +++ /dev/null @@ -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 and 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 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)))))) diff --git a/scheme/lib/pop3.scm b/scheme/lib/pop3.scm deleted file mode 100644 index 059f278..0000000 --- a/scheme/lib/pop3.scm +++ /dev/null @@ -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 -;;; 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 [ arg ] -;; -;; Replies from the server are of the form -;; -;; status [ Informative message ] -;; -;; 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 .. 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))) diff --git a/scheme/lib/rate-limit.scm b/scheme/lib/rate-limit.scm deleted file mode 100644 index 7b19182..0000000 --- a/scheme/lib/rate-limit.scm +++ /dev/null @@ -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)) diff --git a/scheme/lib/rfc822.scm b/scheme/lib/rfc822.scm deleted file mode 100644 index 73d8137..0000000 --- a/scheme/lib/rfc822.scm +++ /dev/null @@ -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 -;;; Copyright (c) 2003 by Mike Sperber -;;; 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))) diff --git a/scheme/lib/smtp.scm b/scheme/lib/smtp.scm deleted file mode 100644 index 0813528..0000000 --- a/scheme/lib/smtp.scm +++ /dev/null @@ -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 -;;; 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 -(define smtp-helo (unary-smtp-command "HELO")) - -;; MAIL FROM: -(define smtp-mail (unary-smtp-command "MAIL FROM:")) - -;; RECIPIENT TO: -(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: -(define smtp-send (unary-smtp-command "SEND FROM:")) - -;; SEND OR MAIL -(define smtp-soml (unary-smtp-command "SOML FROM:")) - -;; SEND AND MAIL -(define smtp-saml (unary-smtp-command "SOML SAML:")) - -;; RESET -(define smtp-rset (nullary-smtp-command "RSET")) - -;; VERIFY -(define smtp-vrfy (unary-smtp-command "VRFY")) - -;; EXPAND -(define smtp-expn (unary-smtp-command "EXPN")) - -;; HELP

-(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 Service ready -;;; 221 Service closing transmission channel -;;; 421 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 -;;; 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 -;;; 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 . -;;; 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 diff --git a/scheme/lib/sunet-utilities.scm b/scheme/lib/sunet-utilities.scm deleted file mode 100644 index ec617dd..0000000 --- a/scheme/lib/sunet-utilities.scm +++ /dev/null @@ -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)))) diff --git a/scheme/lib/uri.scm b/scheme/lib/uri.scm deleted file mode 100644 index bc796d1..0000000 --- a/scheme/lib/uri.scm +++ /dev/null @@ -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)))))))) diff --git a/scheme/lib/url.scm b/scheme/lib/url.scm deleted file mode 100644 index 92a0042..0000000 --- a/scheme/lib/url.scm +++ /dev/null @@ -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 . Pick the appropriate URL parser and parse. - - -;;; Server strings: //:@:/ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; A SERVER record describes path-prefixes of the form -;;; //:@:/ -;;; 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: -;;; : ? # , , and -;;; are strings; 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 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 "")))) diff --git a/scheme/packages.scm b/scheme/packages.scm deleted file mode 100644 index b246468..0000000 --- a/scheme/packages.scm +++ /dev/null @@ -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))))))) diff --git a/scheme/xml/doc.txt b/scheme/xml/doc.txt deleted file mode 100644 index 126762e..0000000 --- a/scheme/xml/doc.txt +++ /dev/null @@ -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 tag - notation instead of writing . 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)))) - diff --git a/scheme/xml/plt.scm b/scheme/xml/plt.scm deleted file mode 100644 index cf55698..0000000 --- a/scheme/xml/plt.scm +++ /dev/null @@ -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) \ No newline at end of file diff --git a/scheme/xml/reader.scm b/scheme/xml/reader.scm deleted file mode 100644 index 7b4c2ad..0000000 --- a/scheme/xml/reader.scm +++ /dev/null @@ -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 ) - (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 (stringstring 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))) - - diff --git a/scheme/xml/space.scm b/scheme/xml/space.scm deleted file mode 100644 index 1448611..0000000 --- a/scheme/xml/space.scm +++ /dev/null @@ -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))) diff --git a/scheme/xml/structures.scm b/scheme/xml/structures.scm deleted file mode 100644 index c590132..0000000 --- a/scheme/xml/structures.scm +++ /dev/null @@ -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)) diff --git a/scheme/xml/writer.scm b/scheme/xml/writer.scm deleted file mode 100644 index 1f5dc7c..0000000 --- a/scheme/xml/writer.scm +++ /dev/null @@ -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 "" 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 ""))))) - -;; 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 "" (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 "" (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)) diff --git a/scheme/xml/xexpr.scm b/scheme/xml/xexpr.scm deleted file mode 100644 index 95ebafa..0000000 --- a/scheme/xml/xexpr.scm +++ /dev/null @@ -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 stringstring 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)))) diff --git a/scheme/xml/xml-packages.scm b/scheme/xml/xml-packages.scm deleted file mode 100644 index 68ac8a8..0000000 --- a/scheme/xml/xml-packages.scm +++ /dev/null @@ -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)) - - \ No newline at end of file diff --git a/start-extended-web-server b/start-extended-web-server deleted file mode 100755 index 186fc74..0000000 --- a/start-extended-web-server +++ /dev/null @@ -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: \ No newline at end of file diff --git a/start-web-server b/start-web-server deleted file mode 100755 index 4843ae1..0000000 --- a/start-web-server +++ /dev/null @@ -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: \ No newline at end of file diff --git a/web-server/.gitignore b/web-server/.gitignore deleted file mode 100644 index d2341ad..0000000 --- a/web-server/.gitignore +++ /dev/null @@ -1 +0,0 @@ -httpd.log diff --git a/web-server/root/cgi-bin/comments.sh b/web-server/root/cgi-bin/comments.sh deleted file mode 100755 index 319908e..0000000 --- a/web-server/root/cgi-bin/comments.sh +++ /dev/null @@ -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 "

This is the cgi script.

" -echo "
Current date: " -echo `date` -echo diff --git a/web-server/root/cgi-bin/move.sh b/web-server/root/cgi-bin/move.sh deleted file mode 100755 index 3bfc101..0000000 --- a/web-server/root/cgi-bin/move.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -# Example for server redirection -echo Location:http://www.scsh.net/resources/sunet.html diff --git a/web-server/root/htdocs/files/text.txt b/web-server/root/htdocs/files/text.txt deleted file mode 100644 index 5adcd3d..0000000 --- a/web-server/root/htdocs/files/text.txt +++ /dev/null @@ -1 +0,0 @@ -This is a text file. diff --git a/web-server/root/htdocs/files/zipped.gz b/web-server/root/htdocs/files/zipped.gz deleted file mode 100644 index c215803..0000000 Binary files a/web-server/root/htdocs/files/zipped.gz and /dev/null differ diff --git a/web-server/root/htdocs/index.html b/web-server/root/htdocs/index.html deleted file mode 100644 index 3ddb87b..0000000 --- a/web-server/root/htdocs/index.html +++ /dev/null @@ -1,8 +0,0 @@ - -Home - -

-Hello world! (more...) -

- - diff --git a/web-server/root/htdocs/index2.html b/web-server/root/htdocs/index2.html deleted file mode 100644 index c5fda3e..0000000 --- a/web-server/root/htdocs/index2.html +++ /dev/null @@ -1,39 +0,0 @@ - - - Scheme Unterground - -

-

Hello Unterground!

- - Following files are available: - -
- And nothing else... - -
- - -Last modified: Wed Jan 15 16:16:58 MET 2003 - - - - -

- - diff --git a/web-server/root/htdocs/seval.html b/web-server/root/htdocs/seval.html deleted file mode 100644 index f4397d7..0000000 --- a/web-server/root/htdocs/seval.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - Evaluating Scheme Expressions Interactively - - - -

Evaluating Scheme Expressions Interactively

- -
- Type in your scheme expression as you would at any REPL: -
- - - -
- -
-
Andreas Bernauer
- - -Last modified: Wed Aug 28 16:40:54 CEST 2002 - - -