Add to git. master
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 25 Apr 2014 10:40:26 +0000 (11:40 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Fri, 25 Apr 2014 10:40:26 +0000 (11:40 +0100)
47 files changed:
.cvsignore [new file with mode: 0644]
COPYING.LIB [new file with mode: 0644]
Makefile+ [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
cfg.c [new file with mode: 0644]
cfg.h [new file with mode: 0644]
conf/default.in [new file with mode: 0644]
conf/rws.conf.in [new file with mode: 0644]
configure [new file with mode: 0755]
dir.c [new file with mode: 0644]
dir.h [new file with mode: 0644]
doc/.cvsignore [new file with mode: 0644]
doc/index.html [new file with mode: 0644]
doc/rws_request_canonical_path.3.html [new file with mode: 0644]
doc/rws_request_file_path.3.html [new file with mode: 0644]
doc/rws_request_host_header.3.html [new file with mode: 0644]
doc/rws_request_http_request.3.html [new file with mode: 0644]
doc/rws_request_io.3.html [new file with mode: 0644]
doc/rws_request_pool.3.html [new file with mode: 0644]
doc/rws_request_pth.3.html [new file with mode: 0644]
errors.c [new file with mode: 0644]
errors.h [new file with mode: 0644]
examples/.cvsignore [new file with mode: 0644]
examples/hello.c [new file with mode: 0644]
examples/show_params.c [new file with mode: 0644]
exec.c [new file with mode: 0644]
exec.h [new file with mode: 0644]
exec_so.c [new file with mode: 0644]
exec_so.h [new file with mode: 0644]
file.c [new file with mode: 0644]
file.h [new file with mode: 0644]
html/index.html [new file with mode: 0644]
main.c [new file with mode: 0644]
mime_types.c [new file with mode: 0644]
mime_types.h [new file with mode: 0644]
process_rq.c [new file with mode: 0644]
process_rq.h [new file with mode: 0644]
re.h [new file with mode: 0644]
rewrite.c [new file with mode: 0644]
rewrite.h [new file with mode: 0644]
rws.rc [new file with mode: 0755]
rws_request.c [new file with mode: 0644]
rws_request.h [new file with mode: 0644]
rwsd.1 [new file with mode: 0644]
test_rws.sh [new file with mode: 0755]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..1964f99
--- /dev/null
@@ -0,0 +1 @@
+build-*
\ No newline at end of file
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644 (file)
index 0000000..eb685a5
--- /dev/null
@@ -0,0 +1,481 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                    675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+     Appendix: How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile+ b/Makefile+
new file mode 100644 (file)
index 0000000..8272b35
--- /dev/null
+++ b/Makefile+
@@ -0,0 +1,442 @@
+# -*- Makefile -*-
+#
+# This is a make+ file. Make+ is a set of scripts which enhance GNU
+# make and let you build RPMs, and other package types with just one
+# control file.  To build this package you will need to download make+
+# from this site: http://www.annexia.org/freeware/makeplus/
+
+PACKAGE                := rws
+VERSION_MAJOR  := 1
+VERSION_MINOR  := 2.0
+VERSION                := $(VERSION_MAJOR).$(VERSION_MINOR)
+
+SUMMARY                := Rich\'s Web Server
+COPYRIGHT      := GNU LGPL
+AUTHOR         := Richard W.M. Jones <rich@annexia.org>
+
+define DESCRIPTION
+RWS is a tiny, fast and elegant web server. It can serve simple files
+or run CGI scripts. It supports virtual hosts.
+endef
+
+RPM_REQUIRES   := pthrlib >= 3.2.0, c2lib >= 1.3.0
+RPM_GROUP      := System Environment/Daemons
+
+CFLAGS         += -Wall -Werror -g -O2 -I$(includedir)/c2lib \
+                  -I$(shell pg_config --includedir)
+ifneq ($(shell uname), SunOS)
+# Avoid a warning about reordering system include paths.
+CFLAGS         += $(shell pcre-config --cflags)
+endif
+
+LIBS           += -L$(libdir) -lpthrlib -lc2lib \
+                  -L$(shell pg_config --libdir) -lpq \
+                   $(shell pcre-config --libs)
+
+# make+ needs to support a better method of handling libraries than this:
+ifneq ($(shell uname), OpenBSD)
+ifneq ($(shell uname), FreeBSD)
+LIBS           += -ldl
+endif
+endif
+
+ifeq ($(shell uname), SunOS)
+LIBS           += -lnsl -lsocket
+endif
+
+LIBS           += -lm
+
+OBJS   := main.o cfg.o dir.o errors.o exec.o exec_so.o file.o mime_types.o \
+          process_rq.o rewrite.o
+HEADERS        := $(srcdir)/rws_request.h
+
+all:   build
+
+configure:
+       $(MP_CONFIGURE_START)
+       $(MP_CHECK_LIB) precomp c2lib
+       $(MP_CHECK_LIB) current_pth pthrlib
+       $(MP_CHECK_FUNCS) dlclose dlerror dlopen dlsym glob globfree \
+       putenv setenv
+       $(MP_CHECK_HEADERS) alloca.h arpa/inet.h dirent.h dlfcn.h fcntl.h \
+       glob.h grp.h netinet/in.h pwd.h setjmp.h signal.h string.h \
+       sys/mman.h sys/socket.h sys/stat.h sys/syslimits.h sys/types.h \
+       sys/wait.h syslog.h time.h unistd.h
+       $(MP_CONFIGURE_END)
+
+build: librws.a librws.so rwsd manpages conffiles syms \
+       examples/hello.so examples/show_params.so
+
+# Program.
+
+rwsd:  $(OBJS)
+       $(CC) $(CFLAGS) $^ -L. -lrws $(LIBS) -o $@
+
+# Library.
+
+librws.a: rws_request.o
+       $(MP_LINK_STATIC) $@ $^
+
+librws.so: rws_request.lo
+       $(MP_LINK_DYNAMIC) $@ $^
+
+# Examples.
+
+examples/%.so: examples/%.lo
+ifneq ($(shell uname), SunOS)
+       $(CC) $(CFLAGS) -shared -Wl,-soname,$@ $^ -L. -lrws $(LIBS) -o $@
+else
+# XXX make+ needs to support this.
+       $(CC) $(CFLAGS) -shared -Wl,-h,$@ $^ -L. -lrws $(LIBS) -o $@
+endif
+
+# Build the manual pages.
+
+manpages: $(srcdir)/*.h
+       if cdoc; then \
+               rm -f *.3; \
+               cdoc \
+                       --author '$(AUTHOR)' \
+                       --license '$(COPYRIGHT)' \
+                       --version '$(PACKAGE)-$(VERSION)' \
+                       $^; \
+       fi
+
+# Build the configuration files.
+
+conffiles: conf/default conf/rws.conf
+
+conf/default: conf/default.in
+       sed 's,@pkgdatadir@,$(pkgdatadir),g' < $^ > $@
+
+conf/rws.conf: conf/rws.conf.in
+       sed 's,@pkgdatadir@,$(pkgdatadir),g' < $^ > $@
+
+# Build the symbols table.
+
+syms:  rwsd.syms librws.syms
+
+rwsd.syms: rwsd
+       nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@
+
+librws.syms: librws.so
+       nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@
+
+# Run the simple test.
+
+test:  test_rws.sh
+       LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) $(MP_RUN_TESTS) $^
+
+install:
+       install -d $(DESTDIR)$(sbindir)
+       install -d $(DESTDIR)$(libdir)
+       install -d $(DESTDIR)$(includedir)
+       install -d $(DESTDIR)$(sysconfdir)/init.d
+       install -d $(DESTDIR)$(sysconfdir)/rws
+       install -d $(DESTDIR)$(sysconfdir)/rws/hosts
+       install -d $(DESTDIR)$(pkgdatadir)/symtabs
+       install -d $(DESTDIR)$(pkgdatadir)/so-bin
+       install -d $(DESTDIR)$(pkgdatadir)/html
+       install -d $(DESTDIR)$(man1dir)
+       install -d $(DESTDIR)$(man3dir)
+
+       install -m 0755 rwsd $(DESTDIR)$(sbindir)
+
+       $(MP_INSTALL_STATIC_LIB) librws.a
+       $(MP_INSTALL_DYNAMIC_LIB) librws.so
+
+       install -m 0644 $(HEADERS) $(DESTDIR)$(includedir)
+
+       install -m 0755 $(srcdir)/rws.rc $(DESTDIR)$(sysconfdir)/init.d/rws
+
+       if [ ! -f $(DESTDIR)$(sysconfdir)/rws/rws.conf ]; then \
+         install -m 0644 conf/rws.conf $(DESTDIR)$(sysconfdir)/rws; \
+       fi
+       if [ ! -f $(DESTDIR)$(sysconfdir)/rws/hosts/default ]; then \
+         install -m 0644 conf/default $(DESTDIR)$(sysconfdir)/rws/hosts; \
+       fi
+       install -m 0644 *.syms $(DESTDIR)$(pkgdatadir)/symtabs
+       install -m 0755 examples/*.so $(DESTDIR)$(pkgdatadir)/so-bin
+       install -m 0644 $(srcdir)/html/*.html $(DESTDIR)$(pkgdatadir)/html
+       install -m 0644 $(srcdir)/rwsd.1 $(DESTDIR)$(man1dir)
+       install -m 0644 *.3 $(DESTDIR)$(man3dir)
+
+define WEBSITE
+<% include page_header.msp %>
+
+    <h1>$(PACKAGE) - $(SUMMARY)</h1>
+
+    <p>
+      <code>rws</code> is a small, fast web server written in
+      C. It uses the <a href="../pthrlib/">pthrlib</a>
+      threading/server library and the <a href="../c2lib/">c2lib</a>
+      library of Perl-like basics for C. These make it
+      amongst the smallest, fastest and most straightforwardly
+      written web servers available now.
+    </p>
+
+    <p>
+      As of version 1.0.11, the stripped binary is 27K and
+      <code>size</code> reports:
+    </p>
+
+<pre>
+   text    data     bss     dec     hex filename
+  24332     828      88   25248    62a0 rwsd
+</pre>
+
+    <p>
+      Of course, most of the magic is in <a href="../pthrlib/">pthrlib</a>.
+    </p>
+
+    <p>
+      It supports a fairly minimal set of features so far:
+    </p>
+
+    <ul>
+      <li> Complies (mostly) with <code>HTTP/1.1</code>.
+      <li> Serves files and includes an <code>mmap(2)</code>
+       file cache.
+      <li> Directory listings.
+      <li> CGI script execution (NPH scripts only!)
+      <li> Virtual hosts and aliases.
+      <li> Shared object scripts: essentially CGI scripts
+       written in C which are dynamically linked into the
+       server memory at runtime. Very fast.
+      <li> Rewrite rules using regular expressions.
+    </ul>
+
+    <p>
+      <a href="doc/">There is extensive documentation and a tutorial here.</a>
+    </p>
+
+    <h1>Download</h1>
+
+    <table border="1">
+      <tr>
+       <th> File </th>
+       <th> Format </th>
+       <th> Contents </th>
+      </tr>
+      <tr>
+       <td> <a href="$(PACKAGE)-$(VERSION).tar.gz">$(PACKAGE)-$(VERSION).tar.gz</a> </td>
+       <td> tar.gz </td>
+       <td> Latest source distribution </td>
+      </tr>
+      <tr>
+       <td> <a href="$(PACKAGE)-$(VERSION)-1.i686.rpm">$(PACKAGE)-$(VERSION)-1.i686.rpm</a> </td>
+       <td> i686 binary RPM </td>
+       <td> Binary server, header files, man pages
+         for Red Hat Linux </td>
+      </tr>
+      <tr>
+       <td> <a href="$(PACKAGE)-$(VERSION)-1.src.rpm">$(PACKAGE)-$(VERSION)-1.src.rpm</a> </td>
+       <td> source RPM </td>
+       <td> Source files for Red Hat Linux </td>
+      </tr>
+    </table>
+
+    <p>
+      You must install the latest <a href="../pthrlib/">pthrlib</a>
+      and <a href="../c2lib/">c2lib</a> libraries first!
+    </p>
+
+    <!--
+    <p>
+      <a href="/cgi-bin/autopatch.pl?dir=rws">Patches between versions
+       ...</a>
+    </p>
+    -->
+
+    <h2>News</h2>
+
+<p>
+<b>Sat Feb  8 17:00:47 GMT 2003:</b>
+Ported to Solaris, OpenBSD and FreeBSD (thanks to
+<a href="http://www.azazel.net/">Jeremy Sowden</a>
+and <a href="http://www.mondaymorning.org/">Richard Baker</a>
+for help and equipment).
+Added a test script which actually starts up and runs
+rws and verifies static file serving, CGI scripts and shared
+object scripts. Added <code>-ldl</code> to <code>LIBS</code>
+(thanks to jeffrey at box-sol.com). Build fixes for RH 7.3.
+</p>
+
+       <p>
+       <b>Sun Dec  8 16:07:20 GMT 2002:</b>
+       Enabled debugging and optimisations. Converted to use
+       <a href="../makeplus/">make+</a>. Changed to support
+       changes in the <code>pthrlib</code> API. The
+       <code>-f</code> option prevents the server from
+       changing directory as well. More descriptive thread
+       names. Give idle threads a different name.
+       </p>
+
+       <p>
+       <b>Mon Nov 25 09:31:37 GMT 2002:</b>
+       Added a symbols file for full symbol resolution in monolith.
+       Added <code>debian/conffiles</code> so that Debian package
+       won't splat configuration files.
+       Changed <code>process_rq</code> to set thread name to the
+       canonical path (useful for debugging, stats).
+       Set a maximum number of requests that we will service with
+       a single thread.
+       Enable stack traces.
+       </p>
+
+       <p>
+       <b>Sun Nov 17 23:31:32 GMT 2002:</b> Debian packages. Added MSP files.
+       <code>rws_request_*</code> symbols are now in a separate
+       library. Added <code>rwsd.1</code> manual page. RWS now
+       forces connection close on bad requests. Multiple fixes
+       to the way directories/subdirectories are handled. <code>exec_so</code>
+       catches <code>pth_die</code> exceptions and prints a
+       message. More rewrite fixes. Change the default root
+       to <code>/var/www</code>. Added <code>postinst</code> script
+       for Debian.
+       </p>
+
+    <p>
+      <b>Thu Nov 14 15:33:29 GMT 2002:</b> Major checkpoint release
+      for Monolith.
+    </p>
+
+    <p>
+      <b>Sun Oct 20 14:57:29 BST 2002:</b> Allow empty entries
+      in rws configuration file. Correct compilation flags for
+      PCRE. Allow the stack size to be selected from the configuration
+      file. Fixed path parsing. Added include files to RPM. Added
+      example MSP configuration to configuration file.
+    </p>
+
+    <p>
+      <b>Tue Oct 15 23:40:42 BST 2002:</b> Multiple bug fixes.
+    </p>
+
+    <p>
+      <b>Sun Oct 13 19:04:16 BST 2002:</b> Added
+      <code>-a</code>, <code>-d</code> and <code>-f</code>
+      flags which allow you to run the server on only
+      a single interface and make it simpler to debug.
+      (Thanks to Steve Atkins, steve at blighty com, for
+      this patch). You need the most recent <a href="../pthrlib/">pthrlib</a>.
+    </p>
+
+    <p>
+      <b>Sun Oct  6 13:00:39 BST 2002:</b> New <q>rewrite</q>
+      module allows comprehensive URL rewriting. Updated to use
+      the newest version of <a href="../c2lib/">c2lib</a>.
+    </p>
+
+    <p>
+      <b>Sat Sep  7 15:51:10 BST 2002:</b> Packages are now
+      available as i686 binary RPMs and source RPMs.
+    </p>
+
+    <h2>Old news and old versions</h2>
+
+    <p>
+      <b>Sat Aug 31 17:39:36 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="rws-1.0.0.tar.gz">rws-1.0.0.tar.gz</a> released.
+      This includes a tutorial.
+    </p>
+
+    <p>
+      <b>Thu Aug 22 13:20:32 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="rws-0.9.6.tar.gz">rws-0.9.6.tar.gz</a> released.
+      This includes manual pages.
+    </p>
+
+    <p>
+      <b>Thu Aug 22 12:27:16 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="rws-0.9.5.tar.gz">rws-0.9.5.tar.gz</a> released.
+      I have changed the interface to shared object scripts to
+      allow me to extend it in the future without ever changing
+      it again (hopefully :-) See the README file and
+      &lt;rws_request.h&gt; for more
+      details.
+    </p>
+
+    <p>
+      <b>Wed Aug 21 14:20:12 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="rws-0.9.4.tar.gz">rws-0.9.4.tar.gz</a> released.
+      Support for shared object scripts.
+    </p>
+
+    <p>
+      <b>Thu Jun 21 23:14:48 BST 2001</b>
+    </p>
+
+    <p>
+      <a href="rws-0.9.2.tar.gz">rws-0.9.2.tar.gz</a> released.
+      Directory listings are sorted alphabetically. Server
+      signature is printed at the bottom of directory listings.
+      Make sure you have <a href="../c2lib/">c2lib &gt;= 1.2.12</a>.
+    </p>
+
+    <p>
+      <b>Tue May 22 14:22:06 BST 2001</b>
+    </p>
+
+    <p>
+      <a href="rws-0.0.9.tar.gz">rws-0.0.9.tar.gz</a> released.
+      URL paths are now unescaped correctly.
+      Make sure you have <a href="../pthrlib/">pthrlib &gt;= 2.0.5</a>.
+    </p>
+
+    <p>
+      <b>Tue May 22 11:37:10 BST 2001</b>
+    </p>
+
+    <p>
+      <a href="rws-0.0.8.tar.gz">rws-0.0.8.tar.gz</a> released.
+      Added configuration files and init scripts to RPM.
+      Don't hold file descriptors open for files in the mmapped file cache.
+      Fixes to example rws.conf file. Fixed the mmapped file cache
+      so it no longer grows indefinitely (:-)
+      Make sure you have <a href="../pthrlib/">pthrlib &gt;= 2.0.4</a>.
+    </p>
+
+    <p>
+      <b>Tue Apr 10 16:04:41 BST 2001</b>
+    </p>
+
+    <p>
+      <a href="rws-0.0.7.tar.gz">rws-0.0.7.tar.gz</a> released.
+      Generates <code>access_log</code> file. Directory
+      listings have been improved considerably.
+    </p>
+
+    <p>
+      <b>Mon Apr  9 17:34:31 BST 2001</b>
+    </p>
+
+    <p>
+      <a href="rws-0.0.6.tar.gz">rws-0.0.6.tar.gz</a> released.
+      This is the first public version.
+    </p>
+
+<% include page_footer.msp %>
+
+endef
+
+upload_website:
+       scp $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION)-1.*.rpm \
+       $(PACKAGE)-$(VERSION).bin.tar.gz \
+       10.0.0.248:annexia.org/freeware/$(PACKAGE)/
+       scp index.html \
+       10.0.0.248:annexia.org/freeware/$(PACKAGE)/index.msp
+
+.PHONY:        build configure test upload_website
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..2e10c78
--- /dev/null
+++ b/README
@@ -0,0 +1,91 @@
+rws - a fast, small and efficient web server written in C
+written by Richard Jones <rich@annexia.org>
+
+GENERAL INFO
+------------
+
+``rws'' is yet another web server. This one offers the advantage
+that it is both cleanly written internally, as well as simple,
+and, of course, very fast. Current features include:
+
+* Serves files.
+* Memory-mapped file cache.
+* Serves directory listings.
+* Supports virtual hosts.
+* Supports aliases.
+* CGI scripts (NPH scripts only).
+* Shared object scripts (see below).
+* Access and error logs.
+
+The following are the features that I intend to add before I
+declare that ``rws'' is complete:
+
+* Run CGI scripts as a local user (similar to Apache suexec).
+* IP-based access control.
+* Manual pages.
+
+INSTALLATION INSTRUCTIONS
+-------------------------
+
+You must install c2lib and pthrlib libraries first. Get these
+from:
+
+       c2lib           http://www.annexia.org/freeware/c2lib/
+       pthrlib         http://www.annexia.org/freeware/pthrlib/
+
+If you are having problems with ``rws'', make sure that you
+have the latest versions of these libraries installed before
+reporting any errors to me.
+
+Build it:
+
+       ./configure --sysconfdir=/etc   [--prefix=/usr]
+       make+
+       make+ test
+
+Install it (as root):
+
+       make+ install
+
+As configured above the server installs itself as
+/usr/local/sbin/rwsd, and expects to find configuration files in the
+/etc/rws directory (unless you specify different --prefix and
+--sysconfdir parameters).
+
+Edit /etc/rws/rws.conf (which is the global level configuration file)
+as appropriate for your site.
+
+Edit /etc/rws/hosts/default to configure as required. Note that by
+default, the document root is set to /usr/local/share/rws/html, which
+just contains a demo page.
+
+Start up the web server:
+
+       /usr/local/sbin/rwsd
+
+These are early days. If you find a bug, please report it to me
+by mailing rich@annexia.org. Please include a complete description.
+
+FAQ
+---
+
+1. The web server seems to have loads of memory leaks! It just sits
+   there sucking up more and more memory.
+
+   A. Not so. The web server stores a cache of memory mapped files,
+      making it quicker to serve the same file subsequent times.
+      Unfortunately top(1) or ps(1) count the memory mapped files
+      against the size of the process, even though they are never
+      really loaded into memory. To get a real picture of what's
+      going on, look at /proc/<<PID>>/maps and /proc/<<PID>>/fd/
+      (replace <<PID>> with the process ID of ``rws'').
+
+2. What's all this about shared object scripts and .so files?
+
+   A. See doc/index.html
+
+3. Can I run rws under Apache?
+
+   A. Yes, using mod_proxy. More instructions to come, but this is the
+      recommended approach if you want to integrate monolith applications
+      with your existing Apache-driven site.
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..2098617
--- /dev/null
+++ b/TODO
@@ -0,0 +1,34 @@
+Support for SMP
+---------------
+
+It's actually going to be quite easy to modify rws so that it supports
+SMP - even though the underlying threading library is lightweight user
+threads. The plan is as follows, I'm just waiting for the patch!
+
+Support should probably go directly into pthrlib/src/pthr_server.c so
+that it can benefit all pthrlib-based servers.
+
+At start up, based on some command line flag and/or autodetection of
+the number of CPUs, the server should fork N times where N is the
+number of CPUs. This results in N+1 processes. Process 0 (the parent)
+will have the listening socket. Processes 1-N will be connected by
+Unix domain sockets back to process 0. Processes 1-N will have special
+modified listen threads listening on their respective Unix domain
+socket. (NB. Use the socketpair function to create each Unix domain
+socket).
+
+When process 0 receives a connection, based on some hashing function
+of the peername, it should decide which of the 1-N processes will
+handle the request. The important thing is that the hashing function
+must always assign the same client to the same process, which is why
+the hash should be based on the peername.
+
+When a new client connection arrives at process 0, let's say it
+decides to pass this to process n (1 <= n <= N). It should pass the
+file descriptor over the corresponding Unix domain socket to process n
+using the sendmsg(2) function (see cmsg(3) for details). Process n
+will receive the file descriptor and can then continue to handle the
+request.
+
+This method should scale up over small numbers of processors (eg. up
+to 8).
diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..93330ac
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef RWS_CONFIG_H
+#define RWS_CONFIG_H
+
+@TOP@
+
+#undef PACKAGE
+#undef VERSION
+
+@BOTTOM@
+
+#endif /* RWS_CONFIG_H */
diff --git a/cfg.c b/cfg.c
new file mode 100644 (file)
index 0000000..95337d9
--- /dev/null
+++ b/cfg.c
@@ -0,0 +1,319 @@
+/* Configuration file parsing.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: cfg.c,v 1.11 2002/10/15 21:28:32 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <hash.h>
+#include <vector.h>
+#include <pre.h>
+
+#include "re.h"
+#include "cfg.h"
+
+struct config_data
+{
+  sash data;
+
+  /* Aliases -- these are only present in the host-specific configuration
+   * files, not in CFG_MAIN.
+   */
+  shash aliases;               /* Hash of string -> struct alias_data * */
+};
+
+struct alias_data
+{
+  sash data;
+};
+
+static pool cfg_pool = 0;
+static shash cfg_hosts;                /* Hash of string -> struct config_data * */
+static struct config_data *cfg_main;
+
+static struct config_data *read_config (FILE *fp, int is_main, const char *filename);
+static void config_err (const char *filename, const char *line, const char *msg);
+
+/* The PATH argument will point to the base for configuration
+ * files, eg. "/etc/rws". We append "/rws.conf" to get the main
+ * configuration file and "/hosts/" to get the virtual hosts
+ * directory.
+ */
+void
+cfg_reread_config (const char *path)
+{
+  const char *config_file;
+  const char *hosts_dir;
+  FILE *fp;
+  DIR *dir;
+  struct dirent *d;
+  pool tmp;
+
+  /* Show the message about reloading the configuration file, but only
+   * the second and subsequent times this function is run.
+   */
+  if (cfg_pool)
+    fprintf (stderr, "reloading configuration file ...\n");
+
+  /* Remove any configuration data from previous configuration run. */
+  if (cfg_pool) delete_pool (cfg_pool);
+
+  /* Create new data structures. */
+  cfg_pool = new_subpool (global_pool);
+  tmp = new_subpool (cfg_pool);
+  cfg_hosts = new_shash (cfg_pool, struct config_data *);
+
+  config_file = psprintf (tmp, "%s/rws.conf", path);
+  hosts_dir = psprintf (tmp, "%s/hosts/", path);
+
+  /* Read in main configuration file. */
+  fp = fopen (config_file, "r");
+  if (fp == 0) { perror (config_file); exit (1); }
+  cfg_main = read_config (fp, 1, config_file);
+  fclose (fp);
+
+  /* Read in each virtual host configuration file. */
+  dir = opendir (hosts_dir);
+  if (dir)
+    {
+      while ((d = readdir (dir)) != 0)
+       {
+         struct config_data *c;
+
+         if (d->d_name[0] != '.') /* Ignore ".", ".." and dotfiles. */
+           {
+             const char *p;
+
+             p = psprintf (tmp, "%s/%s", hosts_dir, d->d_name);
+
+             fp = fopen (p, "r");
+             if (fp == 0) { perror (p); exit (1); }
+             c = read_config (fp, 0, p);
+             fclose (fp);
+
+             shash_insert (cfg_hosts, d->d_name, c);
+           }
+       }
+
+      closedir (dir);
+    }
+
+  delete_pool (tmp);
+}
+
+/* Read in a config file from FP. */
+static struct config_data *
+read_config (FILE *fp, int is_main, const char *filename)
+{
+  pool tmp = new_subpool (cfg_pool);
+  char *line = 0;
+  struct config_data *c;
+  struct alias_data *a = 0;
+
+  c = pmalloc (cfg_pool, sizeof *c);
+  c->data = new_sash (cfg_pool);
+  if (!is_main) c->aliases = new_shash (cfg_pool, struct alias_data *);
+  else c->aliases = 0;
+
+  while ((line = pgetlinec (tmp, fp, line)))
+    {
+      vector v;
+
+      if (!is_main && (v = prematch (tmp, line, re_alias_start, 0)))
+       {
+         const char *aliasname;
+
+         if (a) config_err (filename, line, "nested alias");
+
+         vector_get (v, 1, aliasname);
+
+         a = pmalloc (cfg_pool, sizeof *a);
+         a->data = new_sash (cfg_pool);
+
+         if (shash_insert (c->aliases, aliasname, a))
+           config_err (filename, line, "duplicate alias");
+       }
+      else if (!is_main && prematch (tmp, line, re_alias_end, 0))
+       {
+         if (!a)
+           config_err (filename, line,
+                      "end alias found, but not inside an alias definition");
+
+         a = 0;
+       }
+      else if ((v = prematch (tmp, line, re_begin, 0)))
+       {
+         const char *key;
+         char *value, *end_line;
+         sash s;
+
+         vector_get (v, 1, key);
+
+         /* Read the data lines until we get to 'end key' line. */
+         value = pstrdup (tmp, "");
+         end_line = psprintf (tmp, "end %s", key);
+         while ((line = pgetlinec (tmp, fp, line)))
+           {
+             if (strcmp (line, end_line) == 0)
+               break;
+
+             value = pstrcat (tmp, value, line);
+             value = pstrcat (tmp, value, "\n");
+           }
+
+         if (!line)
+           config_err (filename, "EOF", "missing end <key> line");
+
+         if (a) s = a->data;
+         else s = c->data;
+
+         if (sash_insert (s, key, value))
+           config_err (filename, line, "duplicate definition");
+       }
+      else if ((v = prematch (tmp, line, re_conf_line, 0)))
+       {
+         const char *key, *value;
+         sash s;
+
+         vector_get (v, 1, key);
+         vector_get (v, 2, value);
+
+         /* 'key:' means define key as the empty string (as opposed to
+          * commenting it out which leaves 'key' undefined).
+          */
+         if (value == 0) value = "";
+
+         if (a) s = a->data;
+         else s = c->data;
+
+         if (sash_insert (s, key, value))
+           config_err (filename, line, "duplicate definition");
+       }
+      else
+       config_err (filename, line, "unexpected line");
+    }
+
+  delete_pool (tmp);
+
+  return c;
+}
+
+static void
+config_err (const char *filename, const char *line, const char *msg)
+{
+  fprintf (stderr,
+          "rws: %s: %s\n"
+          "rws: near ``%s''\n",
+          filename, msg,
+          line);
+  exit (1);
+}
+
+void *
+cfg_get_host (const char *host)
+{
+  struct config_data *c = 0;
+
+  shash_get (cfg_hosts, host, c);
+  return c;
+}
+
+void *
+cfg_get_alias (void *host_ptr, const char *path)
+{
+  struct config_data *c = (struct config_data *) host_ptr;
+  struct alias_data *a = 0;
+
+  shash_get (c->aliases, path, a);
+  return a;
+}
+
+const char *
+cfg_get_string (void *host_ptr, void *alias_ptr,
+               const char *key, const char *default_value)
+{
+  struct config_data *c = (struct config_data *) host_ptr;
+  struct alias_data *a = (struct alias_data *) alias_ptr;
+  const char *value;
+
+  if (a && sash_get (a->data, key, value))
+    return value;
+  if (c && sash_get (c->data, key, value))
+    return value;
+  if (sash_get (cfg_main->data, key, value))
+    return value;
+
+  return default_value;
+}
+
+int
+cfg_get_int (void *host_ptr, void *alias_ptr, const char *key, int default_value)
+{
+  const char *value = cfg_get_string (host_ptr, alias_ptr, key, 0);
+  int r;
+
+  if (!value) return default_value;
+
+  if (sscanf (value, "%d", &r) != 1) return default_value;
+
+  return r;
+}
+
+int
+cfg_get_bool (void *host_ptr, void *alias_ptr, const char *key, int default_value)
+{
+  const char *value = cfg_get_string (host_ptr, alias_ptr, key, 0);
+
+  if (!value) return default_value;
+
+  if (value[0] == '0' ||
+      value[0] == 'f' || value[0] == 'F' ||
+      value[0] == 'n' || value[0] == 'N' ||
+      (value[0] == 'o' && value[1] == 'f') ||
+      (value[0] == 'O' && value[1] == 'F'))
+    return 0;
+  else if (value[0] == '1' ||
+      value[0] == 't' || value[0] == 'T' ||
+      value[0] == 'y' || value[0] == 'Y' ||
+      (value[0] == 'o' && value[1] == 'n') ||
+      (value[0] == 'O' && value[1] == 'N'))
+    return 1;
+  else
+    return default_value;
+}
diff --git a/cfg.h b/cfg.h
new file mode 100644 (file)
index 0000000..c41cbb7
--- /dev/null
+++ b/cfg.h
@@ -0,0 +1,61 @@
+/* Configuration file parsing.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: cfg.h,v 1.3 2001/03/24 17:26:28 rich Exp $
+ */
+
+#ifndef CFG_H
+#define CFG_H
+
+#include <hash.h>
+
+/* Reread the configuration file. */
+extern void cfg_reread_config (const char *path);
+
+/* If there is host matching HOST, return an opaque pointer to the host's
+ * configuration data.
+ */
+extern void *cfg_get_host (const char *host);
+
+/* If there is an alias exactly matching PATH for host HOST_PTR, return
+ * an opaque pointer to the alias's configuration data.
+ */
+extern void *cfg_get_alias (void *host_ptr, const char *path);
+
+/* Return the configuration string named KEY.
+ *
+ * HOST_PTR and ALIAS_PTR may be optionally given to narrow the search
+ * down to a particular host/alias combination.
+ *
+ * If the configuration string named KEY cannot be found, then DEFAULT_
+ * VALUE is returned instead.
+ */
+extern const char *cfg_get_string (void *host_ptr, void *alias_ptr,
+                                  const char *key,
+                                  const char *default_value);
+
+/* Similar to CFG_GET_STRING but the string is converted to an integer.
+ */
+extern int cfg_get_int (void *host_ptr, void *alias_ptr,
+                       const char *key, int default_value);
+
+/* Similar to CFG_GET_STRING but the string is converted to a boolean.
+ */
+extern int cfg_get_bool (void *host_ptr, void *alias_ptr,
+                        const char *key, int default_value);
+
+#endif /* CFG_H */
diff --git a/conf/default.in b/conf/default.in
new file mode 100644 (file)
index 0000000..d26a37f
--- /dev/null
@@ -0,0 +1,84 @@
+# Example ``/etc/rws/hosts/<<HOSTNAME>>'' configuration file. You
+# need one of these files for every virtual host you serve (although
+# you can use symbolic links to save time) plus one file called ``default''
+# which is used for when the client doesn't send a ``Host:'' HTTP header.
+
+# The document root.
+
+alias /
+       # Path to the document root.
+       path:           @pkgdatadir@/html
+
+       # Allow files to be viewed.
+       show:           1
+
+       # Do directory listings.
+       list:           1
+end alias
+
+# Example CGI directory.
+
+alias /cgi-bin/
+
+       path:           /path/to/cgi/scripts
+
+       # Allow CGI scripts to be executed in here. Note that
+       # show and list are both off by default.
+
+       exec:           1
+
+end alias
+
+# Example shared object scripts directory (see doc/index.html).
+
+alias /so-bin/
+
+       path:           @pkgdatadir@/so-bin
+
+       # Allow shared object scripts to be executed in here. Note
+       # that show and list are both of by default.
+
+       exec so:        1
+
+end alias
+
+# For Monolith, you need these aliases.
+
+alias /ml-styles/
+       path:           @pkgdatadir@/ml-styles
+       show:           1
+end alias
+
+alias /ml-icons/
+       path:           @pkgdatadir@/ml-icons
+       show:           1
+end alias
+
+# Rewrite rules applying to this host.
+
+begin rewrite
+
+# Rules apply in order. Use 'last' flag on a rule to cause execution
+# to finish at that rule if it matches.
+
+# Simple rewrite rule (external: the browser gets a redirect instruction).
+#^/default.html$ /index.html   external
+
+# Simple rewrite rule (internal: browser is unaware of the redirect).
+#^/default.html$ /index.html
+
+# Monolith parsed pages (demonstrating the use of $1, $2, ... placeholders).
+# 'qsa' appends the original query string (if any) to the end of the
+# rewritten URL.
+#^/annexia/(.*\.msp)$    /so-bin/msp.so?page=$1  last,qsa
+#^/annexia/$             /annexia/index.msp      qsa,external
+#^/annexia/(.*)/$        /annexia/$1/index.msp   qsa,external
+
+# Conditional rewrite rules are not yet implemented.
+
+end rewrite
+
+# msp root: /home/rich/annexia
+# msp database: dbname=rich
+# monolith user database: dbname=rich
+# chatbot database: dbname=rich
diff --git a/conf/rws.conf.in b/conf/rws.conf.in
new file mode 100644 (file)
index 0000000..93cbd40
--- /dev/null
@@ -0,0 +1,71 @@
+# Example ``/etc/rws/rws.conf'' file.
+
+# If the server is started as root, drop privileges and change to user
+# ID given below.
+#
+# Default: nobody
+#
+#user: web
+
+# Set the path to search for the mime.types file.
+#
+# Default: /etc/mime.types
+#
+#mime types file: /etc/httpd/conf/mime.types
+
+# The place to put the web server error log.
+#
+# Default: /tmp/error_log
+#
+#error log: /var/log/httpd/error_log
+
+# The place to put the web server access log.
+#
+# Default: /tmp/access_log
+#
+#access log: /var/log/httpd/access_log
+
+# Requests time out after this many seconds.
+#
+# Default: 60
+#
+#request timeout: 300
+
+# The email address of the maintainer, displayed in error messages.
+#
+# Default: (none)
+#
+#maintainer: bob@example.com
+
+# The default expiry time. This has the form '[+|-]NN[s|m|h|d|y]', for
+# example, '+1d' means set the expiry for current time + 1 day. The
+# default is to send no Expires: headers, but setting this to a small
+# value such as 1 day can dramatically improve the performance of your
+# website, especially given that rws currently doesn't implement the
+# GET/If-Modified-Since requirement of RFC 2616.
+#
+# Default: (none)
+#
+expires: +1d
+
+# Icons used in directory listings.
+
+icon for application/*:                /icons/binary.gif 20x22 "Application"
+icon for application/x-tar:    /icons/tar.gif 20x22 "Unix tape archive file"
+icon for application/x-gzip:   /icons/compressed.gif 20x22 "Compressed file"
+icon for application/zip:      /icons/compressed.gif 20x22 "Compressed file"
+icon for audio/*:              /icons/sound1.gif 20x22 "Audio file"
+icon for image/*:              /icons/image2.gif 20x22 "Image"
+icon for message/*:            /icons/quill.gif 20x22 "Mail message"
+icon for text/*:               /icons/text.gif 20x22 "Text file"
+icon for video/*:              /icons/movie.gif 20x22 "Video file"
+
+no type icon:                  /icons/generic.gif 20x22 "File"
+
+unknown icon:                  /icons/unknown.gif 20x22 "Unknown file type"
+
+directory icon:                        /icons/dir.gif 20x22 "Directory"
+
+link icon:                     /icons/link.gif 20x22 "Symbolic link"
+
+special icon:                  /icons/sphere2.gif 20x22 "Special file"
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..04d9db0
--- /dev/null
+++ b/configure
@@ -0,0 +1,66 @@
+#!/bin/sh -
+#
+# This is make+. Make+ is a set of scripts which enhance GNU make and
+# let you build RPMs, and other packages types with just one control
+# file. Read more at http://www.annexia.org/freeware/makeplus/
+#
+# The original author is Richard W.M. Jones <rich@annexia.org>.
+#
+# This software has been explicitly placed in the PUBLIC DOMAIN.  You
+# do not need any sort of license or agreement to use or copy this
+# software. You may also copyright this software yourself, and/or
+# relicense it under any terms you want, at any time and at no cost.
+# This allows you (among other things) to include this software with
+# other packages so that the user does not need to download and
+# install make+ separately.
+
+mp_options=""
+
+usage ()
+{
+    cat <<EOF
+./configure [--options]
+
+Installation directory options:
+  --prefix=PREFIX       Installation prefix [default: /usr/local]
+  --sysconfdir=SYSCONF  Installation prefix for configuration files
+                         [default: PREFIX/etc]
+  --localstatedir=STATE Installation prefix for writable files
+                         [default: PREFIX/var]
+
+Help options:
+  --help                Display this help and exit.
+  --print-mp-cmd        Display the make+ command and exit (use as final arg)
+EOF
+    exit 1
+}
+
+while [ $# -gt 0 ]; do
+    opt=$1 ; shift
+
+    case "$opt" in
+       --help)
+           usage
+           ;;
+       --print-mp-cmd)
+           echo "rm -f build-\*/config.mk"
+           echo "make+ $mp_options configure"
+           exit 0
+           ;;
+       --*prefix|--*dir)
+           opt=`echo $opt | sed 's/^--//'`
+           arg=$1 ; shift
+           mp_options="$mp_options $opt=$arg"
+           ;;
+       --*prefix=*|--*dir=*)
+           opt=`echo $opt | sed 's/^--//'`
+           mp_options="$mp_options $opt"
+           ;;
+       *)
+           mp_options="$mp_options $opt"
+           ;;
+    esac
+done
+
+rm -f build-*/config.mk
+make+ $mp_options configure
diff --git a/dir.c b/dir.c
new file mode 100644 (file)
index 0000000..65b1552
--- /dev/null
+++ b/dir.c
@@ -0,0 +1,386 @@
+/* Directory serving.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: dir.c,v 1.13 2003/02/05 23:02:51 rich Exp $
+ */
+
+#include "config.h"
+
+#include <assert.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+#ifdef HAVE_SYS_SYSLIMITS_H
+#include <sys/syslimits.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <vector.h>
+#include <pre.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+#include "process_rq.h"
+#include "mime_types.h"
+#include "file.h"
+#include "errors.h"
+#include "cfg.h"
+#include "re.h"
+#include "dir.h"
+
+static void choose_icon (process_rq p,
+                        const char *filename, const struct stat *statbuf,
+                        const char **icon, const char **icon_alt,
+                        int *icon_width, int *icon_height);
+static void standard_icon (process_rq p, const char *name,
+                          const char **icon, const char **icon_alt,
+                          int *icon_width, int *icon_height);
+static void unknown_icon (process_rq p,
+                         const char **icon, const char **icon_alt,
+                         int *icon_width, int *icon_height);
+static int parse_icon_str (process_rq p, const char *icon_str,
+                          const char **icon, const char **icon_alt,
+                          int *icon_width, int *icon_height);
+static const char *get_printable_size (process_rq p,
+                                      const struct stat *statbuf);
+static const char *get_link_field (process_rq p, const char *filename);
+
+static int
+my_strcmp (const char **p1, const char **p2)
+{
+  return strcmp (*p1, *p2);
+}
+
+int
+dir_serve (process_rq p)
+{
+  http_response http_response;
+  int close, i;
+  char *index_file;
+  struct stat index_statbuf;
+  DIR *dir;
+  struct dirent *d;
+  vector files;
+
+  /* Is there an index file in this directory? If so, internally redirect
+   * the request to that file.
+   */
+  index_file = psprintf (p->pool, "%s/index.html", p->file_path);
+  if (stat (index_file, &index_statbuf) == 0 &&
+      S_ISREG (index_statbuf.st_mode))
+    {
+      /* Update the request structure appropriately. */
+      p->file_path = index_file;
+      p->remainder = psprintf (p->pool, "%s/index.html", p->remainder);
+      p->statbuf = index_statbuf;
+
+      /* Serve the file. */
+      return file_serve (p);
+    }
+
+  /* Are we allowed to generate a directory listing? */
+  if (!cfg_get_bool (p->host, p->alias, "list", 0))
+    return bad_request_error (p, "directory listing not allowed");
+
+  /* Yes: read the files into a local vector. */
+  dir = opendir (p->file_path);
+  if (dir == 0)
+    return bad_request_error (p, "error opening directory");
+
+  files = new_vector (p->pool, const char *);
+  while ((d = readdir (dir)) != 0)
+    {
+      if (d->d_name[0] != '.') /* Ignore hidden files. */
+       {
+         char *name = pstrdup (p->pool, d->d_name);
+         vector_push_back (files, name);
+       }
+    }
+  closedir (dir);
+
+  /* Sort them into alphabetical order. */
+  psort (files, my_strcmp);
+
+  /* Not changed, so it's a real cache hit. */
+  http_response = new_http_response (p->pool, p->http_request, p->io,
+                                    200, "OK");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/html",
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (p->http_request)) return close;
+
+  io_fprintf (p->io,
+             "<html><head><title>Directory listing: %s</title></head>" CRLF
+             "<body bgcolor=\"#ffffff\">" CRLF
+             "<h1>Directory listing: %s</h1>" CRLF
+             "<a href=\"..\">Go up to parent directory</a>" CRLF
+             "<table border=\"0\">" CRLF,
+             p->canonical_path, p->canonical_path);
+
+  for (i = 0; i < vector_size (files); ++i)
+    {
+      const char *name, *pathname, *icon, *icon_alt;
+      const char *size = "", *link_field = "";
+      int icon_width, icon_height;
+      struct stat file_statbuf;
+
+      vector_get (files, i, name);
+
+      /* Generate the full pathname. */
+      pathname = psprintf (p->pool, "%s/%s", p->file_path, name);
+
+      /* Stat the file to get type and size information. */
+      if (lstat (pathname, &file_statbuf) == 0)
+       {
+         /* Choose an icon type. */
+         choose_icon (p, name, &file_statbuf, &icon, &icon_alt,
+                      &icon_width, &icon_height);
+
+         /* Get the size. */
+         if (S_ISREG (file_statbuf.st_mode))
+           size = get_printable_size (p, &file_statbuf);
+
+         /* If it's a link, get the link field. */
+         if (S_ISLNK (file_statbuf.st_mode))
+           link_field = get_link_field (p, pathname);
+
+         /* Print the pathname. */
+         io_fprintf (p->io,
+                     "<tr><td><img src=\"%s\" alt=\"%s\" width=\"%d\" height=\"%d\"></td><td><a href=\"%s%s\">%s</a> %s</td><td>%s</td></tr>" CRLF,
+                     icon, icon_alt, icon_width, icon_height,
+                     name,
+                     S_ISDIR (file_statbuf.st_mode) ? "/" : "",
+                     name,
+                     link_field,
+                     size);
+       }
+    }
+
+  io_fprintf (p->io,
+             "</table>" CRLF
+             "<hr>%s<br>" CRLF
+             "</body></html>" CRLF,
+             http_get_servername ());
+
+  return close;
+}
+
+static void
+choose_icon (process_rq p,
+            const char *filename, const struct stat *statbuf,
+            const char **icon, const char **icon_alt,
+            int *icon_width, int *icon_height)
+{
+  if (S_ISREG (statbuf->st_mode))
+    {
+      const char *mime_type = 0;
+      const char *icon_str;
+      vector v;
+
+      /* Get the file extension and map it to a MIME type. */
+      if ((v = prematch (p->pool, filename, re_ext, 0)) != 0)
+       {
+         char *ext;
+
+         vector_get (v, 1, ext);
+         mime_type = mime_types_get_type (ext);
+       }
+      if (!mime_type)
+       {
+         standard_icon (p, "no type",
+                        icon, icon_alt, icon_width, icon_height);
+         return;
+       }
+
+      /* If there a icon specified for this MIME type? */
+      icon_str = cfg_get_string (p->host, p->alias,
+                                psprintf (p->pool, "icon for %s", mime_type),
+                                0);
+      if (!icon_str)
+       {
+         /* Try looking for an icon for class / * instead. */
+         v = pstrcsplit (p->pool, mime_type, '/');
+         if (vector_size (v) >= 1)
+           {
+             const char *mime_class;
+
+             vector_get (v, 0, mime_class);
+             icon_str = cfg_get_string (p->host, p->alias,
+                                        psprintf (p->pool, "icon for %s/*",
+                                                  mime_class), 0);
+           }
+       }
+
+      if (!icon_str)
+       {
+         unknown_icon (p, icon, icon_alt, icon_width, icon_height);
+         return;
+       }
+
+      /* Split up the string. */
+      if (!parse_icon_str (p, icon_str, icon, icon_alt, icon_width, icon_height))
+       {
+         fprintf (stderr,
+                  "cannot parse icon description: %s (mime_type = %s)\n",
+                  icon_str, mime_type);
+         unknown_icon (p, icon, icon_alt, icon_width, icon_height);
+         return;
+       }
+    }
+  else if (S_ISDIR (statbuf->st_mode))
+    {
+      standard_icon (p, "directory", icon, icon_alt, icon_width, icon_height);
+    }
+  else if (S_ISLNK (statbuf->st_mode))
+    {
+      standard_icon (p, "link", icon, icon_alt, icon_width, icon_height);
+    }
+  else
+    {
+      standard_icon (p, "special", icon, icon_alt, icon_width, icon_height);
+    }
+}
+
+static void
+standard_icon (process_rq p, const char *name,
+              const char **icon, const char **icon_alt,
+              int *icon_width, int *icon_height)
+{
+  const char *icon_str;
+
+  icon_str = cfg_get_string (p->host, p->alias,
+                            psprintf (p->pool, "%s icon", name), 0);
+  if (!icon_str)
+    {
+      unknown_icon (p, icon, icon_alt, icon_width, icon_height);
+      return;
+    }
+
+  if (!parse_icon_str (p, icon_str, icon, icon_alt, icon_width, icon_height))
+    {
+      fprintf (stderr, "cannot parse icon description: %s\n", icon_str);
+      unknown_icon (p, icon, icon_alt, icon_width, icon_height);
+      return;
+    }
+}
+
+static void
+unknown_icon (process_rq p,
+             const char **icon, const char **icon_alt,
+             int *icon_width, int *icon_height)
+{
+  const char *icon_str;
+
+  icon_str = cfg_get_string (p->host, p->alias, "unknown icon", 0);
+  if (!icon_str)
+    {
+      fprintf (stderr,
+              "``unknown icon'' must be present in configuration file\n");
+      exit (1);
+    }
+
+  if (!parse_icon_str (p, icon_str, icon, icon_alt, icon_width, icon_height))
+    {
+      fprintf (stderr, "cannot parse icon description: %s\n", icon_str);
+      exit (1);
+    }
+}
+
+static int
+parse_icon_str (process_rq p, const char *icon_str,
+               const char **icon, const char **icon_alt,
+               int *icon_width, int *icon_height)
+{
+  vector v;
+  char *s;
+
+  /* Split up the string. */
+  if ((v = prematch (p->pool, icon_str, re_icon, 0)) == 0)
+    return 0;
+
+  if (vector_size (v) != 5) return 0;
+
+  vector_get (v, 1, *icon);
+  vector_get (v, 2, s);
+  sscanf (s, "%d", icon_width);
+  vector_get (v, 3, s);
+  sscanf (s, "%d", icon_height);
+  vector_get (v, 4, *icon_alt);
+
+  return 1;
+}
+
+static const char *
+get_printable_size (process_rq p,
+                   const struct stat *statbuf)
+{
+  unsigned long size = statbuf->st_size;
+
+  if (size < 1024)
+    return psprintf (p->pool, "%lu bytes", size);
+  else if (size < 1024 * 1024)
+    return psprintf (p->pool, "%.1f KB", size / 1024.0);
+  else
+    return psprintf (p->pool, "%.1f MB", size / (1024 * 1024.0));
+}
+
+static const char *
+get_link_field (process_rq p, const char *filename)
+{
+  const char prefix[] = "-&gt; ";
+  const int prefix_sz = sizeof prefix - 1;
+  char *buffer;
+  int n;
+
+#ifndef NAME_MAX
+  /* Solaris defines NAME_MAX on a per-filesystem basis.
+   * See: http://lists.spine.cx/archives/everybuddy/2002-May/001419.html
+   */
+  long NAME_MAX = pathconf (filename, _PC_NAME_MAX);
+#endif
+
+  buffer = pmalloc (p->pool, NAME_MAX + 1 + prefix_sz);
+
+  memcpy (buffer, prefix, prefix_sz);
+
+  n = readlink (filename, buffer + prefix_sz, NAME_MAX + 1);
+  if (n == -1) return "";
+
+  buffer[n + prefix_sz] = '\0';
+  return buffer;
+}
diff --git a/dir.h b/dir.h
new file mode 100644 (file)
index 0000000..3c921fa
--- /dev/null
+++ b/dir.h
@@ -0,0 +1,40 @@
+/* Directory serving.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: dir.h,v 1.3 2001/03/24 17:26:28 rich Exp $
+ */
+
+#ifndef DIR_H
+#define DIR_H
+
+#include "config.h"
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+#include "process_rq.h"
+
+extern int dir_serve (process_rq p);
+
+#endif /* DIR_H */
diff --git a/doc/.cvsignore b/doc/.cvsignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/index.html b/doc/index.html
new file mode 100644 (file)
index 0000000..c266726
--- /dev/null
@@ -0,0 +1,489 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>rws documentation index</title>
+    <style type="text/css"><!--
+      h1 {
+      text-align: center;
+      }
+      pre {
+      background-color: #eeeeff;
+      }
+      code {
+      color: green;
+      font-weight: bold;
+      }
+      --></style>
+  </head>
+
+  <body bgcolor="#ffffff">
+    <h1>rws documentation index</h1>
+
+    <h2>Shared object scripts</h2>
+
+    <p>
+      Shared object scripts are a possibly unique feature of <code>rws</code>.
+      A shared object script is a CGI script, written in C, which
+      is loaded into the address space of the server at runtime.
+      Thus shared object scripts are very fast because they are
+      written in C, loaded just once, and able to run without
+      needing a <code>fork(2)</code>.
+    </p>
+
+    <p>
+      On the other hand, the penalty for speed is security, although
+      competent C programmers who are using all the features of
+      <a href="http://www.annexia.org/freeware/c2lib/">c2lib</a> and
+      <a href="http://www.annexia.org/freeware/pthrlib/">pthrlib</a>
+      should be able to write code which is free of buffer overflows
+      and some other common security issues. (However if you allow
+      your server to run shared object scripts from untrusted
+      third parties, then you have essentially no security at all, since
+      shared object scripts can interfere with the internal workings
+      of the webserver in arbitrary ways).
+    </p>
+
+    <h3>The anatomy of a shared object script</h3>
+
+    <p>
+      A shared object script is a <q><code>.so</code></q>
+      file (in other words, a shared library or <q>DLL</q>).
+      It should contain a single external symbol called
+      <code>handle_request</code>, prototyped as:
+    </p>
+
+<pre>
+int handle_request (rws_request rq);
+</pre>
+
+    <p>
+      The <code>rws_request</code> object is defined in
+      <code>rws_request.h</code>.
+    </p>
+
+    <p>
+      The first time that any client requests the shared
+      object script, <code>rws</code> calls <code>dlopen(3)</code>
+      on the file. As noted in the <code>dlopen(3)</code>
+      manual page, this will cause <code>_init</code> and any
+      constructor functions in the file to be run.
+      Then <code>rws</code> creates the <code>rws_request</code>
+      object (see below) and calls <code>handle_request</code>.
+      The shared object script remains loaded in memory
+      after <code>handle_request</code> has returned, ready
+      for the next invocation.
+    </p>
+
+    <p>
+      On subsequent invocations, <code>dlopen(3)</code> is
+      <em>not</em> called, so constructors only run once.
+    </p>
+
+    <p>
+      However, on each invocation, <code>rws</code> checks the
+      modification time of the file on disk, and if it has
+      changed, then it will attempt to reload the file. To
+      do this, it calls <code>dlclose(3)</code> first, which
+      will cause <code>_fini</code> and destructors in the
+      library to run, and unloads the library from memory. It
+      then reopens (<code>dlopen(3)</code>) the new file on
+      disk, as above. Beware that there are some occasions when
+      <code>rws</code> actually cannot reload a shared object
+      script, even though it notices that the file has changed
+      on disk. <code>rws</code> keeps a use count of the number
+      of threads currently using the shared object script, and
+      for safety reasons it cannot reload the file until this
+      usage count drops to zero. This means that in some cases
+      (eg. under very heavy load) a shared object script might
+      never be reloaded, even if it changes on disk.
+    </p>
+
+    <h3>Configuring rws to recognise shared object scripts</h3>
+
+    <p>
+      <code>rws</code> will not try to run shared object scripts
+      unless the <code>exec so</code> flag has been set on the
+      alias, and the shared object script itself is executable (mode 0755).
+      Here is an example shared object scripts directory:
+    </p>
+
+<pre>
+alias /so-bin/
+       path: /usr/share/rws/so-bin
+       exec so: 1
+end alias
+</pre>
+
+    <p>
+      Make sure that the <code>so-bin</code> directory is only
+      writable by trusted users, and make sure each shared object
+      script is executable, mode 0755.
+    </p>
+
+    <p>
+      If you can't make your shared object scripts run, then here
+      is a checklist before you email me:
+    </p>
+
+    <ul>
+      <li> Make sure you have put the above alias section into
+       the correct host file.
+      <li> <code>exec so</code> option is set?
+      <li> Restarted <code>rwsd</code>?
+      <li> Directory is world readable, executable (mode 0755)?
+      <li> Shared object script is world readable, executable (mode 0755)?
+      <li> Any unresolved symbols (<code>ldd -r script.so</code>), apart
+       from the <code>rws_request_*</code> symbols which will be resolved
+       when the library is loaded into <code>rws</code>?
+      <li> Missing <code>handle_request</code> function?
+      <li> <code>handle_request</code> is exported in the dynamic
+       symbol table (<code>nm -D script.so</code>)?
+      <li> Check the contents of your error_log file to see
+       if any error messages were reported.
+    </ul>
+
+    <p>
+      I have quite successfully used <code>gdb</code> on a running
+      server to debug and diagnose problems in shared object
+      scripts. However note that by default <code>gdb</code> may
+      have trouble loading the symbol table for your script. Use
+      the <code>sharedlibrary script.so</code>
+      command to load symbols instead.
+    </p>
+
+    <h3>Shared object scripts vs. Monolith applications</h3>
+
+    <p>
+      If you've been looking at the
+      <a href="http://www.annexia.org/freeware/monolith/">Monolith
+       application framework</a> pages, then you may be confused
+      about how shared object scripts relate to Monolith.
+    </p>
+
+    <p>
+      Shared object scripts are the direct analogy to CGI scripts,
+      the only difference being that CGI scripts are usually written
+      in very high level languages like Perl and PHP, and shared
+      object scripts are loaded into the server process for efficiency.
+      (Perl CGI scripts can also be loaded into the Apache
+      server process using <code>mod_perl</code>, and this is done
+      for similar reasons of efficiency).
+    </p>
+
+    <p>
+      Monolith programs are entire applications, the sort of
+      thing which normally would be written using dozens of
+      cooperating CGI scripts. In the case of Monolith, however,
+      the entire application compiles down to a single <code>.so</code>
+      file which happens to be (you guessed it) a shared object script.
+    </p>
+
+    <p>
+      Imagine that you are going to write yet another web-based email
+      client. For some reason you want to write this in C (please
+      don't try this at home: I wrote one in Perl at my last job and
+      that was hard enough). Here are three possible approaches
+      using C and <code>rws</code>:
+    </p>
+
+    <ol>
+      <li>
+       <p>
+         Write forty or so shared object scripts. Each displays
+         a single frame of the application, one might generate
+         the frameset, a couple of dozen to implement specific
+         operations like emptying trash or moving a message between
+         folders.
+       </p>
+       <p>
+         This is very much the normal way of writing CGI-based
+         applications.
+       </p>
+      <li> Write a Monolith application. This will probably be
+       in lots of C files, but will compile down and be linked
+       into a single <code>.so</code> file (eg. <code>email.so</code>)
+       which is dropped into the <code>so-bin</code> directory.
+      <li>
+       <p>
+         Write a Monolith email super-widget. This is going
+         to exist in a shared library called
+         <code>/usr/lib/libmyemail.so</code>
+         with a corresponding header file defining the interface
+         called <code>myemail.h</code>.
+       </p>
+       <p>
+         Write a tiny Monolith application which just instantiates
+         a window and an email widget, and embeds the email widget
+         in the window. This will compile into <code>email.so</code>
+         (it'll be very tiny) which is dropped into <code>so-bin</code>.
+       </p>
+       <p>
+         The advantage of this final approach is that you can
+         reuse the email widget in other places, or indeed sell
+         it to other Monolith users.
+       </p>
+    </ol>
+
+    <p>
+      So Monolith is good when you want to build applications
+      from widgets as you would if you were building a
+      Java/Swing, Windows MFC, gtk, Tcl/Tk graphical application.
+      It's also good if code re-use is important to you.
+      Shared object scripts are good when you are familiar with
+      CGI-based techniques to build websites.
+    </p>
+
+    <p>
+      Of course, the same <code>rws</code> server can serve
+      shared object scripts, multiple Monolith applications,
+      flat files, and directory listings, all at the same time.
+    </p>
+
+    <h3>Tutorial on writing shared object scripts</h3>
+
+    <p>
+      In this tutorial I will explain how the two shared object
+      script examples supplied with <code>rws</code> work. You
+      will also need to have read the tutorials for
+      <a href="http://www.annexia.org/freeware/c2lib/">c2lib</a> and
+      <a href="http://www.annexia.org/freeware/pthrlib/">pthrlib</a>
+      which you can find by going to their respective web pages.
+    </p>
+
+    <p>
+      The first example, <code>hello.c</code> is very simple indeed.
+      It's just a "hello world" program. The program starts by
+      including <code>rws_request.h</code>:
+    </p>
+
+<pre>
+#include &lt;rws_request.h&gt;
+</pre>
+
+    <p>
+      Following this is the <code>handle_request</code>
+      function. This is the function which <code>rws</code>
+      will call every time a user requests the script:
+    </p>
+
+<pre>
+int
+handle_request (rws_request rq)
+{
+  pseudothread pth = rws_request_pth (rq);
+  http_request http_request = rws_request_http_request (rq);
+  io_handle io = rws_request_io (rq);
+
+  int close;
+  http_response http_response;
+
+  /* Begin response. */
+  http_response = new_http_response (pth, http_request, io,
+                                    200, "OK");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/plain",
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (http_request)) return close;
+
+  io_fprintf (io, "hello, world!");
+
+  return close;
+}
+</pre>
+
+    <p>
+      We first extract some fields from the <code>rws_request</code>
+      object. <code>rws</code> has already taken the time to
+      parse the HTTP headers from the client, but we need to
+      generate the reply headers (shared object scripts
+      are always "nph" -- no parsed headers). The
+      <code>pthrlib</code> functions
+      <code>new_http_response</code>,
+      <code>http_response_send_headers</code> and
+      <code>http_response_end_headers</code> do this. Note
+      that we send a <code>Content-Type: text/plain</code>
+      header. You must always generate a correct
+      <code>Content-Type</code> header.
+    </p>
+
+    <p>
+      If the original request was a <code>HEAD</code> request, then
+      the client only wants to see the headers, so we stop here.
+    </p>
+
+    <p>
+      Otherwise we generate our message and return.
+    </p>
+
+    <p>
+      NB. Don't call <code>io_fclose</code> on the I/O handle! If you
+      really want to force the connection to close, set the
+      <code>close</code> variable to 1 and return it. This is
+      because the client (or proxy) might be issuing several
+      separate HTTP requests over the same kept-alive TCP connection.
+    </p>
+
+    <p>
+      The second example, <code>show_params.c</code>, is just slightly
+      more complex, but demonstrates how to do parameter parsing.
+      After reading this you should have enough knowledge to
+      go away and write your own shared object scripts that
+      actually do useful stuff.
+    </p>
+
+    <p>
+      As before, we start by including a few useful headers:
+    </p>
+
+<pre>
+#include &lt;pool.h&gt;
+#include &lt;vector.h&gt;
+#include &lt;pthr_cgi.h&gt;
+
+#include &lt;rws_request.h&gt;
+</pre>
+
+    <p>
+      The <code>handle_request</code> function starts the same way
+      as before:
+    </p>
+
+<pre>
+int
+handle_request (rws_request rq)
+{
+  pool pool = rws_request_pool (rq);
+  pseudothread pth = rws_request_pth (rq);
+  http_request http_request = rws_request_http_request (rq);
+  io_handle io = rws_request_io (rq);
+</pre>
+
+    <p>
+      Then we define some variables that we're going to use:
+    </p>
+
+<pre>
+  cgi cgi;
+  int close, i;
+  http_response http_response;
+  vector headers, params;
+  struct http_header header;
+  const char *name, *value;
+</pre>
+
+    <p>
+      The actual job of parsing out the CGI parameters is simplified
+      because <code>pthrlib</code> contains a CGI library
+      (similar to Perl's <code>CGI.pm</code>):
+    </p>
+
+<pre>
+  /* Parse CGI parameters. */
+  cgi = new_cgi (pool, http_request, io);
+</pre>
+
+    <p>
+      The response phase begins by sending the HTTP
+      headers as before:
+    </p>
+
+<pre>
+  /* Begin response. */
+  http_response = new_http_response (pth, http_request, io,
+                                    200, "OK");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/plain",
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (http_request)) return close;
+</pre>
+
+    <p>
+      Now we print out the actual contents of both the
+      <code>http_request</code> object and the <code>cgi</code>
+      object. HTTP headers first:
+    </p>
+
+<pre>
+  io_fprintf (io, "This is the show_params shared object script.\r\n\r\n");
+  io_fprintf (io, "Your browser sent the following headers:\r\n\r\n");
+
+  headers = http_request_get_headers (http_request);
+  for (i = 0; i &lt; vector_size (headers); ++i)
+    {
+      vector_get (headers, i, header);
+      io_fprintf (io, "\t%s: %s\r\n", header.key, header.value);
+    }
+
+  io_fprintf (io, "----- end of headers -----\r\n");
+</pre>
+
+    <p>
+      The full URL (including the query string), the path alone,
+      the query string:
+    </p>
+
+<pre>
+  io_fprintf (io, "The URL was: %s\r\n",
+             http_request_get_url (http_request));
+  io_fprintf (io, "The path component was: %s\r\n",
+             http_request_path (http_request));
+  io_fprintf (io, "The query string was: %s\r\n",
+             http_request_query_string (http_request));
+  io_fprintf (io, "The query arguments were:\r\n");
+</pre>
+
+    <p>
+      Finally we print out the CGI parameters from the <code>cgi</code>
+      object:
+    </p>
+
+<pre>
+  params = cgi_params (cgi);
+  for (i = 0; i &lt; vector_size (params); ++i)
+    {
+      vector_get (params, i, name);
+      value = cgi_param (cgi, name);
+      io_fprintf (io, "\t%s=%s\r\n", name, value);
+    }
+
+  io_fprintf (io, "----- end of parameters -----\r\n");
+
+  return close;
+}
+</pre>
+
+    <h2>Further examples</h2>
+
+    <p>
+      That's the end of this tutorial. I hope you enjoyed it. Please
+      contact the author about corrections or to obtain more information.
+    </p>
+
+    <h2>Links to manual pages</h2>
+
+    <ul>
+      <li> <a href="rws_request_canonical_path.3.html"><code>rws_request_canonical_path(3)</code></a> </li>
+      <li> <a href="rws_request_file_path.3.html"><code>rws_request_file_path(3)</code></a> </li>
+      <li> <a href="rws_request_host_header.3.html"><code>rws_request_host_header(3)</code></a> </li>
+      <li> <a href="rws_request_http_request.3.html"><code>rws_request_http_request(3)</code></a> </li>
+      <li> <a href="rws_request_io.3.html"><code>rws_request_io(3)</code></a> </li>
+      <li> <a href="rws_request_pool.3.html"><code>rws_request_pool(3)</code></a> </li>
+      <li> <a href="rws_request_pth.3.html"><code>rws_request_pth(3)</code></a> </li>
+    </ul>
+
+    <hr>
+    <address><a href="mailto:rich@annexia.org">Richard Jones</a></address>
+<!-- Created: Wed May  1 19:36:16 BST 2002 -->
+<!-- hhmts start -->
+Last modified: Wed Oct  9 20:02:40 BST 2002
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/doc/rws_request_canonical_path.3.html b/doc/rws_request_canonical_path.3.html
new file mode 100644 (file)
index 0000000..917f417
--- /dev/null
@@ -0,0 +1,153 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;rws_request.h&gt;
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/rws_request_file_path.3.html b/doc/rws_request_file_path.3.html
new file mode 100644 (file)
index 0000000..917f417
--- /dev/null
@@ -0,0 +1,153 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;rws_request.h&gt;
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/rws_request_host_header.3.html b/doc/rws_request_host_header.3.html
new file mode 100644 (file)
index 0000000..917f417
--- /dev/null
@@ -0,0 +1,153 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;rws_request.h&gt;
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/rws_request_http_request.3.html b/doc/rws_request_http_request.3.html
new file mode 100644 (file)
index 0000000..917f417
--- /dev/null
@@ -0,0 +1,153 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;rws_request.h&gt;
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/rws_request_io.3.html b/doc/rws_request_io.3.html
new file mode 100644 (file)
index 0000000..917f417
--- /dev/null
@@ -0,0 +1,153 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;rws_request.h&gt;
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/rws_request_pool.3.html b/doc/rws_request_pool.3.html
new file mode 100644 (file)
index 0000000..a4853e6
--- /dev/null
@@ -0,0 +1,153 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:09 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;rws_request.h&gt;
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/rws_request_pth.3.html b/doc/rws_request_pth.3.html
new file mode 100644 (file)
index 0000000..a4853e6
--- /dev/null
@@ -0,0 +1,153 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:09 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;rws_request.h&gt;
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
diff --git a/errors.c b/errors.c
new file mode 100644 (file)
index 0000000..aa1437e
--- /dev/null
+++ b/errors.c
@@ -0,0 +1,132 @@
+/* Deliver errors back to the user.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: errors.c,v 1.5 2002/12/01 14:58:01 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+#include "process_rq.h"
+#include "cfg.h"
+#include "errors.h"
+
+int
+bad_request_error (process_rq p, const char *text)
+{
+  http_response http_response;
+  int close;
+  const char *maintainer;
+
+  maintainer = cfg_get_string (p->host, p->alias,
+                              "maintainer", "(no maintainer)"); /* XXX */
+
+  http_response = new_http_response (p->pool, p->http_request, p->io,
+                                    500, "Internal server error");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/html",
+                             NO_CACHE_HEADERS,
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (p->http_request)) return close;
+
+  /* XXX Escaping. */
+  io_fprintf (p->io,
+             "<html><head><title>Internal server error</title></head>" CRLF
+             "<body bgcolor=\"#ffffff\">" CRLF
+             "<h1>500 Internal server error</h1>" CRLF
+             "There was an error serving this request:" CRLF
+             "<pre>" CRLF
+             "%s" CRLF
+             "</pre>" CRLF
+             "<hr>" CRLF
+             "<address>%s</address>" CRLF
+             "</body></html>" CRLF,
+             text, maintainer);
+
+  /* It's always a good idea to force the connection to close after an
+   * error. This is particularly important with monolith applications
+   * after they have thrown an exception.
+   */
+  /* return close; */
+  return 1;
+}
+
+int
+file_not_found_error (process_rq p)
+{
+  http_response http_response;
+  int close;
+  const char *maintainer;
+
+  maintainer = cfg_get_string (p->host, p->alias,
+                              "maintainer", "(no maintainer)");
+
+  http_response = new_http_response (p->pool, p->http_request, p->io,
+                                    404, "File or directory not found");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/html",
+                             NO_CACHE_HEADERS,
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (p->http_request)) return close;
+
+  io_fprintf (p->io,
+             "<html><head><title>File or directory not found</title></head>" CRLF
+             "<body bgcolor=\"#ffffff\">" CRLF
+             "<h1>404 File or directory not found</h1>" CRLF
+             "The file you requested was not found on this server." CRLF
+             "<hr>" CRLF
+             "<address>%s</address>" CRLF
+             "</body></html>" CRLF,
+             maintainer);
+
+  return close;
+}
+
+int
+moved_permanently (process_rq p, const char *location)
+{
+  http_response http_response;
+  int close;
+
+  http_response = new_http_response (p->pool, p->http_request, p->io,
+                                    301, "Moved permanently");
+  http_response_send_headers (http_response,
+                             /* Content length. */
+                             "Content-Length", "0",
+                             /* Location. */
+                             "Location", location,
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (p->http_request)) return close;
+
+  return close;
+}
diff --git a/errors.h b/errors.h
new file mode 100644 (file)
index 0000000..069cc8b
--- /dev/null
+++ b/errors.h
@@ -0,0 +1,36 @@
+/* Deliver errors back to the user.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: errors.h,v 1.1 2001/03/24 17:26:29 rich Exp $
+ */
+
+#ifndef ERRORS_H
+#define ERRORS_H
+
+#include "config.h"
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+extern int bad_request_error (process_rq p, const char *text);
+extern int file_not_found_error (process_rq p);
+extern int moved_permanently (process_rq p, const char *location);
+
+#endif /* ERRORS_H */
diff --git a/examples/.cvsignore b/examples/.cvsignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/hello.c b/examples/hello.c
new file mode 100644 (file)
index 0000000..52ffbea
--- /dev/null
@@ -0,0 +1,48 @@
+/* Simplest possible example of a shared object script.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: hello.c,v 1.3 2002/12/01 16:16:04 rich Exp $
+ */
+
+#include "rws_request.h"
+
+int
+handle_request (rws_request rq)
+{
+  http_request http_request = rws_request_http_request (rq);
+  io_handle io = rws_request_io (rq);
+
+  int close;
+  http_response http_response;
+
+  /* Begin response. */
+  http_response = new_http_response (pth_get_pool (current_pth),
+                                    http_request, io,
+                                    200, "OK");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/plain",
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (http_request)) return close;
+
+  io_fprintf (io, "hello, world!");
+
+  return close;
+}
diff --git a/examples/show_params.c b/examples/show_params.c
new file mode 100644 (file)
index 0000000..418e6ce
--- /dev/null
@@ -0,0 +1,87 @@
+/* More complex example shared object script showing parameter parsing.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: show_params.c,v 1.4 2002/12/01 16:16:04 rich Exp $
+ */
+
+#include <pool.h>
+#include <vector.h>
+#include <pthr_cgi.h>
+
+#include "rws_request.h"
+
+int
+handle_request (rws_request rq)
+{
+  pool pool = pth_get_pool (current_pth);
+  http_request http_request = rws_request_http_request (rq);
+  io_handle io = rws_request_io (rq);
+
+  cgi cgi;
+  int close, i;
+  http_response http_response;
+  vector headers, params;
+  struct http_header header;
+  const char *name, *value;
+
+  /* Parse CGI parameters. */
+  cgi = new_cgi (pool, http_request, io);
+
+  /* Begin response. */
+  http_response = new_http_response (pool, http_request, io,
+                                    200, "OK");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/plain",
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (http_request)) return close;
+
+  io_fprintf (io, "This is the show_params shared object script.\r\n\r\n");
+  io_fprintf (io, "Your browser sent the following headers:\r\n\r\n");
+
+  headers = http_request_get_headers (http_request);
+  for (i = 0; i < vector_size (headers); ++i)
+    {
+      vector_get (headers, i, header);
+      io_fprintf (io, "\t%s: %s\r\n", header.key, header.value);
+    }
+
+  io_fprintf (io, "----- end of headers -----\r\n");
+
+  io_fprintf (io, "The URL was: %s\r\n",
+             http_request_get_url (http_request));
+  io_fprintf (io, "The path component was: %s\r\n",
+             http_request_path (http_request));
+  io_fprintf (io, "The query string was: %s\r\n",
+             http_request_query_string (http_request));
+  io_fprintf (io, "The query arguments were:\r\n");
+
+  params = cgi_params (cgi);
+  for (i = 0; i < vector_size (params); ++i)
+    {
+      vector_get (params, i, name);
+      value = cgi_param (cgi, name);
+      io_fprintf (io, "\t%s=%s\r\n", name, value);
+    }
+
+  io_fprintf (io, "----- end of parameters -----\r\n");
+
+  return close;
+}
diff --git a/exec.c b/exec.c
new file mode 100644 (file)
index 0000000..0c25c50
--- /dev/null
+++ b/exec.c
@@ -0,0 +1,237 @@
+/* CGI scripts
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: exec.c,v 1.6 2003/02/05 23:02:51 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_http.h>
+
+#include "process_rq.h"
+#include "errors.h"
+#include "exec.h"
+
+#ifndef HAVE_SETENV
+#ifdef HAVE_PUTENV
+/* setenv implementation for architectures which only have putenv. The
+ * apparent memory leak in this code does not actually matter because
+ * this is only called in the child process just before invoking exec.
+ */
+static inline void
+setenv (const char *name, const char *value, int overwrite)
+{
+  int len = strlen (name) + strlen (value) + 2;
+  char *str = malloc (len);
+
+  snprintf (str, len, "%s=%s", name, value);
+  putenv (str);
+}
+#else
+#error "no setenv or putenv in your libc"
+#endif
+#endif
+
+/* Note: For performance reasons and because I wanted to simplify the
+ * server, this code only handles NPH scripts.
+ *
+ * You must ensure that your CGI program can generate full NPH headers.
+ * This is generally quite simple. For example, with Perl's CGI.pm,
+ * do this:
+ *
+ * use CGI qw(:standard -nph);
+ */
+int
+exec_file (process_rq p)
+{
+  int pid, to_script[2], from_script[2], len, i;
+  io_handle to_io, from_io;
+  const char *content_length;
+
+  content_length
+    = http_request_get_header (p->http_request, "Content-Length");
+
+  /* Set up two pipes between us and the script, one for reading, one
+   * for writing.
+   */
+  if (pipe (to_script) == -1 || pipe (from_script) == -1)
+    return bad_request_error (p, "cannot create pipes to script");
+
+  /* Fork off a process to run the request. */
+  pid = fork ();
+  if (pid == -1)
+    {
+      close (to_script[0]); close (to_script[1]);
+      close (from_script[0]); close (from_script[1]);
+      return bad_request_error (p, "cannot fork");
+    }
+
+  if (pid == 0)                        /* Child process -- runs the script. */
+    {
+      int j;
+      const char *query_string, *content_type;
+      int major, minor, method;
+      char *header, *env;
+      vector headers;
+      struct sockaddr_in addr;
+      socklen_t addrlen;
+
+      /* XXX Currently all fds will be correctly closed over the exec
+       * except the accepting socket. This requires a small change to
+       * pthrlib to fix. Ignore it for now.
+       */
+      /* Set up fds 0 and 1 to point to the pipes connecting us to
+       * the main rwsd process. Fd 2 points to the error log, so just
+       * leave that one alone.
+       */
+      close (to_script[1]);
+      if (to_script[0] != 0)
+       {
+         dup2 (to_script[0], 0);
+         close (to_script[0]);
+       }
+      close (from_script[0]);
+      if (from_script[1] != 1)
+       {
+         dup2 (from_script[1], 1);
+         close (from_script[1]);
+       }
+
+      /* Query string environment variable. */
+      query_string = http_request_query_string (p->http_request);
+      if (query_string)
+       setenv ("QUERY_STRING", query_string, 1);
+
+      /* Set server protocol. */
+      http_request_version (p->http_request, &major, &minor);
+      setenv ("SERVER_PROTOCOL",
+             psprintf (p->pool, "HTTP/%d.%d", major, minor), 1);
+
+      /* Set request method. */
+      method = http_request_method (p->http_request);
+      setenv ("REQUEST_METHOD",
+             (method == HTTP_METHOD_GET ? "GET" :
+              (method == HTTP_METHOD_POST ? "POST" :
+               (method == HTTP_METHOD_HEAD ? "HEAD" :
+                "unknown"))), 1);
+
+      /* Content length, content type. */
+      if (content_length) setenv ("CONTENT_LENGTH", content_length, 1);
+      content_type
+       = http_request_get_header (p->http_request, "Content-Type");
+      if (content_type) setenv ("CONTENT_TYPE", content_type, 1);
+
+      /* Get peer address. */
+      addrlen = sizeof addr;
+      getpeername (p->sock, (struct sockaddr *) &addr, &addrlen);
+
+      /* General CGI environment variables. */
+      setenv ("SERVER_SOFTWARE", http_get_servername (), 1);
+      setenv ("SERVER_NAME", p->host_header, 1);
+      setenv ("GATEWAY_INTERFACE", "CGI/1.1", 1);
+      /*setenv ("SERVER_PORT", pitoa (p->pool, port), 1); XXX */
+      setenv ("PATH_INFO", p->canonical_path, 1);
+      setenv ("PATH_TRANSLATED", p->file_path, 1);
+      setenv ("SCRIPT_NAME", p->canonical_path, 1);
+      setenv ("REMOTE_ADDR", inet_ntoa (addr.sin_addr), 1);
+
+      /* Convert any other headers into HTTP_* environment variables. */
+      headers = http_request_get_headers (p->http_request);
+      for (i = 0; i < vector_size (headers); ++i)
+       {
+         vector_get (headers, i, header);
+         env = pstrdup (p->pool, header);
+         pstrupr (env);
+         for (j = 0; j < strlen (env); ++j)
+           if (env[j] == '-') env[j] = '_';
+         env = psprintf (p->pool, "HTTP_%s", env);
+         setenv (env, http_request_get_header (p->http_request, header), 1);
+       }
+
+      /* Run the CGI script. */
+      execl (p->file_path, p->file_path, 0);
+
+      perror ("exec");
+      exit (1);
+    }
+
+  /* Close the unneeded halves of each pipe. */
+  close (to_script[0]);
+  close (from_script[1]);
+
+  /* Set the ends of the pipes to non-blocking mode. */
+  if (fcntl (to_script[1], F_SETFL, O_NONBLOCK) < 0 ||
+      fcntl (from_script[0], F_SETFL, O_NONBLOCK) < 0)
+    { perror ("fcntl"); exit (1); }
+
+  /* Associate the ends of the pipe with IO handles. This will also
+   * close them automagically in case of error.
+   */
+  to_io = io_fdopen (to_script[1]);
+  from_io = io_fdopen (from_script[0]);
+  if (to_io == 0 || from_io == 0)
+    return bad_request_error (p, "error associating pipes with IO handles");
+
+  /* If this is a POST method, copy the required amount of data
+   * to the CGI script.
+   */
+  if (http_request_method (p->http_request) == HTTP_METHOD_POST)
+    {
+      /* How much to copy? Is content-length set? */
+      len = -1;
+      if (content_length) sscanf (content_length, "%d", &len);
+
+      /* Copy the data to the script. */
+      io_copy (p->io, to_io, len);
+    }
+
+  /* Read data back from the script and out to the client. */
+  io_copy (from_io, p->io, -1);
+
+  /* Force us to close the connection back to the client now. */
+  return 1;
+}
diff --git a/exec.h b/exec.h
new file mode 100644 (file)
index 0000000..56acb3c
--- /dev/null
+++ b/exec.h
@@ -0,0 +1,30 @@
+/* CGI scripts
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: exec.h,v 1.1 2001/03/26 15:39:48 rich Exp $
+ */
+
+#ifndef EXEC_H
+#define EXEC_H
+
+#include "config.h"
+
+#include "process_rq.h"
+
+extern int exec_file (process_rq p);
+
+#endif /* EXEC_H */
diff --git a/exec_so.c b/exec_so.c
new file mode 100644 (file)
index 0000000..e4c93ba
--- /dev/null
+++ b/exec_so.c
@@ -0,0 +1,195 @@
+/* Shared object scripts.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: exec_so.c,v 1.10 2003/01/31 14:36:22 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+#include <hash.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_iolib.h>
+#include <pthr_http.h>
+#include <pthr_cgi.h>
+
+#include "rws_request.h"
+#include "process_rq.h"
+#include "errors.h"
+#include "cfg.h"
+#include "exec_so.h"
+
+/* XXX make+ configure should figure this out. */
+#ifndef __OpenBSD__
+#define HANDLE_REQUEST_SYM "handle_request"
+#else
+#define HANDLE_REQUEST_SYM "_handle_request"
+#endif
+
+static shash cache = 0;
+struct shared_object
+{
+  void *dl_handle;             /* Handle returned by dlopen(3) */
+                               /* Pointer to 'handle_request' fn. */
+  int (*handle_request) (rws_request rq);
+  time_t mtime;                        /* Modification time of this file at load. */
+  int use_count;               /* Number of current users. */
+};
+
+/* This structure is used when jumping into the handle_request function,
+ * so we can catch errors and return values from this function.
+ */
+struct fn_result
+{
+  struct shared_object *so;    /* Parameter to the call. */
+  rws_request rq;              /* Parameter to the call. */
+  int close;                   /* Return value from the call. */
+};
+
+static void call_handle_request (void *data);
+static int do_error (process_rq p, const char *msg);
+
+void
+exec_so_init ()
+{
+  cache = new_shash (global_pool, struct shared_object *);
+}
+
+int
+exec_so_file (process_rq p)
+{
+  struct shared_object *so;
+  const char *error;
+  rws_request rq;
+  struct fn_result fn_result;
+
+  /* Check our cache of currently loaded .so files to see if this one
+   * has already been loaded.
+   */
+  if (!shash_get (cache, p->file_path, so))
+    {
+      /* No: Need to dlopen this file. */
+      so = pmalloc (global_pool, sizeof *so);
+
+    reload:
+      so->dl_handle = dlopen (p->file_path,
+#ifndef __OpenBSD__
+                             RTLD_NOW
+#else
+                             O_RDWR
+#endif
+                             );
+      if (so->dl_handle == 0)
+       {
+         fprintf (stderr, "%s\n", dlerror ());
+         return bad_request_error (p,
+                                   "failed to load shared object file");
+       }
+
+      /* Check it contains the 'handle_request' function. */
+      so->handle_request = dlsym (so->dl_handle, HANDLE_REQUEST_SYM);
+      if ((error = dlerror ()) != 0)
+       {
+         fprintf (stderr, "%s\n", error);
+         dlclose (so->dl_handle);
+         return bad_request_error (p,
+                                   "shared object file does not contain "
+                                   "handle_request function");
+       }
+
+      so->mtime = p->statbuf.st_mtime;
+      so->use_count = 0;
+
+      /* Add it to the cache. */
+      shash_insert (cache, p->file_path, so);
+    }
+
+  /* Check the modification time. We may need to reload this script if it's
+   * changed on disk. But if there are other current users, then we can't
+   * safely unload the library, so don't try (a later request will reload
+   * it when it's quiet anyway).
+   */
+  if (p->statbuf.st_mtime > so->mtime && so->use_count == 0)
+    {
+      shash_erase (cache, p->file_path);
+      dlclose (so->dl_handle);
+      goto reload;
+    }
+
+  /* OK, we're now about to use this file. */
+  so->use_count++;
+
+  /* Generate the rws_request object. */
+  rq = new_rws_request (p->pool,
+                       p->http_request,
+                       p->io,
+                       p->host_header,
+                       p->canonical_path,
+                       p->file_path,
+                       p->host,
+                       p->alias,
+                       cfg_get_string,
+                       cfg_get_int,
+                       cfg_get_bool);
+
+  /* Call the 'handle_request' function.
+   * XXX We could pass environment parameters here, but this requires
+   * a change to pthrlib to allow environment variables to be handled
+   * across context switches.
+   */
+  fn_result.so = so;
+  fn_result.rq = rq;
+  error = pth_catch (call_handle_request, &fn_result);
+
+  /* Finished using the file. */
+  so->use_count--;
+
+  if (error)
+    return do_error (p, error);
+
+  return fn_result.close;
+}
+
+static void
+call_handle_request (void *data)
+{
+  struct fn_result *fn_result = (struct fn_result *) data;
+
+  fn_result->close = fn_result->so->handle_request (fn_result->rq);
+}
+
+static int
+do_error (process_rq p, const char *msg)
+{
+  /* XXX In the future, we'd like to extend this function so that
+   * other non-500 errors can be displayed (particularly for 404
+   * Page Not Found errors).
+   */
+  return bad_request_error (p, msg);
+}
diff --git a/exec_so.h b/exec_so.h
new file mode 100644 (file)
index 0000000..3c57633
--- /dev/null
+++ b/exec_so.h
@@ -0,0 +1,32 @@
+/* Shared object scripts.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: exec_so.h,v 1.1 2002/08/21 13:28:31 rich Exp $
+ */
+
+#ifndef EXEC_SO_H
+#define EXEC_SO_H
+
+#include "config.h"
+
+#include "process_rq.h"
+
+extern void exec_so_init (void);
+
+extern int exec_so_file (process_rq p);
+
+#endif /* EXEC_SO_H */
diff --git a/file.c b/file.c
new file mode 100644 (file)
index 0000000..fdcc0e0
--- /dev/null
+++ b/file.c
@@ -0,0 +1,425 @@
+/* File serving.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: file.c,v 1.15 2003/02/05 23:02:51 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <hash.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+#include "process_rq.h"
+#include "mime_types.h"
+#include "errors.h"
+#include "exec.h"
+#include "exec_so.h"
+#include "cfg.h"
+#include "re.h"
+#include "file.h"
+
+/* XXX This code doesn't deal with the "If-Modified-Since" header
+ * correctly. It is important to get this fixed in the near future.
+ *
+ * Similarly the code should send "Last-Modified" headers.
+ */
+
+struct hash_key
+{
+  dev_t st_dev;
+  ino_t st_ino;
+};
+
+struct file_info
+{
+  struct pool *pool;
+  struct stat statbuf;
+  void *addr;
+};
+
+static pool file_pool = 0;
+
+#define MAX_MMAP_SIZE (10 * 1024 * 1024)
+#define MAX_ENTRIES   100
+#define MAX_SIZE      (100 * 1024 * 1024)
+
+static int total_size = 0;
+static int nr_entries = 0;
+
+/* This is the list of files (of type struct file_info) which are
+ * currently memory mapped. It is stored in no particular order and
+ * may contain blank entries (where a file has been unmapped for example).
+ */
+static vector file_list = 0;
+
+/* This is a list of integers indexing into file_list, stored in LRU
+ * order. Element 0 is the oldest, and larger numbered elements are
+ * younger. New entries are pushed onto the back of this list.
+ */
+static vector lru_list = 0;
+
+/* This hash of { device, inode } -> integer maps unique stat information
+ * about files to their offset in the file_list array above.
+ */
+static hash file_hash = 0;
+
+static void invalidate_entry (void *);
+static int quickly_serve_it (process_rq p, const struct file_info *info, const char *mime_type);
+static int slowly_serve_it (process_rq p, int fd, const char *mime_type);
+static void expires_header (process_rq p, http_response http_response);
+
+/* Initialize structures. */
+void
+file_init ()
+{
+  file_pool = new_subpool (global_pool);
+  file_list = new_vector (file_pool, struct file_info);
+  lru_list = new_vector (file_pool, int);
+  file_hash = new_hash (file_pool, struct hash_key, int);
+}
+
+int
+file_serve (process_rq p)
+{
+  vector extv;
+  const char *mime_type = 0;
+  int offset, fd;
+  struct hash_key key;
+  struct file_info info;
+  void *m;
+
+  /* If this file is an executable .so file, and we are allowed to
+   * run .so files from this directory, then it's a shared object
+   * script. Hand it off to exec_so.c to run.
+   */
+  if (cfg_get_bool (p->host, p->alias, "exec so", 0) &&
+      prematch (p->pool, p->remainder, re_so, 0) &&
+      (p->statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+    return exec_so_file (p);
+
+  /* If this file is executable, and we are allowed to run files from
+   * this directory, then it's a CGI script. Hand it off to exec.c to
+   * run.
+   */
+  if (cfg_get_bool (p->host, p->alias, "exec", 0) &&
+      (p->statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+    return exec_file (p);
+
+  /* Are we permitted to show files in this directory? */
+  if (!cfg_get_bool (p->host, p->alias, "show", 0))
+    return bad_request_error (p,
+                             "you are not permitted to view files "
+                             "in this directory");
+
+  /* Map the file's name to its MIME type. */
+  if ((extv = prematch (p->pool, p->remainder, re_ext, 0)) != 0)
+    {
+      char *ext;
+
+      vector_get (extv, 1, ext);
+      mime_type = mime_types_get_type (ext);
+    }
+  if (!mime_type) mime_type = "application/octet-stream"; /* Default. */
+
+  /* Check the hash to see if we know anything about this file already. */
+  memset (&key, 0, sizeof key);
+  key.st_dev = p->statbuf.st_dev;
+  key.st_ino = p->statbuf.st_ino;
+  if (hash_get (file_hash, key, offset))
+    {
+      /* Cache hit ... */
+      vector_get (file_list, offset, info);
+
+      /* ... but has the file on disk changed since we mapped it? */
+      if (info.statbuf.st_mtime == p->statbuf.st_mtime)
+       return quickly_serve_it (p, &info, mime_type);
+      else
+       /* File has changed: invalidate the cache entry. */
+       delete_pool (info.pool);
+    }
+
+  /* Try to open the file. */
+  fd = open (p->file_path, O_RDONLY);
+  if (fd < 0) return file_not_found_error (p);
+
+  /* Set the FD_CLOEXEC flag so that when we fork off CGI scripts, they
+   * won't inherit the file descriptor.
+   */
+  if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0) { perror ("fcntl"); exit (1); }
+
+  /* If the file's too large, don't mmap it. */
+  if (p->statbuf.st_size > MAX_MMAP_SIZE)
+    return slowly_serve_it (p, fd, mime_type);
+
+  /* Map the file into memory. */
+  m = mmap (0, p->statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+  if (m == MAP_FAILED)
+    return slowly_serve_it (p, fd, mime_type);
+
+  close (fd);
+
+  /* Evict some entries from the cache to make enough room. */
+  while (nr_entries >= MAX_ENTRIES || total_size >= MAX_SIZE)
+    {
+      vector_get (lru_list, 0, offset);
+      vector_get (file_list, offset, info);
+      delete_pool (info.pool);
+    }
+
+  /* Add the entry to the cache. */
+  info.pool = new_subpool (file_pool);
+  info.statbuf = p->statbuf;
+  info.addr = m;
+  nr_entries++;
+  total_size += p->statbuf.st_size;
+
+  for (offset = 0; offset < vector_size (file_list); ++offset)
+    {
+      struct file_info entry;
+
+      vector_get (file_list, offset, entry);
+      if (entry.pool == 0)
+       {
+         vector_replace (file_list, offset, info);
+         goto added_it;
+       }
+    }
+
+  vector_push_back (file_list, info);
+
+ added_it:
+  hash_insert (file_hash, key, offset);
+  vector_push_back (lru_list, offset);
+
+  pool_register_cleanup_fn (info.pool, invalidate_entry, (void *) offset);
+
+  /* Serve it from memory. */
+  return quickly_serve_it (p, &info, mime_type);
+}
+
+static int
+quickly_serve_it (process_rq p, const struct file_info *info,
+                 const char *mime_type)
+{
+  http_response http_response;
+  int cl;
+
+  /* Not changed, so it's a real cache hit. */
+  http_response = new_http_response (p->pool, p->http_request, p->io,
+                                    200, "OK");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", mime_type,
+                             /* Content length. */
+                             "Content-Length", pitoa (p->pool,
+                                                      info->statbuf.st_size),
+                             /* End of headers. */
+                             NULL);
+  expires_header (p, http_response);
+  cl = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (p->http_request)) return cl;
+
+  io_fwrite (info->addr, info->statbuf.st_size, 1, p->io);
+
+  return cl;
+}
+
+static int
+slowly_serve_it (process_rq p, int fd, const char *mime_type)
+{
+  http_response http_response;
+  const int n = 4096;
+  char *buffer = alloca (n);
+  int r, cl;
+
+  /* Cannot memory map this file. Instead fall back to just reading
+   * it and sending it back through the socket.
+   */
+  http_response = new_http_response (p->pool, p->http_request, p->io,
+                                    200, "OK");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", mime_type,
+                             /* Content length. */
+                             "Content-Length", pitoa (p->pool,
+                                                      p->statbuf.st_size),
+                             /* End of headers. */
+                             NULL);
+  expires_header (p, http_response);
+  cl = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (p->http_request)) return cl;
+
+  while ((r = read (fd, buffer, n)) > 0)
+    {
+      io_fwrite (buffer, r, 1, p->io);
+    }
+
+  if (r < 0)
+    {
+      perror ("read");
+    }
+
+  close (fd);
+
+  return cl;
+}
+
+/* Send the Expires header, if configured. */
+static void
+expires_header (process_rq p, http_response http_response)
+{
+  const char *expires;
+  char pm, unit;
+  int length;
+
+  expires = cfg_get_string (p->host, p->alias, "expires", 0);
+  if (!expires) return;
+
+  /* Parse the configuration string. */
+  if (sscanf (expires, "%c%d%c", &pm, &length, &unit) == 3 &&
+      (pm == '+' || pm == '-') &&
+      length > 0 &&
+      (unit == 's' || unit == 'm' || unit == 'h' ||
+       unit == 'd' || unit == 'y'))
+    {
+      time_t t;
+      struct tm *tm;
+      char header[64];
+
+      time (&t);
+
+      if (pm == '+')
+       {
+         switch (unit)
+           {
+           case 's': t += length; break;
+           case 'm': t += length * 60; break;
+           case 'h': t += length * (60 * 60); break;
+           case 'd': t += length * (60 * 60 * 24); break;
+           case 'y': t += length * (60 * 60 * 24 * 366); break;
+           }
+       }
+      else
+       {
+         switch (unit)
+           {
+           case 's': t -= length; break;
+           case 'm': t -= length * 60; break;
+           case 'h': t -= length * (60 * 60); break;
+           case 'd': t -= length * (60 * 60 * 24); break;
+           case 'y': t -= length * (60 * 60 * 24 * 366); break;
+           }
+       }
+
+      tm = gmtime (&t);
+      strftime (header, sizeof header, "%a, %d %b %Y %H:%M:%S GMT", tm);
+
+      http_response_send_header (http_response, "Expires", header);
+    }
+  else
+    {
+      fprintf (stderr, "file.c: expires_header: cannot parse '%s'\n",
+              expires);
+    }
+}
+
+static void
+invalidate_entry (void *offset_ptr)
+{
+  int offset = (int) offset_ptr;
+  int i, j;
+  struct file_info info;
+  struct hash_key key;
+
+  /* Pull the invalidated entry out of the file_list. */
+  vector_get (file_list, offset, info);
+
+  /* Remove from the file_hash. */
+  memset (&key, 0, sizeof key);
+  key.st_dev = info.statbuf.st_dev;
+  key.st_ino = info.statbuf.st_ino;
+  if (!hash_erase (file_hash, key)) abort ();
+
+  /* Remove from the lru_list. */
+  for (i = 0; i < vector_size (lru_list); ++i)
+    {
+      vector_get (lru_list, i, j);
+
+      if (j == offset)
+       {
+         vector_erase (lru_list, i);
+         goto found_it;
+       }
+    }
+  abort ();
+
+ found_it:
+  /* Unmap the memory. */
+  munmap (info.addr, info.statbuf.st_size);
+
+  /* Invalidate this entry in the file_list. */
+  info.pool = 0;
+  vector_replace (file_list, offset, info);
+
+  /* Update counters. */
+  nr_entries--;
+  total_size -= info.statbuf.st_size;
+}
diff --git a/file.h b/file.h
new file mode 100644 (file)
index 0000000..4f08000
--- /dev/null
+++ b/file.h
@@ -0,0 +1,32 @@
+/* File serving.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: file.h,v 1.4 2001/03/26 15:39:48 rich Exp $
+ */
+
+#ifndef FILE_H
+#define FILE_H
+
+#include "config.h"
+
+#include "process_rq.h"
+
+extern void file_init (void);
+
+extern int file_serve (process_rq p);
+
+#endif /* FILE_H */
diff --git a/html/index.html b/html/index.html
new file mode 100644 (file)
index 0000000..ac10674
--- /dev/null
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><!-- -*- html -*- -->
+<html>
+  <head>
+    <title>RWS introductory page</title>
+    <style type="text/css">
+    body {
+       background-color: #ffffff;
+    }
+    h1 {
+       font-size: 14pt;
+       font-weight: bold;
+       text-align: left;
+       color: #444400;
+    }
+    h2 {
+       font-size: 12pt;
+       font-weight: bold;
+       text-align: left;
+       color: #444400;
+    }
+    code {
+       color: green;
+       font-weight: bold;
+    }
+    </style>
+  </head>
+  <body bgcolor="#ffffff">
+
+    <h1>Welcome to RWS</h1>
+    <p>
+    Congratulations! RWS is now installed and running on your system.
+    </p>
+
+    <h2>Shared object script demos</h2>
+
+    <p>
+    Unless you have changed the configuration file, the following links
+    should run the shared object script demos from the <code>examples/</code>
+    directory in the source distribution:
+    </p>
+
+    <ul>
+    <li> <a href="/so-bin/hello.so">/so-bin/hello.so</a> </li>
+    <li> <a href="/so-bin/show_params.so?abc=1&amp;def=2">/so-bin/show_params.so?abc=1&amp;def=2</a> </li>
+    </ul>
+
+    <p>
+    These files are in <code>$prefix/share/rws/so-bin</code>.
+    </p>
+
+    <h2>Monolith demos</h2>
+
+    <p>
+    If you have installed
+    <a href="http://www.annexia.org/freeware/monolith/">monolith</a>,
+    then the following links should run the monolith demos:
+    </p>
+
+    <ul>
+    <li> <a href="/so-bin/01_label_and_button.so">/so-bin/01_label_and_button.so</a> </li>
+    <li> <a href="/so-bin/02_toy_calculator.so">/so-bin/02_toy_calculator.so</a> </li>
+    <li> <a href="/so-bin/03_many_toy_calculators.so">/so-bin/03_many_toy_calculators.so</a> </li>
+    <li> <a href="/so-bin/04_animal_vegetable_mineral.so">/so-bin/04_animal_vegetable_mineral.so</a> </li>
+    <li> <a href="/so-bin/05_popup_windows_and_frames.so">/so-bin/05_popup_windows_and_frames.so</a> </li>
+    <li> <a href="/so-bin/06_big_form.so">/so-bin/06_big_form.so</a> </li>
+    <li> <a href="/so-bin/07_toggle_buttons.so">/so-bin/07_toggle_buttons.so</a> </li>
+    <li> <a href="/so-bin/08_menus.so">/so-bin/08_menus.so</a> </li>
+    </ul>
+
+    <p>
+    These files are in <code>$prefix/share/rws/so-bin</code>.
+    </p>
+
+    <hr>
+
+    <p>
+    The current file is <code>$prefix/share/rws/html/index.html</code>, ie.
+    probably one of the following:
+    </p>
+    <ul>
+    <li> <code>/usr/local/share/rws/html/index.html</code> </li>
+    <li> <code>/usr/share/rws/html/index.html</code> </li>
+    </ul>
+  </body>
+</html>
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..27db3c8
--- /dev/null
+++ b/main.c
@@ -0,0 +1,266 @@
+/* RWS main program.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: main.c,v 1.16 2002/11/27 18:45:23 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include <pthr_http.h>
+#include <pthr_server.h>
+
+#include "cfg.h"
+#include "file.h"
+#include "exec_so.h"
+#include "mime_types.h"
+#include "process_rq.h"
+#include "rewrite.h"
+#include "re.h"
+
+static void startup (int argc, char *argv[]);
+static void start_thread (int sock, void *data);
+static void catch_reload_signal (int sig);
+static void catch_quit_signal (int sig);
+static void catch_child_signal (int sig);
+static void reload_config (void);
+
+const char *config_path = "/etc/rws";
+FILE *access_log;
+
+const pcre *re_alias_start,
+  *re_alias_end,
+  *re_begin,
+  *re_conf_line,
+  *re_ext,
+  *re_icon,
+  *re_so,
+  *re_ws,
+  *re_comma;
+
+int
+main (int argc, char *argv[])
+{
+  const char *user, *name, *stderr_file;
+  int c, stack_size;
+  struct sigaction sa;
+  int foreground = 0;
+  int debug = 0;
+
+  /* Initialise various shared regular expressions. */
+  re_alias_start = precomp (global_pool, "^alias[[:space:]]+(.*)$", 0);
+  re_alias_end = precomp (global_pool, "^end[[:space:]]+alias$", 0);
+  re_begin = precomp (global_pool, "^begin[[:space:]]+(.*):?[[:space:]]*$", 0);
+  re_conf_line = precomp (global_pool, "^(.*):[[:space:]]*(.*)?$", 0);
+  re_ext = precomp (global_pool, "\\.([^.]+)$", 0);
+  re_icon = precomp (global_pool,
+    "([^[:space:]]+)[[:space:]]+([0-9]+)x([0-9]+)[[:space:]]+\"(.*)\"", 0);
+  re_so = precomp (global_pool, "\\.so$", 0);
+  re_ws = precomp (global_pool, "[ \t]+", 0);
+  re_comma = precomp (global_pool, "[,;]+", 0);
+
+  while ((c = getopt (argc, argv, "C:p:a:fd")) != -1)
+    {
+      switch (c)
+       {
+       case 'p':
+         /* ignore */
+         break;
+        case 'a':
+          /* ignore */
+          break;
+
+       case 'C':
+         config_path = optarg;
+         break;
+
+        case 'f':
+          foreground = 1;
+          break;
+        case 'd':
+          debug = 1;
+          break;
+
+       default:
+         fprintf (stderr, "usage: rws [-d] [-f] [-a address] [-p port] [-C configpath]\n");
+         exit (1);
+       }
+    }
+
+  /* Read configuration file. Do this early so we have configuration
+   * data available for other initializations.
+   */
+  reload_config ();
+
+  /* Change the thread stack size? */
+  stack_size = cfg_get_int (0, 0, "stack size", 0);
+  if (stack_size)
+    pseudothread_set_stack_size (stack_size * 1024);
+
+  /* Initialize the file cache. */
+  file_init ();
+
+  /* Initialize the shared object script cache. */
+  exec_so_init ();
+
+  /* Intercept signals. */
+  memset (&sa, 0, sizeof sa);
+  sa.sa_handler = catch_reload_signal;
+  sa.sa_flags = SA_RESTART;
+  sigaction (SIGHUP, &sa, 0);
+
+  sa.sa_handler = catch_quit_signal;
+  sigaction (SIGINT, &sa, 0);
+  sigaction (SIGQUIT, &sa, 0);
+  sigaction (SIGTERM, &sa, 0);
+
+  sa.sa_handler = catch_child_signal;
+  sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
+  sigaction (SIGCHLD, &sa, 0);
+
+  /* ... but ignore SIGPIPE errors. */
+  sa.sa_handler = SIG_IGN;
+  sa.sa_flags = SA_RESTART;
+  sigaction (SIGPIPE, &sa, 0);
+
+  /* Change user on startup. */
+  user = cfg_get_string (0, 0, "user", "nobody");
+  pthr_server_username (user);
+
+  if (foreground)
+    {
+      pthr_server_disable_chdir ();
+      pthr_server_disable_fork ();
+    }
+
+  if (debug)
+    pthr_server_disable_close ();
+  else
+    {
+      /* Errors to error log file. */
+      stderr_file = cfg_get_string (0, 0, "error log", "/tmp/error_log");
+      pthr_server_stderr_file (stderr_file);
+
+      /* Enable stack trace on SIGSEGV. */
+      pthr_server_enable_stack_trace_on_segv ();
+    }
+
+  /* Set server name. */
+  name = psprintf (global_pool,
+                  PACKAGE "/" VERSION " %s",
+                  http_get_servername ());
+  http_set_servername (name);
+
+  /* Extra startup. */
+  pthr_server_startup_fn (startup);
+
+  /* Start up the server. */
+  pthr_server_main_loop (argc, argv, start_thread);
+
+  exit (0);
+}
+
+static void
+startup (int argc, char *argv[])
+{
+  FILE *access_log;
+
+  /* Open the access log. */
+  access_log
+    = fopen (cfg_get_string (0, 0, "access log", "/tmp/access_log"), "a");
+  if (access_log == 0)
+    {
+      perror ("open: access log");
+      exit (1);
+    }
+  if (fcntl (fileno (access_log), F_SETFD, FD_CLOEXEC) < 0)
+    { perror ("fcntl"); exit (1); }
+
+  http_set_log_file (access_log);
+}
+
+static void
+start_thread (int sock, void *data)
+{
+  (void) new_process_rq (sock);
+}
+
+static void
+catch_reload_signal (int sig)
+{
+  reload_config ();
+}
+
+static void
+catch_quit_signal (int sig)
+{
+  /* Exit gracefully (how!?!) XXX */
+  exit (0);
+}
+
+static void
+catch_child_signal (int sig)
+{
+  /* Clean up the child process. */
+  wait (0);
+}
+
+static void
+reload_config ()
+{
+  /* Reread configuration file. */
+  cfg_reread_config (config_path);
+
+  /* Read /etc/mime.types file. */
+  mime_types_reread_config (cfg_get_string (0, 0, "mime types file",
+                                           "/etc/mime.types"));
+
+  /* Reset rewrite rules. */
+  rewrite_reset_rules ();
+}
diff --git a/mime_types.c b/mime_types.c
new file mode 100644 (file)
index 0000000..91a08e2
--- /dev/null
@@ -0,0 +1,92 @@
+/* MIME types.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: mime_types.c,v 1.3 2002/10/06 11:57:22 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <pool.h>
+#include <hash.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include "re.h"
+#include "mime_types.h"
+
+static pool mt_pool = 0;
+static sash mt_map = 0;
+
+void
+mime_types_reread_config (const char *path)
+{
+  FILE *fp;
+  char *line = 0;
+  pool tmp;
+  vector v;
+  int i;
+  char *mt, *ext;
+
+  if (mt_pool) delete_pool (mt_pool);
+  mt_pool = new_subpool (global_pool);
+
+  mt_map = new_sash (mt_pool);
+
+  tmp = new_subpool (mt_pool);
+
+  /* Read the /etc/mime.types file. */
+  fp = fopen (path, "r");
+  if (fp == 0) { perror (path); exit (1); }
+
+  while ((line = pgetlinec (tmp, fp, line)) != 0)
+    {
+      v = pstrresplit (tmp, line, re_ws);
+
+      switch (vector_size (v))
+       {
+       case 0:
+         abort ();
+
+       case 1:
+         break;
+
+       default:
+         vector_get (v, 0, mt);
+         for (i = 1; i < vector_size (v); ++i)
+           {
+             vector_get (v, i, ext);
+             sash_insert (mt_map, ext, mt);
+           }
+         break;
+       }
+    }
+
+  fclose (fp);
+
+  delete_pool (tmp);
+}
+
+const char *
+mime_types_get_type (const char *ext)
+{
+  const char *mt = 0;
+
+  sash_get (mt_map, ext, mt);
+  return mt;
+}
diff --git a/mime_types.h b/mime_types.h
new file mode 100644 (file)
index 0000000..2fa6b62
--- /dev/null
@@ -0,0 +1,27 @@
+/* MIME types.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: mime_types.h,v 1.1 2001/03/23 17:51:33 rich Exp $
+ */
+
+#ifndef MIME_TYPES_H
+#define MIME_TYPES_H
+
+extern void mime_types_reread_config (const char *file);
+extern const char *mime_types_get_type (const char *ext);
+
+#endif /* MIME_TYPES_H */
diff --git a/process_rq.c b/process_rq.c
new file mode 100644 (file)
index 0000000..a04ae91
--- /dev/null
@@ -0,0 +1,338 @@
+/* Request processing thread.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: process_rq.c,v 1.25 2003/03/01 12:11:33 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <hash.h>
+#include <pstring.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_iolib.h>
+#include <pthr_http.h>
+#include <pthr_cgi.h>
+
+#include "cfg.h"
+#include "file.h"
+#include "dir.h"
+#include "errors.h"
+#include "rewrite.h"
+#include "process_rq.h"
+
+/* Maximum number of requests to service in one thread. This just acts
+ * as a check on the size of the thread pool, preventing it from growing
+ * out of control.
+ */
+#define MAX_REQUESTS_IN_THREAD 30
+
+#define PR_DEBUG 0             /* Set this to enable debugging. */
+
+static void run (void *vp);
+
+process_rq
+new_process_rq (int sock)
+{
+  pool pool;
+  process_rq p;
+
+  pool = new_pool ();
+  p = pmalloc (pool, sizeof *p);
+
+  memset (p, 0, sizeof *p);
+
+  /* Set the FD_CLOEXEC flag so that when we fork off CGI scripts, they
+   * won't inherit the socket.
+   */
+  if (fcntl (sock, F_SETFD, FD_CLOEXEC) < 0) { perror ("fcntl"); exit (1); }
+
+  p->sock = sock;
+  p->pth = new_pseudothread (pool, run, p, "process_rq");
+
+  pth_start (p->pth);
+
+  return p;
+}
+
+#define THREAD_NAME "rws process request thread"
+
+static void
+run (void *vp)
+{
+  process_rq p = (process_rq) vp;
+  int close = 0;
+  int request_timeout;
+  vector path_comps, v;
+  int i, is_dir, nr_requests = 1;
+  const char *location;
+
+  p->pool = pth_get_pool (p->pth);
+  p->io = io_fdopen (p->sock);
+
+  request_timeout = cfg_get_int (0, 0, "request timeout", 60);
+
+  /* Sit in a loop reading HTTP requests. */
+  while (!close && nr_requests <= MAX_REQUESTS_IN_THREAD)
+    {
+      /* Generic name for this thread. */
+      pth_set_name (THREAD_NAME " (idle)");
+
+      /* Count the number of requests serviced in this thread. */
+      nr_requests++;
+
+      /* Timeout requests. */
+      pth_timeout (request_timeout);
+
+      /* Read the request. */
+      p->http_request = new_http_request (p->pool, p->io);
+      if (p->http_request == 0) /* Normal end of file. */
+        break;
+
+      /* Reset timeout. */
+      pth_timeout (0);
+
+      /* Choose the correct configuration file based on the Host: header. */
+      p->host_header = http_request_get_header (p->http_request, "Host");
+      if (p->host_header)
+       {
+         p->host_header = pstrlwr (pstrdup (p->pool, p->host_header));
+
+         if ((p->host = cfg_get_host (p->host_header)) != 0)
+           goto found_host;
+         fprintf (stderr, "unknown virtual host: %s, trying default\n",
+                  p->host_header);
+       }
+
+      p->host_header = "default";
+      if ((p->host = cfg_get_host (p->host_header)) == 0)
+       {
+         close = bad_request_error (p, "no \"default\" virtual host!");
+         continue;
+       }
+    found_host:
+
+      /* Get the originally requested path. */
+      p->requested_path = http_request_path (p->http_request);
+      if (!p->requested_path || p->requested_path[0] != '/')
+       {
+         close = bad_request_error (p, "bad pathname");
+         continue;
+       }
+
+      /* Path may contain % sequences. Unescape them. */
+      p->requested_path = cgi_unescape (p->pool, p->requested_path);
+
+      /* If the path ends in a /, then it's a request for a directory.
+       * Record this fact now, because pstrcsplit will forget about the
+       * trailing slash otherwise.
+       */
+      is_dir = p->requested_path[strlen (p->requested_path)-1] == '/';
+
+      /* Split up the path into individual components. */
+      path_comps = pstrcsplit (p->pool, p->requested_path, '/');
+
+      /* Remove "", "." and ".." components. */
+      for (i = 0; i < vector_size (path_comps); ++i)
+       {
+         char *comp;
+
+         vector_get (path_comps, i, comp);
+
+         if (strcmp (comp, "") == 0 || strcmp (comp, ".") == 0)
+           {
+             vector_erase (path_comps, i);
+             i--;
+           }
+         else if (strcmp (comp, "..") == 0)
+           {
+             if (i > 0)
+               {
+                 vector_erase_range (path_comps, i-1, i+1);
+                 i -= 2;
+               }
+             else
+               {
+                 vector_erase (path_comps, i);
+                 i--;
+               }
+           }
+       }
+
+      /* Construct the canonical path. Add a trailing slash if the
+       * original request was for a directory.
+       */
+      p->canonical_path = psprintf (p->pool, "/%s",
+                                   pjoin (p->pool, path_comps, "/"));
+      if (strlen (p->canonical_path) > 1 && is_dir)
+       p->canonical_path = psprintf (p->pool, "%s/", p->canonical_path);
+
+#if PR_DEBUG
+      fprintf (stderr, "canonical path is %s\n", p->canonical_path);
+#endif
+
+      /* Update the name of the thread with the full request URL. */
+      pth_set_name (psprintf (p->pool, THREAD_NAME " http://%s%s",
+                             p->host_header,
+                             p->canonical_path));
+
+      /* Apply internal and external rewrite rules. */
+      i = apply_rewrites (p, p->canonical_path, &location);
+      if (i == 1)              /* External rewrite. */
+       {
+#if PR_DEBUG
+         fprintf (stderr, "external rewrite rule to %s\n", location);
+#endif
+         close = moved_permanently (p, location);
+         continue;
+       }
+      else if (i == 2)         /* Internal rewrite. */
+       {
+#if PR_DEBUG
+         fprintf (stderr, "internal rewrite rule to %s\n", location);
+#endif
+
+         /* Update the http_request object with the new path. This also
+          * changes the query string held in this object so that the cgi
+          * library works correctly.
+          */
+         http_request_set_url (p->http_request, location);
+
+         /* Get the path, minus query string. */
+         p->rewritten_path = http_request_path (p->http_request);
+
+         /* Resplit the path. */
+         path_comps = pstrcsplit (p->pool, p->rewritten_path, '/');
+       }
+
+      /* Look for longest matching alias. */
+      for (i = vector_size (path_comps); i >= 0; --i)
+       {
+         if (i > 0)
+           {
+             v = new_subvector (p->pool, path_comps, 0, i);
+             p->aliasname =
+               psprintf (p->pool, "/%s/", pjoin (p->pool, v, "/"));
+           }
+         else
+           p->aliasname = "/";
+
+#if PR_DEBUG
+         fprintf (stderr, "try to find alias matching %s\n", p->aliasname);
+#endif
+
+         if ((p->alias = cfg_get_alias (p->host, p->aliasname)) != 0)
+           goto found_alias;
+       }
+
+#if PR_DEBUG
+      fprintf (stderr, "no matching alias found\n");
+#endif
+
+      /* No alias. */
+      close = file_not_found_error (p);
+      continue;
+
+    found_alias:
+      /* Build up the remainder of the path and the file. */
+      v = new_subvector (p->pool, path_comps, i, vector_size (path_comps));
+      p->remainder = pjoin (p->pool, v, "/");
+
+      /* Find the root path for this alias. */
+      p->root = cfg_get_string (p->host, p->alias, "path", 0);
+      if (p->root == 0)
+       {
+         close = file_not_found_error (p);
+         continue;
+       }
+
+      /* Construct the file path. */
+      p->file_path = psprintf (p->pool, "%s/%s", p->root, p->remainder);
+
+#if PR_DEBUG
+      fprintf (stderr,
+              "rp = %s, cp = %s, rew = %s, "
+              "an = %s, rem = %s, root = %s, fp = %s, qs = %s\n",
+              p->requested_path, p->canonical_path, p->rewritten_path,
+              p->aliasname, p->remainder, p->root,
+              p->file_path,
+              http_request_query_string (p->http_request) ? : "(null)");
+#endif
+
+      /* Find the file to serve and stat it. */
+      if (stat (p->file_path, &p->statbuf) == -1)
+       {
+         close = file_not_found_error (p);
+         continue;
+       }
+
+      /* If it's a directory, but the last component of the name isn't
+       * a "/" character, then we need to add a "/" character and send
+       * a browser redirect back.
+       */
+      if (S_ISDIR (p->statbuf.st_mode) &&
+         p->canonical_path[strlen(p->canonical_path)-1] != '/')
+       {
+         location = psprintf (p->pool, "%s/", p->requested_path);
+         close = moved_permanently (p, location);
+         continue;
+       }
+
+      /* What type of file are we serving? */
+      if (S_ISREG (p->statbuf.st_mode))
+       {
+         close = file_serve (p);
+         continue;
+       }
+      else if (S_ISDIR (p->statbuf.st_mode))
+       {
+         close = dir_serve (p);
+         continue;
+       }
+
+      /* Bad request. */
+      close = bad_request_error (p, "not a regular file or directory");
+    }
+
+  io_fclose (p->io);
+
+  pth_exit ();
+}
diff --git a/process_rq.h b/process_rq.h
new file mode 100644 (file)
index 0000000..453ac04
--- /dev/null
@@ -0,0 +1,127 @@
+/* Request processing thread.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: process_rq.h,v 1.7 2002/09/02 07:50:09 rich Exp $
+ */
+
+#ifndef PROCESS_RQ_H
+#define PROCESS_RQ_H
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+/* The PROCESS_RQ type is both the pseudothread object which handles
+ * the request, and also the request information structure itself.
+ */
+
+struct process_rq
+{
+  pseudothread pth;            /* Pseudothread handle. */
+  pool pool;                   /* Thread pool for all allocations. */
+  int sock;                    /* Socket fd. */
+  io_handle io;                        /* IO handle. */
+  http_request http_request;   /* HTTP request object. */
+  const char *host_header;     /* Host header or "default". */
+
+  /* These are used to establish context by the cfg (configuration) code. */
+  void *host;                  /* Host object. */
+  void *alias;                 /* Alias object. */
+
+  /* The various different paths.
+   *
+   * REQUESTED_PATH is the path as requested by the user (sans query
+   * string). This path is grotty, containing ".", "..", "///", etc. Do
+   * not use it, except perhaps when displaying error messages.
+   *
+   * CANONICAL_PATH is the requested path cleaned up to remove ".", ".."
+   * etc.
+   *
+   * REWRITTEN_PATH is the path after internal rewrite rules have been
+   * applied.
+   *
+   * ALIASNAME is the alias which matches this path. REMAINDER is the
+   * remaining part of the path. Thus (in theory at least), ALIASNAME +
+   * REMAINDER == CANONICAL_PATH.
+   *
+   * ROOT is the document root corresponding to the matching alias. This
+   * corresponds to the actual path of the file on disk. FILE_PATH is
+   * the full path to the actual file on disk. Thus, ROOT + "/" + REMAINDER
+   * == FILE_PATH.
+   *
+   * Directories are always followed by a "/". If a user requests a
+   * directory which isn't followed by a "/" then the path parsing code
+   * transparently issues a 301 (Permanently Moved) browser redirect
+   * including the corrected path.
+   *
+   * Example (no internal rewrite):
+   *   REQUESTED_PATH       "/cgi-bin/../docs/dir///file.html"
+   *   CANONICAL_PATH       "/docs/dir/file.html"
+   *   REWRITTEN_PATH       "/docs/dir/file.html"
+   *   ALIASNAME            "/docs/"
+   *   REMAINDER            "dir/file.html"
+   *   ROOT                 "/home/rich/mydocs"
+   *   FILE_PATH            "/home/rich/mydocs/dir/file.html"
+   *
+   * Example (with internal rewrite):
+   *   REQUESTED_PATH       "/cgi-bin/../docs/dir///file.html"
+   *   CANONICAL_PATH       "/docs/dir/file.html"
+   *   REWRITTEN_PATH       "/newdocs/dir/file.html"
+   *   ALIASNAME            "/newdocs/"
+   *   REMAINDER            "dir/file.html"
+   *   ROOT                 "/home/rich/mynewdocs"
+   *   FILE_PATH            "/home/rich/mynewdocs/dir/file.html"
+   */
+  const char *requested_path;
+  const char *canonical_path;
+  const char *rewritten_path;
+  const char *aliasname;
+  const char *remainder;
+  const char *root;
+  const char *file_path;
+
+  struct stat statbuf;         /* Stat of file. */
+};
+
+typedef struct process_rq *process_rq;
+
+extern process_rq new_process_rq (int sock);
+
+/* Define some RFC-compliant dates to represent past and future. */
+#define DISTANT_PAST   "Thu, 01 Dec 1994 16:00:00 GMT"
+#define DISTANT_FUTURE "Sun, 01 Dec 2030 16:00:00 GMT"
+
+/* Headers which are sent to defeat caches. */
+#define NO_CACHE_HEADERS "Cache-Control", "must-revalidate", "Expires", DISTANT_PAST, "Pragma", "no-cache"
+
+#define CRLF "\r\n"
+
+#endif /* PROCESS_RQ_H */
diff --git a/re.h b/re.h
new file mode 100644 (file)
index 0000000..d4c1f56
--- /dev/null
+++ b/re.h
@@ -0,0 +1,39 @@
+/* Various shared regular expressions.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: re.h,v 1.1 2002/10/06 11:57:22 rich Exp $
+ */
+
+#ifndef RE_H
+#define RE_H
+
+#include <pcre.h>
+
+/* Various shared regular expressions. These are actually defined
+ * in main ().
+ */
+extern const pcre *re_alias_start,
+  *re_alias_end,
+  *re_begin,
+  *re_conf_line,
+  *re_ext,
+  *re_icon,
+  *re_so,
+  *re_ws,
+  *re_comma;
+
+#endif /* RE_H */
diff --git a/rewrite.c b/rewrite.c
new file mode 100644 (file)
index 0000000..f2de137
--- /dev/null
+++ b/rewrite.c
@@ -0,0 +1,307 @@
+/* Rewrite rules.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: rewrite.c,v 1.7 2002/10/20 13:09:10 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pcre.h>
+
+#include <pool.h>
+#include <vector.h>
+#include <hash.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include "cfg.h"
+#include "process_rq.h"
+#include "re.h"
+#include "rewrite.h"
+
+#define RW_DEBUG 0             /* Set this to enable debugging. */
+
+static pool rw_pool = 0;
+
+/* Cache of host -> struct rw *. The null host is stored with key = "". */
+static shash rw_cache;
+
+/* Pre-parsed rules. */
+struct rw
+{
+  vector rules;                        /* Vector of struct rw_rule. */
+};
+
+/* Each rule. */
+struct rw_rule
+{
+  const char *pattern_text;
+  const pcre *pattern;
+  const char *sub;
+  int flags;
+#define RW_RULE_EXTERNAL 0x0001
+#define RW_RULE_LAST     0x0002
+#define RW_RULE_QSA      0x0004
+};
+
+static struct rw *parse_rules (const char *cfg);
+static const char *append_qs (process_rq p, const char *path);
+
+void
+rewrite_reset_rules ()
+{
+  if (rw_pool) delete_pool (rw_pool);
+  rw_pool = new_subpool (global_pool);
+
+  rw_cache = new_shash (rw_pool, struct rw *);
+}
+
+int
+apply_rewrites (const process_rq p, const char *path, const char **location)
+{
+  struct rw *rw = 0;
+  const char *host = p->host_header ? p->host_header : "";
+  int i, matches = 0, qsa = 0;
+
+#if RW_DEBUG
+  fprintf (stderr, "apply_rewrites: original path = %s\n",
+          p->canonical_path);
+#endif
+
+  /* Get the configuration entry. (Note the alias is not known
+   * yet, so rewrite rules inside alias sections have no effect).
+   */
+  if (!shash_get (rw_cache, host, rw))
+    {
+      const char *cfg;
+
+      cfg = cfg_get_string (p->host, 0, "rewrite", 0);
+      if (cfg) rw = parse_rules (cfg);
+      shash_insert (rw_cache, host, rw);
+    }
+
+  if (!rw)                     /* No matching rule. */
+    {
+#if RW_DEBUG
+      fprintf (stderr, "apply_rewrites: no matching rule\n");
+#endif
+      return 0;
+    }
+
+  /* Look for a matching rule. */
+  for (i = 0; i < vector_size (rw->rules); ++i)
+    {
+      struct rw_rule rule;
+      const char *old_path = path;
+
+      vector_get (rw->rules, i, rule);
+
+#if RW_DEBUG
+      fprintf (stderr, "apply_rewrites: try matching against %s\n",
+              rule.pattern_text);
+#endif
+
+      path = presubst (p->pool, old_path, rule.pattern, rule.sub, 0);
+      if (path != old_path) /* It matched. */
+       {
+         matches = 1;
+         if (rule.flags & RW_RULE_QSA) qsa = 1;
+
+#if RW_DEBUG
+         fprintf (stderr, "apply_rewrites: it matches %s\n",
+                  rule.pattern_text);
+#endif
+
+         /* External link? If so, send a redirect. External rules are
+          * always 'last'.
+          */
+         if (rule.flags & RW_RULE_EXTERNAL)
+           {
+#if RW_DEBUG
+             fprintf (stderr,
+                      "apply_rewrites: external: send redirect to %s\n",
+                      path);
+#endif
+             *location = qsa ? append_qs (p, path) : path;
+             return 1;
+           }
+
+         /* Last rule? */
+         if (rule.flags & RW_RULE_LAST)
+           {
+#if RW_DEBUG
+             fprintf (stderr,
+                      "apply_rewrites: last rule: finished with %s\n",
+                      path);
+#endif
+             *location = qsa ? append_qs (p, path) : path;
+             return 2;
+           }
+
+         /* Jump back to the beginning of the list. */
+         i = -1;
+       }
+    }
+
+#if RW_DEBUG
+  fprintf (stderr,
+          "apply_rewrites: finished with %s\n",
+          path);
+#endif
+
+  if (matches)
+    {
+      *location = qsa ? append_qs (p, path) : path;
+      return 2;
+    }
+
+  return 0;
+}
+
+/* Append query string, if there is one. */
+static const char *
+append_qs (process_rq p, const char *path)
+{
+  pool pool = p->pool;
+  const char *qs = http_request_query_string (p->http_request);
+
+  if (qs && strlen (qs) > 0)
+    {
+      const char *t = strchr (path, '?');
+
+      if (t)                   /* Path already has a query string? */
+       {
+         if (t[1] != '\0')
+           return psprintf (pool, "%s&%s", path, qs);
+         else
+           return psprintf (pool, "%s%s", path, qs);
+       }
+      else                     /* Path doesn't have a query string. */
+       return psprintf (pool, "%s?%s", path, qs);
+    }
+  return path;                 /* No query string. */
+}
+
+static void parse_error (const char *line, const char *msg);
+
+static struct rw *
+parse_rules (const char *cfg)
+{
+  pool tmp = new_subpool (rw_pool);
+  vector lines;
+  const char *line;
+  struct rw *rw;
+  struct rw_rule rule;
+  int i;
+
+  /* Split up the configuration string into lines. */
+  lines = pstrcsplit (tmp, cfg, '\n');
+
+  /* Remove any empty lines (these have probably already been removed,
+   * but we can safely do this again anyway).
+   */
+  for (i = 0; i < vector_size (lines); ++i)
+    {
+      vector_get (lines, i, line);
+
+      if (strcmp (line, "") == 0)
+       {
+         vector_erase (lines, i);
+         i--;
+       }
+    }
+
+  if (vector_size (lines) == 0) { delete_pool (tmp); return 0; }
+
+  /* Allocate space for the return structure. */
+  rw = pmalloc (rw_pool, sizeof *rw);
+  rw->rules = new_vector (rw_pool, struct rw_rule);
+
+  /* Each line is a separate rule in the current syntax, so examine
+   * each line and turn it into a rule.
+   */
+  for (i = 0; i < vector_size (lines); ++i)
+    {
+      vector v;
+
+      vector_get (lines, i, line);
+
+      v = pstrresplit (tmp, line, re_ws);
+
+      if (vector_size (v) < 2 || vector_size (v) > 3)
+       parse_error (line, "unrecognised format");
+      vector_get (v, 0, rule.pattern_text);
+      rule.pattern_text = pstrdup (rw_pool, rule.pattern_text);
+      rule.pattern = precomp (rw_pool, rule.pattern_text, 0);
+      vector_get (v, 1, rule.sub);
+      rule.sub = pstrdup (rw_pool, rule.sub);
+
+      /* Parse the flags. */
+      rule.flags = 0;
+      if (vector_size (v) == 3)
+       {
+         const char *flags;
+         int j;
+
+         vector_get (v, 2, flags);
+         v = pstrresplit (tmp, flags, re_comma);
+
+         for (j = 0; j < vector_size (v); ++j)
+           {
+             const char *flag;
+
+             vector_get (v, j, flag);
+
+             if (strcasecmp (flag, "external") == 0)
+               rule.flags |= RW_RULE_EXTERNAL;
+             else if (strcasecmp (flag, "last") == 0)
+               rule.flags |= RW_RULE_LAST;
+             else if (strcasecmp (flag, "qsa") == 0)
+               rule.flags |= RW_RULE_QSA;
+             else
+               parse_error (line, "unknown flag");
+           }
+       }
+
+#if RW_DEBUG
+      fprintf (stderr,
+              "parse rule: pattern=%s sub=%s flags=0x%04x\n",
+              rule.pattern_text, rule.sub, rule.flags);
+#endif
+
+      vector_push_back (rw->rules, rule);
+    }
+
+  delete_pool (tmp);
+  return rw;
+}
+
+static void
+parse_error (const char *line, const char *msg)
+{
+  fprintf (stderr,
+          "rewrite rule: %s\n"
+          "at line: %s\n",
+          msg, line);
+  exit (1);
+}
diff --git a/rewrite.h b/rewrite.h
new file mode 100644 (file)
index 0000000..8129ce9
--- /dev/null
+++ b/rewrite.h
@@ -0,0 +1,40 @@
+/* Rewrite rules.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: rewrite.h,v 1.4 2002/10/09 19:13:14 rich Exp $
+ */
+
+#ifndef REWRITE_H
+#define REWRITE_H
+
+#include <pool.h>
+
+#include "process_rq.h"
+
+/* Reset the rewrite rules. */
+extern void rewrite_reset_rules (void);
+
+/* This function applies internal and external rewrite rules found
+ * in the configuration file. If there is no rewrite, *location is
+ * left alone and the function returns 0. If there is an external
+ * rewrite, *location points to the rewritten path and the function
+ * returns 1. Internal rewrites are the same as external rewrites
+ * except the function returns 2.
+ */
+extern int apply_rewrites (const process_rq p, const char *path, const char **location);
+
+#endif /* REWRITE_H */
diff --git a/rws.rc b/rws.rc
new file mode 100755 (executable)
index 0000000..fa46754
--- /dev/null
+++ b/rws.rc
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# rws      This shell script takes care of starting and stopping rws.
+#
+# chkconfig: 2345 80 30
+# description: RWS is a webserver
+# processname: rwsd
+# config: /etc/rws/rws.conf
+# pidfile: /var/run/rws.pid
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+[ -f /usr/sbin/rwsd ] || exit 0
+
+# See how we were called.
+case "$1" in
+  start)
+       # Start daemons.
+
+       echo -n "Starting rws: "
+       daemon /usr/sbin/rwsd
+       echo
+       touch /var/lock/subsys/rws
+       ;;
+  stop)
+       # Stop daemons.
+       echo -n "Shutting down rws: "
+       killproc rwsd
+       echo
+       rm -f /var/lock/subsys/rws
+       ;;
+  restart)
+       $0 stop
+       $0 start
+       ;;
+  status)
+       status rwsd
+       ;;
+  *)
+       echo "Usage: rws.rc {start|stop|restart|status}"
+       exit 1
+esac
+
+exit 0
+
diff --git a/rws_request.c b/rws_request.c
new file mode 100644 (file)
index 0000000..4ed9ec2
--- /dev/null
@@ -0,0 +1,119 @@
+/* Shared object scripts.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: rws_request.c,v 1.5 2002/12/01 14:58:02 rich Exp $
+ */
+
+#include "config.h"
+
+#include "cfg.h"
+#include "rws_request.h"
+
+struct rws_request
+{
+  http_request http_request;
+  io_handle io;
+  const char *host_header;
+  const char *canonical_path;
+  const char *file_path;
+
+  /* These are used for retrieving configuration information.
+   * XXX These are also a huge hack which will be removed when we
+   * have a decent configuration object type in c2lib.
+   */
+  void *host;
+  void *alias;
+  const char * (*cfg_get_string) (void *, void *, const char *, const char *);
+  int (*cfg_get_int) (void *, void *, const char *, int);
+  int (*cfg_get_bool) (void *, void *, const char *, int);
+};
+
+rws_request
+new_rws_request (pool pool, http_request http_request, io_handle io,
+                const char *host_header, const char *canonical_path,
+                const char *file_path, void *host, void *alias,
+                const char * (*cfg_get_string)
+                (void *, void *, const char *, const char *),
+                int (*cfg_get_int) (void *, void *, const char *, int),
+                int (*cfg_get_bool) (void *, void *, const char *, int))
+{
+  rws_request p = pmalloc (pool, sizeof *p);
+
+  p->http_request = http_request;
+  p->io = io;
+  p->host_header = host_header;
+  p->canonical_path = canonical_path;
+  p->file_path = file_path;
+  p->host = host;
+  p->alias = alias;
+  p->cfg_get_string = cfg_get_string;
+  p->cfg_get_int = cfg_get_int;
+  p->cfg_get_bool = cfg_get_bool;
+
+  return p;
+}
+
+http_request
+rws_request_http_request (rws_request p)
+{
+  return p->http_request;
+}
+
+io_handle
+rws_request_io (rws_request p)
+{
+  return p->io;
+}
+
+const char *
+rws_request_host_header (rws_request p)
+{
+  return p->host_header;
+}
+
+const char *
+rws_request_canonical_path (rws_request p)
+{
+  return p->canonical_path;
+}
+
+const char *
+rws_request_file_path (rws_request p)
+{
+  return p->file_path;
+}
+
+const char *
+rws_request_cfg_get_string (rws_request p,
+                           const char *key, const char *default_value)
+{
+  return p->cfg_get_string (p->host, p->alias, key, default_value);
+}
+
+int
+rws_request_cfg_get_int (rws_request p,
+                        const char *key, int default_value)
+{
+  return p->cfg_get_int (p->host, p->alias, key, default_value);
+}
+
+int
+rws_request_cfg_get_bool (rws_request p,
+                         const char *key, int default_value)
+{
+  return p->cfg_get_bool (p->host, p->alias, key, default_value);
+}
diff --git a/rws_request.h b/rws_request.h
new file mode 100644 (file)
index 0000000..f0ae49a
--- /dev/null
@@ -0,0 +1,93 @@
+/* RWS request object, passed to shared object scripts.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: rws_request.h,v 1.4 2002/12/01 14:58:02 rich Exp $
+ */
+
+#ifndef RWS_REQUEST_H
+#define RWS_REQUEST_H
+
+#include <pool.h>
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+struct rws_request;
+typedef struct rws_request *rws_request;
+
+/* This is the private interface to building a new rws_request object. It
+ * is called inside rwsd. Shared object scripts will never need to call
+ * this. Use the public interface below only.
+ */
+extern rws_request new_rws_request (pool, http_request, io_handle, const char *host_header, const char *canonical_path, const char *file_path, void *host, void *alias, const char * (*cfg_get_string) (void *, void *, const char *, const char *), int (*cfg_get_int) (void *, void *, const char *, int), int (*cfg_get_bool) (void *, void *, const char *, int));
+
+/* Function: rws_request_http_request - retrieve fields in rws_request object
+ * Function: rws_request_io
+ * Function: rws_request_host_header
+ * Function: rws_request_canonical_path
+ * Function: rws_request_file_path
+ * Function: rws_request_cfg_get_string
+ * Function: rws_request_cfg_get_int
+ * Function: rws_request_cfg_get_bool
+ *
+ * These functions retrieve the fields in an @code{rws_request} object.
+ * This object is passed to shared object scripts when they are invoked
+ * by rws as:
+ *
+ * @code{int handle_request (rws_request rq)}
+ *
+ * @code{rws_request_http_request} returns the current HTTP request
+ * (see @ref{new_http_request(3)}). To parse the CGI parameters, you
+ * need to call @code{new_cgi} (see @ref{new_cgi(3)}).
+ *
+ * @code{rws_request_io} returns the IO handle connected to the
+ * browser.
+ *
+ * @code{rws_request_host_header} returns the contents of the
+ * HTTP @code{Host:} header, or the string @code{default} if none was given.
+ *
+ * @code{rws_request_canonical_path} returns the canonical path
+ * requested by the browser (after removing @code{..}, @code{//}, etc.),
+ * eg. @code{/so-bin/file.so}.
+ *
+ * @code{rws_request_file_path} returns the actual path to the
+ * SO file being requested, eg. @code{/usr/share/rws/so-bin/file.so}.
+ *
+ * @code{rws_request_cfg_get_string} returns the configuration file
+ * string for @code{key}. If there is no entry in the configuration
+ * file, this returns @code{default_value}.
+ *
+ * @code{rws_request_cfg_get_int} returns the string converted to
+ * an integer.
+ *
+ * @code{rws_request_cfg_get_bool} returns the string converted to
+ * a boolean.
+ *
+ * See also: @ref{new_cgi(3)}, @ref{new_http_response(3)},
+ * @ref{new_http_request(3)}, pthrlib tutorial, rws @code{examples/}
+ * directory.
+ */
+extern http_request rws_request_http_request (rws_request);
+extern io_handle rws_request_io (rws_request);
+extern const char *rws_request_host_header (rws_request);
+extern const char *rws_request_canonical_path (rws_request);
+extern const char *rws_request_file_path (rws_request);
+extern const char *rws_request_cfg_get_string (rws_request, const char *key, const char *default_value);
+extern int rws_request_cfg_get_int (rws_request, const char *key, int default_value);
+extern int rws_request_cfg_get_bool (rws_request, const char *key, int default_value);
+
+#endif /* RWS_REQUEST_H */
diff --git a/rwsd.1 b/rwsd.1
new file mode 100644 (file)
index 0000000..2cd8e32
--- /dev/null
+++ b/rwsd.1
@@ -0,0 +1,97 @@
+.TH rwsd 1 "October 22, 2002" "GNU" "Programmer's Manual"
+
+.SH NAME
+rwsd \- a small, fast web server
+
+.SH SYNOPSIS
+.B rwsd
+[\fI\-p port\fR] [\fI\-C configpath\fR]
+[\fI\-d\fR] [\fI\-f\fR] [\fI\-a address\fR]
+
+.SH DESCRIPTION
+\fBrwsd\fR is a small, fast web server, written in C, but with a
+very comprehensible and straightforward design. It uses a lightweight
+threading library called \fBpthrlib\fR (so it runs as a single process)
+and a set of basic data types for C called \fBc2lib\fR.
+
+.SH "COMMAND LINE OPTIONS"
+.TP
+\fB\-p port\fR
+listen on the given port (default is to listen on port 80)
+.TP
+\fB\-C configpath\fR
+find configuration files under path (default is \fB/etc/rws/\fR)
+.TP
+\fB\-d\fR
+do not send stderr to the \fBerror_log\fR file (used when debugging)
+.TP
+\fB\-f\fR
+do not fork into the background (used when debugging)
+.TP
+\fB\-a address\fR
+bind only to the given interface (default is to bind to all interfaces)
+
+.SH CONFIGURATION
+The server is configured through files in the \fB/etc/rws/\fR
+directory. The main configuration file is \fB/etc/rws/rws.conf\fR
+and virtual hosts are configured using files in
+\fB/etc/rws/hosts/\fR\fIhostname\fR, for each virtual host
+\fIhostname\fR.
+
+
+
+
+
+
+.SH "RUNNING THE SERVER"
+To start the server running, log in as root and do:
+.PP
+.B /usr/sbin/rwsd
+.PP
+To stop the server running, do:
+.PP
+.B killall rwsd
+.PP
+A SysV start up script is also installed by default, if you prefer
+to use that method for starting and stopping the service. Do:
+.PP
+.B /etc/init.d/rws.rc start
+.PP
+or:
+.PP
+.B /etc/init.d/rws.rc stop
+.PP
+\fBrws\fR cannot run from \fBinetd\fR.
+
+.SH "HTML FILES"
+
+.SH "DIRECTORIES"
+
+.SH "CGI SCRIPTS"
+
+.SH "SHARED OBJECT SCRIPTS"
+
+
+.SH FILES
+\fB/etc/rws/rws.conf\fR
+.PP
+\fB/etc/rws/hosts/default\fR
+.PP
+\fB/etc/rws/hosts/\fR\fIhostname\fR
+
+.SH "SEE ALSO"
+\fBnew_pool\fP(3),
+\fBnew_pseudothread\fP(3).
+.PP
+\fBhttp://www.annexia.org/freeware/rws/\fP
+
+.SH AUTHOR
+The primary author is Richard W.M. Jones <\fBrich@annexia.org\fR>.
+
+.SH COPYRIGHT
+Copyright \(co 2000-2002 Richard W.M. Jones <\fBrich@annexia.org\fR>.
+
+.SH LICENSE
+This software is licensed under the terms of the GNU Library General Public
+License (GNU LGPL). Please see the file \fBCOPYING.LIB\fR which
+accompanies the source distribution.
diff --git a/test_rws.sh b/test_rws.sh
new file mode 100755 (executable)
index 0000000..16e2f3d
--- /dev/null
@@ -0,0 +1,215 @@
+#!/bin/sh -
+#
+# Simple shell script which tests whether rws is basically working.
+# - by Richard W.M. Jones <rich@annexia.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free
+# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Id: test_rws.sh,v 1.4 2003/02/05 23:02:51 rich Exp $
+
+# A random, hopefully free, port.
+port=14136
+
+# We need either 'wget' or 'nc'.
+wget --help >/dev/null 2>&1
+if [ $? -eq 0 ]; then
+       mode=wget
+else
+       nc -h 2>/dev/null
+       if [ $? -eq 1 ]; then
+               mode=nc
+       else
+               echo "Please install either 'wget' or 'nc'."
+               echo "This test did not run."
+               exit 0
+       fi
+fi
+
+echo "Using $mode to fetch URLs."
+
+tmp=/tmp/rws.$$
+rm -rf $tmp
+mkdir $tmp
+
+# Create the configuration directory.
+mkdir -p $tmp/etc/rws/hosts
+
+cat > $tmp/etc/rws/rws.conf <<EOF
+mime types file: $tmp/etc/mime.types
+error log: $tmp/log/error_log
+access log: $tmp/log/access_log
+icon for application/*:         /icons/binary.gif 20x22 "Application"
+icon for application/x-tar:     /icons/tar.gif 20x22 "Unix tape archive file"
+icon for application/x-gzip:    /icons/compressed.gif 20x22 "Compressed file"
+icon for application/zip:       /icons/compressed.gif 20x22 "Compressed file"
+icon for audio/*:               /icons/sound1.gif 20x22 "Audio file"
+icon for image/*:               /icons/image2.gif 20x22 "Image"
+icon for message/*:             /icons/quill.gif 20x22 "Mail message"
+icon for text/*:                /icons/text.gif 20x22 "Text file"
+icon for video/*:               /icons/movie.gif 20x22 "Video file"
+no type icon:                   /icons/generic.gif 20x22 "File"
+unknown icon:                   /icons/unknown.gif 20x22 "Unknown file type"
+directory icon:                 /icons/dir.gif 20x22 "Directory"
+link icon:                      /icons/link.gif 20x22 "Symbolic link"
+special icon:                   /icons/sphere2.gif 20x22 "Special file"
+EOF
+
+cat > $tmp/etc/rws/hosts/default <<EOF
+alias /
+       path:   $tmp/html
+       show:   1
+       list:   1
+end alias
+alias /so-bin/
+       path:   $tmp/so-bin
+       exec so: 1
+end alias
+alias /cgi-bin/
+       path:   $tmp/cgi-bin
+       exec:   1
+end alias
+EOF
+(cd $tmp/etc/rws/hosts; ln -s default localhost:$port)
+
+cat > $tmp/etc/mime.types <<EOF
+text/html html
+EOF
+
+mkdir $tmp/log
+
+# Create the content directory.
+mkdir $tmp/html
+mkdir $tmp/html/files
+
+cat > $tmp/html/index.html <<EOF
+<html>
+<body>
+This is the test page.
+MAGIC-1234
+</body>
+</html>
+EOF
+
+cp *.o $tmp/html/files
+
+# Create the so-bin directory.
+mkdir $tmp/so-bin
+cp examples/show_params.so $tmp/so-bin
+chmod 0755 $tmp/so-bin/show_params.so
+
+# Create the CGI directory
+mkdir $tmp/cgi-bin
+cat > $tmp/cgi-bin/test.sh <<EOF
+#!/bin/sh
+echo "HTTP/1.0 200 OK"
+echo "Content-Type: text/plain"
+echo
+echo "This is the test CGI script"
+echo "MAGIC-4321"
+EOF
+chmod 0755 $tmp/cgi-bin/test.sh
+
+# Try to start up the server.
+./rwsd -p $port -f -a 127.0.0.1 -C $tmp/etc/rws &
+rws_pid=$!; sleep 1
+
+# Did it start up?
+if kill -0 $rws_pid; then :;
+else
+       echo "Server did not start up. Check any preceeding messages."
+       exit 1
+fi
+
+echo "Started rwsd instance."
+
+# Fetch function: fetch (server, port, serverpath, file)
+fetch()
+{
+       server=$1
+       port=$2
+       serverpath=$3
+       file=$4
+
+       echo "Fetching http://$server:$port$serverpath ..."
+
+       if [ $mode = "nc" ]; then
+               nc $server $port > $file <<EOF
+GET $serverpath HTTP/1.0
+User-Agent: test_rws.sh
+
+EOF
+               if [ $? -ne 0 ]; then exit 1; fi
+       else            # wget mode
+               wget -q -O $file http://$server:$port$serverpath
+               if [ $? -ne 0 ]; then kill $rws_pid; exit 1; fi
+       fi
+}
+
+# Fetch the test file.
+fetch localhost $port /index.html $tmp/downloaded
+if grep -q MAGIC-1234 $tmp/downloaded; then :;
+else
+       echo "Download of a simple file failed!"
+       echo "Look at $tmp/downloaded for clues."
+       kill $rws_pid
+       exit 1
+fi
+rm $tmp/downloaded
+
+# Fetch the directory listing.
+fetch localhost $port /files/ $tmp/downloaded
+if grep -q main.o $tmp/downloaded; then :;
+else
+       echo "Download of a directory listing failed!"
+        echo "Look at $tmp/downloaded for clues."
+       kill $rws_pid
+       exit 1
+fi
+rm $tmp/downloaded
+
+# Test shared object scripts.
+echo "Testing shared object scripts."
+fetch localhost $port '/so-bin/show_params.so?key=value' $tmp/downloaded
+if grep -q 'This is the show_params shared object script' $tmp/downloaded
+then :;
+else
+        echo "Execution of a shared object script failed!"
+        echo "Look at $tmp/downloaded for clues."
+        kill $rws_pid
+        exit 1
+fi
+rm $tmp/downloaded
+
+# Test CGI scripts.
+echo "Testing CGI scripts."
+fetch localhost $port /cgi-bin/test.sh $tmp/downloaded
+if grep -q MAGIC-4321 $tmp/downloaded; then :;
+else
+        echo "Execution of a CGI script failed!"
+        echo "Look at $tmp/downloaded for clues."
+        kill $rws_pid
+        exit 1
+fi
+rm $tmp/downloaded
+
+echo "Test completed OK."
+
+# Kill the server.
+kill $rws_pid
+
+# Remove the temporary directory.
+rm -rf $tmp
+
+exit 0