camlspotter avatar camlspotter committed c20e6c1

copied from the original CamlGI 0.6

Comments (0)

Files changed (19)

+camlGI.cmo: camlGI.cmi
+camlGI.cmx: camlGI.cmi
+cgi.cmo: cookie.cmo cgi_types.cmo cgi_std.cmo cgi_fast.cmo cgi_common.cmi
+cgi.cmx: cookie.cmx cgi_types.cmx cgi_std.cmx cgi_fast.cmx cgi_common.cmx
+cgi_common.cmo: cgi_types.cmo cgi_common.cmi
+cgi_common.cmx: cgi_types.cmx cgi_common.cmi
+cgi_fast.cmo: cgi_types.cmo cgi_common.cmi
+cgi_fast.cmx: cgi_types.cmx cgi_common.cmx
+cgi_std.cmo: cgi_types.cmo cgi_common.cmi
+cgi_std.cmx: cgi_types.cmx cgi_common.cmx
+cgi_types.cmo:
+cgi_types.cmx:
+cookie.cmo: expires.cmo cgi_common.cmi
+cookie.cmx: expires.cmx cgi_common.cmx
+dbiPool.cmo:
+dbiPool.cmx:
+expires.cmo: cgi_common.cmi
+expires.cmx: cgi_common.cmx
+sendmail.cmo:
+sendmail.cmx:
+template.cmo: cgi_common.cmi
+template.cmx: cgi_common.cmx
+camlGI.cmi:
+cgi_common.cmi: cgi_types.cmo
+This Library is distributed under the terms of the GNU Lesser General
+Public License version 2.1 (included below).
+
+As a special exception to the GNU Lesser General Public License, you
+may link, statically or dynamically, a "work that uses the Library"
+with a publicly distributed version of the Library to produce an
+executable file containing portions of the Library, and distribute
+that executable file under terms of your choice, without any of the
+additional requirements listed in clause 6 of the GNU Lesser General
+Public License.  By "a publicly distributed version of the Library",
+we mean either the unmodified Library as distributed, or a modified
+version of the Library that is distributed under the conditions
+defined in clause 3 of the GNU Library General Public License.  This
+exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU Lesser General Public
+License.
+
+
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  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 Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    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 Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+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 other code 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.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  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, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser 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.
+
+  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.
+
+  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.
+
+  6. As an exception to the Sections above, you may also combine 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) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) 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.
+
+    d) 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.
+
+    e) 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 materials to be 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.
+
+  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 with
+this License.
+
+  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 Lesser 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.
+
+  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
+
+           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 Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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!
+
+
+# Specifications for the OCaml-Cgi library                 -*-shell-script-*-
+# 	$Id: META,v 1.7 2005/01/31 19:35:35 chris_77 Exp $	
+name="CamlGI"
+description="OCaml (F)CGI library"
+requires = "threads unix str"
+archive(byte) = "camlGI.cma"
+archive(native) = "camlGI.cmxa"
+version = "0.6"
+# CamlGI
+# Copyright (C) 2005: Christophe TROESTLER
+#	$Id: Makefile,v 1.17 2005/06/12 21:46:50 chris_77 Exp $
+#
+PKGNAME		= $(shell grep name META | \
+			sed -e "s/.*\"\([^\"]*\)\".*/\1/")
+PKGVERSION	= $(shell grep version META | \
+			sed -e "s/.*\"\([^\"]*\)\".*/\1/")
+
+SRC_WEB		= web
+SF_WEB		= /home/groups/o/oc/ocaml-cgi/htdocs
+
+include Makefile.config
+
+OCAMLFLAGS 	= -dtypes -I +threads
+OCAMLOPTFLAGS	= -p -inline 2 -I +threads
+OCAMLDOCFLAGS	=
+PP		= -pp "camlp4o pa_macro.cmo pr_o.cmo"
+
+SOURCES = camlGI.ml \
+    cgi_types.ml cgi_common.ml expires.ml cookie.ml sendmail.ml \
+    cgi_std.ml cgi_fast.ml cgi.ml template.ml dbiPool.ml
+INTERFACES = camlGI.mli
+PRIVATE_INTERFACES = cgi_common.mli
+DEMOS = 
+
+DISTFILES	= README LICENSE META \
+  Makefile Makefile.config $(SOURCES) $(INTERFACES) $(PRIVATE_INTERFACES)
+
+PKG_TARBALL 	= $(PKGNAME)-$(PKGVERSION).tar.gz
+ARCHIVE 	= $(shell grep "archive(byte)" META | \
+			sed -e "s/.*\"\([^\"]*\)\".*/\1/")
+XARCHIVE 	= $(shell grep "archive(native)" META | \
+			sed -e "s/.*\"\([^\"]*\)\".*/\1/")
+
+.PHONY: all byte opt install install-byte install-opt doc ps dist
+all: byte opt
+byte: $(ARCHIVE)
+opt: $(XARCHIVE)
+doc: html
+ps: cgi.ps
+
+.PHONY: ex examples examples-byte examples-opt
+ex: examples
+examples: examples-byte examples-opt
+examples-byte: byte
+	cd examples/; $(MAKE) byte
+examples-opt: opt
+	cd examples/; $(MAKE) opt
+
+camlGI.cma: $(SOURCES) camlGI.cmi
+	$(OCAMLC) $(PP) -a -o $@ $(OCAMLFLAGS) $<
+
+camlGI.cmxa: $(SOURCES) camlGI.cmi
+	$(OCAMLOPT) $(PP) -a -o $@ $(OCAMLOPTFLAGS) $<
+
+ifdef OCAMLLIBDIR
+OCAMLFIND_DESTDIR=-destdir $(OCAMLLIBDIR)
+else
+OCAMLFIND_DESTDIR=
+endif
+# The install rule is separate because "ocamlfind" cannot install
+# incrementally
+install: byte opt
+	$(OCAMLFIND) remove  $(PKGNAME) || true
+	$(OCAMLFIND) install $(OCAMLFIND_DESTDIR) $(PKGNAME) META \
+	  $(ARCHIVE) $(XARCHIVE) $(ARCHIVE:.cma=.cmi) $(ARCHIVE:.cma=.mli)
+
+install-byte: byte
+	$(OCAMLFIND) remove  $(PKGNAME) || true
+	$(OCAMLFIND) install $(OCAMLFIND_DESTDIR) $(PKGNAME) META \
+	  $(ARCHIVE) $(ARCHIVE:.cma=.cmi) $(ARCHIVE:.cma=.mli)
+
+install-opt: opt
+	$(OCAMLFIND) remove  $(PKGNAME) || true
+	$(OCAMLFIND) install $(OCAMLFIND_DESTDIR) $(PKGNAME) META \
+	  $(XARCHIVE) $(ARCHIVE:.cmxa=.cmi) $(ARCHIVE:.cmxa=.mli)
+
+install-doc: doc
+	[ -d $(DOCDIR) ] || mkdir -p $(DOCDIR)
+	cp html/* $(DOCDIR)
+
+uninstall:
+	$(OCAMLFIND) remove  $(PKGNAME)
+	$(REMOVE) -rf $(DOCDIR)
+
+# Documentation
+.PHONY: html
+html: html/index.html
+
+html/index.html: $(INTERFACES) $(INTERFACES:.mli=.cmi)
+	[ -d html/ ] || mkdir html
+	$(OCAMLDOC) -d html -html $(OCAMLDOCFLAGS) $(INTERFACES)
+
+# Packaging
+.PHONY: dist
+dist:
+	[ -d $(PKGNAME)-$(PKGVERSION) ] || mkdir $(PKGNAME)-$(PKGVERSION)
+	cp --preserve -r $(DISTFILES) $(PKGNAME)-$(PKGVERSION)/
+	tar --exclude "CVS" --exclude ".cvsignore" --exclude-from=.cvsignore \
+	  -zcvf $(PKG_TARBALL) $(PKGNAME)-$(PKGVERSION)
+	rm -rf $(PKGNAME)-$(PKGVERSION)
+
+# Dependency graph
+.PHONY: Camlgi.ps
+Camlgi.ps: camlGI.ps
+camlGI.ps: $(INTERFACES) $(SOURCES:.ml=.cmo)
+	$(OCAMLDOC) -o cgi.dot -dot -I +threads $(INTERFACES) $(SOURCES)
+	dot -Tps cgi.dot > $@
+	rm -f cgi.dot
+
+# Release a Sourceforge tarball and publish the HTML doc
+-include Makefile.pub
+
+# Caml general dependencies
+.SUFFIXES: .ml .mli .cmo .cmi .cmx
+.mli.cmi:
+	$(OCAMLC) $(OCAMLFLAGS) -c $<
+%.cmo: %.ml
+	$(OCAMLC) $(OCAMLFLAGS) -c $<
+%.cma: # Dependencies to be set elsewhere
+	$(OCAMLC) -a -o $@ $(OCAMLFLAGS) $(filter %.cmo, $^)
+.ml.cmx:
+	$(OCAMLOPT) $(OCAMLOPTFLAGS) -c $<
+%.cmxa: # Dependencies to be set elsewhere
+	$(OCAMLOPT) -a -o $@ $(OCAMLOPTFLAGS) $(filter %.cmx, $^)
+
+.PHONY: depend dep
+dep: .depend
+depend: .depend
+.depend: $(wildcard *.ml) $(wildcard *.mli)
+	$(OCAMLDEP) $(PP) $^ > .depend
+
+include .depend
+
+########################################################################
+
+.PHONY: clean dist-clean
+clean:
+	rm -f *~ .*~ *.{o,a} *.cm[aiox] *.cmxa *.annot *.css
+	rm -f $(PKG_TARBALL) cgi.ps
+	rm -rf html/
+	cd examples; $(MAKE) clean
+	find . -not -name *.sh -type f -perm +u=x -exec rm -f {} \;
+
+dist-clean: clean
+	rm .depend
+# CamlGI configuration                                -*- Makefile -*-
+# Copyright (C) Christophe TROESTLER
+#	$Id: Makefile.config,v 1.6 2005/06/12 21:46:50 chris_77 Exp $	
+#
+# $(PKGNAME) and $(PKGVERSION) are defined.
+
+# OCAMLC, OCAMLOPT, OCAMLDEP, OCAMLDOC
+# The location of the OCaml compiler and tools. The defaults should be OK.
+#
+OCAMLC	 = ocamlc
+OCAMLOPT = ocamlopt
+OCAMLDEP = ocamldep
+OCAMLDOC = ocamldoc
+OCAMLFIND = ocamlfind
+
+PREFIX=/usr/local
+#PREFIX=/usr/local/stow/ocaml-$(PKGNAME)-$(PKGVERSION)
+
+# OCAMLLIBDIR: location of OCaml's library files.
+# Set it if you want another directory than the default findlib directory.
+#OCAMLLIBDIR = $(PREFIX)/lib/ocaml/$(shell ocamlc -version)
+
+# DOCDIR: where we will install the documentation.
+#
+DOCDIR = $(PREFIX)/doc/$(PKGNAME)-$(PKGVERSION)
+
+# Utilities
+#
+COPY = cp
+#COPY = copy
+REMOVE = rm
+#	$Id: README,v 1.4 2005/01/25 19:35:42 chris_77 Exp $
+
+  CamlGI - FastCGI and CGI library
+
+
+  Copyright 2004, Christophe Troestler
+
+  This library is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version
+  2.1 as published by the Free Software Foundation, with the special
+  exception on linking described in file LICENSE.
+
+  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 file
+  LICENSE for more details.
+----------------------------------------------------------------------
+
+INSTALL : See the file of the same name!
+
+
+LIMITATIONS
+
+Beware that FCGI does not support the non-parsed header feature of
+CGI, so do not use it if you want your scripts to be portable accross
+the two systems.
+
+
+APACHE MODULE
+
+There is an module to enable fastcgi for Apache at http://fastcgi.com/
+An alternative is http://fastcgi.coremail.cn/
+
+
+RELATED PROJECTS
+
+- mod_caml (https://savannah.nongnu.org/projects/modcaml/)
+
+- OCamlnet (http://ocamlnet.sourceforge.net/)
+
+- fcgi-ocaml (http://sourceforge.net/projects/tcl-fastcgi/)
+
+- cgi (http://www.lri.fr/~filliatr/ftp/ocaml/cgi/)
+
+
+BASIC SECURITY MEASURES
+
+- http://www.w3.org/Security/Faq/www-security-faq.html
+
+- http://www.advosys.ca/papers/web-security.html
+
+- http://www.dwheeler.com/secure-programs/
+
+(* File: camlGI.ml
+
+   Copyright (C) 2004
+
+     Christophe Troestler
+     email: Christophe.Troestler@umh.ac.be
+     WWW: http://www.umh.ac.be/math/an/software/
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   version 2.1 as published by the Free Software Foundation, with the
+   special exception on linking described in file LICENSE.
+
+   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 file
+   LICENSE for more details.
+*)
+(* 	$Id: camlGI.ml,v 1.1 2005/06/12 21:45:11 chris_77 Exp $	 *)
+
+(* This file only gathers the various modules, much as -pack would do
+   but without its limitations (i.e., works on all platforms). *)
+
+module Cgi_types = struct INCLUDE "cgi_types.ml" end
+
+(* module type CGI_COMMON *)
+INCLUDE "cgi_common.mli"
+module Cgi_common : CGI_COMMON = struct INCLUDE "cgi_common.ml" end
+
+
+module Expires = struct INCLUDE "expires.ml" end
+module Cookie = struct INCLUDE "cookie.ml" end
+module Sendmail = struct INCLUDE "sendmail.ml" end
+
+module Cgi_std  = struct INCLUDE "cgi_std.ml" end
+module Cgi_fast = struct INCLUDE "cgi_fast.ml" end
+module Cgi = struct INCLUDE "cgi.ml" end
+
+module Template = struct INCLUDE "template.ml" end
+module DbiPool  = struct INCLUDE "dbiPool.ml" end
+(* File: camlGI.mli
+
+   Copyright (C) 2004
+
+     Christophe Troestler
+     email: Christophe.Troestler@umh.ac.be
+     WWW: http://www.umh.ac.be/math/an/software/
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   version 2.1 as published by the Free Software Foundation, with the
+   special exception on linking described in file LICENSE.
+
+   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 file
+   LICENSE for more details.
+*)
+(* 	$Id: camlGI.mli,v 1.8 2005/06/12 21:44:22 chris_77 Exp $	 *)
+
+(** Library for writing (F)CGI programs.
+
+    CamlGI supports multiple connections and multiplexed requests.
+    You can execute those concurrently with the model of your choice
+    (unix fork, threads,...).
+
+    This library is mostly compatible with the Cgi library of mod_caml
+    so porting programs between the two is fairly straightforward.
+*)
+
+
+(** Generate and parse cookies *)
+module Cookie :
+sig
+  class cookie :
+    name:string -> value:string -> max_age:int option ->
+    domain:string -> path:string -> secure:bool ->
+  object
+    method name : string
+      (** Return the name of the cookie. *)
+    method value : string
+      (** Return the value of the cookie. *)
+    method max_age : int option
+      (** Lifetime of the cookie in seconds. *)
+    method domain : string
+      (** Return the domain of the cookie, or "" if not set. *)
+    method path : string
+      (** Return the path of the cookie, or "" if not set. *)
+    method secure : bool
+      (** Return true if the cookie is a secure cookie. *)
+
+    method set_name : string -> unit
+      (** Set the name of the cookie. *)
+    method set_value : string -> unit
+      (** Set the value of the cookie. *)
+    method set_max_age : int option -> unit
+      (** [#set_max_age (Some s)] set the lifetime of the cookie to
+	  [s] seconds [s].  [#set_max_age None] means that the cookie
+	  will be discarded when the client broser exits. *)
+    method set_domain : string -> unit
+      (** Set the domain of the cookie. *)
+    method set_path : string -> unit
+      (** Set the path of the cookie. *)
+    method set_secure : bool -> unit
+      (** Set the cookie as a secure cookie.  Secure cookies are only
+	  transmitted to HTTPS servers. *)
+
+    method to_string : string
+      (** Return the string representation of the cookie. *)
+  end
+
+  val cookie : ?max_age:int -> ?domain:string -> ?path:string ->
+    ?secure:bool -> string -> string -> cookie
+    (** [cookie ?expires ?domain ?path name value] creates a cookie
+	with name [name] and value [value].
+
+	@param max_age lifetime of the cookie in seconds (default: none).
+	@param domain  domain of the cookie (default: "").
+	@param path    path of the cookie (default: "").
+	@param secure  whether the cookie is secure (default: [false]).
+    *)
+
+  val parse : string -> cookie list
+    (** [parse header] parse zero or more cookies.  Normally [header]
+	comes from a "Cookie: header" field. *)
+end
+
+
+(** {2 Main CGI module} *)
+
+(** (F)CGI high level functions *)
+module Cgi :
+sig
+  exception HttpError of int
+
+  module Request :
+  sig
+    type t
+      (** Type representing the information contained in one request
+	  of the web server. *)
+
+    (** Possible roles of the CGI script *)
+    type role =
+      | Responder (** Receive the information associated with an HTTP
+		      request and generates an HTTP response. *)
+      | Authorizer (** Receive the information associated with an HTTP
+		       request and generates an (un)authorized
+		       decision. *)
+      | Filter (** Receive the information associated with an HTTP
+		   request plus an extra stream of data and generates a
+		   filtered version of the data stream as an HTTP
+		   response. *)
+
+    (** CGI or FCGI script *)
+    type gateway =
+      | CGI of int * int
+      | FCGI of int
+
+    val gateway : t -> gateway
+      (** The type and version of the CGI used. *)
+    val role : t -> role
+      (** The role of the script. *)
+
+    val path_info : t -> string
+      (** Returns the PATH_INFO, that is the portion of the URI
+	  following the script name but preceding the query data.  "/"
+	  represent a single void path segment.  The CGI specifications
+	  recommend to return "404 Not Found" if path_info <> "" but is
+	  not used. *)
+    val protocol : t -> string
+      (** The protocol of the request, in uppercase.  E.g. "HTTP/1.1". *)
+    val remote_addr : t -> string
+      (** The IP adress of the client making the request.  Note it can
+	  be the one of a proxy in the middle. *)
+    val server_name : t -> string
+      (** Name of the server, derived from the host part of the script URI. *)
+    val server_port : t -> int
+      (** The port on which the request was received. *)
+    val server_software : t -> string
+      (** The name and version of the web server software answering the
+	  request. *)
+
+    val accept : t -> string
+      (** Returns the list of accepted MIME types by the client. *)
+    val accept_charset : t -> string
+      (** Return a list of charset supported by the client. *)
+    val accept_encoding : t -> string
+      (** List of encodings supported by the client. *)
+    val auth : t -> string
+      (** The HTTP authentication scheme.  E.g. "Basic".  See section 11
+	  of the HTTP/1.1 specification for more details. *)
+    val user : t -> string
+      (** The user-ID supplied when [auth r = "Basic"]. *)
+    val user_agent : t -> string
+      (** The identification of the client browser. *)
+
+    val metavar : t -> string -> string
+      (** [metavar r name] returns the value of the CGI metavariable
+	  [name] for the request [r].  (Remember that CGI does not
+	  distinguish between nonexisting arguments and arguments with
+	  value [""].) *)
+
+    val print_string : t -> string -> unit
+    val prerr_string : t -> string -> unit
+  end
+
+
+  (** {3 Setting up the application server} *)
+
+  type connection
+
+  val establish_server : ?max_conns:int -> ?max_reqs:int ->
+    ?sockaddr:Unix.sockaddr -> ?post_max:int ->
+    (connection -> unit) -> unit
+    (** [establish_server ?max_conns ?max_reqs ?sockaddr ?post_max f]
+	starts a server listening on the socket appropriate for CGI or
+	FCGI and, for each accepted connection [conn], executes [f
+	conn].  The exceptions possibly raised by [f] are not caught
+	by [establish_server].  It is no problem that [f] starts a new
+	thread to handle the connection (and thus returns immediately).
+
+	@param max_conns is the maximum of connections the web server
+	can make to this script.  By default, each connection is
+	processed sequentially, so the default value for [max_conns]
+	is [1].  If you start processes or threads to handle
+	connections, it is your responsability not to accept more than
+	[max_conns] connections.  The value of [max_conns] only serves
+	to inform the web server about the capabilities of the FCGI
+	script.
+
+	@param max_reqs is the maximum of requests a web server can
+	multiplex through a given connection.  Again, if you start
+	processes ot threads to handle requests, it is your
+	responsability to limit the number of them.  [max_reqs] is
+	only used to inform the web server about how many requests it
+	can multiplex on a given connection.  Beware that if you set
+	[max_reqs] to [1] but have threads handling different requests
+	of a given connection, the outputs may mix-up (thus be
+	incorrect).
+
+	@param sockaddr the unix or the TCP/IP socket that the script
+	will use to communicate with the web server.  Setting this
+	implies that the script uses the FCGI protocol.  By default,
+	on uses what is appropriate for the CGI OR FCGI protocol.  For
+	example, if your script is listening on port 8888 on a
+	possibly remote machine, you can use
+	[Unix.ADDR_INET(Unix.inet_addr_any, 8888)].
+
+	@param post_max set the maximum size for POSTed requests in
+	bytes.  This is a security feature to prevent clients from
+	overrunning the server with data.  The default is
+	[Sys.max_string_length], meaning no limit (besides OCaml
+	ones).
+
+	For FastCGI, the environment variable FCGI_WEB_SERVER_ADDRS
+	may be used to specify a coma separated list of IP addresses
+	from which the web server can connect.  If not set, any
+	address is accepted.  *)
+
+  val handle_requests : ?fork:((Request.t -> unit) -> Request.t -> unit) ->
+    (Request.t -> unit) -> connection -> unit
+    (** [handle_requests ?fork f conn] listen on the connection [conn]
+	for requests.  For each completed request [req], it executes
+	[fork f req].
+
+	@param fork the function that starts a new process or thread.
+	The default is to execute [f] and only after continue to
+	listen for more requests.
+
+	Exceptions thrown by [f] are caught (so the possible thread
+	executing [f] will not be terminated by these).  The exception
+	[Exit] is caught and ignored (this is considered a valid way
+	of ending a script).  {!CamlGI.Cgi.HttpError} exceptions are
+	turned into appropriate error codes.  All other exceptions
+	provoke a internal server error and are logged in the server
+	error log.
+
+	Note that the exceptions raised by [fork] are NOT caught. *)
+
+  val register_script : ?sockaddr:Unix.sockaddr -> (Request.t -> unit) -> unit
+    (** Scripts must call [register_script f] once to register their
+	main function [f].  This should be called last (nothing that
+	follows will be executed).  The data is buffered and may not
+	be fully written before [f] ends.
+
+	This is actually a convenience function that sets up a server (with
+	[establish_server]) and processes (through [handle_requests])
+	all connections and requests sequentially -- i.e. no
+	fork/thread. *)
+
+
+  (** {3 Cgi} *)
+
+  (** Type of acceptable template objects. *)
+  class type template =
+  object
+    method output : (string -> unit) -> unit
+      (** [#output print] must use the [print] function to output the
+	  template (with the necessary substitutions done,...). *)
+  end
+
+  exception Abort
+    (** Exception raised by all terminating cgi methods if the server
+	requires to abort the request. *)
+
+  (** Type returned by [cgi#upload] method. *)
+  type upload_data = {
+    upload_value: string;
+    upload_filename: string;
+    upload_content_type: string
+  }
+
+  (** [new cgi r] creates a cgi object for the request [r].  Note that
+      you are advised not to create more than one cgi per request
+      unless you know what you are doing.  *)
+  class cgi : Request.t ->
+  object
+    method header : ?content_type:string -> ?cookie:Cookie.cookie ->
+      ?cookies:Cookie.cookie list -> ?cookie_cache:bool -> unit -> unit
+      (** Emit the header. The default content type is "text/html". *)
+
+    method template : 'a. ?content_type:string -> ?cookie:Cookie.cookie ->
+      ?cookies:Cookie.cookie list -> ?cookie_cache:bool ->
+      (#template as 'a) -> unit
+      (** Emit the header (unless #header was issued before) followed by
+	  the given template.  @raise Failure if the output is not
+	  successful. *)
+
+    method exit : unit -> 'a
+    (** Exit the current cgi script.  (You need not to call this at
+	the end of your code, just if you want to exit earlier.)  *)
+
+    method redirect : ?cookie:Cookie.cookie -> ?cookies:Cookie.cookie list ->
+      ?cookie_cache:bool -> string -> 'a
+      (** [#redirect ?cookie ?cookies url] quits the current cgi
+	  script and send to the client a redirection header to [url]. *)
+
+    method url : unit -> string
+      (** Return the URL of the script. *)
+
+    method param : string -> string
+    (** [#param name] returns the "first" value of the parameter
+	[name].  @raise Not_found if [name] does not designate a valid
+	parameter. *)
+
+    method param_all : string -> string list
+      (** [#param_all name] returns all the values of the parameter
+	  [name].  @raise Not_found if [name] does not designate a
+	  valid parameter. *)
+
+    method param_exists : string -> bool
+      (** Return true iff the named parameter exists. *)
+
+    method param_true : string -> bool
+    (** This method returns false if the named parameter is missing,
+	is an empty string, or is the string ["0"]. Otherwise it
+	returns true. Thus the intent of this is to return true in the
+	Perl sense of the word.  If a parameter appears multiple
+	times, then this uses the first definition and ignores the
+	others. *)
+
+    method params : (string * string) list
+      (** Return an assoc-list mapping name -> value for all parameters.
+	  Note that CGI scripts may have multiple values for a single name. *)
+
+    method is_multipart : bool
+      (** Returns true iff the request was a [multipart/form-data]
+	  [POST] request.  Such requests are used when you need to
+	  upload files to a CGI script.  *)
+
+    method upload : string -> upload_data
+      (** For multipart forms only.  Returns the full upload data passed
+	  by the browser for a particular parameter.
+	  @raise Not_found is no such parameter exists.  *)
+
+    method upload_all : string -> upload_data list
+      (** For multipart forms only.  As for [#upload], but returns all
+	  uploads.  *)
+
+    method cookie : string -> Cookie.cookie
+      (** Return the named cookie, or throw [Not_found]. *)
+
+    method cookies : Cookie.cookie list
+      (** Return a list of all cookies passed to the script. *)
+
+    method log : string -> unit
+      (** [log s] Log the message [s] into the webserver log. *)
+
+    method request : Request.t
+      (** Returns the original request object (passed in the constructor). *)
+  end
+
+
+  val random_sessionid : unit -> string
+    (** Generates a 128 bit (32 hex digits) random string which can be
+	used for session IDs, random cookies and so on.  The string
+	returned will be very random and hard to predict, at least if
+	your platform possesses /dev/urandom *)
+
+
+  module Cgi_args :
+  sig
+    val parse : string -> (string * string) list
+      (** [parse qs] parses up a standard CGI query string (such as
+	  ["a=1&b=2"]), and returns the list of name, value pairs.  This
+	  is a helper function.  The [cgi] class does this parsing for
+	  you, so you shouldn't need to use this function unless you're
+	  doing something out of the ordinary.  *)
+
+    val make : (string * string) list -> string
+      (** [make bindings] returns a query string from the list
+	  [bindings] of name, value pairs.  For example, [make
+	  [("a", "1"); ("b", "2")]] returns ["a=1&b=2"]. *)
+  end
+
+
+  (** {3 Apache names for HTTP errors} *)
+
+  val cHTTP_CONTINUE : int
+  val cHTTP_SWITCHING_PROTOCOLS : int
+  val cHTTP_PROCESSING : int
+  val cHTTP_OK : int
+  val cHTTP_CREATED : int
+  val cHTTP_ACCEPTED : int
+  val cHTTP_NON_AUTHORITATIVE : int
+  val cHTTP_NO_CONTENT : int
+  val cHTTP_RESET_CONTENT : int
+  val cHTTP_PARTIAL_CONTENT : int
+  val cHTTP_MULTI_STATUS : int
+  val cHTTP_MULTIPLE_CHOICES : int
+  val cHTTP_MOVED_PERMANENTLY : int
+  val cHTTP_MOVED_TEMPORARILY : int
+  val cHTTP_SEE_OTHER : int
+  val cHTTP_NOT_MODIFIED : int
+  val cHTTP_USE_PROXY : int
+  val cHTTP_TEMPORARY_REDIRECT : int
+  val cHTTP_BAD_REQUEST : int
+  val cHTTP_UNAUTHORIZED : int
+  val cHTTP_PAYMENT_REQUIRED : int
+  val cHTTP_FORBIDDEN : int
+  val cHTTP_NOT_FOUND : int
+  val cHTTP_METHOD_NOT_ALLOWED : int
+  val cHTTP_NOT_ACCEPTABLE : int
+  val cHTTP_PROXY_AUTHENTICATION_REQUIRED : int
+  val cHTTP_REQUEST_TIME_OUT : int
+  val cHTTP_CONFLICT : int
+  val cHTTP_GONE : int
+  val cHTTP_LENGTH_REQUIRED : int
+  val cHTTP_PRECONDITION_FAILED : int
+  val cHTTP_REQUEST_ENTITY_TOO_LARGE : int
+  val cHTTP_REQUEST_URI_TOO_LARGE : int
+  val cHTTP_UNSUPPORTED_MEDIA_TYPE : int
+  val cHTTP_RANGE_NOT_SATISFIABLE : int
+  val cHTTP_EXPECTATION_FAILED : int
+  val cHTTP_UNPROCESSABLE_ENTITY : int
+  val cHTTP_LOCKED : int
+  val cHTTP_FAILED_DEPENDENCY : int
+  val cHTTP_INTERNAL_SERVER_ERROR : int
+  val cHTTP_NOT_IMPLEMENTED : int
+  val cHTTP_BAD_GATEWAY : int
+  val cHTTP_SERVICE_UNAVAILABLE : int
+  val cHTTP_GATEWAY_TIME_OUT : int
+  val cHTTP_VERSION_NOT_SUPPORTED : int
+  val cHTTP_VARIANT_ALSO_VARIES : int
+  val cHTTP_INSUFFICIENT_STORAGE : int
+  val cHTTP_NOT_EXTENDED : int
+
+  val cDOCUMENT_FOLLOWS : int
+  val cPARTIAL_CONTENT : int
+  val cMULTIPLE_CHOICES : int
+  val cMOVED : int
+  val cREDIRECT : int
+  val cUSE_LOCAL_COPY : int
+  val cBAD_REQUEST : int
+  val cAUTH_REQUIRED : int
+  val cFORBIDDEN : int
+  val cNOT_FOUND : int
+  val cMETHOD_NOT_ALLOWED : int
+  val cNOT_ACCEPTABLE : int
+  val cLENGTH_REQUIRED : int
+  val cPRECONDITION_FAILED : int
+  val cSERVER_ERROR : int
+  val cNOT_IMPLEMENTED : int
+  val cBAD_GATEWAY : int
+  val cVARIANT_ALSO_VARIES : int
+end
+
+
+(** {2 Useful modules to write scripts} *)
+
+(** Template system compatible with mod_caml one. *)
+module Template :
+sig
+  type var =
+    | VarString of string			(** ::tag:: *)
+    | VarTable of table_row list		(** ::table(tag):: *)
+    | VarConditional of bool			(** ::if(tag):: *)
+    | VarCallback of (string list -> string)	(** ::call(f, x1,...):: *)
+  and table_row = (string * var) list
+
+  (** Variables are either simple string, tables, conditionals or callbacks.
+
+      A simple string is set with [template#set "name" s] where [s] will
+      be automatically escaped depending on the declaration in the template:
+
+      - [::name::] does no escaping;
+      - [::name_url::] escapes for URL encoding, make it suitable in a
+        link [<a href="::name_url::">];
+      - [::name_html::] escapes for HTML display of the string;
+      - [::name_html_tag::] escapes the string to make it suitable to be
+        placed between quotes in an HTML tag, e.g.
+        [<input value="::name_html_tag::">];
+      - [::name_html_textarea::] escapes the string to make it suitable
+        to be placed between [<textarea>...</textarea>].
+
+      Tables are declared in the template by [::table(name)::] {i row
+      template} [::end::].  The {i row template} can contain other
+      variables.  Calling [template#table "name" rows], where [rows] is
+      a list [[row1, row2,...,rowN]], will insert [N] {i row templates}
+      with each template having its variables set thanks to [row1],...
+      each of which is an associative list name -> value (of type
+      {!Template.table_row}).
+
+      Conditionals are declared in the template by [::if(name)
+      .. ::else:: .. ::end::] with the "else" clause being optional.
+      Calling [template#conditional] sets up a conditional value.
+
+      Calling [template#callback "fname" f] sets up the callback
+      function declared by [::call(fname,arg1,...,argN)::] replacing the
+      call by the value of [f] applied to the list [[arg1,...,argN]].
+      The string returned by [f] can be escaped by using suffices in the
+      template as for simple tags: [::call(fname,arg1,...,argN)_html::],...
+
+      A template may also include other templates with [::include(filename)::].
+  *)
+
+
+  (** [new template ?filename tpl] computes a new template from the
+      string [tpl].  Once the object has been created, it can be used
+      in a single thread.
+
+      @param filename if set, it is used to determine the base path for
+      [::include()::] tags in the template (default: current directory)
+      and to reuse the templates of already compiled files
+      (e.g. headers, footers,...).  *)
+  class template : ?filename:string -> string ->
+  object
+    method set : string -> string -> unit
+      (** Set a variable in the template. *)
+
+    method table : string -> table_row list -> unit
+      (** Set a table in the template. *)
+
+    method conditional : string -> bool -> unit
+      (** Set a conditional in the template. *)
+
+    method callback : string -> (string list -> string) -> unit
+      (** Set a callback in the template. *)
+
+    method to_string : string
+      (** Return the template as a string. *)
+
+    method to_channel : out_channel -> unit
+      (** Write the template to a channel. *)
+
+    method output :(string -> unit) -> unit
+      (** [output out] outputs the template, calling [out s] for each
+	  write of a string [s]. *)
+
+    method source : string
+      (** Return the original source code for the template. *)
+  end
+
+  val template : string -> template
+    (** Compile the template from a named file.  Not thread safe. *)
+
+  val template_from_string : ?filename:string -> string -> template
+    (** Compile the template from a literal string.  Not thread safe. *)
+
+  val template_from_channel : ?filename:string -> in_channel -> template
+    (** Compile the template from a channel.  Not thread safe. *)
+end
+
+
+module DbiPool :
+sig
+  module type DbiDriverT =
+  sig
+    type connection
+    val connect : ?host:string -> ?port:string ->
+      ?user:string -> ?password:string -> string -> connection
+    val close : connection -> unit
+    val closed : connection -> bool
+    val commit : connection -> unit
+    val ping : connection -> bool
+    val rollback : connection -> unit
+  end
+
+
+  module DbiPool(Dbi_driver : DbiDriverT) :
+    (sig
+       type connection = Dbi_driver.connection
+       val get : Cgi.Request.t ->
+	 ?host:string -> ?port:string -> ?user:string -> ?password:string ->
+	 string -> connection
+     end)
+    (** [module MyPool = DbiPool(Dbi_postgresql)] creates a pool of
+	PostgreSQL database handles.  To use them:
+
+	[let dbh = MyPool.get request "database_name"]
+
+	Returns an unused or newly created [Dbi.connection] handle
+	[dbh] from the pool of database handles which is valid until
+	the end of the current request.
+
+	The parameters uniquely identify the database name.  Separate
+	pools are maintained for each combination of parameters.
+
+	The connection is automatically returned to the pool at the end of
+	the current request.  After this time the connection may be
+	given away to another user.  For this reason, the calling code must
+	NEVER stash the connection across requests (instead, call
+	[get] to get a new handle each time).
+
+	On returning the handle to the pool, the pool performs a
+	ROLLBACK operation on the handle, thus losing any changes
+	(INSERT, etc.) made to the database.  If the program wants to
+	preserve changes, it must perform a COMMIT operation itself,
+	by calling [Dbi.connection.commit].  *)
+end
+
+(** Dates conforming RFC 1123 *)
+module Expires :
+sig
+  val make : int -> string
+    (** [make s] generates an a date [s] seconds from now in fixed
+	format (RFC 1123).  [s] may be negative.  The date format is
+	suitable used for e.g. a HTTP 'Expires' header -- if [s < 0],
+	it means the page expires in the past, and should be removed
+	from content caches.  *)
+  val past : unit -> string
+    (** Generate an date in the past (in theory this forces caches
+	along the way to remove content).  *)
+  val short : unit -> string
+    (** Generate a date now + 5 minutes.  This can typically be used
+	for pages containing news which is updated frequently.  *)
+  val medium : unit -> string
+    (** Generate a date now + 24 hours.  This can be used for content
+	generated from a database which doesn't change much.  *)
+  val long : unit -> string
+    (** Generate a date now + 2 years.  This should be used for
+	content which really will never change.  *)
+end
+
+(** Elementary email functions *)
+module Sendmail :
+sig
+  exception Failure of string
+    (** This exception may be thrown by any of the functions in this
+	module, to indicate some problem running sendmail.  *)
+
+  val sendmail : string ref
+    (** This contains the path to sendmail (or the binary which acts
+	like sendmail, as in the case of exim).  Normally this is set
+	to the correct value for your OS, eg. ["/usr/sbin/sendmail"].
+    *)
+  val sendmail_args : string ref
+    (** This contains the arguments passed to sendmail, normally ["-t
+	-i"].  You could add the ["-f"] option to specify the sender
+	email address, provided that the current user is one of
+	sendmail's trusted users.  *)
+
+  val send : unit -> out_channel
+    (** Begin sending an email.  This returns a channel on which you
+	should write the mail headers, followed by a blank line,
+	followed by the message body.  After writing the mail you
+	must call {!CamlGI.Sendmail.close}.
+
+	[send] does not perform any sort of processing or escaping on
+	the message.  *)
+
+  val close : out_channel -> unit
+    (** Close the output channel.  You must call this on the channel
+	returned from {!CamlGI.Sendmail.send}.  *)
+
+  val send_mail : ?subject:string ->
+    ?to_addr:string list -> ?cc:string list -> ?bcc:string list ->
+    ?from:string -> ?content_type:string ->
+    ?headers:(string * string) list ->
+    string -> unit
+    (** This is a less flexible, but simpler interface to sending
+	mail.  You specify, optionally, Subject line, To, Cc, Bcc,
+	From and Content-Type headers, and a body, and a mail is
+	generated.
+
+	[send_mail] will correctly escape headers, provided they
+	are in strict 7 bit US-ASCII.  Its behaviour on non-7 bit
+	sequences is currently undefined (and probably wrong).
+	[send_mail] will not process or escape any part of the body.
+    *)
+end
+(* File: cgi.ml
+
+   Objective Caml Library for writing (F)CGI programs.
+
+   Copyright (C) 2004
+
+     Christophe Troestler
+     email: Christophe.Troestler@umh.ac.be
+     WWW: http://www.umh.ac.be/math/an/software/
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   version 2.1 as published by the Free Software Foundation, with the
+   special exception on linking described in file LICENSE.
+
+   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 file
+   LICENSE for more details.
+*)
+(* 	$Id: cgi.ml,v 1.18 2005/06/12 21:35:46 chris_77 Exp $	 *)
+
+
+open Cgi_common
+include Cgi_types
+open Printf
+
+
+module Request =
+struct
+  type t = Cgi_types.request
+
+  type role = Cgi_types.role =
+    | Responder
+    | Authorizer
+    | Filter
+
+  type gateway = Cgi_types.gateway =
+    | CGI of int * int
+    | FCGI of int
+
+  let gateway r = r.gateway
+  let role r = r.role
+
+  let metavar r name =
+    try Hashtbl.find r.metavars name with Not_found -> ""
+
+  let path_info r = metavar r "PATH_INFO"
+  let protocol r = String.uppercase(metavar r "SERVER_PROTOCOL")
+  let remote_addr r = metavar r "REMOTE_ADDR"
+  let server_name r = metavar r "SERVER_NAME"
+  let server_port r =
+    try int_of_string(metavar r "SERVER_PORT") with _ -> 80
+  let server_software r = metavar r "SERVER_SOFTWARE"
+
+  let accept r = metavar r "HTTP_ACCEPT"
+  let accept_charset r = metavar r "HTTP_ACCEPT_CHARSET"
+  let accept_encoding r = metavar r "HTTP_ACCEPT_ENCODING"
+  let auth r = metavar r "AUTH_TYPE"
+  let user r = metavar r "REMOTE_USER"
+  let user_agent r = metavar r "HTTP_USER_AGENT"
+
+  let print_string r s = r.print_string s
+  let prerr_string r s = r.prerr_string s
+end
+
+
+(* Generate a suitable random number (32 hex digits) for use in random
+   cookies, session IDs, etc.  These numbers are supposed to be very
+   hard to predict.  *)
+let random_sessionid =
+  if Sys.file_exists "/dev/urandom" then begin
+    fun () ->
+      let s = String.create 32 (* local => thread safe *) in
+      let chan = open_in_bin "/dev/urandom" in
+      for i = 0 to 15 do
+	let b = input_byte chan in
+	let i2 = 2 * i in
+	s.[i2] <- char_of_hex(b lsr 4);
+	s.[i2 + 1] <- char_of_hex(b land 0x0F)
+      done;
+      close_in chan;
+      s
+  end
+  else begin
+    Random.self_init();
+    fun () ->
+      let s = String.create 32 in
+      for i = 0 to 7 do
+	let b = Random.int 0x10000
+	and i4 = 4 * i in
+	s.[i4] <- char_of_hex(b land 0xF);
+	let b = b lsr 4 in
+	s.[i4 + 1] <- char_of_hex(b land 0xF);
+	let b = b lsr 4 in
+	s.[i4 + 2] <- char_of_hex(b land 0xF);
+	s.[i4 + 3] <- char_of_hex(b lsr 4)
+      done;
+      s
+  end
+
+
+
+(* cgi object
+ **********************************************************************
+ * The fact that it is a CGI or a fast CGI is hidden by the object.
+ *)
+
+class type template =
+object
+  method output : (string -> unit) -> unit
+end
+
+let cookie_header r ~(cookie:Cookie.cookie option)
+    ~(cookies:Cookie.cookie list option) cookie_cache =
+  (match cookie with
+   | None -> ()
+   | Some c -> r.print_string("Set-Cookie: " ^ c#to_string ^ "\r\n"));
+  (match cookies with
+   | None -> ()
+   | Some cs ->
+       List.iter(fun c -> r.print_string("Set-Cookie: "
+					 ^ c#to_string ^ "\r\n")) cs);
+  if not cookie_cache then begin
+    r.print_string "Cache-control: no-cache=\"set-cookie\"\r\n";
+    (* For HTTP/1.0 proxies along the way.  Cache-control directives
+       override this for HTTP/1.1.  *)
+    r.print_string "Expires: Thu, 1 Jan 1970 00:00:00 GMT\r\n";
+    r.print_string "Pragma: no-cache\r\n"
+  end
+
+
+class cgi r =
+  let cheader =
+    try Hashtbl.find r.metavars "HTTP_COOKIE" with Not_found -> "" in
+  let cookies = Cookie.parse cheader in
+  let log =
+    match r.gateway with
+    | CGI _ -> Cgi_std.log
+    | FCGI _ -> r.prerr_string (* Date and executable name already
+				  provided by FCGI handler *)
+  in
+object(self)
+
+  method header ?(content_type="text/html")
+    ?cookie ?cookies ?(cookie_cache=false) () =
+    if r.abort then raise Abort;
+    if r.header_not_emitted then begin
+      cookie_header r ?cookie ?cookies cookie_cache;
+      r.print_string(sprintf "Content-type: %s\r\nStatus: 200 OK\r\n\r\n"
+		       content_type);
+      r.header_not_emitted <- false;
+    end
+
+  method template : 'a. ?content_type:string -> ?cookie:Cookie.cookie ->
+    ?cookies:Cookie.cookie list -> ?cookie_cache:bool ->
+    (#template as 'a) -> unit =
+    fun ?content_type ?cookie ?cookies ?(cookie_cache=false) template ->
+      self#header ?content_type ?cookie ?cookies ();
+      template#output (fun s -> r.print_string s)
+
+  method exit : 'a. unit -> 'a = (fun () -> raise Exit)
+
+  method redirect : 'a. ?cookie:Cookie.cookie ->
+    ?cookies:Cookie.cookie list -> ?cookie_cache:bool -> string -> 'a
+    = fun ?cookie ?cookies ?(cookie_cache=false) url ->
+      cookie_header r ?cookie ?cookies cookie_cache;
+      r.print_string("Location: " ^ url ^ "\r\n\r\n"); (* FIXME *)
+      raise(HttpError cHTTP_MOVED_TEMPORARILY)
+
+  method url () =
+    if r.abort then raise Abort;
+    try Hashtbl.find r.metavars "SCRIPT_NAME"
+    with Not_found -> ""
+
+  method param name =
+    if r.abort then raise Abort;
+    Hashtbl.find r.params name
+
+  method param_all name =
+    if r.abort then raise Abort;
+    Hashtbl.find_all r.params name
+
+  method param_exists name =
+    if r.abort then raise Abort;
+    try  ignore (self#param name); true
+    with Not_found -> false
+
+  method param_true name =
+    try  let str = self#param name in str <> "" && str <> "0"
+    with Not_found -> false
+
+  method params =
+    if r.abort then raise Abort;
+    Hashtbl.fold (fun k v l -> (k,v) :: l) r.params []
+
+  method is_multipart =
+    if r.abort then raise Abort;
+    (Hashtbl.length r.uploads > 0)
+
+  method upload name =
+    if r.abort then raise Abort;
+    Hashtbl.find r.uploads name
+
+  method upload_all name =
+    if r.abort then raise Abort;
+    Hashtbl.find_all r.uploads name
+
+  method cookie name =
+    if r.abort then raise Abort;
+    List.find (fun cookie -> cookie#name = name) cookies
+
+  method cookies =
+    if r.abort then raise Abort;
+    cookies
+
+  method log = log
+
+  method request = r
+end
+
+
+module Cgi_args =
+struct
+  (* We will not use [parse_query_range] because the user may not
+     expect the query string to be overwritten.  In order not to copy
+     the entire query string (which may be big), we will split it
+     first. *)
+  let parse qs =
+    let key_val = rev_split '&' qs in
+    let split s =
+      try
+	let i = String.index s '=' in
+	(decode_range s 0 i,  decode_range s (succ i) (String.length s))
+      with
+	Not_found -> (decode_range s 0 (String.length s), "")   in
+    List.rev_map split key_val
+
+
+  let make bindings =
+    let encode (key, value) = encode key ^ "=" ^ encode value in
+    String.concat "&" (List.map encode bindings)
+end
+
+
+
+(* Registering scripts
+ ***********************************************************************)
+
+type cgi_type =
+  | CGI_std	(* Standard CGI *)
+  | CGI_socket	(* FastCGI -- Unix sockets *)
+  | CGI_pipe	(* FastCGI -- Win named pipes *)
+
+(* Test whether the script is used as fastcgi. *)
+let cgi_type() =
+  match Sys.os_type with
+  | "Unix" | "Cygwin" ->
+      begin
+	try let _ = Unix.getpeername Cgi_fast.fcgi_listensock in CGI_std
+	with
+	| Unix.Unix_error(Unix.ENOTCONN, _, _) -> CGI_socket
+	| Unix.Unix_error(_, _, _) -> CGI_std
+      end
+  | "Win32" ->
+      begin
+	try let _ = Unix.getenv "REQUEST_METHOD" in CGI_std
+	with Not_found -> CGI_pipe
+      end
+  | os -> failwith("Unsupported platform: " ^ os)
+
+
+let no_fork f req = f req
+
+(* FIXME: post_max unused *)
+let establish_server ?(max_conns=1) ?(max_reqs=1) ?sockaddr
+    ?(post_max=Sys.max_string_length) (f : connection -> unit) =
+  match sockaddr with
+  | None ->
+      begin match cgi_type() with
+      | CGI_std ->
+	  (* Normal CGI -- prepare a single request *)
+	  f { fd = Unix.stdin;
+	      max_conns = 1;
+	      max_reqs = 1;
+	      handle_requests = Cgi_std.handle_request;
+	      requests = Hashtbl.create 0;
+	    }
+      | CGI_socket ->
+	  Cgi_fast.establish_server_socket ~max_conns ~max_reqs
+	    Cgi_fast.fcgi_listensock f
+      | CGI_pipe ->
+	  Cgi_fast.establish_server_pipe ~max_conns:1 ~max_reqs
+	    Cgi_fast.fcgi_listensock f
+      end
+
+  | Some sockaddr ->
+      (* FastCGI on a distant machine, listen on the given socket. *)
+      let sock =
+	Unix.socket (Unix.domain_of_sockaddr sockaddr) Unix.SOCK_STREAM 0 in
+      Unix.setsockopt sock Unix.SO_REUSEADDR true;
+      Unix.bind sock sockaddr;
+      Cgi_fast.establish_server_socket ~max_conns ~max_reqs sock f
+
+
+let handle_requests ?(fork=no_fork) f conn =
+  conn.handle_requests fork f conn
+
+let register_script ?sockaddr f =
+  establish_server ~max_conns:1 ~max_reqs:1 ?sockaddr
+    (fun conn -> conn.handle_requests no_fork f conn)
+(* File: cgi_common.ml
+
+   Objective Caml Library for writing (F)CGI programs.
+
+   Copyright (C) 2005
+
+     Christophe Troestler
+     email: Christophe.Troestler@umh.ac.be
+     WWW: http://www.umh.ac.be/math/an/software/
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   version 2.1 as published by the Free Software Foundation, with the
+   special exception on linking described in file LICENSE.
+
+   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 file
+   LICENSE for more details.
+*)
+(* 	$Id: cgi_common.ml,v 1.10 2006/01/08 22:19:31 chris_77 Exp $	 *)
+
+open Cgi_types
+
+
+let rev_split =
+  let rec split_next acc i0 i1 delim s =
+    if i1 = String.length s then
+      (String.sub s i0 (i1 - i0)) :: acc
+    else if String.unsafe_get s i1 = delim then
+      split_next ((String.sub s i0 (i1 - i0)) :: acc) (i1+1) (i1+1) delim s
+    else
+      split_next acc i0 (i1 + 1) delim s  in
+  split_next [] 0 0
+
+let is_prefix =
+  let rec is_pre i len pre s =
+    if i < len then
+      (String.unsafe_get pre i = String.unsafe_get s i)
+      && is_pre (i+1) len pre s
+    else true in
+  fun prefix s ->
+    (String.length prefix <= String.length s)
+    && is_pre 0 (String.length prefix) prefix s
+
+
+(* URL encoding and decoding
+ **********************************************************************
+ * See RFC 2396.
+ *)
+
+(* Decoding *)
+
+exception Hex_of_char
+
+let hex_of_char =
+  let code_a = Char.code 'a' - 10
+  and code_A = Char.code 'A' - 10 in
+  function
+  | '0' .. '9' as c -> Char.code c - Char.code '0'
+  | 'a' .. 'f' as c -> Char.code c - code_a
+  | 'A' .. 'F' as c -> Char.code c - code_A
+  | _ -> raise Hex_of_char
+
+
+let rec decode_range_loop i0 i up s =
+  if i0 >= up then i else begin
+    match String.unsafe_get s i0 with
+    | '+' ->
+	String.unsafe_set s i ' ';
+	decode_range_loop (succ i0) (succ i) up s
+    | '%' when i0 + 2 < up ->
+        let i1 = succ i0 in
+        let i2 = succ i1 in
+        let i0_next =
+          try
+            let v = hex_of_char(String.unsafe_get s i1) lsl 4
+                    + hex_of_char(String.unsafe_get s i2) in
+	    String.unsafe_set s i (Char.chr v);
+	    succ i2
+          with Hex_of_char ->
+	    String.unsafe_set s i '%';
+	    i1 in
+	decode_range_loop i0_next (succ i) up s
+    | c ->
+	String.unsafe_set s i c;
+	decode_range_loop (succ i0) (succ i) up s
+  end
+
+let is_space c =  c = ' ' || c = '\t' || c = '\r' || c = '\n'
+
+(* [rm_htspace s] returns the substring [s.[low .. up - 1]] stripped
+   of heading and trailing spaces. *)
+let rm_htspace =
+  let rec trailing_spaces j s = (* assume there is i s.t. s.[i] <> ' ' *)
+    if is_space(String.unsafe_get s j) then trailing_spaces (pred j) s
+    else j in
+  let rec rm_spaces i up s =
+    if i >= up then "" else begin
+      if is_space(String.unsafe_get s i) then rm_spaces (succ i) up s
+      else
+        (* s.[i] <> space so trailing_spaces will stop and return j >= i. *)
+        String.sub s i (trailing_spaces (pred up) s + 1 - i)
+    end in
+  fun low up s -> rm_spaces low up s
+
+(* We strip heading and trailing spaces of key-value data (even though
+   it does not conform the specs) because certain browsers do it, so
+   the user should not rely on them.
+   See e.g. https://bugzilla.mozilla.org/show_bug.cgi?id=114997#c6 *)
+let decode_range s low up =
+  if low >= up then "" else
+    let up = decode_range_loop low low up s in
+    rm_htspace low up s
+    (* String.sub s low (up - low) *)
+
+
+
+(* Encoding *)
+
+(* Use a table lookup for speed. *)
+let char_of_hex =
+  let hex = [| '0'; '1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9';
+	       'A'; 'B'; 'C'; 'D'; 'E'; 'F' |] in
+  fun i -> Array.unsafe_get hex i
+
+
+let encode_wrt is_special s0 =
+  let len = String.length s0 in
+  let encoded_length = ref len in
+  for i = 0 to len - 1 do
+    if is_special(String.unsafe_get s0 i) then
+      encoded_length := !encoded_length + 2
+  done;
+  let s = String.create !encoded_length in
+  let rec do_enc i0 i = (* copy the encoded string in s *)
+    if i0 < len then begin
+      let s0i0 = String.unsafe_get s0 i0 in
+      if is_special s0i0 then begin
+        let c = Char.code s0i0 in
+        let i1 = succ i in
+        let i2 = succ i1 in
+        String.unsafe_set s i '%';
+        String.unsafe_set s i1 (char_of_hex (c lsr 4));
+        String.unsafe_set s i2 (char_of_hex (c land 0x0F));
+        do_enc (succ i0) (succ i2)
+      end
+      else if s0i0 = ' ' then begin
+	String.unsafe_set s i '+';
+        do_enc (succ i0) (succ i)
+      end
+      else begin
+        String.unsafe_set s i s0i0;
+        do_enc (succ i0) (succ i)
+      end
+    end in
+  do_enc 0 0;
+  s
+
+
+(* Unreserved characters consist of all alphanumeric chars and the
+   following limited set of punctuation marks and symbols: '-' | '_' |
+   '.' | '!' | '~' | '*' | '\'' | '(' | ')'.  According to RFC 2396,
+   they should not be escaped unless the context requires it. *)
+let special_rfc2396 = function
+  | ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ',' (* Reserved *)
+  | '\000' .. '\031' | '\127' .. '\255' (* Control chars and non-ASCII *)
+  | '<' | '>' | '#' | '%' | '"'         (* delimiters *)
+  | '{' | '}' | '|' | '\\' | '^' | '[' | ']' | '`' (* unwise *)
+      -> true
+  | _ -> false
+(* ' ' must also be encoded but its encoding '+' takes a single char. *)
+
+let encode = encode_wrt special_rfc2396
+
+
+let special_rfc2068 = function
+  | '\000' .. '\031' | '\127' .. '\255' (* Control chars and non-ASCII *)
+  | '(' | ')' | '<' | '>' | '@' | ',' | ';' | ':' | '\\' | '"'
+  | '/' | '[' | ']' | '?' | '=' | '{' | '}' (* tspecials *)
+  | '+' | '%' (* not in RFC2068 but they serve to encode *)
+      -> true
+  | _ -> false
+(* ' ' must also be encoded but its encoding '+' takes a single char. *)
+
+let encode_cookie = encode_wrt special_rfc2068
+
+
+(* Query parsing
+ ***********************************************************************)
+(* We assume that the query string is just a temporary handle of the
+   key-val pairs so may be overwritten -- but we don't want to copy it
+   as it may be large.  So the decoding is optimized to be in-place.
+   The key-val pairs found are added to the hash [h]. *)
+
+let rec get_key qs i0 i up h =
+  if i >= up then Hashtbl.add h (decode_range qs i0 up) "" else
+    match String.unsafe_get qs i with
+    | '=' -> get_val qs (i+1) (i+1) up (decode_range qs i0 i) h
+    | '&' ->
+	Hashtbl.add h (decode_range qs i0 i) ""; (* key but no val *)
+	get_key qs (i+1) (i+1) up h
+    | _ ->
+	get_key qs i0 (i+1) up h
+and get_val qs i0 i up key h =
+  if i >= up then Hashtbl.add h key (decode_range qs i0 up) else
+    match String.unsafe_get qs i with
+    | '&' ->
+	Hashtbl.add h key (decode_range qs i0 i);
+	get_key qs (i+1) (i+1) up h
+    | _ -> get_val qs i0 (i+1) up key h
+
+(* [parse_query_range qs low up h] adds the key-val pairs found in
+   [qs.[low .. up-1]] to the hash [h]. *)
+let parse_query_range qs low up h =
+  get_key qs low low up h
+
+let parse_query qs h =
+  if qs <> "" then get_key qs 0 0 (String.length qs) h
+
+
+(* Knuth-Morris-Pratt algorithm
+ ***********************************************************************)
+
+let preprocess pat m =
+  let b = Array.make (m + 1) (-1) in
+  (* [b.(i)] = width of  the widest border of [pat.[0 .. i-1]]. *)
+  let j = ref(-1) in
+  for i = 0 to m-1 do
+    while !j >= 0 && String.unsafe_get pat !j <> String.unsafe_get pat i do
+      j := Array.unsafe_get b !j
+    done;
+    incr j;
+    Array.unsafe_set b (i+1) !j
+  done;
+  b
+
+
+(* [search pat s i0 i1] search the string [pat] in [s.[i0 .. i1-1]]
+   and return the position of the first match.
+   @raise Not_found if [pat] is not found. *)
+(* We favored the following imperative code because it is the fastest. *)
+exception Found of int
+
+let search pat =
+  let m = String.length pat in
+  let b = preprocess pat m in
+  fun s i0 i1 ->
+    let i = ref i0
+    and j = ref 0 in
+    try
+      while !i < i1 do
+	while !j >= 0 && String.unsafe_get s !i <> String.unsafe_get pat !j do
+	  j := Array.unsafe_get b !j
+	done;
+	incr i;
+	incr j;
+	if !j = m then raise(Found(!i - !j))
+      done;
+      raise Not_found
+    with Found i -> i
+
+
+(* [search_case_fold pat s i0 i1] does the same as [search pat s i0
+   i1] but in a case insensitive manner. *)
+let search_case_fold pat =
+  let m = String.length pat in
+  let pat = String.lowercase pat in
+  let b = preprocess pat m in
+  fun s i0 i1 ->
+      let i = ref i0
+    and j = ref 0 in
+    try
+      while !i < i1 do
+	while !j >= 0 && Char.lowercase(String.unsafe_get s !i)
+	  <> String.unsafe_get pat !j do
+	  j := Array.unsafe_get b !j
+	done;
+	incr i;
+	incr j;
+	if !j = m then raise(Found(!i - !j))
+      done;
+      raise Not_found
+    with Found i -> i
+
+(* RFC 2045 and RFC 822 values
+ ***********************************************************************)
+
+(* value ::= token | quoted-string         ; RFC2045
+ * token ::= 1*<any US-ASCII CHAR except SPACE, CTLs, tspecials>
+ *
+ * quoted-string ::= '"' *(qtext|quoted-pair) '"'
+ * qtext ::= <any CHAR except '"', '\\', CR and including linear-white-space>
+ * linear-white-space ::= 1*([CRLF] (SPACE|HTAB))     ; folding
+ * quoted-pair ::= '\\' CHAR
+ *)
+
+let special_rfc2045 = function
+  | '\000' .. '\031' | '\127' .. '\255' (* CTLs and non-ASCII (e.g. '\t') *)
+  | '(' | ')' | '<' | '>' | '@' | ',' | ';' | ':' | '\\' | '"'
+  | '/' | '[' | ']' | '?' | '='         (* tspecials *)
+  | ' ' -> true
+  | _ -> false
+
+let rec get_token s i1 i =
+  if i = i1 || special_rfc2045(String.unsafe_get s i)
+  then i
+  else get_token s i1 (succ i)
+
+(* ',' allowed in boundary but unlikely -- disabled for MSIE bug, see
+   [get_boundary] *)
+let rec get_token_boundary s i1 i =
+  if i = i1 || (let c = String.unsafe_get s i in special_rfc2045 c || c = ',')
+  then i
+  else get_token_boundary s i1 (succ i)
+
+
+(* [get_quoted_string s i0 i1 i j] get the string starting at [i0]
+   (i.e. s.[i0-1] = '"') and un-escape it.  [s.[i0 .. i1-1]] may be
+   overwritten. *)
+let rec get_quoted_string s i1 i j =
+  if i = i1 then raise Not_found;
+  match String.unsafe_get s i with
+  | '"' -> j
+  | '\\' ->
+      let i' = succ i in
+      if i' = i1 then raise Not_found;
+      String.unsafe_set s j (String.unsafe_get s i');
+      get_quoted_string s i1 (succ i') (succ j)
+  | '\r' ->
+      (* Check if folding *)
+      let i' = succ i in
+      let i'' = succ i' in
+      if i'' < i1 && String.unsafe_get s i' = '\n'
+	&& (let c = String.unsafe_get s i'' in c = ' ' || c = '\t') then begin
+	  String.unsafe_set s j ' '; (* folding equiv SPACE *)
+	  skip_folding s i1 (succ i'') (succ j)
+	end
+      else raise Not_found;
+  | c ->
+      (* qtext *)
+      String.unsafe_set s j c;
+      get_quoted_string s i1 (succ i) (succ j)
+
+and skip_folding s i1 i j =
+  if i > i1 then raise Not_found;
+  (* We should check that ['\r'] is followed by ['\n' SPACE] but we
+     are permissive... *)
+  match String.unsafe_get s i with
+  | ' ' | '\t' | '\r' | '\n' -> skip_folding s i1 (succ i) j
+  | _ -> get_quoted_string s i1 i j
+
+
+let get_value s i0 i1 =
+  if String.unsafe_get s i0 = '"'
+  then
+    let i0 = succ i0 in
+    let i = get_quoted_string s i1 i0 i0 in
+    String.sub s i0 (i - i0)
+  else
+    String.sub s i0 ((get_token s i1 i0) - i0)
+
+let get_value_boundary s i0 i1 =
+  if String.unsafe_get s i0 = '"'
+  then
+    let i0 = succ i0 in
+      let i = ref(get_quoted_string s i1 i0 i0) in
+      (* Remove trailing spaces *)
+      while !i >= i0
+	&& (let c = String.unsafe_get s !i in c = ' ' || c = '\t')
+      do decr i done;
+      String.sub s i0 (!i - i0)
+  else
+    String.sub s i0 ((get_token_boundary s i1 i0) - i0)
+
+
+
+(* multipart/form-data parsing (defined by RFC 2388)
+ ***********************************************************************)
+
+(* MSIE: with SSL connections, it sometimes produces CONTENT_TYPE like
+   "multipart/form-data; boundary=..., multipart/form-data; boundary=..."
+   Thus we have disabled ',' in [get_token_boundary] above.
+ *)
+let get_boundary =
+  let bd = "boundary=" in
+  let lbd = String.length bd in
+  let bd = search_case_fold bd in
+  fun content_type ->
+    try
+      let endbd = bd content_type 0 (String.length content_type) + lbd in
+      get_value_boundary content_type endbd (String.length content_type)
+    with
+      Not_found -> ""
+
+(* Headers of the different parts *)
+
+let get_name =
+  let srch = search_case_fold "name=" in
+  fun s i0 i1 ->
+    try
+      let end_name = srch s i0 i1 + 5 (* length "name=" *) in
+      get_value s end_name i1
+    with
+      Not_found -> ""
+
+(* @raise Not_found if a filename cannot be found.
+
+   Note: Most browsers send filenames without properly escaping them
+   (e.g. "file\"\n") and, in these cases, the filename returned by
+   this function may be different from the one actually uploaded. *)
+let get_filename =
+  let srch = search_case_fold "filename=" in
+  fun s i0 i1 ->
+    let end_filename = srch s i0 i1 + 9 (* length "filename=" *) in
+    get_value s end_filename i1
+
+
+(* If there is no "filename", then the default Content-Type is
+   "text/plain" but we do not care because in this case the content
+   will be treated as a param and not a file.  *)
+let get_content_type =
+  let srch = search_case_fold "Content-Type:" in
+  fun s i0 i1 ->
+    try
+      let i = ref(srch s i0 i1 + 13 (* "Content-Type:" *) ) in
+      (* Skip space *)
+      while !i < i1
+	&& (let c = String.unsafe_get s !i in c = ' ' || c = '\t')
+      do incr i done;
+      let i0 = !i in
+      i := get_token s i1 !i;  (* First token *)
+      if !i < i1 && String.unsafe_get s !i = '/' then
+	i := get_token s i1 (!i + 1);  (* Second token *)
+      String.lowercase(String.sub s i0 (!i - i0))
+    with
+      Not_found -> "application/octet-stream"
+
+
+let get_content_transfer_encoding =
+  let srch = search_case_fold "Content-Transfer-Encoding:" in
+  fun s i0 i1 ->
+    try
+      let i = ref(srch s i0 i1 + 26 (* "Content-Transfer-Encoding:" *) ) in
+      (* Skip space *)
+      while !i < i1
+	&& (let c = String.unsafe_get s !i in c = ' ' || c = '\t')
+      do incr i done;
+      String.lowercase(get_value s !i i1)
+    with
+      Not_found -> ""
+
+
+let search_crlf = search "\r\n"
+let search_2crlf = search "\r\n\r\n"
+let search_2dash = search "--"
+
+(* The multipart is structured as:
+
+   --boundary
+   <header lines>
+
+   <body>
+   --boundary
+   ...
+   --boundary--
+
+   All the lines are terminated with "\r\n", so the "empty line" is
+   exactly "\r\n".  The [boundary] is given in the CONTENT-TYPE
+   meta-variable.
+
+   [parse_multipart boundary data request] parses [data] and fill the
+   appropriate [request] fields.
+   @raise Not_found if an expected pattern is not found. *)
+let parse_multipart boundary data request =
+  let len_data = String.length data in
+  let boundary, part1 =
+    if boundary = "" then
+      (* It seems older versions of Netscape are unreliable about
+	 providing a boundary string.  We try to determine it from
+	 [data] by looking for "--" followed by a token. *)
+      let i0 = search_2dash data 0 (String.length data) in
+      let i1 = ref i0 in
+      while !i1 < len_data
+	&& not(special_rfc2045(String.unsafe_get data !i1)) do incr i1 done;
+      let i2 = search_crlf data !i1 len_data in
+      (String.sub data i0 (!i1 - i0), i2)
+    else
+      let bd = "--" ^ boundary in
+      let i0 = search bd  data 0 len_data in
+      let i2 = search_crlf data (i0 + String.length bd) len_data in
+      (bd, i2)  in
+  let search_bd = search ("\r\n" ^ boundary) in
+  let len_bd = 2 (* \r\n *) + String.length boundary in
+
+  let begin_part = ref part1 in
+  try
+    while true do
+      (* Get head information (if any) *)
+      let end_head = search_2crlf data !begin_part len_data in
+      (* FIXME: Should we restrict the search for "name=" and
+	 "filename=" to the "Content-Disposition:" line and be more
+	 careful they do not apprear in values? *)
+      let name = get_name data !begin_part end_head in
+      let filename, is_file =
+	try (get_filename data !begin_part end_head, true)
+	with Not_found -> ("", false) in
+      let content_type = get_content_type data !begin_part end_head in
+      let encoding = get_content_transfer_encoding data !begin_part end_head in
+      (* Get data and add it to the request *)
+      let d0 = end_head + 4 (* \r\n\r\n *) in
+      let d1 = search_bd data d0 len_data in
+      let value = String.sub data d0 (d1 - d0) in
+      if is_file then
+	Hashtbl.add request.uploads name {
+	  upload_value = value;
+	  upload_filename = filename;
+	  upload_content_type = content_type;
+	  (* encoding *)
+	}
+      else
+	Hashtbl.add request.params name value;
+      (* Is it the closing boundary? *)
+      let end_bd = d1 + len_bd in
+      if end_bd + 2 <= len_data && String.unsafe_get data end_bd = '-'
+	  && String.unsafe_get data (end_bd + 1) = '-' then
+	    raise Exit;
+      (* Skip the boundary line and get the next part *)
+      begin_part := search_crlf data end_bd len_data
+    done
+  with
+    Exit -> ()
+
+
+
+exception Unsupported_media_type of string
+
+(* [is_prefix_ci p s] chekw whether [p] is a prefix of [s] in a case
+   insensitive way.  [p] is assumed to be lowercase. *)
+let is_prefix_ci p s =
+  let lp = String.length p in
+  (lp <= String.length s) && (p = String.lowercase(String.sub s 0 lp))
+
+
+(* FIXME: using Knutt-Moris-Pratt algo, one should be able to parse
+   post-data as it comes (from packets or stdin).  Requires a special
+   Buffer module with deletion.  *)
+(* FIXME: enforce max sizes *)
+let parse_post_data data request =
+  let content_type = metavar_string request "CONTENT_TYPE" in
+  (* media-type = type "/" subtype (";" parameter)* *)
+  if is_prefix_ci content_type "application/x-www-form-urlencoded" then
+    parse_query data request.params